diff --git a/lib/bitlk/bitlk.c b/lib/bitlk/bitlk.c index b6065738..a31314e7 100644 --- a/lib/bitlk/bitlk.c +++ b/lib/bitlk/bitlk.c @@ -3,6 +3,7 @@ * * Copyright (C) 2019 Red Hat, Inc. All rights reserved. * Copyright (C) 2019 Milan Broz + * Copyright (C) 2019 Vojtech Trefny * * This file is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -25,25 +26,583 @@ #include #include #include +#include +#include +#include #include "libcryptsetup.h" #include "bitlk.h" #include "internal.h" +#include "bitops.h" -int BITLK_read_sb(struct crypt_device *cd, struct crypt_params_bitlk *params) +#define BITLK_BOOTCODE_V1 "\xeb\x52\x90" +#define BITLK_BOOTCODE_V2 "\xeb\x58\x90" +#define BITLK_SIGNATURE "-FVE-FS-" +#define BITLK_SIGNATURE_TOGO "MSWIN4.1" +#define BITLK_HEADER_METADATA_OFFSET 160 +#define BITLK_HEADER_METADATA_OFFSET_TOGO 424 + +#define BITLK_FVE_METADATA_HEADER_LEN 64 + 48 +#define BITLK_FVE_METADATA_SIZE 64 * 1024 +#define BITLK_ENTRY_HEADER_LEN 8 +#define BITLK_VMK_HEADER_LEN 28 + +/* January 1, 1970 as MS file time */ +#define EPOCH_AS_FILETIME 116444736000000000 +#define HUNDREDS_OF_NANOSECONDS 10000000 + +/* taken from libfdisk gpt.c -- TODO: this is a good candidate for adding to libuuid */ +struct bitlk_guid { + uint32_t time_low; + uint16_t time_mid; + uint16_t time_hi_and_version; + uint8_t clock_seq_hi; + uint8_t clock_seq_low; + uint8_t node[6]; +} __attribute__ ((packed)); + +static void swap_guid(struct bitlk_guid *guid) { + guid->time_low = swab32(guid->time_low); + guid->time_mid = swab16(guid->time_mid); + guid->time_hi_and_version = swab16(guid->time_hi_and_version); +} + +static void guid_to_string(struct bitlk_guid *guid, char *out) { + swap_guid(guid); + uuid_unparse((unsigned char *) guid, out); +} + +struct bitlk_signature { + uint8_t boot_code[3]; + uint8_t signature[8]; +} __attribute__ ((packed)); + +struct bitlk_superblock { + struct bitlk_guid guid; + uint64_t fve_offset[3]; +} __attribute__ ((packed)); + +struct bitlk_fve_metadata { + uint8_t signature[8]; + uint16_t fve_size; + uint16_t fve_version; + uint32_t unknown; + uint64_t volume_size; + uint32_t unknown2; + uint32_t volume_header_size; + uint64_t fve_offset[3]; + uint64_t volume_header_offset; + uint32_t metadata_size; + uint32_t metadata_version; + uint32_t metadata_header_size; + uint32_t metada_size_copy; + struct bitlk_guid guid; + uint32_t next_nonce; + uint16_t encryption; + uint16_t unknown3; + uint64_t creation_time; +} __attribute__ ((packed)); + +struct bitlk_entry_header_block { + uint64_t offset; + uint64_t size; +} __attribute__ ((packed)); + +struct bitlk_entry_vmk { + struct bitlk_guid guid; + uint8_t modified[8]; + uint16_t _unknown; + uint16_t protection; +} __attribute__ ((packed)); + +static BITLKVMKProtection get_vmk_protection(uint16_t protection) { - int devfd; + switch (protection) { + case 0x0000: + return BITLK_PROTECTION_CLEAR_KEY; + case 0x0100: + return BITLK_PROTECTION_TPM; + case 0x0200: + return BITLK_PROTECTION_STARTUP_KEY; + case 0x0500: + return BITLK_PROTECTION_TPM_PIN; + case 0x0800: + return BITLK_PROTECTION_RECOVERY_PASSPHRASE; + case 0x2000: + return BITLK_PROTECTION_PASSPHRASE; + default: + return BITLK_PROTECTION_UNKNOWN; + } +} - devfd = device_open(cd, crypt_data_device(cd), O_RDONLY); - if(devfd < 0) - return -EINVAL; +static const char* get_vmk_protection_string(BITLKVMKProtection protection) +{ + switch (protection) { + case BITLK_PROTECTION_CLEAR_KEY: + return "VMK protected with clear key"; + case BITLK_PROTECTION_TPM: + return "VMK protected with TPM"; + case BITLK_PROTECTION_STARTUP_KEY: + return "VMK protected with startup key"; + case BITLK_PROTECTION_TPM_PIN: + return "VMK protected with TPM and PIN"; + case BITLK_PROTECTION_PASSPHRASE: + return "VMK protected with passphrase"; + case BITLK_PROTECTION_RECOVERY_PASSPHRASE: + return "VMK protected with recovery passphrase"; + default: + return "VMK with unknown protection"; + } +} + +/* TODO -- move to some utils file */ +static void hexprint(struct crypt_device *cd, const char *d, int n, const char *sep) +{ + int i; + for(i = 0; i < n; i++) + log_std(cd, "%02hhx%s", (const char)d[i], sep); +} + +static uint64_t filetime_to_unixtime(uint64_t time) +{ + return (time - EPOCH_AS_FILETIME) / HUNDREDS_OF_NANOSECONDS; +} + +static int convert_to_utf8(struct crypt_device *cd, uint8_t *input, size_t inlen, char **out) +{ + char *outbuf = NULL; + iconv_t ic; + size_t ic_inlen = inlen; + size_t ic_outlen = inlen; + char *ic_outbuf = NULL; + size_t r = 0; + + outbuf = malloc(inlen); + if (outbuf == NULL) + return -ENOMEM; + + memset(outbuf, 0, inlen); + ic_outbuf = outbuf; + + ic = iconv_open("UTF-8", "UTF-16"); + r = iconv(ic, (char **) &input, &ic_inlen, &ic_outbuf, &ic_outlen); + iconv_close(ic); + + if (r == 0) + *out = strdup(outbuf); + else { + *out = NULL; + log_dbg(cd, "Failed to covert volume description: %s", strerror(errno)); + r = 0; + } + + free(outbuf); + return r; +} + +static int parse_vmk_entry(struct crypt_device *cd, uint8_t *data, int start, int end, struct bitlk_vmk **vmk) +{ + uint16_t key_entry_size = 0; + uint16_t key_entry_type = 0; + uint16_t key_entry_value = 0; + size_t key_size = 0; + const char *key = NULL; + struct volume_key *vk = NULL; + + while (end - start > 2) { + /* size of this entry */ + memcpy(&key_entry_size, data + start, sizeof(key_entry_size)); + if (key_entry_size == 0) + break; + + /* type and value of this entry */ + memcpy(&key_entry_type, data + start + sizeof(key_entry_size), sizeof(key_entry_type)); + memcpy(&key_entry_value, + data + start + sizeof(key_entry_size) + sizeof(key_entry_type), + sizeof(key_entry_value)); + + if (key_entry_type != BITLK_ENTRY_TYPE_PROPERTY) { + log_err(cd, _("Unexpected metadata entry found when parsing VMK.")); + return -EINVAL; + } + + /* stretch key with salt, skip 4 B (encryption method of the stretch key) */ + if (key_entry_value == BITLK_ENTRY_VALUE_STRETCH_KEY) + memcpy((*vmk)->salt, + data + start + BITLK_ENTRY_HEADER_LEN + 4, + sizeof((*vmk)->salt)); + /* AES-CCM encrypted key */ + else if (key_entry_value == BITLK_ENTRY_VALUE_ENCRYPTED_KEY) { + /* nonce */ + memcpy((*vmk)->nonce, + data + start + BITLK_ENTRY_HEADER_LEN, + sizeof((*vmk)->nonce)); + /* MAC tag */ + memcpy((*vmk)->mac_tag, + data + start + BITLK_ENTRY_HEADER_LEN + BITLK_NONCE_SIZE, + sizeof((*vmk)->mac_tag)); + /* AES-CCM encrypted key */ + key_size = key_entry_size - (BITLK_ENTRY_HEADER_LEN + BITLK_NONCE_SIZE + BITLK_VMK_MAC_TAG_SIZE); + key = (const char *) data + start + BITLK_ENTRY_HEADER_LEN + BITLK_NONCE_SIZE + BITLK_VMK_MAC_TAG_SIZE; + vk = crypt_alloc_volume_key(key_size, key); + if (vk == NULL) + return -ENOMEM; + crypt_volume_key_add_next(&((*vmk)->vk), vk); + /* clear key for a partially decrypted volume */ + } else if (key_entry_value == BITLK_ENTRY_VALUE_KEY) { + /* We currently don't want to support opening a partially decrypted + * device so we don't need to store this key. + * + * key_size = key_entry_size - (BITLK_ENTRY_HEADER_LEN + 4); + * key = (const char *) data + start + BITLK_ENTRY_HEADER_LEN + 4; + * vk = crypt_alloc_volume_key(key_size, key); + * if (vk == NULL) + * return -ENOMEM; + * crypt_volume_key_add_next(&((*vmk)->vk), vk); + */ + log_dbg(cd, "Skipping clear key metadata entry."); + } else { + log_err(cd, _("Unexpected metadata entry found when parsing VMK.")); + return -EINVAL; + } + + start += key_entry_size; + } return 0; } -int BITLK_dump(struct crypt_device *cd, struct device *device) +void BITLK_bitlk_fvek_free(struct bitlk_fvek *fvek) { - log_std(cd, "Info for BITLK device %s.\n", device_path(device)); + if (!fvek) + return; + + crypt_free_volume_key(fvek->vk); +} + +void BITLK_bitlk_vmk_free(struct bitlk_vmk *vmk) +{ + struct bitlk_vmk *vmk_next = NULL; + + while (vmk) { + if (vmk->guid) + free(vmk->guid); + crypt_free_volume_key(vmk->vk); + vmk_next = vmk->next; + free(vmk); + vmk = vmk_next; + } +} + +void BITLK_bitlk_metadata_free(struct bitlk_metadata *metadata) +{ + free(metadata->guid); + if (metadata->description) + free(metadata->description); + BITLK_bitlk_vmk_free(metadata->vmks); + BITLK_bitlk_fvek_free(metadata->fvek); +} + +int BITLK_read_sb(struct crypt_device *cd, struct bitlk_metadata *params) +{ + int devfd; + struct device *device = crypt_metadata_device(cd); + struct bitlk_signature sig = {0}; + struct bitlk_superblock sb = {0}; + struct bitlk_fve_metadata fve = {0}; + struct bitlk_entry_vmk entry_vmk = {0}; + uint8_t *fve_entries = NULL; + int fve_offset = 0; + char guid_buf[UUID_STR_LEN] = {0}; + uint16_t entry_size = 0; + uint16_t entry_type = 0; + int r = 0; + int start = 0; + int end = 0; + size_t key_size = 0; + const char *key = NULL; + + struct bitlk_vmk *vmk = NULL; + struct bitlk_vmk *vmk_p = params->vmks; + + devfd = device_open(cd, crypt_data_device(cd), O_RDONLY); + if (devfd < 0) { + r = -EINVAL; + goto out; + } + + /* read and check the BitLocker signature */ + if (read_lseek_blockwise(devfd, device_block_size(cd, device), + device_alignment(device), &sig, sizeof(sig), 0) != sizeof(sig)) { + log_err(cd, _("Failed to read bitlocker signature from %s."), device_path(device)); + r = -EINVAL; + goto out; + } + + if (memcmp(sig.boot_code, BITLK_BOOTCODE_V1, sizeof(sig.boot_code)) == 0) { + log_err(cd, _("BitLocker version from Windows Vista is currently not supported")); + r = -ENOTSUP; + goto out; + } else if (memcmp(sig.boot_code, BITLK_BOOTCODE_V2, sizeof(sig.boot_code)) == 0) + ; + else { + log_std(cd, _("Invalid or unknown boot signature for a BitLocker device.")); + r = -EINVAL; + goto out; + } + + if (memcmp(sig.signature, BITLK_SIGNATURE, sizeof(sig.signature)) == 0) { + params->togo = false; + fve_offset = BITLK_HEADER_METADATA_OFFSET; + } else if (memcmp(sig.signature, BITLK_SIGNATURE_TOGO, sizeof(sig.signature)) == 0) { + params->togo = true; + fve_offset = BITLK_HEADER_METADATA_OFFSET_TOGO; + } else { + log_std(cd, _("Invalid or unknown signature for a BitLocker device.")); + r = -EINVAL; + goto out; + } + + /* read GUID and FVE metadata offsets */ + if (read_lseek_blockwise(devfd, device_block_size(cd, device), + device_alignment(device), &sb, sizeof(sb), fve_offset) != sizeof(sb)) { + log_err(cd, _("Failed to read BitLocker header from %s."), device_path(device)); + r = -EINVAL; + goto out; + } + + log_dbg(cd, "Reading BitLocker FVE metadata of size %zu on device %s, offset %" PRIu64 ".", + sizeof(fve), device_path(device), sb.fve_offset[0]); + + /* read FVE metadata from the first metadata area */ + if (read_lseek_blockwise(devfd, device_block_size(cd, device), + device_alignment(device), &fve, sizeof(fve), sb.fve_offset[0]) != sizeof(fve) || + memcmp(fve.signature, BITLK_SIGNATURE, sizeof(fve.signature)) || + fve.fve_version != 2) { + log_err(cd, _("Failed to read BitLocker FVE metadata from %s."), device_path(device)); + r = -EINVAL; + goto out; + } + + params->metadata_version = le32_to_cpu(fve.fve_version); + for (int i = 0; i < 3; i++) + params->metadata_offset[i] = le64_to_cpu(sb.fve_offset[i]); + + switch (fve.encryption) { + /* AES-CBC with Elephant difuser */ + case 0x8000: + params->key_size = 128; + params->cipher = "aes"; + params->cipher_mode = "cbc-elephant"; + break; + case 0x8001: + params->key_size = 256; + params->cipher = "aes"; + params->cipher_mode = "cbc-elephant"; + break; + /* AES-CBC */ + case 0x8002: + params->key_size = 128; + params->cipher = "aes"; + params->cipher_mode = "cbc-eboiv"; + break; + case 0x8003: + params->key_size = 256; + params->cipher = "aes"; + params->cipher_mode = "cbc-eboiv"; + break; + /* AES-XTS */ + case 0x8004: + params->key_size = 128; + params->cipher = "aes"; + params->cipher_mode = "xts-plain64"; + break; + case 0x8005: + params->key_size = 256; + params->cipher = "aes"; + params->cipher_mode = "xts-plain64"; + break; + default: + log_err (cd, _("Unknown or unsupported encryption")); + params->key_size = 0; + params->cipher = NULL; + params->cipher_mode = NULL; + r = -ENOTSUP; + goto out; + }; + + /* BitLocker device GUID */ + guid_to_string(&fve.guid, guid_buf); + params->guid = strdup(guid_buf); + if (!params->guid) { + r = -ENOMEM; + goto out; + } + + params->creation_time = filetime_to_unixtime(le64_to_cpu(fve.creation_time)); + + /* read and parse all FVE metadata entries */ + fve_entries = malloc(fve.metadata_size - fve.fve_size); + if (!fve_entries) { + r = -ENOMEM; + goto out; + } + memset(fve_entries, 0, (fve.metadata_size - fve.fve_size)); + + log_dbg(cd, "Reading BitLocker FVE metadata entries of size %" PRIu32 " on device %s, offset %" PRIu64 ".", + fve.metadata_size - fve.fve_size, device_path(device), + sb.fve_offset[0] + BITLK_FVE_METADATA_HEADER_LEN); + + if (read_lseek_blockwise(devfd, device_block_size(cd, device), + device_alignment(device), fve_entries, fve.metadata_size - fve.fve_size, + sb.fve_offset[0] + BITLK_FVE_METADATA_HEADER_LEN) != fve.metadata_size - fve.fve_size) { + log_err(cd, _("Failed to read BitLocker metadata entries from %s."), device_path(device)); + r = -EINVAL; + goto out; + } + + end = fve.metadata_size - fve.fve_size; + while (end - start > 2) { + /* size of this entry */ + memcpy(&entry_size, fve_entries + start, sizeof(entry_size)); + if (entry_size == 0) + break; + + /* type of this entry */ + memcpy(&entry_type, fve_entries + start + sizeof(entry_size), sizeof(entry_type)); + + /* VMK */ + if (entry_type == BITLK_ENTRY_TYPE_VMK) { + /* skip first four variables in the entry (entry size, type, value and version) */ + memcpy(&entry_vmk, + fve_entries + start + BITLK_ENTRY_HEADER_LEN, + sizeof(entry_vmk)); + + vmk = malloc(sizeof(struct bitlk_vmk)); + memset(vmk, 0, sizeof(struct bitlk_vmk)); + + guid_to_string(&entry_vmk.guid, guid_buf); + vmk->guid = strdup (guid_buf); + + vmk->protection = get_vmk_protection(entry_vmk.protection); + + /* more data in another entry list */ + r = parse_vmk_entry(cd, fve_entries, + start + BITLK_ENTRY_HEADER_LEN + BITLK_VMK_HEADER_LEN, + start + entry_size, &vmk); + if (r < 0) { + BITLK_bitlk_vmk_free(vmk); + goto out; + } + + if (params->vmks == NULL) + params->vmks = vmk; + else + vmk_p->next = vmk; + + vmk_p = vmk; + vmk = vmk->next; + /* FVEK */ + } else if (entry_type == BITLK_ENTRY_TYPE_FVEK) { + params->fvek = malloc(sizeof(struct bitlk_fvek)); + memcpy(params->fvek->nonce, + fve_entries + start + BITLK_ENTRY_HEADER_LEN, + sizeof(params->fvek->nonce)); + /* MAC tag */ + memcpy(params->fvek->mac_tag, + fve_entries + start + BITLK_ENTRY_HEADER_LEN + BITLK_NONCE_SIZE, + sizeof(params->fvek->mac_tag)); + /* AES-CCM encrypted key */ + key_size = entry_size - (BITLK_ENTRY_HEADER_LEN + BITLK_NONCE_SIZE + BITLK_VMK_MAC_TAG_SIZE); + key = (const char *) fve_entries + start + BITLK_ENTRY_HEADER_LEN + BITLK_NONCE_SIZE + BITLK_VMK_MAC_TAG_SIZE; + params->fvek->vk = crypt_alloc_volume_key(key_size, key); + if (params->fvek->vk == NULL) { + r = -ENOMEM; + goto out; + } + /* volume header info (location and size) */ + } else if (entry_type == BITLK_ENTRY_TYPE_VOLUME_HEADER) { + struct bitlk_entry_header_block entry_header; + memcpy(&entry_header, + fve_entries + start + BITLK_ENTRY_HEADER_LEN, + sizeof(entry_header)); + params->volume_header_offset = le64_to_cpu(entry_header.offset); + params->volume_header_size = le64_to_cpu(entry_header.size); + /* volume description (utf-16 string) */ + } else if (entry_type == BITLK_ENTRY_TYPE_DESCRIPTION) { + r = convert_to_utf8(cd, fve_entries + start + BITLK_ENTRY_HEADER_LEN, + entry_size - BITLK_ENTRY_HEADER_LEN, + &(params->description)); + if (r < 0) { + BITLK_bitlk_vmk_free(vmk); + goto out; + } + } + + start += entry_size; + } + +out: + if (fve_entries) + free(fve_entries); + return r; +} + +int BITLK_dump(struct crypt_device *cd, struct device *device, struct bitlk_metadata *params) +{ + struct volume_key *vk_p; + struct bitlk_vmk *vmk_p; + int next_id = 0; + int i = 0; + + log_std(cd, "Info for BITLK%s device %s.\n", params->togo ? " To Go" : "", device_path(device)); + log_std(cd, "Version: \t%u\n", params->metadata_version); + log_std(cd, "GUID: \t%s\n", params->guid); + log_std(cd, "Created: \t%s", ctime((time_t *)&(params->creation_time))); + log_std(cd, "Description: \t%s\n", params->description); + log_std(cd, "Cipher name: \t%s\n", params->cipher); + log_std(cd, "Cipher mode: \t%s\n", params->cipher_mode); + log_std(cd, "Cipher key: \t%u bits\n", params->key_size); + + log_std(cd, "\n"); + + log_std(cd, "Keyslots:\n"); + vmk_p = params->vmks; + while (vmk_p) { + log_std(cd, " %d: VMK\n", next_id); + log_std(cd, "\tGUID: \t%s\n", vmk_p->guid); + log_std(cd, "\tProtection: \t%s\n", get_vmk_protection_string (vmk_p->protection)); + log_std(cd, "\tSalt: \t"); + hexprint(cd, (const char *) vmk_p->salt, 16, ""); + + vk_p = params->vmks->vk; + while (vk_p) { + log_std(cd, "\n"); + log_std(cd, "\tKey data size:\t%zu [bytes]\n", vk_p->keylength); + vk_p = vk_p->next; + } + vmk_p = vmk_p->next; + next_id++; + } + + log_std(cd, " %d: FVEK\n", next_id); + log_std(cd, "\tKey data size:\t%zu [bytes]\n", params->fvek->vk->keylength); + + log_std(cd, "\n"); + + log_std(cd, "Metadata segments:\n"); + + for (i = 0; i < 3; i++) { + log_std(cd, " %d: FVE metadata area\n", i); + log_std(cd, "\tOffset: \t%" PRIu64 " [bytes]\n", params->metadata_offset[i]); + log_std(cd, "\tSize: \t%d [bytes]\n", BITLK_FVE_METADATA_SIZE); + } + + log_std(cd, " %d: Volume header\n", i); + log_std(cd, "\tOffset: \t%" PRIu64 " [bytes]\n", params->volume_header_offset); + log_std(cd, "\tSize: \t%" PRIu64 " [bytes]\n", params->volume_header_size); + log_std(cd, "\tCipher: \t%s-%s\n", params->cipher, params->cipher_mode); + return 0; } @@ -51,7 +610,7 @@ int BITLK_activate(struct crypt_device *cd, const char *name, const char *password, size_t passwordLen, - const struct crypt_params_bitlk *params, + const struct bitlk_metadata *params, uint32_t flags) { int r; @@ -75,7 +634,7 @@ int BITLK_activate(struct crypt_device *cd, /* First sector mapped to DM_LINEAR */ r = dm_linear_target_set(&dmd.segment, 0, 1, crypt_data_device(cd), 0); - if (r) + if (r) goto out; /* The rest is mapped to DM_ZERO (for now) */ diff --git a/lib/bitlk/bitlk.h b/lib/bitlk/bitlk.h index 238db6cd..305a3bfb 100644 --- a/lib/bitlk/bitlk.h +++ b/lib/bitlk/bitlk.h @@ -3,6 +3,7 @@ * * Copyright (C) 2019 Red Hat, Inc. All rights reserved. * Copyright (C) 2019 Milan Broz + * Copyright (C) 2019 Vojtech Trefny * * This file is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -25,19 +26,96 @@ #include #include +#include "internal.h" + struct crypt_device; struct device; struct crypt_params_bitlk; -int BITLK_read_sb(struct crypt_device *cd, struct crypt_params_bitlk *params); +#define BITLK_NONCE_SIZE 12 +#define BITLK_SALT_SIZE 16 +#define BITLK_VMK_MAC_TAG_SIZE 16 -int BITLK_dump(struct crypt_device *cd, struct device *device); +#define BITLK_VMK_OPEN_KEY "openkey" + +typedef enum { + BITLK_PROTECTION_CLEAR_KEY = 0, + BITLK_PROTECTION_TPM, + BITLK_PROTECTION_STARTUP_KEY, + BITLK_PROTECTION_TPM_PIN, + BITLK_PROTECTION_RECOVERY_PASSPHRASE, + BITLK_PROTECTION_PASSPHRASE, + BITLK_PROTECTION_UNKNOWN, +} BITLKVMKProtection; + +typedef enum { + BITLK_ENTRY_TYPE_PROPERTY = 0x0000, + BITLK_ENTRY_TYPE_VMK = 0x0002, + BITLK_ENTRY_TYPE_FVEK = 0x0003, + BITLK_ENTRY_TYPE_STARTUP_KEY = 0x0006, + BITLK_ENTRY_TYPE_DESCRIPTION = 0x0007, + BITLK_ENTRY_TYPE_VOLUME_HEADER = 0x000f, +} BITLKFVEEntryType; + +typedef enum { + BITLK_ENTRY_VALUE_ERASED = 0x0000, + BITLK_ENTRY_VALUE_KEY = 0x0001, + BITLK_ENTRY_VALUE_STRING = 0x0002, + BITLK_ENTRY_VALUE_STRETCH_KEY = 0x0003, + BITLK_ENTRY_VALUE_USE_KEY = 0x0004, + BITLK_ENTRY_VALUE_ENCRYPTED_KEY = 0x0005, + BITLK_ENTRY_VALUE_TPM_KEY = 0x0006, + BITLK_ENTRY_VALUE_VALIDATION = 0x0007, + BITLK_ENTRY_VALUE_VMK = 0x0008, + BITLK_ENTRY_VALUE_EXTERNAL_KEY = 0x0009, + BITLK_ENTRY_VALUE_OFFSET_SIZE = 0x000f, +} BITLKFVEEntryValue; + +struct bitlk_vmk { + char *guid; + BITLKVMKProtection protection; + uint8_t salt[BITLK_SALT_SIZE]; + uint8_t mac_tag[BITLK_VMK_MAC_TAG_SIZE]; + uint8_t nonce[BITLK_NONCE_SIZE]; + struct volume_key *vk; + struct bitlk_vmk *next; +}; + +struct bitlk_fvek { + uint8_t mac_tag[BITLK_VMK_MAC_TAG_SIZE]; + uint8_t nonce[BITLK_NONCE_SIZE]; + struct volume_key *vk; +}; + +struct bitlk_metadata { + bool togo; + const char *cipher; + const char *cipher_mode; + uint16_t key_size; + char *guid; + uint64_t creation_time; + char *description; + uint64_t metadata_offset[3]; + uint32_t metadata_version; + uint64_t volume_header_offset; + uint64_t volume_header_size; + struct bitlk_vmk *vmks; + struct bitlk_fvek *fvek; +}; + +int BITLK_read_sb(struct crypt_device *cd, struct bitlk_metadata *params); + +int BITLK_dump(struct crypt_device *cd, struct device *device, struct bitlk_metadata *params); int BITLK_activate(struct crypt_device *cd, const char *name, const char *password, size_t passwordLen, - const struct crypt_params_bitlk *params, + const struct bitlk_metadata *params, uint32_t flags); +void BITLK_bitlk_fvek_free(struct bitlk_fvek *fvek); +void BITLK_bitlk_vmk_free(struct bitlk_vmk *vmk); +void BITLK_bitlk_metadata_free(struct bitlk_metadata *params); + #endif diff --git a/lib/setup.c b/lib/setup.c index 3de9df99..d8e44824 100644 --- a/lib/setup.c +++ b/lib/setup.c @@ -113,7 +113,8 @@ struct crypt_device { uint32_t sb_flags; } integrity; struct { /* used in CRYPT_BITLK */ - struct crypt_params_bitlk params; + struct bitlk_metadata params; + char *cipher_spec; } bitlk; struct { /* used if initialized without header by name */ char *active_name; @@ -974,7 +975,7 @@ static int _crypt_load_integrity(struct crypt_device *cd, } static int _crypt_load_bitlk(struct crypt_device *cd, - struct crypt_params_bitlk *params) + struct bitlk_metadata *params) { int r; @@ -986,6 +987,12 @@ static int _crypt_load_bitlk(struct crypt_device *cd, if (r < 0) return r; + if (asprintf(&cd->u.bitlk.cipher_spec, "%s-%s", + cd->u.bitlk.params.cipher, cd->u.bitlk.params.cipher_mode) < 0) { + cd->u.bitlk.cipher_spec = NULL; + return -ENOMEM; + } + if (!cd->type && !(cd->type = strdup(CRYPT_BITLK))) return -ENOMEM; @@ -1128,6 +1135,9 @@ static void crypt_free_type(struct crypt_device *cd) free(CONST_CAST(void*)cd->u.integrity.params.journal_crypt); crypt_free_volume_key(cd->u.integrity.journal_crypt_key); crypt_free_volume_key(cd->u.integrity.journal_mac_key); + } else if (isBITLK(cd->type)) { + free(cd->u.bitlk.cipher_spec); + BITLK_bitlk_metadata_free(&cd->u.bitlk.params); } else if (!cd->type) { free(cd->u.none.active_name); cd->u.none.active_name = NULL; @@ -4751,7 +4761,7 @@ int crypt_dump(struct crypt_device *cd) else if (isINTEGRITY(cd->type)) return INTEGRITY_dump(cd, crypt_data_device(cd), 0); else if (isBITLK(cd->type)) - return BITLK_dump(cd, crypt_data_device(cd)); + return BITLK_dump(cd, crypt_data_device(cd), &cd->u.bitlk.params); log_err(cd, _("Dump operation is not supported for this device type.")); return -EINVAL; @@ -4770,6 +4780,8 @@ const char *crypt_get_cipher_spec(struct crypt_device *cd) return cd->u.plain.cipher_spec; else if (isLOOPAES(cd->type)) return cd->u.loopaes.cipher_spec; + else if (isBITLK(cd->type)) + return cd->u.bitlk.cipher_spec; else if (!cd->type && !_init_by_name_crypt_none(cd)) return cd->u.none.cipher_spec; @@ -4800,6 +4812,9 @@ const char *crypt_get_cipher(struct crypt_device *cd) if (isTCRYPT(cd->type)) return cd->u.tcrypt.params.cipher; + if (isBITLK(cd->type)) + return cd->u.bitlk.params.cipher; + if (!cd->type && !_init_by_name_crypt_none(cd)) return cd->u.none.cipher; @@ -4830,6 +4845,9 @@ const char *crypt_get_cipher_mode(struct crypt_device *cd) if (isTCRYPT(cd->type)) return cd->u.tcrypt.params.mode; + if (isBITLK(cd->type)) + return cd->u.bitlk.params.cipher_mode; + if (!cd->type && !_init_by_name_crypt_none(cd)) return cd->u.none.cipher_mode; @@ -4904,6 +4922,9 @@ const char *crypt_get_uuid(struct crypt_device *cd) if (isVERITY(cd->type)) return cd->u.verity.uuid; + if (isBITLK(cd->type)) + return cd->u.bitlk.params.guid; + return NULL; } @@ -4964,6 +4985,9 @@ int crypt_get_volume_key_size(struct crypt_device *cd) if (isTCRYPT(cd->type)) return cd->u.tcrypt.params.key_size; + if (isBITLK(cd->type)) + return cd->u.bitlk.params.key_size; + if (!cd->type && !_init_by_name_crypt_none(cd)) return cd->u.none.key_size;