Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions include/dislocker/accesses/user_pass/user_pass.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
int get_vmk_from_user_pass(dis_metadata_t dis_meta, dis_config_t* cfg, void** vmk_datum);
int get_vmk_from_user_pass2(dis_metadata_t dis_meta, uint8_t** user_password, void** vmk_datum);

int get_vmk_from_tpm_pin(dis_metadata_t dis_meta, dis_config_t* cfg, void** vmk_datum);

int user_key(const uint8_t *user_password, const uint8_t *salt, uint8_t *result_key);

int prompt_up(uint8_t** up);
Expand Down
2 changes: 2 additions & 0 deletions include/dislocker/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ typedef enum {
DIS_OPT_SET_FVEK_FILE_PATH,
DIS_OPT_USE_VMK_FILE,
DIS_OPT_SET_VMK_FILE_PATH,
DIS_OPT_USE_TPM_PIN,
DIS_OPT_SET_TPM_DATUM_FILE_PATH,
DIS_OPT_VERBOSITY,
DIS_OPT_LOG_FILE_PATH,
DIS_OPT_FORCE_BLOCK,
Expand Down
5 changes: 4 additions & 1 deletion include/dislocker/config.priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,12 @@ typedef enum {
DIS_USE_RECOVERY_PASSWORD = (1 << 2),
DIS_USE_BEKFILE = (1 << 3),
DIS_USE_FVEKFILE = (1 << 4),
DIS_USE_TPM_PIN = (1 << 5),
DIS_USE_VMKFILE = (1 << 8)
} DIS_DECRYPT_MEAN;

/* Don't use this as a decryption mean, but as the last one */
#define LAST_MEAN (1 << 5)
#define LAST_MEAN (1 << 6)


/**
Expand Down Expand Up @@ -80,6 +81,8 @@ typedef struct _dis_cfg {
char* fvek_file;
/* Use directly the VMK file DECRYPT_MEAN */
char* vmk_file;
/* Path to the TPM datum file for TPM+PIN DECRYPT_MEAN */
char* tpm_datum_file;

/* Output verbosity */
DIS_LOGS verbosity;
Expand Down
23 changes: 23 additions & 0 deletions src/accesses/accesses.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,29 @@ int dis_get_access(dis_context_t dis_ctx)
break;
}
}
else if(dis_ctx->cfg.decryption_mean & DIS_USE_TPM_PIN)
{
if(!get_vmk_from_tpm_pin(dis_ctx->metadata, &dis_ctx->cfg, &vmk_datum))
{
dis_ctx->cfg.decryption_mean &= (unsigned) ~DIS_USE_TPM_PIN;
}
else
{
dis_printf(L_INFO, "Used TPM+PIN decryption method\n");
dis_ctx->cfg.decryption_mean = DIS_USE_TPM_PIN;

/* We don't need the user password anymore */
if(dis_ctx->cfg.user_password)
{
memclean(
(char*) dis_ctx->cfg.user_password,
strlen((char*) dis_ctx->cfg.user_password)
);
dis_ctx->cfg.user_password = NULL;
}
break;
}
}
else if(dis_ctx->cfg.decryption_mean & DIS_USE_USER_PASSWORD)
{
if(!get_vmk_from_user_pass(dis_ctx->metadata, &dis_ctx->cfg, &vmk_datum))
Expand Down
233 changes: 233 additions & 0 deletions src/accesses/user_pass/user_pass.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@
#include <termios.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


/**
Expand Down Expand Up @@ -178,6 +181,236 @@ int get_vmk_from_user_pass2(dis_metadata_t dis_meta,
}


/**
* Get the VMK datum using a TPM datum file and a user PIN
*
* The TPM datum file contains a raw AES-CCM datum (as obtained from the TPM).
* The PIN/user password is used together with the salt from the volume metadata
* to derive an intermediate key that decrypts the TPM datum. The result is then
* used to decrypt the actual VMK from the volume metadata.
*
* @param dis_meta The metadata structure
* @param cfg The configuration structure (provides tpm_datum_file and user_password)
* @param vmk_datum The datum_key_t found, containing the unencrypted VMK
* @return TRUE if result can be trusted, FALSE otherwise
*/
int get_vmk_from_tpm_pin(dis_metadata_t dis_meta,
dis_config_t* cfg,
void** vmk_datum)
{
if(!dis_meta || !cfg || !cfg->tpm_datum_file)
return FALSE;

uint8_t user_hash[32] = {0,};
uint8_t salt[16] = {0,};
void* vmk_key = NULL;
size_t vmk_key_size = 0;
int file_fd = -1;
off_t file_size;
ssize_t rs;
void* tpm_aesccm = NULL;

/* If the user password/PIN wasn't provided, ask for it */
if(!cfg->user_password)
if(!prompt_up(&cfg->user_password))
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit, I think this is more clear to read and avoids the unbracketed if followed by a bracketed if.

if(!cfg->user_password) && !prompt_up(@cfg->user_password)() {
			dis_printf(L_ERROR, "Cannot get valid user PIN. Abort.\n");
			return FALSE;
}

{
dis_printf(L_ERROR, "Cannot get valid user PIN. Abort.\n");
return FALSE;
}

dis_printf(
L_DEBUG,
"Using TPM+PIN method with datum file '%s'.\n",
cfg->tpm_datum_file
);

/* Read the TPM datum file */
file_fd = dis_open(cfg->tpm_datum_file, O_RDONLY);
if(file_fd == -1)
{
dis_printf(L_ERROR, "Cannot open TPM datum file (%s)\n", cfg->tpm_datum_file);
return FALSE;
}

file_size = dis_lseek(file_fd, 0, SEEK_END);
if(file_size < (off_t)sizeof(datum_aes_ccm_t) || file_size > 65536)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why 65536? Is this the max an unsigned 16 bit can hold, which is 0xFFFF or 65535? Should we use the UINT16_MAX define or create a new one here to make the size restriction more clear.

{
dis_printf(
L_ERROR,
"Invalid TPM datum file size: %d (expected at least %lu bytes)\n",
(int)file_size,
(unsigned long)sizeof(datum_aes_ccm_t)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

%jd for off_t and %zu for size_t. I am not sure if there some weird thing where this has to be c89 or some anchient compiler support needed, so I won't mention these again. But there are lots of spots where %zu or other format specifiers could be used vs casting. Then you wont have any truncation or other issues.

);
dis_close(file_fd);
return FALSE;
}

tpm_aesccm = dis_malloc((size_t)file_size);
dis_lseek(file_fd, 0, SEEK_SET);
rs = dis_read(file_fd, tpm_aesccm, (size_t)file_size);
dis_close(file_fd);

if(rs != file_size)
{
dis_printf(L_ERROR, "Cannot read TPM datum file completely\n");
dis_free(tpm_aesccm);
return FALSE;
}

dis_printf(L_DEBUG, "TPM datum file read (%d bytes):\n", (int)file_size);
hexdump(L_DEBUG, tpm_aesccm, (size_t)file_size);

/*
* Iterate over all VMK datums in the metadata, looking for one that
* has both a STRETCH_KEY and an AES_CCM nested datum. For each candidate,
* try to decrypt the TPM blob with the PIN-derived key, then use the
* result to decrypt the actual VMK.
*/
void* current_vmk = NULL;

while(get_next_datum(
dis_meta,
DATUMS_ENTRY_VMK,
DATUMS_VALUE_VMK,
current_vmk,
&current_vmk
))
{
/* Look for a STRETCH_KEY nested datum (contains the salt) */
void* stretch_datum = NULL;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would change this to type datum_stretch_key_t and then pass that to get_nested_datumvaluetype, then you don't need to cast it again later. You can pass any pointer as a void pointer too.

if(!get_nested_datumvaluetype(
current_vmk,
DATUMS_VALUE_STRETCH_KEY,
&stretch_datum
) ||
!stretch_datum)
{
continue;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bracket consistency, looks like single line conditions don't get brackets.


memcpy(salt, ((datum_stretch_key_t*) stretch_datum)->salt, 16);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use sizeof(stretch_datum->salt) instead of repeating the constant 16.


dis_printf(L_DEBUG, "Found VMK with stretch key, salt:\n");
hexdump(L_DEBUG, salt, 16);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same, sizeof(salt) here.


/*
* Derive the intermediate key from the user PIN and the salt
*/
if(!user_key(cfg->user_password, salt, user_hash))
{
dis_printf(L_DEBUG, "Cannot stretch user PIN with this salt, trying next VMK.\n");
continue;
}

dis_printf(L_DEBUG, "Derived user hash:\n");
hexdump(L_DEBUG, user_hash, 32);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sizeof(user_hash) here as well.


/*
* Step 1: Decrypt the TPM datum with the PIN-derived key.
* This yields an intermediate VMK key.
*/
/*
* Initialize to tpm_aesccm so get_vmk's debug print has a
* valid datum to display before decryption overwrites it.
*/
datum_key_t* intermediate_key = (datum_key_t*) tpm_aesccm;
if(!get_vmk(
(datum_aes_ccm_t*) tpm_aesccm,
user_hash,
32,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same, sizeof(user_hash)

&intermediate_key
))
{
dis_printf(L_DEBUG, "Cannot decrypt TPM datum with this VMK's salt, trying next.\n");
continue;
}

/* Extract the raw key bytes from the intermediate key datum */
if(!get_payload_safe(intermediate_key, &vmk_key, &vmk_key_size))
{
dis_printf(
L_DEBUG,
"Cannot extract payload from intermediate key, trying next.\n"
);
dis_free(intermediate_key);
continue;
}

dis_printf(L_DEBUG, "Intermediate VMK key (%lu bytes):\n",
(unsigned long)vmk_key_size);
hexdump(L_DEBUG, vmk_key, vmk_key_size);

/*
* Step 2: Get the AES_CCM nested datum from the metadata VMK
* and decrypt it with the intermediate key to get the real VMK.
*/
void* aesccm_vmk_datum = NULL;
if(!get_nested_datumvaluetype(
current_vmk,
DATUMS_VALUE_AES_CCM,
&aesccm_vmk_datum
) ||
!aesccm_vmk_datum)
{
dis_printf(
L_DEBUG,
"No AES_CCM datum in this VMK entry, trying next.\n"
);
dis_free(intermediate_key);
dis_free(vmk_key);
vmk_key = NULL;
continue;
}

/* Make a copy since get_vmk may modify the pointer */
size_t aesccm_size = ((datum_aes_ccm_t*)aesccm_vmk_datum)->header.datum_size;
void* aesccm_copy = dis_malloc(aesccm_size);
memcpy(aesccm_copy, aesccm_vmk_datum, aesccm_size);

dis_printf(L_DEBUG, "AES_CCM VMK datum (%lu bytes):\n",
(unsigned long)aesccm_size);
hexdump(L_DEBUG, aesccm_copy, aesccm_size);

datum_key_t* final_vmk = (datum_key_t*) aesccm_copy;
if(!get_vmk(
(datum_aes_ccm_t*) aesccm_copy,
vmk_key,
(vmk_key_size > 32) ? 32 : vmk_key_size,
&final_vmk
))
{
dis_printf(
L_DEBUG,
"Cannot decrypt VMK with intermediate key, trying next.\n"
);
dis_free(intermediate_key);
dis_free(vmk_key);
dis_free(aesccm_copy);
vmk_key = NULL;
continue;
}

/* Success! */
dis_printf(L_INFO, "Successfully decrypted VMK using TPM+PIN method.\n");
*vmk_datum = final_vmk;

dis_free(intermediate_key);
dis_free(vmk_key);
dis_free(aesccm_copy);
dis_free(tpm_aesccm);
return TRUE;
}

dis_printf(
L_ERROR,
"Failed to decrypt VMK using TPM+PIN method. "
"No matching VMK datum found or wrong PIN.\n"
);
dis_free(tpm_aesccm);
return FALSE;
}


/**
* Get the user's pass without displaying it.
*
Expand Down
Loading