/src/cryptsetup/lib/bitlk/bitlk.c
Line | Count | Source |
1 | | // SPDX-License-Identifier: LGPL-2.1-or-later |
2 | | /* |
3 | | * BITLK (BitLocker-compatible) volume handling |
4 | | * |
5 | | * Copyright (C) 2019-2025 Red Hat, Inc. All rights reserved. |
6 | | * Copyright (C) 2019-2025 Milan Broz |
7 | | * Copyright (C) 2019-2025 Vojtech Trefny |
8 | | */ |
9 | | |
10 | | #include <errno.h> |
11 | | #include <string.h> |
12 | | #include <uuid/uuid.h> |
13 | | #include <time.h> |
14 | | #include <limits.h> |
15 | | |
16 | | #include "bitlk.h" |
17 | | #include "internal.h" |
18 | | |
19 | 577 | #define BITLK_BOOTCODE_V1 "\xeb\x52\x90" |
20 | 576 | #define BITLK_BOOTCODE_V2 "\xeb\x58\x90" |
21 | 2.50k | #define BITLK_SIGNATURE "-FVE-FS-" |
22 | 871 | #define BITLK_SIGNATURE_TOGO "MSWIN4.1" |
23 | 561 | #define BITLK_HEADER_METADATA_OFFSET 160 |
24 | 16 | #define BITLK_HEADER_METADATA_OFFSET_TOGO 424 |
25 | | |
26 | | /* FVE metadata header is split into two parts */ |
27 | 0 | #define BITLK_FVE_METADATA_BLOCK_HEADER_LEN 64 |
28 | 0 | #define BITLK_FVE_METADATA_HEADER_LEN 48 |
29 | 0 | #define BITLK_FVE_METADATA_HEADERS_LEN BITLK_FVE_METADATA_BLOCK_HEADER_LEN + BITLK_FVE_METADATA_HEADER_LEN |
30 | | |
31 | | /* total size of the FVE area (64 KiB) */ |
32 | 676 | #define BITLK_FVE_METADATA_SIZE 64 * 1024 |
33 | | |
34 | 0 | #define BITLK_ENTRY_HEADER_LEN 8 |
35 | 0 | #define BITLK_VMK_HEADER_LEN 28 |
36 | | |
37 | 0 | #define BITLK_OPEN_KEY_METADATA_LEN 12 |
38 | | |
39 | 0 | #define BITLK_RECOVERY_KEY_LEN 55 |
40 | | #define BITLK_RECOVERY_PARTS 8 |
41 | 0 | #define BITLK_RECOVERY_PART_LEN 6 |
42 | | |
43 | 0 | #define BITLK_BEK_FILE_HEADER_LEN 48 |
44 | 0 | #define BITLK_STARTUP_KEY_HEADER_LEN 24 |
45 | | |
46 | 0 | #define BITLK_KDF_HASH "sha256" |
47 | 0 | #define BITLK_KDF_ITERATION_COUNT 0x100000 |
48 | | |
49 | | /* maximum number of segments for the DM device */ |
50 | | #define MAX_BITLK_SEGMENTS 10 |
51 | | |
52 | | /* January 1, 1970 as MS file time */ |
53 | 0 | #define EPOCH_AS_FILETIME 116444736000000000 |
54 | 0 | #define HUNDREDS_OF_NANOSECONDS 10000000 |
55 | | |
56 | | /* not available in older version of libuuid */ |
57 | | #ifndef UUID_STR_LEN |
58 | | #define UUID_STR_LEN 37 |
59 | | #endif |
60 | | |
61 | | /* known types of GUIDs from the BITLK superblock */ |
62 | | const uint8_t BITLK_GUID_NORMAL[16] = { 0x3b, 0xd6, 0x67, 0x49, 0x29, 0x2e, 0xd8, 0x4a, |
63 | | 0x83, 0x99, 0xf6, 0xa3, 0x39, 0xe3, 0xd0, 0x01 }; |
64 | | const uint8_t BITLK_GUID_EOW[16] = { 0x3b, 0x4d, 0xa8, 0x92, 0x80, 0xdd, 0x0e, 0x4d, |
65 | | 0x9e, 0x4e, 0xb1, 0xe3, 0x28, 0x4e, 0xae, 0xd8 }; |
66 | | |
67 | | /* taken from libfdisk gpt.c -- TODO: this is a good candidate for adding to libuuid */ |
68 | | struct bitlk_guid { |
69 | | uint32_t time_low; |
70 | | uint16_t time_mid; |
71 | | uint16_t time_hi_and_version; |
72 | | uint8_t clock_seq_hi; |
73 | | uint8_t clock_seq_low; |
74 | | uint8_t node[6]; |
75 | | } __attribute__ ((packed)); |
76 | | |
77 | 0 | static void swap_guid(struct bitlk_guid *guid) { |
78 | 0 | guid->time_low = swab32(guid->time_low); |
79 | 0 | guid->time_mid = swab16(guid->time_mid); |
80 | 0 | guid->time_hi_and_version = swab16(guid->time_hi_and_version); |
81 | 0 | } |
82 | | |
83 | 0 | static void guid_to_string(struct bitlk_guid *guid, char *out) { |
84 | 0 | swap_guid(guid); |
85 | 0 | uuid_unparse((unsigned char *) guid, out); |
86 | 0 | } |
87 | | |
88 | | typedef enum { |
89 | | BITLK_SEGTYPE_CRYPT, |
90 | | BITLK_SEGTYPE_ZERO, |
91 | | } BitlkSegmentType; |
92 | | |
93 | | struct segment { |
94 | | uint64_t offset; |
95 | | uint64_t length; |
96 | | uint64_t iv_offset; |
97 | | BitlkSegmentType type; |
98 | | }; |
99 | | |
100 | | struct bitlk_signature { |
101 | | uint8_t boot_code[3]; |
102 | | uint8_t signature[8]; |
103 | | uint16_t sector_size; |
104 | | } __attribute__ ((packed)); |
105 | | |
106 | | struct bitlk_superblock { |
107 | | struct bitlk_guid guid; |
108 | | uint64_t fve_offset[3]; |
109 | | } __attribute__ ((packed)); |
110 | | |
111 | | struct bitlk_fve_metadata { |
112 | | /* FVE metadata block header */ |
113 | | uint8_t signature[8]; |
114 | | /* size of this block (in 16-byte units) */ |
115 | | uint16_t fve_size; |
116 | | uint16_t fve_version; |
117 | | uint16_t curr_state; |
118 | | uint16_t next_state; |
119 | | uint64_t volume_size; |
120 | | uint32_t unknown2; |
121 | | uint32_t volume_header_size; |
122 | | uint64_t fve_offset[3]; |
123 | | uint64_t volume_header_offset; |
124 | | /* FVE metadata header */ |
125 | | uint32_t metadata_size; |
126 | | uint32_t metadata_version; |
127 | | uint32_t metadata_header_size; |
128 | | uint32_t metada_size_copy; |
129 | | struct bitlk_guid guid; |
130 | | uint32_t next_nonce; |
131 | | uint16_t encryption; |
132 | | uint16_t unknown3; |
133 | | uint64_t creation_time; |
134 | | } __attribute__ ((packed)); |
135 | | |
136 | | struct bitlk_validation_hash { |
137 | | uint16_t size; |
138 | | uint16_t role; |
139 | | uint16_t type; |
140 | | uint16_t flags; |
141 | | /* likely a hash type code, anything other than 0x2005 isn't supported */ |
142 | | uint16_t hash_type; |
143 | | uint16_t unknown1; |
144 | | /* SHA-256 */ |
145 | | uint8_t hash[32]; |
146 | | } __attribute__ ((packed)); |
147 | | |
148 | | struct bitlk_fve_metadata_validation { |
149 | | /* FVE metadata validation block header */ |
150 | | uint16_t validation_size; |
151 | | uint16_t validation_version; |
152 | | uint32_t fve_crc32; |
153 | | /* this is a single nested structure's header defined here for simplicity */ |
154 | | uint16_t nested_struct_size; |
155 | | uint16_t nested_struct_role; |
156 | | uint16_t nested_struct_type; |
157 | | uint16_t nested_struct_flags; |
158 | | /* datum containing a similar nested structure (encrypted using VMK) with hash (SHA256) */ |
159 | | uint8_t nested_struct_data[BITLK_VALIDATION_VMK_DATA_SIZE]; |
160 | | } __attribute__ ((packed)); |
161 | | |
162 | | struct bitlk_entry_header_block { |
163 | | uint64_t offset; |
164 | | uint64_t size; |
165 | | } __attribute__ ((packed)); |
166 | | |
167 | | struct bitlk_entry_vmk { |
168 | | struct bitlk_guid guid; |
169 | | uint8_t modified[8]; |
170 | | uint16_t _unknown; |
171 | | uint16_t protection; |
172 | | } __attribute__ ((packed)); |
173 | | |
174 | | struct bitlk_kdf_data { |
175 | | char last_sha256[32]; |
176 | | char initial_sha256[32]; |
177 | | char salt[16]; |
178 | | uint64_t count; |
179 | | }; |
180 | | |
181 | | struct bitlk_bek_header { |
182 | | uint32_t metadata_size; |
183 | | uint32_t metadata_version; |
184 | | uint32_t metadata_header_size; |
185 | | uint32_t metada_size_copy; |
186 | | struct bitlk_guid guid; |
187 | | uint32_t next_nonce; |
188 | | uint16_t encryption; |
189 | | uint16_t unknown; |
190 | | uint64_t creation_time; |
191 | | } __attribute__ ((packed)); |
192 | | |
193 | | static BITLKVMKProtection get_vmk_protection(uint16_t protection) |
194 | 0 | { |
195 | 0 | switch (protection) { |
196 | 0 | case 0x0000: |
197 | 0 | return BITLK_PROTECTION_CLEAR_KEY; |
198 | 0 | case 0x0100: |
199 | 0 | return BITLK_PROTECTION_TPM; |
200 | 0 | case 0x0200: |
201 | 0 | return BITLK_PROTECTION_STARTUP_KEY; |
202 | 0 | case 0x0500: |
203 | 0 | return BITLK_PROTECTION_TPM_PIN; |
204 | 0 | case 0x0800: |
205 | 0 | return BITLK_PROTECTION_RECOVERY_PASSPHRASE; |
206 | 0 | case 0x1000: |
207 | 0 | return BITLK_PROTECTION_SMART_CARD; |
208 | 0 | case 0x2000: |
209 | 0 | return BITLK_PROTECTION_PASSPHRASE; |
210 | 0 | default: |
211 | 0 | return BITLK_PROTECTION_UNKNOWN; |
212 | 0 | } |
213 | 0 | } |
214 | | |
215 | | static const char* get_vmk_protection_string(BITLKVMKProtection protection) |
216 | 0 | { |
217 | 0 | switch (protection) { |
218 | 0 | case BITLK_PROTECTION_CLEAR_KEY: |
219 | 0 | return "VMK protected with clear key"; |
220 | 0 | case BITLK_PROTECTION_TPM: |
221 | 0 | return "VMK protected with TPM"; |
222 | 0 | case BITLK_PROTECTION_STARTUP_KEY: |
223 | 0 | return "VMK protected with startup key"; |
224 | 0 | case BITLK_PROTECTION_TPM_PIN: |
225 | 0 | return "VMK protected with TPM and PIN"; |
226 | 0 | case BITLK_PROTECTION_PASSPHRASE: |
227 | 0 | return "VMK protected with passphrase"; |
228 | 0 | case BITLK_PROTECTION_RECOVERY_PASSPHRASE: |
229 | 0 | return "VMK protected with recovery passphrase"; |
230 | 0 | case BITLK_PROTECTION_SMART_CARD: |
231 | 0 | return "VMK protected with smart card"; |
232 | 0 | default: |
233 | 0 | return "VMK with unknown protection"; |
234 | 0 | } |
235 | 0 | } |
236 | | |
237 | | static const char* get_bitlk_type_string(BITLKEncryptionType type) |
238 | 472 | { |
239 | 472 | switch (type) |
240 | 472 | { |
241 | 2 | case BITLK_ENCRYPTION_TYPE_NORMAL: |
242 | 2 | return "normal"; |
243 | 3 | case BITLK_ENCRYPTION_TYPE_EOW: |
244 | 3 | return "encrypt-on-write"; |
245 | 467 | default: |
246 | 467 | return "unknown"; |
247 | 472 | } |
248 | 472 | } |
249 | | |
250 | | static uint64_t filetime_to_unixtime(uint64_t time) |
251 | 0 | { |
252 | 0 | return (time - EPOCH_AS_FILETIME) / HUNDREDS_OF_NANOSECONDS; |
253 | 0 | } |
254 | | |
255 | | static int parse_vmk_entry(struct crypt_device *cd, uint8_t *data, int start, int end, struct bitlk_vmk **vmk) |
256 | 0 | { |
257 | 0 | uint16_t key_entry_size = 0; |
258 | 0 | uint16_t key_entry_type = 0; |
259 | 0 | uint16_t key_entry_value = 0; |
260 | 0 | size_t key_size = 0; |
261 | 0 | char *string = NULL; |
262 | 0 | const char *key = NULL; |
263 | 0 | struct volume_key *vk = NULL; |
264 | 0 | bool supported = false; |
265 | 0 | int r = 0; |
266 | | |
267 | | /* only passphrase, recovery passphrase, startup key and clearkey vmks are supported (can be used to activate) */ |
268 | 0 | supported = (*vmk)->protection == BITLK_PROTECTION_PASSPHRASE || |
269 | 0 | (*vmk)->protection == BITLK_PROTECTION_RECOVERY_PASSPHRASE || |
270 | 0 | (*vmk)->protection == BITLK_PROTECTION_STARTUP_KEY || |
271 | 0 | (*vmk)->protection == BITLK_PROTECTION_CLEAR_KEY; |
272 | |
|
273 | 0 | while ((end - start) >= (ssize_t)(sizeof(key_entry_size) + sizeof(key_entry_type) + sizeof(key_entry_value))) { |
274 | | /* size of this entry */ |
275 | 0 | memcpy(&key_entry_size, data + start, sizeof(key_entry_size)); |
276 | 0 | key_entry_size = le16_to_cpu(key_entry_size); |
277 | 0 | if (key_entry_size == 0) |
278 | 0 | break; |
279 | | |
280 | 0 | if (key_entry_size > (end - start)) |
281 | 0 | return -EINVAL; |
282 | | |
283 | | /* type and value of this entry */ |
284 | 0 | memcpy(&key_entry_type, data + start + sizeof(key_entry_size), sizeof(key_entry_type)); |
285 | 0 | memcpy(&key_entry_value, |
286 | 0 | data + start + sizeof(key_entry_size) + sizeof(key_entry_type), |
287 | 0 | sizeof(key_entry_value)); |
288 | 0 | key_entry_type = le16_to_cpu(key_entry_type); |
289 | 0 | key_entry_value = le16_to_cpu(key_entry_value); |
290 | |
|
291 | 0 | if (key_entry_type != BITLK_ENTRY_TYPE_PROPERTY) { |
292 | 0 | if (supported) { |
293 | 0 | log_err(cd, _("Unexpected metadata entry type '%u' found when parsing supported Volume Master Key."), key_entry_type); |
294 | 0 | return -EINVAL; |
295 | 0 | } else { |
296 | 0 | log_dbg(cd, "Unexpected metadata entry type '%u' found when parsing unsupported VMK.", key_entry_type); |
297 | 0 | } |
298 | 0 | } |
299 | | |
300 | | /* stretch key with salt, skip 4 B (encryption method of the stretch key) */ |
301 | 0 | if (key_entry_value == BITLK_ENTRY_VALUE_STRETCH_KEY) { |
302 | 0 | if ((end - start) < (BITLK_ENTRY_HEADER_LEN + BITLK_SALT_SIZE + 4)) |
303 | 0 | return -EINVAL; |
304 | 0 | memcpy((*vmk)->salt, |
305 | 0 | data + start + BITLK_ENTRY_HEADER_LEN + 4, |
306 | 0 | BITLK_SALT_SIZE); |
307 | | /* AES-CCM encrypted key */ |
308 | 0 | } else if (key_entry_value == BITLK_ENTRY_VALUE_ENCRYPTED_KEY) { |
309 | 0 | if (key_entry_size < (BITLK_ENTRY_HEADER_LEN + BITLK_NONCE_SIZE + BITLK_VMK_MAC_TAG_SIZE)) |
310 | 0 | return -EINVAL; |
311 | | /* nonce */ |
312 | 0 | memcpy((*vmk)->nonce, |
313 | 0 | data + start + BITLK_ENTRY_HEADER_LEN, |
314 | 0 | BITLK_NONCE_SIZE); |
315 | | /* MAC tag */ |
316 | 0 | memcpy((*vmk)->mac_tag, |
317 | 0 | data + start + BITLK_ENTRY_HEADER_LEN + BITLK_NONCE_SIZE, |
318 | 0 | BITLK_VMK_MAC_TAG_SIZE); |
319 | | /* AES-CCM encrypted key */ |
320 | 0 | key_size = key_entry_size - (BITLK_ENTRY_HEADER_LEN + BITLK_NONCE_SIZE + BITLK_VMK_MAC_TAG_SIZE); |
321 | 0 | key = (const char *) data + start + BITLK_ENTRY_HEADER_LEN + BITLK_NONCE_SIZE + BITLK_VMK_MAC_TAG_SIZE; |
322 | 0 | vk = crypt_alloc_volume_key(key_size, key); |
323 | 0 | if (vk == NULL) |
324 | 0 | return -ENOMEM; |
325 | 0 | crypt_volume_key_add_next(&((*vmk)->vk), vk); |
326 | | /* clear key for a partially decrypted volume */ |
327 | 0 | } else if (key_entry_value == BITLK_ENTRY_VALUE_KEY) { |
328 | | /* For clearkey protection, we need to store this key */ |
329 | 0 | key_size = key_entry_size - (BITLK_ENTRY_HEADER_LEN + 4); |
330 | 0 | key = (const char *) data + start + BITLK_ENTRY_HEADER_LEN + 4; |
331 | 0 | vk = crypt_alloc_volume_key(key_size, key); |
332 | 0 | if (vk == NULL) |
333 | 0 | return -ENOMEM; |
334 | 0 | crypt_volume_key_add_next(&((*vmk)->vk), vk); |
335 | | /* unknown timestamps in recovery protected VMK */ |
336 | 0 | } else if (key_entry_value == BITLK_ENTRY_VALUE_RECOVERY_TIME) { |
337 | 0 | ; |
338 | | /* optional hint (?) string (masked email?), we can safely ignore it */ |
339 | 0 | } else if (key_entry_value == BITLK_ENTRY_VALUE_HINT) { |
340 | 0 | ; |
341 | 0 | } else if (key_entry_value == BITLK_ENTRY_VALUE_STRING) { |
342 | 0 | if (key_entry_size < BITLK_ENTRY_HEADER_LEN) |
343 | 0 | return -EINVAL; |
344 | 0 | string = malloc((key_entry_size - BITLK_ENTRY_HEADER_LEN) * 2 + 1); |
345 | 0 | if (!string) |
346 | 0 | return -ENOMEM; |
347 | 0 | r = crypt_utf16_to_utf8(&string, CONST_CAST(char16_t *)(data + start + BITLK_ENTRY_HEADER_LEN), |
348 | 0 | key_entry_size - BITLK_ENTRY_HEADER_LEN); |
349 | 0 | if (r < 0 || !string) { |
350 | 0 | free(string); |
351 | 0 | log_err(cd, _("Invalid string found when parsing Volume Master Key.")); |
352 | 0 | return -EINVAL; |
353 | 0 | } else if ((*vmk)->name != NULL) { |
354 | 0 | if (supported) { |
355 | 0 | log_err(cd, _("Unexpected string ('%s') found when parsing supported Volume Master Key."), string); |
356 | 0 | free(string); |
357 | 0 | return -EINVAL; |
358 | 0 | } |
359 | 0 | log_dbg(cd, "Unexpected string ('%s') found when parsing unsupported VMK.", string); |
360 | 0 | free(string); |
361 | 0 | string = NULL; |
362 | 0 | } else { |
363 | | /* Assume that strings in VMK are the name of the VMK */ |
364 | 0 | (*vmk)->name = string; |
365 | 0 | string = NULL; |
366 | 0 | } |
367 | | /* no idea what this is, lets hope it's not important */ |
368 | 0 | } else if (key_entry_value == BITLK_ENTRY_VALUE_USE_KEY && (*vmk)->protection == BITLK_PROTECTION_STARTUP_KEY) { |
369 | 0 | ; |
370 | | /* quietly ignore unsupported TPM key */ |
371 | 0 | } else if (key_entry_value == BITLK_ENTRY_VALUE_TPM_KEY && (*vmk)->protection == BITLK_PROTECTION_TPM) { |
372 | 0 | ; |
373 | 0 | } else { |
374 | 0 | if (supported) { |
375 | 0 | log_err(cd, _("Unexpected metadata entry value '%u' found when parsing supported Volume Master Key."), key_entry_value); |
376 | 0 | return -EINVAL; |
377 | 0 | } else { |
378 | 0 | log_dbg(cd, "Unexpected metadata entry value '%u' found when parsing unsupported VMK.", key_entry_value); |
379 | 0 | } |
380 | 0 | } |
381 | | |
382 | 0 | start += key_entry_size; |
383 | 0 | } |
384 | | |
385 | 0 | return 0; |
386 | 0 | } |
387 | | |
388 | | static bool check_fve_metadata(struct bitlk_fve_metadata *fve) |
389 | 1.06k | { |
390 | 1.06k | if (memcmp(fve->signature, BITLK_SIGNATURE, sizeof(fve->signature)) || le16_to_cpu(fve->fve_version) != 2 || |
391 | 204 | (fve->fve_size << 4) > BITLK_FVE_METADATA_SIZE) |
392 | 877 | return false; |
393 | | |
394 | 191 | return true; |
395 | 1.06k | } |
396 | | |
397 | | static bool check_fve_metadata_validation(struct bitlk_fve_metadata_validation *validation) |
398 | 191 | { |
399 | | /* only check if there is room for CRC-32, the actual size must be larger */ |
400 | 191 | if (le16_to_cpu(validation->validation_size) < 8 || le16_to_cpu(validation->validation_version > 2)) |
401 | 71 | return false; |
402 | | |
403 | 120 | return true; |
404 | 191 | } |
405 | | |
406 | | static bool parse_fve_metadata_validation(struct bitlk_metadata *params, struct bitlk_fve_metadata_validation *validation) |
407 | 0 | { |
408 | | /* extra checks for a nested structure (MAC) and BITLK FVE metadata */ |
409 | |
|
410 | 0 | if (le16_to_cpu(validation->validation_size) < sizeof(struct bitlk_fve_metadata_validation)) |
411 | 0 | return false; |
412 | | |
413 | 0 | if (le16_to_cpu(validation->nested_struct_size != BITLK_VALIDATION_VMK_HEADER_SIZE + BITLK_VALIDATION_VMK_DATA_SIZE) || |
414 | 0 | le16_to_cpu(validation->nested_struct_role) != 0 || |
415 | 0 | le16_to_cpu(validation->nested_struct_type) != 5) |
416 | 0 | return false; |
417 | | |
418 | | /* nonce */ |
419 | 0 | memcpy(params->validation->nonce, |
420 | 0 | validation->nested_struct_data, |
421 | 0 | BITLK_NONCE_SIZE); |
422 | | |
423 | | /* MAC tag */ |
424 | 0 | memcpy(params->validation->mac_tag, |
425 | 0 | validation->nested_struct_data + BITLK_NONCE_SIZE, |
426 | 0 | BITLK_VMK_MAC_TAG_SIZE); |
427 | | |
428 | | /* AES-CCM encrypted datum with SHA256 hash */ |
429 | 0 | memcpy(params->validation->enc_datum, |
430 | 0 | validation->nested_struct_data + BITLK_NONCE_SIZE + BITLK_VMK_MAC_TAG_SIZE, |
431 | 0 | BITLK_VALIDATION_VMK_DATA_SIZE - BITLK_NONCE_SIZE - BITLK_VMK_MAC_TAG_SIZE); |
432 | |
|
433 | 0 | return true; |
434 | 0 | } |
435 | | |
436 | | void BITLK_bitlk_fvek_free(struct bitlk_fvek *fvek) |
437 | 1.43k | { |
438 | 1.43k | if (!fvek) |
439 | 1.43k | return; |
440 | | |
441 | 0 | crypt_free_volume_key(fvek->vk); |
442 | 0 | free(fvek); |
443 | 0 | } |
444 | | |
445 | | void BITLK_bitlk_vmk_free(struct bitlk_vmk *vmk) |
446 | 1.43k | { |
447 | 1.43k | struct bitlk_vmk *vmk_next = NULL; |
448 | | |
449 | 1.43k | while (vmk) { |
450 | 0 | free(vmk->guid); |
451 | 0 | free(vmk->name); |
452 | 0 | crypt_free_volume_key(vmk->vk); |
453 | 0 | vmk_next = vmk->next; |
454 | 0 | free(vmk); |
455 | 0 | vmk = vmk_next; |
456 | 0 | } |
457 | 1.43k | } |
458 | | |
459 | | void BITLK_bitlk_metadata_free(struct bitlk_metadata *metadata) |
460 | 1.43k | { |
461 | 1.43k | if (!metadata) |
462 | 0 | return; |
463 | | |
464 | 1.43k | free(metadata->guid); |
465 | 1.43k | free(metadata->description); |
466 | 1.43k | free(metadata->validation); |
467 | 1.43k | BITLK_bitlk_vmk_free(metadata->vmks); |
468 | 1.43k | BITLK_bitlk_fvek_free(metadata->fvek); |
469 | 1.43k | } |
470 | | |
471 | | int BITLK_read_sb(struct crypt_device *cd, struct bitlk_metadata *params) |
472 | 1.43k | { |
473 | 1.43k | int devfd; |
474 | 1.43k | struct device *device = crypt_metadata_device(cd); |
475 | 1.43k | struct bitlk_signature sig = {}; |
476 | 1.43k | struct bitlk_superblock sb = {}; |
477 | 1.43k | struct bitlk_fve_metadata fve = {}; |
478 | 1.43k | struct bitlk_fve_metadata_validation validation = {}; |
479 | 1.43k | struct bitlk_entry_vmk entry_vmk = {}; |
480 | 1.43k | uint8_t *fve_entries = NULL; |
481 | 1.43k | uint8_t *fve_validated_block = NULL; |
482 | 1.43k | size_t fve_entries_size = 0; |
483 | 1.43k | uint32_t fve_metadata_size = 0; |
484 | 1.43k | uint32_t fve_size_real = 0; |
485 | 1.43k | int fve_offset = 0; |
486 | 1.43k | char guid_buf[UUID_STR_LEN] = {0}; |
487 | 1.43k | uint16_t entry_size = 0; |
488 | 1.43k | uint16_t entry_type = 0; |
489 | 1.43k | int i = 0; |
490 | 1.43k | int r = 0; |
491 | 1.43k | int valid_fve_metadata_idx = -1; |
492 | 1.43k | int start = 0; |
493 | 1.43k | size_t key_size = 0; |
494 | 1.43k | const char *key = NULL; |
495 | 1.43k | char *description = NULL; |
496 | 1.43k | struct crypt_hash *hash; |
497 | | |
498 | 1.43k | struct bitlk_vmk *vmk = NULL; |
499 | 1.43k | struct bitlk_vmk *vmk_p = params->vmks; |
500 | | |
501 | 1.43k | devfd = device_open(cd, crypt_data_device(cd), O_RDONLY); |
502 | 1.43k | if (devfd < 0) { |
503 | 0 | r = -EINVAL; |
504 | 0 | goto out; |
505 | 0 | } |
506 | | |
507 | | /* read and check the signature */ |
508 | 1.43k | if (read_lseek_blockwise(devfd, device_block_size(cd, device), |
509 | 1.43k | device_alignment(device), &sig, sizeof(sig), 0) != sizeof(sig)) { |
510 | 0 | log_dbg(cd, "Failed to read BITLK signature from %s.", device_path(device)); |
511 | 0 | r = -EIO; |
512 | 0 | goto out; |
513 | 0 | } |
514 | | |
515 | 1.43k | if (memcmp(sig.signature, BITLK_SIGNATURE, sizeof(sig.signature)) == 0) { |
516 | 561 | params->togo = false; |
517 | 561 | fve_offset = BITLK_HEADER_METADATA_OFFSET; |
518 | 871 | } else if (memcmp(sig.signature, BITLK_SIGNATURE_TOGO, sizeof(sig.signature)) == 0) { |
519 | 16 | params->togo = true; |
520 | 16 | fve_offset = BITLK_HEADER_METADATA_OFFSET_TOGO; |
521 | 855 | } else { |
522 | 855 | log_dbg(cd, "Invalid or unknown signature for BITLK device."); |
523 | 855 | r = -EINVAL; |
524 | 855 | goto out; |
525 | 855 | } |
526 | | |
527 | 577 | if (memcmp(sig.boot_code, BITLK_BOOTCODE_V1, sizeof(sig.boot_code)) == 0) { |
528 | 1 | log_err(cd, _("BITLK version 1 is currently not supported.")); |
529 | 1 | r = -ENOTSUP; |
530 | 1 | goto out; |
531 | 576 | } else if (memcmp(sig.boot_code, BITLK_BOOTCODE_V2, sizeof(sig.boot_code)) == 0) |
532 | 506 | ; |
533 | 70 | else { |
534 | 70 | log_err(cd, _("Invalid or unknown boot signature for BITLK device.")); |
535 | 70 | r = -EINVAL; |
536 | 70 | goto out; |
537 | 70 | } |
538 | | |
539 | 506 | params->sector_size = le16_to_cpu(sig.sector_size); |
540 | 506 | if (params->sector_size == 0) { |
541 | 60 | log_dbg(cd, "Got sector size 0, assuming 512."); |
542 | 60 | params->sector_size = SECTOR_SIZE; |
543 | 60 | } |
544 | | |
545 | 506 | if (!(params->sector_size == 512 || params->sector_size == 4096)) { |
546 | 34 | log_err(cd, _("Unsupported sector size %" PRIu16 "."), params->sector_size); |
547 | 34 | r = -EINVAL; |
548 | 34 | goto out; |
549 | 34 | } |
550 | | |
551 | | /* read GUID and FVE metadata offsets */ |
552 | 472 | if (read_lseek_blockwise(devfd, device_block_size(cd, device), |
553 | 472 | device_alignment(device), &sb, sizeof(sb), fve_offset) != sizeof(sb)) { |
554 | 0 | log_err(cd, _("Failed to read BITLK header from %s."), device_path(device)); |
555 | 0 | r = -EINVAL; |
556 | 0 | goto out; |
557 | 0 | } |
558 | | |
559 | | /* get encryption "type" based on the GUID from BITLK superblock */ |
560 | 472 | if (memcmp(&sb.guid, BITLK_GUID_NORMAL, 16) == 0) |
561 | 2 | params->type = BITLK_ENCRYPTION_TYPE_NORMAL; |
562 | 470 | else if (memcmp(&sb.guid, BITLK_GUID_EOW, 16) == 0) |
563 | 3 | params->type = BITLK_ENCRYPTION_TYPE_EOW; |
564 | 467 | else |
565 | 467 | params->type = BITLK_ENCRYPTION_TYPE_UNKNOWN; |
566 | 472 | log_dbg(cd, "BITLK type from GUID: %s.", get_bitlk_type_string(params->type)); |
567 | | |
568 | 1.88k | for (i = 0; i < 3; i++) |
569 | 1.41k | params->metadata_offset[i] = le64_to_cpu(sb.fve_offset[i]); |
570 | | |
571 | 472 | fve_validated_block = malloc(BITLK_FVE_METADATA_SIZE); |
572 | 472 | if (fve_validated_block == NULL) { |
573 | 0 | r = -ENOMEM; |
574 | 0 | goto out; |
575 | 0 | } |
576 | | |
577 | 1.80k | for (i = 0; i < 3; i++) { |
578 | | /* iterate over FVE metadata copies and pick the valid one */ |
579 | 1.36k | log_dbg(cd, "Reading BITLK FVE metadata copy #%d of size %zu on device %s, offset %" PRIu64 ".", |
580 | 1.36k | i, sizeof(fve), device_path(device), params->metadata_offset[i]); |
581 | | |
582 | 1.36k | if (read_lseek_blockwise(devfd, device_block_size(cd, device), |
583 | 1.36k | device_alignment(device), &fve, sizeof(fve), params->metadata_offset[i]) != sizeof(fve) || |
584 | 1.06k | !check_fve_metadata(&fve) || |
585 | 191 | (fve_size_real = le16_to_cpu(fve.fve_size) << 4, read_lseek_blockwise(devfd, device_block_size(cd, device), |
586 | 191 | device_alignment(device), &validation, sizeof(validation), params->metadata_offset[i] + fve_size_real) != sizeof(validation)) || |
587 | 191 | !check_fve_metadata_validation(&validation) || |
588 | | /* double-fetch is here, but we aren't validating MAC */ |
589 | 120 | read_lseek_blockwise(devfd, device_block_size(cd, device), device_alignment(device), fve_validated_block, fve_size_real, |
590 | 120 | params->metadata_offset[i]) != fve_size_real || |
591 | 1.33k | (crypt_crc32(~0, fve_validated_block, fve_size_real) ^ ~0) != le32_to_cpu(validation.fve_crc32)) { |
592 | | /* found an invalid FVE metadata copy, log and skip */ |
593 | 1.33k | log_dbg(cd, _("Failed to read or validate BITLK FVE metadata copy #%d from %s."), i, device_path(device)); |
594 | 1.33k | } else { |
595 | | /* found a valid FVE metadata copy, use it */ |
596 | 36 | valid_fve_metadata_idx = i; |
597 | 36 | break; |
598 | 36 | } |
599 | 1.36k | } |
600 | | |
601 | 472 | if (valid_fve_metadata_idx < 0) { |
602 | | /* all FVE metadata copies are invalid, fail */ |
603 | 436 | log_err(cd, _("Failed to read and validate BITLK FVE metadata from %s."), device_path(device)); |
604 | 436 | r = -EINVAL; |
605 | 436 | goto out; |
606 | 436 | } |
607 | | |
608 | | /* check that a valid FVE metadata block is in its expected location */ |
609 | 36 | if (params->metadata_offset[valid_fve_metadata_idx] != le64_to_cpu(fve.fve_offset[valid_fve_metadata_idx])) { |
610 | 36 | log_err(cd, _("Failed to validate the location of BITLK FVE metadata from %s."), device_path(device)); |
611 | 36 | r = -EINVAL; |
612 | 36 | goto out; |
613 | 36 | } |
614 | | |
615 | | /* update offsets from a valid FVE metadata copy */ |
616 | 0 | for (i = 0; i < 3; i++) |
617 | 0 | params->metadata_offset[i] = le64_to_cpu(fve.fve_offset[i]); |
618 | | |
619 | | /* check that the FVE metadata hasn't changed between reads, because we are preparing for the MAC check */ |
620 | 0 | if (memcmp(&fve, fve_validated_block, sizeof(fve)) != 0) { |
621 | 0 | log_err(cd, _("BITLK FVE metadata changed between reads from %s."), device_path(device)); |
622 | 0 | r = -EINVAL; |
623 | 0 | goto out; |
624 | 0 | } |
625 | | |
626 | 0 | crypt_backend_memzero(¶ms->sha256_fve, 32); |
627 | 0 | if (crypt_hash_init(&hash, "sha256")) { |
628 | 0 | log_err(cd, _("Failed to hash BITLK FVE metadata read from %s."), device_path(device)); |
629 | 0 | r = -EINVAL; |
630 | 0 | goto out; |
631 | 0 | } |
632 | 0 | crypt_hash_write(hash, (const char *)fve_validated_block, fve_size_real); |
633 | 0 | crypt_hash_final(hash, (char *)¶ms->sha256_fve, 32); |
634 | 0 | crypt_hash_destroy(hash); |
635 | | |
636 | | /* do some extended checks against FVE metadata, but not including MAC verification */ |
637 | 0 | params->validation = malloc(sizeof(struct bitlk_validation)); |
638 | 0 | if (!params->validation) { |
639 | 0 | r = -ENOMEM; |
640 | 0 | goto out; |
641 | 0 | } |
642 | | |
643 | 0 | if (!parse_fve_metadata_validation(params, &validation)) { |
644 | 0 | log_err(cd, _("Failed to parse BITLK FVE validation metadata from %s."), device_path(device)); |
645 | 0 | r = -EINVAL; |
646 | 0 | goto out; |
647 | 0 | } |
648 | | |
649 | | /* check encryption state for the device */ |
650 | 0 | params->state = true; |
651 | 0 | if (le16_to_cpu(fve.curr_state) != BITLK_STATE_NORMAL || le16_to_cpu(fve.next_state) != BITLK_STATE_NORMAL) { |
652 | 0 | params->state = false; |
653 | 0 | log_dbg(cd, "Unknown/unsupported state detected. Current state: %"PRIu16", next state: %"PRIu16".", |
654 | 0 | le16_to_cpu(fve.curr_state), le16_to_cpu(fve.next_state)); |
655 | 0 | } |
656 | |
|
657 | 0 | params->volume_size = le64_to_cpu(fve.volume_size); |
658 | 0 | params->metadata_version = le16_to_cpu(fve.fve_version); |
659 | |
|
660 | 0 | switch (le16_to_cpu(fve.encryption)) { |
661 | | /* AES-CBC with Elephant difuser */ |
662 | 0 | case 0x8000: |
663 | 0 | params->key_size = 256; |
664 | 0 | params->cipher = "aes"; |
665 | 0 | params->cipher_mode = "cbc-elephant"; |
666 | 0 | break; |
667 | 0 | case 0x8001: |
668 | 0 | params->key_size = 512; |
669 | 0 | params->cipher = "aes"; |
670 | 0 | params->cipher_mode = "cbc-elephant"; |
671 | 0 | break; |
672 | | /* AES-CBC */ |
673 | 0 | case 0x8002: |
674 | 0 | params->key_size = 128; |
675 | 0 | params->cipher = "aes"; |
676 | 0 | params->cipher_mode = "cbc-eboiv"; |
677 | 0 | break; |
678 | 0 | case 0x8003: |
679 | 0 | params->key_size = 256; |
680 | 0 | params->cipher = "aes"; |
681 | 0 | params->cipher_mode = "cbc-eboiv"; |
682 | 0 | break; |
683 | | /* AES-XTS */ |
684 | 0 | case 0x8004: |
685 | 0 | params->key_size = 256; |
686 | 0 | params->cipher = "aes"; |
687 | 0 | params->cipher_mode = "xts-plain64"; |
688 | 0 | break; |
689 | 0 | case 0x8005: |
690 | 0 | params->key_size = 512; |
691 | 0 | params->cipher = "aes"; |
692 | 0 | params->cipher_mode = "xts-plain64"; |
693 | 0 | break; |
694 | 0 | default: |
695 | 0 | log_err(cd, _("Unknown or unsupported encryption type.")); |
696 | 0 | params->key_size = 0; |
697 | 0 | params->cipher = NULL; |
698 | 0 | params->cipher_mode = NULL; |
699 | 0 | r = -ENOTSUP; |
700 | 0 | goto out; |
701 | 0 | }; |
702 | | |
703 | | /* device GUID */ |
704 | 0 | guid_to_string(&fve.guid, guid_buf); |
705 | 0 | params->guid = strdup(guid_buf); |
706 | 0 | if (!params->guid) { |
707 | 0 | r = -ENOMEM; |
708 | 0 | goto out; |
709 | 0 | } |
710 | | |
711 | 0 | params->creation_time = filetime_to_unixtime(le64_to_cpu(fve.creation_time)); |
712 | |
|
713 | 0 | fve_metadata_size = le32_to_cpu(fve.metadata_size); |
714 | 0 | if (fve_metadata_size < (BITLK_FVE_METADATA_HEADER_LEN + sizeof(entry_size) + sizeof(entry_type)) || |
715 | 0 | fve_metadata_size > BITLK_FVE_METADATA_SIZE) { |
716 | 0 | r = -EINVAL; |
717 | 0 | goto out; |
718 | 0 | } |
719 | 0 | fve_entries_size = fve_metadata_size - BITLK_FVE_METADATA_HEADER_LEN; |
720 | | |
721 | | /* read and parse all FVE metadata entries */ |
722 | 0 | fve_entries = malloc(fve_entries_size); |
723 | 0 | if (!fve_entries) { |
724 | 0 | r = -ENOMEM; |
725 | 0 | goto out; |
726 | 0 | } |
727 | 0 | memset(fve_entries, 0, fve_entries_size); |
728 | |
|
729 | 0 | log_dbg(cd, "Getting BITLK FVE metadata entries of size %zu on device %s, offset %" PRIu64 ".", |
730 | 0 | fve_entries_size, device_path(device), params->metadata_offset[valid_fve_metadata_idx] + BITLK_FVE_METADATA_HEADERS_LEN); |
731 | |
|
732 | 0 | if (BITLK_FVE_METADATA_HEADERS_LEN + fve_entries_size > fve_size_real) { |
733 | 0 | log_err(cd, _("Failed to check BITLK metadata entries previously read from %s."), device_path(device)); |
734 | 0 | r = -EINVAL; |
735 | 0 | goto out; |
736 | 0 | } |
737 | | |
738 | | /* fetch these entries from validated buffer to avoid double-fetch */ |
739 | 0 | memcpy(fve_entries, fve_validated_block + BITLK_FVE_METADATA_HEADERS_LEN, fve_entries_size); |
740 | |
|
741 | 0 | while ((fve_entries_size - start) >= (sizeof(entry_size) + sizeof(entry_type))) { |
742 | | |
743 | | /* size of this entry */ |
744 | 0 | memcpy(&entry_size, fve_entries + start, sizeof(entry_size)); |
745 | 0 | entry_size = le16_to_cpu(entry_size); |
746 | 0 | if (entry_size == 0) |
747 | 0 | break; |
748 | | |
749 | 0 | if (entry_size > (fve_entries_size - start)) { |
750 | 0 | r = -EINVAL; |
751 | 0 | goto out; |
752 | 0 | } |
753 | | |
754 | | /* type of this entry */ |
755 | 0 | memcpy(&entry_type, fve_entries + start + sizeof(entry_size), sizeof(entry_type)); |
756 | 0 | entry_type = le16_to_cpu(entry_type); |
757 | | |
758 | | /* VMK */ |
759 | 0 | if (entry_type == BITLK_ENTRY_TYPE_VMK) { |
760 | 0 | if (entry_size < (BITLK_ENTRY_HEADER_LEN + sizeof(entry_vmk))) { |
761 | 0 | r = -EINVAL; |
762 | 0 | goto out; |
763 | 0 | } |
764 | | /* skip first four variables in the entry (entry size, type, value and version) */ |
765 | 0 | memcpy(&entry_vmk, |
766 | 0 | fve_entries + start + BITLK_ENTRY_HEADER_LEN, |
767 | 0 | sizeof(entry_vmk)); |
768 | |
|
769 | 0 | vmk = malloc(sizeof(struct bitlk_vmk)); |
770 | 0 | if (!vmk) { |
771 | 0 | r = -ENOMEM; |
772 | 0 | goto out; |
773 | 0 | } |
774 | 0 | memset(vmk, 0, sizeof(struct bitlk_vmk)); |
775 | |
|
776 | 0 | guid_to_string(&entry_vmk.guid, guid_buf); |
777 | 0 | vmk->guid = strdup (guid_buf); |
778 | |
|
779 | 0 | vmk->name = NULL; |
780 | |
|
781 | 0 | vmk->protection = get_vmk_protection(le16_to_cpu(entry_vmk.protection)); |
782 | | |
783 | | /* more data in another entry list */ |
784 | 0 | r = parse_vmk_entry(cd, fve_entries, |
785 | 0 | start + BITLK_ENTRY_HEADER_LEN + BITLK_VMK_HEADER_LEN, |
786 | 0 | start + entry_size, &vmk); |
787 | 0 | if (r < 0) { |
788 | 0 | BITLK_bitlk_vmk_free(vmk); |
789 | 0 | goto out; |
790 | 0 | } |
791 | | |
792 | 0 | if (params->vmks == NULL) |
793 | 0 | params->vmks = vmk; |
794 | 0 | else |
795 | 0 | vmk_p->next = vmk; |
796 | |
|
797 | 0 | vmk_p = vmk; |
798 | 0 | vmk = vmk->next; |
799 | | /* FVEK */ |
800 | 0 | } else if (entry_type == BITLK_ENTRY_TYPE_FVEK && !params->fvek) { |
801 | 0 | if (entry_size < (BITLK_ENTRY_HEADER_LEN + BITLK_NONCE_SIZE + BITLK_VMK_MAC_TAG_SIZE)) { |
802 | 0 | r = -EINVAL; |
803 | 0 | goto out; |
804 | 0 | } |
805 | 0 | params->fvek = malloc(sizeof(struct bitlk_fvek)); |
806 | 0 | if (!params->fvek) { |
807 | 0 | r = -ENOMEM; |
808 | 0 | goto out; |
809 | 0 | } |
810 | 0 | memcpy(params->fvek->nonce, |
811 | 0 | fve_entries + start + BITLK_ENTRY_HEADER_LEN, |
812 | 0 | BITLK_NONCE_SIZE); |
813 | | /* MAC tag */ |
814 | 0 | memcpy(params->fvek->mac_tag, |
815 | 0 | fve_entries + start + BITLK_ENTRY_HEADER_LEN + BITLK_NONCE_SIZE, |
816 | 0 | BITLK_VMK_MAC_TAG_SIZE); |
817 | | /* AES-CCM encrypted key */ |
818 | 0 | key_size = entry_size - (BITLK_ENTRY_HEADER_LEN + BITLK_NONCE_SIZE + BITLK_VMK_MAC_TAG_SIZE); |
819 | 0 | key = (const char *) fve_entries + start + BITLK_ENTRY_HEADER_LEN + BITLK_NONCE_SIZE + BITLK_VMK_MAC_TAG_SIZE; |
820 | 0 | params->fvek->vk = crypt_alloc_volume_key(key_size, key); |
821 | 0 | if (params->fvek->vk == NULL) { |
822 | 0 | r = -ENOMEM; |
823 | 0 | goto out; |
824 | 0 | } |
825 | | /* volume header info (location and size) */ |
826 | 0 | } else if (entry_type == BITLK_ENTRY_TYPE_VOLUME_HEADER) { |
827 | 0 | struct bitlk_entry_header_block entry_header; |
828 | 0 | if ((fve_entries_size - start) < (BITLK_ENTRY_HEADER_LEN + sizeof(entry_header))) { |
829 | 0 | r = -EINVAL; |
830 | 0 | goto out; |
831 | 0 | } |
832 | 0 | memcpy(&entry_header, |
833 | 0 | fve_entries + start + BITLK_ENTRY_HEADER_LEN, |
834 | 0 | sizeof(entry_header)); |
835 | 0 | params->volume_header_offset = le64_to_cpu(entry_header.offset); |
836 | 0 | params->volume_header_size = le64_to_cpu(entry_header.size); |
837 | | /* volume description (utf-16 string) */ |
838 | 0 | } else if (entry_type == BITLK_ENTRY_TYPE_DESCRIPTION && !params->description) { |
839 | 0 | if (entry_size < BITLK_ENTRY_HEADER_LEN) { |
840 | 0 | r = -EINVAL; |
841 | 0 | goto out; |
842 | 0 | } |
843 | 0 | description = malloc((entry_size - BITLK_ENTRY_HEADER_LEN) * 2 + 1); |
844 | 0 | if (!description) { |
845 | 0 | r = -ENOMEM; |
846 | 0 | goto out; |
847 | 0 | } |
848 | 0 | r = crypt_utf16_to_utf8(&description, CONST_CAST(char16_t *)(fve_entries + start + BITLK_ENTRY_HEADER_LEN), |
849 | 0 | entry_size - BITLK_ENTRY_HEADER_LEN); |
850 | 0 | if (r < 0) { |
851 | 0 | free(description); |
852 | 0 | BITLK_bitlk_vmk_free(vmk); |
853 | 0 | log_err(cd, _("Failed to convert BITLK volume description")); |
854 | 0 | goto out; |
855 | 0 | } |
856 | 0 | params->description = description; |
857 | 0 | } |
858 | | |
859 | 0 | start += entry_size; |
860 | 0 | } |
861 | 1.43k | out: |
862 | 1.43k | free(fve_entries); |
863 | 1.43k | free(fve_validated_block); |
864 | | |
865 | 1.43k | return r; |
866 | 0 | } |
867 | | |
868 | | int BITLK_dump(struct crypt_device *cd, struct device *device, struct bitlk_metadata *params) |
869 | 0 | { |
870 | 0 | struct volume_key *vk_p; |
871 | 0 | struct bitlk_vmk *vmk_p; |
872 | 0 | char time[32]; |
873 | 0 | int next_id = 0; |
874 | 0 | int i = 0; |
875 | |
|
876 | 0 | log_std(cd, "Info for BITLK%s device %s.\n", params->togo ? " To Go" : "", device_path(device)); |
877 | 0 | log_std(cd, "Version: \t%u\n", params->metadata_version); |
878 | 0 | log_std(cd, "GUID: \t%s\n", params->guid); |
879 | 0 | log_std(cd, "Sector size: \t%u [bytes]\n", params->sector_size); |
880 | 0 | log_std(cd, "Volume size: \t%" PRIu64 " [bytes]\n", params->volume_size); |
881 | 0 | if (ctime_r((time_t *)¶ms->creation_time, time)) |
882 | 0 | log_std(cd, "Created: \t%s", time); |
883 | 0 | log_std(cd, "Description: \t%s\n", params->description); |
884 | 0 | log_std(cd, "Cipher name: \t%s\n", params->cipher); |
885 | 0 | log_std(cd, "Cipher mode: \t%s\n", params->cipher_mode); |
886 | 0 | log_std(cd, "Cipher key: \t%u [bits]\n", params->key_size); |
887 | |
|
888 | 0 | log_std(cd, "\n"); |
889 | |
|
890 | 0 | log_std(cd, "Keyslots:\n"); |
891 | 0 | vmk_p = params->vmks; |
892 | 0 | while (vmk_p) { |
893 | 0 | log_std(cd, " %d: VMK\n", next_id); |
894 | 0 | if (vmk_p->name != NULL) { |
895 | 0 | log_std(cd, "\tName: \t%s\n", vmk_p->name); |
896 | 0 | } |
897 | 0 | log_std(cd, "\tGUID: \t%s\n", vmk_p->guid); |
898 | 0 | log_std(cd, "\tProtection: \t%s\n", get_vmk_protection_string (vmk_p->protection)); |
899 | 0 | log_std(cd, "\tSalt: \t"); |
900 | 0 | crypt_log_hex(cd, (const char *) vmk_p->salt, 16, "", 0, NULL); |
901 | 0 | log_std(cd, "\n"); |
902 | |
|
903 | 0 | vk_p = vmk_p->vk; |
904 | 0 | while (vk_p) { |
905 | 0 | log_std(cd, "\tKey data size:\t%zu [bytes]\n", crypt_volume_key_length(vk_p)); |
906 | 0 | vk_p = crypt_volume_key_next(vk_p); |
907 | 0 | } |
908 | 0 | vmk_p = vmk_p->next; |
909 | 0 | next_id++; |
910 | 0 | } |
911 | |
|
912 | 0 | log_std(cd, " %d: FVEK\n", next_id); |
913 | 0 | log_std(cd, "\tKey data size:\t%zu [bytes]\n", crypt_volume_key_length(params->fvek->vk)); |
914 | |
|
915 | 0 | log_std(cd, "\n"); |
916 | |
|
917 | 0 | log_std(cd, "Metadata segments:\n"); |
918 | |
|
919 | 0 | for (i = 0; i < 3; i++) { |
920 | 0 | log_std(cd, " %d: FVE metadata area\n", i); |
921 | 0 | log_std(cd, "\tOffset: \t%" PRIu64 " [bytes]\n", params->metadata_offset[i]); |
922 | 0 | log_std(cd, "\tSize: \t%d [bytes]\n", BITLK_FVE_METADATA_SIZE); |
923 | 0 | } |
924 | |
|
925 | 0 | log_std(cd, " %d: Volume header\n", i); |
926 | 0 | log_std(cd, "\tOffset: \t%" PRIu64 " [bytes]\n", params->volume_header_offset); |
927 | 0 | log_std(cd, "\tSize: \t%" PRIu64 " [bytes]\n", params->volume_header_size); |
928 | 0 | log_std(cd, "\tCipher: \t%s-%s\n", params->cipher, params->cipher_mode); |
929 | |
|
930 | 0 | return 0; |
931 | 0 | } |
932 | | |
933 | | /* check if given passphrase can be a recovery key (has right format) and convert it */ |
934 | | static int get_recovery_key(struct crypt_device *cd, |
935 | | const char *password, |
936 | | size_t passwordLen, |
937 | | struct volume_key **rc_key) |
938 | 0 | { |
939 | 0 | unsigned int i, j = 0; |
940 | 0 | uint16_t parts[BITLK_RECOVERY_PARTS] = {0}; |
941 | 0 | char part_str[BITLK_RECOVERY_PART_LEN + 1] = {0}; |
942 | 0 | long part_num = 0; |
943 | | |
944 | | /* check the passphrase it should be: |
945 | | - 55 characters |
946 | | - 8 groups of 6 divided by '-' |
947 | | - each part is a number dividable by 11 |
948 | | */ |
949 | 0 | if (passwordLen != BITLK_RECOVERY_KEY_LEN) { |
950 | 0 | if (passwordLen == BITLK_RECOVERY_KEY_LEN + 1 && password[passwordLen - 1] == '\n') { |
951 | | /* looks like a recovery key with an extra newline, possibly from a key file */ |
952 | 0 | passwordLen--; |
953 | 0 | log_dbg(cd, "Possible extra EOL stripped from the recovery key."); |
954 | 0 | } else |
955 | 0 | return 0; |
956 | 0 | } |
957 | | |
958 | 0 | for (i = BITLK_RECOVERY_PART_LEN; i < passwordLen; i += BITLK_RECOVERY_PART_LEN + 1) { |
959 | 0 | if (password[i] != '-') |
960 | 0 | return 0; |
961 | 0 | } |
962 | | |
963 | 0 | for (i = 0, j = 0; i < passwordLen; i += BITLK_RECOVERY_PART_LEN + 1, j++) { |
964 | 0 | strncpy(part_str, password + i, BITLK_RECOVERY_PART_LEN); |
965 | |
|
966 | 0 | errno = 0; |
967 | 0 | part_num = strtol(part_str, NULL, 10); |
968 | 0 | if ((errno == ERANGE && (part_num == LONG_MAX || part_num == LONG_MIN)) || |
969 | 0 | (errno != 0 && part_num == 0)) |
970 | 0 | return -errno; |
971 | | |
972 | 0 | if (part_num % 11 != 0) |
973 | 0 | return 0; |
974 | 0 | parts[j] = cpu_to_le16(part_num / 11); |
975 | 0 | } |
976 | | |
977 | 0 | *rc_key = crypt_alloc_volume_key(16, (const char*) parts); |
978 | 0 | if (*rc_key == NULL) |
979 | 0 | return -ENOMEM; |
980 | | |
981 | 0 | return 0; |
982 | 0 | } |
983 | | |
984 | | static int parse_external_key_entry(struct crypt_device *cd, |
985 | | const char *data, |
986 | | int start, |
987 | | int end, |
988 | | struct volume_key **vk, |
989 | | const struct bitlk_metadata *params) |
990 | 0 | { |
991 | 0 | uint16_t key_entry_size = 0; |
992 | 0 | uint16_t key_entry_type = 0; |
993 | 0 | uint16_t key_entry_value = 0; |
994 | 0 | size_t key_size = 0; |
995 | 0 | const char *key = NULL; |
996 | 0 | struct bitlk_guid guid; |
997 | 0 | char guid_buf[UUID_STR_LEN] = {0}; |
998 | |
|
999 | 0 | while ((end - start) >= (ssize_t)(sizeof(key_entry_size) + sizeof(key_entry_type) + sizeof(key_entry_value))) { |
1000 | | /* size of this entry */ |
1001 | 0 | memcpy(&key_entry_size, data + start, sizeof(key_entry_size)); |
1002 | 0 | key_entry_size = le16_to_cpu(key_entry_size); |
1003 | 0 | if (key_entry_size == 0) |
1004 | 0 | break; |
1005 | | |
1006 | 0 | if (key_entry_size > (end - start)) |
1007 | 0 | return -EINVAL; |
1008 | | |
1009 | | /* type and value of this entry */ |
1010 | 0 | memcpy(&key_entry_type, data + start + sizeof(key_entry_size), sizeof(key_entry_type)); |
1011 | 0 | memcpy(&key_entry_value, |
1012 | 0 | data + start + sizeof(key_entry_size) + sizeof(key_entry_type), |
1013 | 0 | sizeof(key_entry_value)); |
1014 | 0 | key_entry_type = le16_to_cpu(key_entry_type); |
1015 | 0 | key_entry_value = le16_to_cpu(key_entry_value); |
1016 | |
|
1017 | 0 | if (key_entry_type != BITLK_ENTRY_TYPE_PROPERTY && key_entry_type != BITLK_ENTRY_TYPE_VOLUME_GUID) { |
1018 | 0 | log_err(cd, _("Unexpected metadata entry type '%u' found when parsing external key."), key_entry_type); |
1019 | 0 | return -EINVAL; |
1020 | 0 | } |
1021 | | |
1022 | 0 | if (key_entry_value == BITLK_ENTRY_VALUE_KEY) { |
1023 | 0 | if (key_entry_size < (BITLK_ENTRY_HEADER_LEN + 4)) |
1024 | 0 | return -EINVAL; |
1025 | 0 | key_size = key_entry_size - (BITLK_ENTRY_HEADER_LEN + 4); |
1026 | 0 | key = (const char *) data + start + BITLK_ENTRY_HEADER_LEN + 4; |
1027 | 0 | *vk = crypt_alloc_volume_key(key_size, key); |
1028 | 0 | if (*vk == NULL) |
1029 | 0 | return -ENOMEM; |
1030 | 0 | return 0; |
1031 | | /* optional "ExternalKey" string, we can safely ignore it */ |
1032 | 0 | } else if (key_entry_value == BITLK_ENTRY_VALUE_STRING) |
1033 | 0 | ; |
1034 | | /* GUID of the BitLocker device we are trying to open with this key */ |
1035 | 0 | else if (key_entry_value == BITLK_ENTRY_VALUE_GUID) { |
1036 | 0 | if ((end - start) < (ssize_t)(BITLK_ENTRY_HEADER_LEN + sizeof(struct bitlk_guid))) |
1037 | 0 | return -EINVAL; |
1038 | 0 | memcpy(&guid, data + start + BITLK_ENTRY_HEADER_LEN, sizeof(struct bitlk_guid)); |
1039 | 0 | guid_to_string(&guid, guid_buf); |
1040 | 0 | if (strcmp(guid_buf, params->guid) != 0) { |
1041 | 0 | log_err(cd, _("BEK file GUID '%s' does not match GUID of the volume."), guid_buf); |
1042 | 0 | return -EINVAL; |
1043 | 0 | } |
1044 | 0 | } else { |
1045 | 0 | log_err(cd, _("Unexpected metadata entry value '%u' found when parsing external key."), key_entry_value); |
1046 | 0 | return -EINVAL; |
1047 | 0 | } |
1048 | | |
1049 | 0 | start += key_entry_size; |
1050 | 0 | } |
1051 | | |
1052 | | /* if we got here we failed to parse the metadata */ |
1053 | 0 | return -EINVAL; |
1054 | 0 | } |
1055 | | |
1056 | | /* check if given passphrase can be a startup key (has right format) and convert it */ |
1057 | | static int get_startup_key(struct crypt_device *cd, |
1058 | | const char *password, |
1059 | | size_t passwordLen, |
1060 | | const struct bitlk_vmk *vmk, |
1061 | | struct volume_key **su_key, |
1062 | | const struct bitlk_metadata *params) |
1063 | 0 | { |
1064 | 0 | struct bitlk_bek_header bek_header = {0}; |
1065 | 0 | char guid_buf[UUID_STR_LEN] = {0}; |
1066 | |
|
1067 | 0 | uint16_t key_entry_size = 0; |
1068 | 0 | uint16_t key_entry_type = 0; |
1069 | 0 | uint16_t key_entry_value = 0; |
1070 | |
|
1071 | 0 | if (passwordLen < (BITLK_BEK_FILE_HEADER_LEN + sizeof(key_entry_size) + sizeof(key_entry_type) + sizeof(key_entry_value))) |
1072 | 0 | return -EPERM; |
1073 | | |
1074 | 0 | memcpy(&bek_header, password, BITLK_BEK_FILE_HEADER_LEN); |
1075 | | |
1076 | | /* metadata should contain GUID of the VMK this startup key is used for */ |
1077 | 0 | guid_to_string(&bek_header.guid, guid_buf); |
1078 | 0 | if (strcmp(guid_buf, vmk->guid) == 0) |
1079 | 0 | log_dbg(cd, "Found matching startup key for VMK %s", vmk->guid); |
1080 | 0 | else |
1081 | 0 | return -EPERM; |
1082 | | |
1083 | 0 | if (le32_to_cpu(bek_header.metadata_version) != 1) { |
1084 | 0 | log_err(cd, _("Unsupported BEK metadata version %" PRIu32), le32_to_cpu(bek_header.metadata_version)); |
1085 | 0 | return -ENOTSUP; |
1086 | 0 | } |
1087 | | |
1088 | 0 | if (le32_to_cpu(bek_header.metadata_size) != passwordLen) { |
1089 | 0 | log_err(cd, _("Unexpected BEK metadata size %" PRIu32 " does not match BEK file length"), |
1090 | 0 | le32_to_cpu(bek_header.metadata_size)); |
1091 | 0 | return -EINVAL; |
1092 | 0 | } |
1093 | | |
1094 | | /* we are expecting exactly one metadata entry starting immediately after the header */ |
1095 | 0 | memcpy(&key_entry_size, password + BITLK_BEK_FILE_HEADER_LEN, sizeof(key_entry_size)); |
1096 | 0 | key_entry_size = le16_to_cpu(key_entry_size); |
1097 | 0 | if (key_entry_size < BITLK_ENTRY_HEADER_LEN) { |
1098 | 0 | log_dbg(cd, "Unexpected metadata entry size %" PRIu16 " when parsing BEK file", key_entry_size); |
1099 | 0 | return -EINVAL; |
1100 | 0 | } |
1101 | | |
1102 | | /* type and value of this entry */ |
1103 | 0 | memcpy(&key_entry_type, password + BITLK_BEK_FILE_HEADER_LEN + sizeof(key_entry_size), sizeof(key_entry_type)); |
1104 | 0 | memcpy(&key_entry_value, |
1105 | 0 | password + BITLK_BEK_FILE_HEADER_LEN + sizeof(key_entry_size) + sizeof(key_entry_type), |
1106 | 0 | sizeof(key_entry_value)); |
1107 | 0 | key_entry_type = le16_to_cpu(key_entry_type); |
1108 | 0 | key_entry_value = le16_to_cpu(key_entry_value); |
1109 | |
|
1110 | 0 | if (key_entry_type == BITLK_ENTRY_TYPE_STARTUP_KEY && key_entry_value == BITLK_ENTRY_VALUE_EXTERNAL_KEY) { |
1111 | 0 | return parse_external_key_entry(cd, password, |
1112 | 0 | BITLK_BEK_FILE_HEADER_LEN + BITLK_ENTRY_HEADER_LEN + BITLK_STARTUP_KEY_HEADER_LEN, |
1113 | 0 | passwordLen, su_key, params); |
1114 | 0 | } else { |
1115 | 0 | log_err(cd, _("Unexpected metadata entry found when parsing startup key.")); |
1116 | 0 | log_dbg(cd, "Entry type: %u, entry value: %u", key_entry_type, key_entry_value); |
1117 | 0 | return -EINVAL; |
1118 | 0 | } |
1119 | 0 | } |
1120 | | |
1121 | | static int bitlk_kdf(const char *password, |
1122 | | size_t passwordLen, |
1123 | | bool recovery, |
1124 | | const uint8_t *salt, |
1125 | | struct volume_key **vk) |
1126 | 0 | { |
1127 | 0 | struct bitlk_kdf_data kdf = {}; |
1128 | 0 | struct crypt_hash *hd = NULL; |
1129 | 0 | int len = 0; |
1130 | 0 | char16_t *utf16Password = NULL; |
1131 | 0 | size_t utf16Len = 0; |
1132 | 0 | int i = 0; |
1133 | 0 | int r = 0; |
1134 | |
|
1135 | 0 | if (!password) |
1136 | 0 | return -EINVAL; |
1137 | | |
1138 | 0 | memcpy(kdf.salt, salt, 16); |
1139 | |
|
1140 | 0 | r = crypt_hash_init(&hd, BITLK_KDF_HASH); |
1141 | 0 | if (r < 0) |
1142 | 0 | return r; |
1143 | 0 | len = crypt_hash_size(BITLK_KDF_HASH); |
1144 | 0 | if (len < 0) { |
1145 | 0 | crypt_hash_destroy(hd); |
1146 | 0 | return len; |
1147 | 0 | } |
1148 | | |
1149 | 0 | if (!recovery) { |
1150 | | /* passphrase: convert to UTF-16 first, then sha256(sha256(pw)) */ |
1151 | 0 | utf16Password = crypt_safe_alloc(sizeof(char16_t) * (passwordLen + 1)); |
1152 | 0 | if (!utf16Password) { |
1153 | 0 | r = -ENOMEM; |
1154 | 0 | goto out; |
1155 | 0 | } |
1156 | 0 | r = crypt_utf8_to_utf16(&utf16Password, CONST_CAST(char*)password, passwordLen); |
1157 | 0 | if (r < 0) |
1158 | 0 | goto out; |
1159 | | |
1160 | 0 | utf16Len = crypt_char16_strlen(utf16Password); |
1161 | 0 | crypt_hash_write(hd, (char*)utf16Password, utf16Len * 2); |
1162 | 0 | r = crypt_hash_final(hd, kdf.initial_sha256, len); |
1163 | 0 | if (r < 0) |
1164 | 0 | goto out; |
1165 | | |
1166 | 0 | crypt_hash_write(hd, kdf.initial_sha256, len); |
1167 | 0 | r = crypt_hash_final(hd, kdf.initial_sha256, len); |
1168 | 0 | if (r < 0) |
1169 | 0 | goto out; |
1170 | 0 | } else { |
1171 | | /* recovery passphrase: already converted in #get_recovery_key, now just sha256(rpw) */ |
1172 | 0 | crypt_hash_write(hd, password, passwordLen); |
1173 | 0 | r = crypt_hash_final(hd, kdf.initial_sha256, len); |
1174 | 0 | if (r < 0) |
1175 | 0 | goto out; |
1176 | 0 | } |
1177 | | |
1178 | 0 | for (i = 0; i < BITLK_KDF_ITERATION_COUNT; i++) { |
1179 | 0 | crypt_hash_write(hd, (const char*) &kdf, sizeof(kdf)); |
1180 | 0 | r = crypt_hash_final(hd, kdf.last_sha256, len); |
1181 | 0 | if (r < 0) |
1182 | 0 | goto out; |
1183 | 0 | kdf.count = cpu_to_le64(le64_to_cpu(kdf.count) + 1); |
1184 | 0 | } |
1185 | | |
1186 | 0 | *vk = crypt_alloc_volume_key(len, kdf.last_sha256); |
1187 | |
|
1188 | 0 | out: |
1189 | 0 | crypt_safe_free(utf16Password); |
1190 | 0 | if (hd) |
1191 | 0 | crypt_hash_destroy(hd); |
1192 | 0 | return r; |
1193 | 0 | } |
1194 | | |
1195 | | static int decrypt_key(struct crypt_device *cd, |
1196 | | struct volume_key **vk, |
1197 | | struct volume_key *enc_key, |
1198 | | struct volume_key *key, |
1199 | | const uint8_t *tag, size_t tag_size, |
1200 | | const uint8_t *iv, size_t iv_size, |
1201 | | bool is_fvek) |
1202 | 0 | { |
1203 | 0 | char *outbuf; |
1204 | 0 | int r; |
1205 | 0 | uint16_t key_size = 0; |
1206 | |
|
1207 | 0 | outbuf = crypt_safe_alloc(crypt_volume_key_length(enc_key)); |
1208 | 0 | if (!outbuf) |
1209 | 0 | return -ENOMEM; |
1210 | | |
1211 | 0 | r = crypt_bitlk_decrypt_key(crypt_volume_key_get_key(key), |
1212 | 0 | crypt_volume_key_length(key), |
1213 | 0 | crypt_volume_key_get_key(enc_key), outbuf, |
1214 | 0 | crypt_volume_key_length(enc_key), |
1215 | 0 | (const char*)iv, iv_size, (const char*)tag, tag_size); |
1216 | 0 | if (r < 0) { |
1217 | 0 | if (r == -ENOTSUP) |
1218 | 0 | log_err(cd, _("This operation is not supported.")); |
1219 | 0 | goto out; |
1220 | 0 | } |
1221 | | |
1222 | | /* key_data has it's size as part of the metadata */ |
1223 | 0 | memcpy(&key_size, outbuf, 2); |
1224 | 0 | key_size = le16_to_cpu(key_size); |
1225 | 0 | if (crypt_volume_key_length(enc_key) != key_size) { |
1226 | 0 | log_err(cd, _("Unexpected key data size.")); |
1227 | 0 | log_dbg(cd, "Expected key data size: %zu, got %" PRIu16 "", |
1228 | 0 | crypt_volume_key_length(enc_key), key_size); |
1229 | |
|
1230 | 0 | r = -EINVAL; |
1231 | 0 | goto out; |
1232 | 0 | } |
1233 | | |
1234 | 0 | if (is_fvek && strcmp(crypt_get_cipher_mode(cd), "cbc-elephant") == 0 && |
1235 | 0 | crypt_get_volume_key_size(cd) == 32) { |
1236 | | /* 128bit AES-CBC with Elephant -- key size is 256 bit (2 keys) but key data is 512 bits, |
1237 | | data: 16B CBC key, 16B empty, 16B elephant key, 16B empty */ |
1238 | 0 | crypt_safe_memcpy(outbuf + 16 + BITLK_OPEN_KEY_METADATA_LEN, |
1239 | 0 | outbuf + 2 * 16 + BITLK_OPEN_KEY_METADATA_LEN, 16); |
1240 | 0 | key_size = 32 + BITLK_OPEN_KEY_METADATA_LEN; |
1241 | 0 | } |
1242 | | |
1243 | |
|
1244 | 0 | *vk = crypt_alloc_volume_key(key_size - BITLK_OPEN_KEY_METADATA_LEN, |
1245 | 0 | (const char *)(outbuf + BITLK_OPEN_KEY_METADATA_LEN)); |
1246 | 0 | r = *vk ? 0 : -ENOMEM; |
1247 | 0 | out: |
1248 | 0 | crypt_safe_free(outbuf); |
1249 | 0 | return r; |
1250 | 0 | } |
1251 | | |
1252 | | static int get_clear_key(struct crypt_device *cd, const struct bitlk_vmk *vmk, struct volume_key **vmk_dec_key) |
1253 | 0 | { |
1254 | 0 | struct volume_key *nested_key = vmk->vk; |
1255 | |
|
1256 | 0 | if (!nested_key) { |
1257 | 0 | log_dbg(cd, "Clearkey VMK structure incomplete - missing nested key"); |
1258 | 0 | return -ENOTSUP; |
1259 | 0 | } |
1260 | | |
1261 | 0 | struct volume_key *encrypted_vmk = crypt_volume_key_next(nested_key); |
1262 | |
|
1263 | 0 | if (!encrypted_vmk) { |
1264 | 0 | log_dbg(cd, "Clearkey VMK structure incomplete - missing encrypted VMK"); |
1265 | 0 | return -ENOTSUP; |
1266 | 0 | } |
1267 | | |
1268 | | /** |
1269 | | * For clearkey protection, we need to decrypt the encrypted VMK using the nested key |
1270 | | * and return the decrypted VMK as vmk_dec_key |
1271 | | */ |
1272 | 0 | struct volume_key *decrypted_vmk = NULL; |
1273 | 0 | int r = decrypt_key(cd, &decrypted_vmk, encrypted_vmk, nested_key, |
1274 | 0 | vmk->mac_tag, BITLK_VMK_MAC_TAG_SIZE, |
1275 | 0 | vmk->nonce, BITLK_NONCE_SIZE, false); |
1276 | |
|
1277 | 0 | if (r == 0 && decrypted_vmk) { |
1278 | 0 | log_dbg(cd, "Successfully decrypted VMK using nested key"); |
1279 | 0 | *vmk_dec_key = decrypted_vmk; |
1280 | 0 | return 0; |
1281 | 0 | } else { |
1282 | 0 | log_dbg(cd, "Failed to decrypt VMK using nested key (error: %d)", r); |
1283 | 0 | return r; |
1284 | 0 | } |
1285 | 0 | } |
1286 | | |
1287 | | int BITLK_get_volume_key(struct crypt_device *cd, |
1288 | | const char *password, |
1289 | | size_t passwordLen, |
1290 | | const struct bitlk_metadata *params, |
1291 | | struct volume_key **open_fvek_key) |
1292 | 0 | { |
1293 | 0 | int r = 0; |
1294 | 0 | struct volume_key *open_vmk_key = NULL; |
1295 | 0 | struct volume_key *vmk_dec_key = NULL; |
1296 | 0 | struct volume_key *recovery_key = NULL; |
1297 | 0 | struct bitlk_validation_hash dec_hash = {}; |
1298 | 0 | const struct bitlk_vmk *next_vmk = NULL; |
1299 | |
|
1300 | 0 | next_vmk = params->vmks; |
1301 | 0 | while (next_vmk) { |
1302 | 0 | bool is_decrypted = false; |
1303 | 0 | if (next_vmk->protection == BITLK_PROTECTION_PASSPHRASE) { |
1304 | 0 | r = bitlk_kdf(password, passwordLen, false, next_vmk->salt, &vmk_dec_key); |
1305 | 0 | if (r) { |
1306 | | /* something wrong happened, but we still want to check other key slots */ |
1307 | 0 | next_vmk = next_vmk->next; |
1308 | 0 | continue; |
1309 | 0 | } |
1310 | 0 | } else if (next_vmk->protection == BITLK_PROTECTION_RECOVERY_PASSPHRASE) { |
1311 | 0 | r = get_recovery_key(cd, password, passwordLen, &recovery_key); |
1312 | 0 | if (r) { |
1313 | | /* something wrong happened, but we still want to check other key slots */ |
1314 | 0 | next_vmk = next_vmk->next; |
1315 | 0 | continue; |
1316 | 0 | } |
1317 | 0 | if (recovery_key == NULL) { |
1318 | | /* r = 0 but no key -> given passphrase is not a recovery passphrase */ |
1319 | 0 | r = -EPERM; |
1320 | 0 | next_vmk = next_vmk->next; |
1321 | 0 | continue; |
1322 | 0 | } |
1323 | 0 | log_dbg(cd, "Trying to use given password as a recovery key."); |
1324 | 0 | r = bitlk_kdf(crypt_volume_key_get_key(recovery_key), |
1325 | 0 | crypt_volume_key_length(recovery_key), |
1326 | 0 | true, next_vmk->salt, &vmk_dec_key); |
1327 | 0 | crypt_free_volume_key(recovery_key); |
1328 | 0 | if (r) |
1329 | 0 | return r; |
1330 | 0 | } else if (next_vmk->protection == BITLK_PROTECTION_STARTUP_KEY) { |
1331 | 0 | r = get_startup_key(cd, password, passwordLen, next_vmk, &vmk_dec_key, params); |
1332 | 0 | if (r) { |
1333 | 0 | next_vmk = next_vmk->next; |
1334 | 0 | continue; |
1335 | 0 | } |
1336 | 0 | log_dbg(cd, "Trying to use external key found in provided password."); |
1337 | 0 | } else if (next_vmk->protection == BITLK_PROTECTION_CLEAR_KEY) { |
1338 | 0 | r = get_clear_key(cd, next_vmk, &vmk_dec_key); |
1339 | 0 | if (r) { |
1340 | | /* something wrong happened, but we still want to check other key slots */ |
1341 | 0 | next_vmk = next_vmk->next; |
1342 | 0 | continue; |
1343 | 0 | } |
1344 | 0 | is_decrypted = true; |
1345 | 0 | open_vmk_key = vmk_dec_key; |
1346 | 0 | log_dbg(cd, "Extracted VMK using clearkey."); |
1347 | 0 | } else { |
1348 | | /* only passphrase, recovery passphrase, startup key and clearkey VMKs supported right now */ |
1349 | 0 | log_dbg(cd, "Skipping %s", get_vmk_protection_string(next_vmk->protection)); |
1350 | 0 | next_vmk = next_vmk->next; |
1351 | 0 | if (r == 0) |
1352 | | /* we need to set error code in case we have only unsupported VMKs */ |
1353 | 0 | r = -ENOTSUP; |
1354 | 0 | continue; |
1355 | 0 | } |
1356 | | |
1357 | 0 | if (!is_decrypted) { |
1358 | 0 | r = decrypt_key(cd, &open_vmk_key, next_vmk->vk, vmk_dec_key, |
1359 | 0 | next_vmk->mac_tag, BITLK_VMK_MAC_TAG_SIZE, |
1360 | 0 | next_vmk->nonce, BITLK_NONCE_SIZE, false); |
1361 | |
|
1362 | 0 | crypt_free_volume_key(vmk_dec_key); |
1363 | 0 | } |
1364 | 0 | if (r < 0) { |
1365 | 0 | log_dbg(cd, "Failed to decrypt VMK using provided passphrase."); |
1366 | |
|
1367 | 0 | if (r == -ENOTSUP) |
1368 | 0 | return r; |
1369 | 0 | next_vmk = next_vmk->next; |
1370 | 0 | continue; |
1371 | 0 | } |
1372 | | |
1373 | 0 | log_dbg(cd, "Trying to decrypt validation metadata using VMK."); |
1374 | 0 | r = crypt_bitlk_decrypt_key(crypt_volume_key_get_key(open_vmk_key), |
1375 | 0 | crypt_volume_key_length(open_vmk_key), |
1376 | 0 | (const char*)params->validation->enc_datum, |
1377 | 0 | (char *)&dec_hash, |
1378 | 0 | BITLK_VALIDATION_VMK_DATA_SIZE - BITLK_NONCE_SIZE - BITLK_VMK_MAC_TAG_SIZE, |
1379 | 0 | (const char*)params->validation->nonce, BITLK_NONCE_SIZE, |
1380 | 0 | (const char*)params->validation->mac_tag, BITLK_VMK_MAC_TAG_SIZE); |
1381 | 0 | if (r < 0) { |
1382 | 0 | log_dbg(cd, "Failed to decrypt validation metadata using VMK."); |
1383 | 0 | crypt_free_volume_key(open_vmk_key); |
1384 | 0 | if (r == -ENOTSUP) |
1385 | 0 | return r; |
1386 | 0 | break; |
1387 | 0 | } |
1388 | | |
1389 | | /* now, do the MAC validation */ |
1390 | 0 | if (le16_to_cpu(dec_hash.role) != 0 ||le16_to_cpu(dec_hash.type) != 1 || |
1391 | 0 | (le16_to_cpu(dec_hash.hash_type) != 0x2005)) { |
1392 | 0 | log_dbg(cd, "Failed to parse decrypted validation metadata."); |
1393 | 0 | crypt_free_volume_key(open_vmk_key); |
1394 | 0 | return -ENOTSUP; |
1395 | 0 | } |
1396 | | |
1397 | 0 | if (memcmp(dec_hash.hash, params->sha256_fve, sizeof(dec_hash.hash)) != 0) { |
1398 | 0 | log_dbg(cd, "Failed MAC validation of BITLK FVE metadata."); |
1399 | 0 | crypt_free_volume_key(open_vmk_key); |
1400 | 0 | return -EINVAL; |
1401 | 0 | } |
1402 | | |
1403 | 0 | r = decrypt_key(cd, open_fvek_key, params->fvek->vk, open_vmk_key, |
1404 | 0 | params->fvek->mac_tag, BITLK_VMK_MAC_TAG_SIZE, |
1405 | 0 | params->fvek->nonce, BITLK_NONCE_SIZE, true); |
1406 | 0 | if (r < 0) { |
1407 | 0 | log_dbg(cd, "Failed to decrypt FVEK using VMK."); |
1408 | 0 | crypt_free_volume_key(open_vmk_key); |
1409 | 0 | if (r == -ENOTSUP) |
1410 | 0 | return r; |
1411 | 0 | } else { |
1412 | 0 | crypt_free_volume_key(open_vmk_key); |
1413 | 0 | break; |
1414 | 0 | } |
1415 | | |
1416 | 0 | next_vmk = next_vmk->next; |
1417 | 0 | } |
1418 | | |
1419 | 0 | if (r) { |
1420 | 0 | log_dbg(cd, "No more VMKs to try."); |
1421 | 0 | return r; |
1422 | 0 | } |
1423 | | |
1424 | 0 | return 0; |
1425 | 0 | } |
1426 | | |
1427 | | static int _activate_check(struct crypt_device *cd, |
1428 | | const struct bitlk_metadata *params) |
1429 | 0 | { |
1430 | 0 | if (!params->state) { |
1431 | 0 | log_err(cd, _("This BITLK device is in an unsupported state and cannot be activated.")); |
1432 | 0 | return -ENOTSUP; |
1433 | 0 | } |
1434 | | |
1435 | 0 | if (params->type != BITLK_ENCRYPTION_TYPE_NORMAL) { |
1436 | 0 | log_err(cd, _("BITLK devices with type '%s' cannot be activated."), get_bitlk_type_string(params->type)); |
1437 | 0 | return -ENOTSUP; |
1438 | 0 | } |
1439 | | |
1440 | 0 | return 0; |
1441 | 0 | } |
1442 | | |
1443 | | static int _activate(struct crypt_device *cd, |
1444 | | const char *name, |
1445 | | struct volume_key *open_fvek_key, |
1446 | | const struct bitlk_metadata *params, |
1447 | | uint32_t flags) |
1448 | 0 | { |
1449 | 0 | int r = 0; |
1450 | 0 | int i = 0; |
1451 | 0 | int j = 0; |
1452 | 0 | int min = 0; |
1453 | 0 | int num_segments = 0; |
1454 | 0 | struct crypt_dm_active_device dmd = { |
1455 | 0 | .flags = flags, |
1456 | 0 | }; |
1457 | 0 | struct dm_target *next_segment = NULL; |
1458 | 0 | struct segment segments[MAX_BITLK_SEGMENTS] = {}; |
1459 | 0 | struct segment temp; |
1460 | 0 | uint64_t next_start = 0; |
1461 | 0 | uint64_t next_end = 0; |
1462 | 0 | uint64_t last_segment = 0; |
1463 | 0 | uint64_t dmt_flags = 0; |
1464 | |
|
1465 | 0 | r = _activate_check(cd, params); |
1466 | 0 | if (r) |
1467 | 0 | return r; |
1468 | | |
1469 | 0 | r = device_block_adjust(cd, crypt_data_device(cd), DEV_EXCL, |
1470 | 0 | 0, &dmd.size, &dmd.flags); |
1471 | 0 | if (r) |
1472 | 0 | return r; |
1473 | | |
1474 | 0 | if (dmd.size * SECTOR_SIZE != params->volume_size) |
1475 | 0 | log_std(cd, _("WARNING: BitLocker volume size %" PRIu64 " does not match the underlying device size %" PRIu64 ""), |
1476 | 0 | params->volume_size, |
1477 | 0 | dmd.size * SECTOR_SIZE); |
1478 | | |
1479 | | /* there will be always 4 dm-zero segments: 3x metadata, 1x FS header */ |
1480 | 0 | for (i = 0; i < 3; i++) { |
1481 | 0 | segments[num_segments].offset = params->metadata_offset[i] / SECTOR_SIZE; |
1482 | 0 | segments[num_segments].length = BITLK_FVE_METADATA_SIZE / SECTOR_SIZE; |
1483 | 0 | segments[num_segments].iv_offset = 0; |
1484 | 0 | segments[num_segments].type = BITLK_SEGTYPE_ZERO; |
1485 | 0 | num_segments++; |
1486 | 0 | } |
1487 | 0 | segments[num_segments].offset = params->volume_header_offset / SECTOR_SIZE; |
1488 | 0 | segments[num_segments].length = params->volume_header_size / SECTOR_SIZE; |
1489 | 0 | segments[num_segments].iv_offset = 0; |
1490 | 0 | segments[num_segments].type = BITLK_SEGTYPE_ZERO; |
1491 | 0 | num_segments++; |
1492 | | |
1493 | | /* filesystem header (moved from the special location) */ |
1494 | 0 | segments[num_segments].offset = 0; |
1495 | 0 | segments[num_segments].length = params->volume_header_size / SECTOR_SIZE; |
1496 | 0 | segments[num_segments].iv_offset = params->volume_header_offset / SECTOR_SIZE; |
1497 | 0 | segments[num_segments].type = BITLK_SEGTYPE_CRYPT; |
1498 | 0 | num_segments++; |
1499 | | |
1500 | | /* now fill gaps between the dm-zero segments with dm-crypt */ |
1501 | 0 | last_segment = params->volume_header_size / SECTOR_SIZE; |
1502 | 0 | while (true) { |
1503 | 0 | next_start = dmd.size; |
1504 | 0 | next_end = dmd.size; |
1505 | | |
1506 | | /* start of the next segment: end of the first existing segment after the last added */ |
1507 | 0 | for (i = 0; i < num_segments; i++) |
1508 | 0 | if (segments[i].offset + segments[i].length < next_start && segments[i].offset + segments[i].length >= last_segment) |
1509 | 0 | next_start = segments[i].offset + segments[i].length; |
1510 | | |
1511 | | /* end of the next segment: start of the next segment after start we found above */ |
1512 | 0 | for (i = 0; i < num_segments; i++) |
1513 | 0 | if (segments[i].offset < next_end && segments[i].offset >= next_start) |
1514 | 0 | next_end = segments[i].offset; |
1515 | | |
1516 | | /* two zero segments next to each other, just bump the last_segment |
1517 | | so the algorithm moves */ |
1518 | 0 | if (next_end - next_start == 0) { |
1519 | 0 | last_segment = next_end + 1; |
1520 | 0 | continue; |
1521 | 0 | } |
1522 | | |
1523 | 0 | segments[num_segments].offset = next_start; |
1524 | 0 | segments[num_segments].length = next_end - next_start; |
1525 | 0 | segments[num_segments].iv_offset = next_start; |
1526 | 0 | segments[num_segments].type = BITLK_SEGTYPE_CRYPT; |
1527 | 0 | last_segment = next_end; |
1528 | 0 | num_segments++; |
1529 | |
|
1530 | 0 | if (next_end == dmd.size) |
1531 | 0 | break; |
1532 | | |
1533 | 0 | if (num_segments == 10) { |
1534 | 0 | log_dbg(cd, "Failed to calculate number of dm-crypt segments for open."); |
1535 | 0 | r = -EINVAL; |
1536 | 0 | goto out; |
1537 | 0 | } |
1538 | 0 | } |
1539 | | |
1540 | | /* device mapper needs the segment sorted */ |
1541 | 0 | for (i = 0; i < num_segments - 1; i++) { |
1542 | 0 | min = i; |
1543 | 0 | for (j = i + 1; j < num_segments; j++) |
1544 | 0 | if (segments[j].offset < segments[min].offset) |
1545 | 0 | min = j; |
1546 | |
|
1547 | 0 | if (min != i) { |
1548 | 0 | temp.offset = segments[min].offset; |
1549 | 0 | temp.length = segments[min].length; |
1550 | 0 | temp.iv_offset = segments[min].iv_offset; |
1551 | 0 | temp.type = segments[min].type; |
1552 | |
|
1553 | 0 | segments[min].offset = segments[i].offset; |
1554 | 0 | segments[min].length = segments[i].length; |
1555 | 0 | segments[min].iv_offset = segments[i].iv_offset; |
1556 | 0 | segments[min].type = segments[i].type; |
1557 | |
|
1558 | 0 | segments[i].offset = temp.offset; |
1559 | 0 | segments[i].length = temp.length; |
1560 | 0 | segments[i].iv_offset = temp.iv_offset; |
1561 | 0 | segments[i].type = temp.type; |
1562 | 0 | } |
1563 | 0 | } |
1564 | |
|
1565 | 0 | if (params->sector_size != SECTOR_SIZE) |
1566 | 0 | dmd.flags |= CRYPT_ACTIVATE_IV_LARGE_SECTORS; |
1567 | |
|
1568 | 0 | r = dm_targets_allocate(&dmd.segment, num_segments); |
1569 | 0 | if (r) |
1570 | 0 | goto out; |
1571 | 0 | next_segment = &dmd.segment; |
1572 | |
|
1573 | 0 | for (i = 0; i < num_segments; i++) { |
1574 | 0 | if (segments[i].type == BITLK_SEGTYPE_ZERO) |
1575 | 0 | r = dm_zero_target_set(next_segment, |
1576 | 0 | segments[i].offset, |
1577 | 0 | segments[i].length); |
1578 | 0 | else if (segments[i].type == BITLK_SEGTYPE_CRYPT) |
1579 | 0 | r = dm_crypt_target_set(next_segment, |
1580 | 0 | segments[i].offset, |
1581 | 0 | segments[i].length, |
1582 | 0 | crypt_data_device(cd), |
1583 | 0 | open_fvek_key, |
1584 | 0 | crypt_get_cipher_spec(cd), |
1585 | 0 | segments[i].iv_offset, |
1586 | 0 | segments[i].iv_offset, |
1587 | 0 | NULL, 0, 0, |
1588 | 0 | params->sector_size); |
1589 | 0 | if (r) |
1590 | 0 | goto out; |
1591 | | |
1592 | 0 | next_segment = next_segment->next; |
1593 | 0 | } |
1594 | | |
1595 | 0 | log_dbg(cd, "Trying to activate BITLK on device %s%s%s.", |
1596 | 0 | device_path(crypt_data_device(cd)), name ? " with name " :"", name ?: ""); |
1597 | |
|
1598 | 0 | r = dm_create_device(cd, name, CRYPT_BITLK, &dmd); |
1599 | 0 | if (r < 0) { |
1600 | 0 | dm_flags(cd, DM_CRYPT, &dmt_flags); |
1601 | 0 | if (!strcmp(params->cipher_mode, "cbc-eboiv") && !(dmt_flags & DM_BITLK_EBOIV_SUPPORTED)) { |
1602 | 0 | log_err(cd, _("Cannot activate device, kernel dm-crypt is missing support for BITLK IV.")); |
1603 | 0 | r = -ENOTSUP; |
1604 | 0 | } |
1605 | 0 | if (!strcmp(params->cipher_mode, "cbc-elephant") && !(dmt_flags & DM_BITLK_ELEPHANT_SUPPORTED)) { |
1606 | 0 | log_err(cd, _("Cannot activate device, kernel dm-crypt is missing support for BITLK Elephant diffuser.")); |
1607 | 0 | r = -ENOTSUP; |
1608 | 0 | } |
1609 | 0 | if ((dmd.flags & CRYPT_ACTIVATE_IV_LARGE_SECTORS) && !(dmt_flags & DM_SECTOR_SIZE_SUPPORTED)) { |
1610 | 0 | log_err(cd, _("Cannot activate device, kernel dm-crypt is missing support for large sector size.")); |
1611 | 0 | r = -ENOTSUP; |
1612 | 0 | } |
1613 | 0 | if (dm_flags(cd, DM_ZERO, &dmt_flags) < 0) { |
1614 | 0 | log_err(cd, _("Cannot activate device, kernel dm-zero module is missing.")); |
1615 | 0 | r = -ENOTSUP; |
1616 | 0 | } |
1617 | 0 | } |
1618 | 0 | out: |
1619 | 0 | dm_targets_free(cd, &dmd); |
1620 | 0 | return r; |
1621 | 0 | } |
1622 | | |
1623 | | int BITLK_activate_by_volume_key(struct crypt_device *cd, |
1624 | | const char *name, |
1625 | | struct volume_key *vk, |
1626 | | const struct bitlk_metadata *params, |
1627 | | uint32_t flags) |
1628 | 0 | { |
1629 | 0 | int r; |
1630 | |
|
1631 | 0 | r = _activate_check(cd, params); |
1632 | 0 | if (r) |
1633 | 0 | return r; |
1634 | | |
1635 | 0 | return _activate(cd, name, vk, params, flags); |
1636 | 0 | } |