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
87 changes: 87 additions & 0 deletions cmake/FindMbedTLS.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# FindMbedTLS.cmake
# Find the mbedTLS library (works with mbedTLS 2.x and 3.x)
#
# This module defines:
# MbedTLS_FOUND - True if mbedTLS was found
# MbedTLS::mbedcrypto - Imported target for mbedcrypto library

include(FindPackageHandleStandardArgs)

# Find the include directory
find_path(MBEDTLS_INCLUDE_DIR
NAMES mbedtls/ssl.h
PATHS
/usr/include
/usr/local/include
)

# Find the crypto library
find_library(MBEDCRYPTO_LIBRARY
NAMES mbedcrypto
PATHS
/usr/lib
/usr/lib/aarch64-linux-gnu
/usr/lib/x86_64-linux-gnu
/usr/local/lib
)

# Find the TLS library
find_library(MBEDTLS_LIBRARY
NAMES mbedtls
PATHS
/usr/lib
/usr/lib/aarch64-linux-gnu
/usr/lib/x86_64-linux-gnu
/usr/local/lib
)

# Find the X509 library
find_library(MBEDX509_LIBRARY
NAMES mbedx509
PATHS
/usr/lib
/usr/lib/aarch64-linux-gnu
/usr/lib/x86_64-linux-gnu
/usr/local/lib
)

# Get version from version.h
if(MBEDTLS_INCLUDE_DIR)
file(STRINGS "${MBEDTLS_INCLUDE_DIR}/mbedtls/version.h" _mbedtls_version_str
REGEX "^#define[ \t]+MBEDTLS_VERSION_STRING[ \t]+\"[^\"]+\"")
if(_mbedtls_version_str)
string(REGEX REPLACE "^#define[ \t]+MBEDTLS_VERSION_STRING[ \t]+\"([^\"]+)\".*" "\\1"
MBEDTLS_VERSION "${_mbedtls_version_str}")
endif()
endif()

find_package_handle_standard_args(MbedTLS
REQUIRED_VARS MBEDCRYPTO_LIBRARY MBEDTLS_INCLUDE_DIR
VERSION_VAR MBEDTLS_VERSION
)

if(MbedTLS_FOUND AND NOT TARGET MbedTLS::mbedcrypto)
add_library(MbedTLS::mbedcrypto UNKNOWN IMPORTED)
set_target_properties(MbedTLS::mbedcrypto PROPERTIES
IMPORTED_LOCATION "${MBEDCRYPTO_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${MBEDTLS_INCLUDE_DIR}"
)
endif()

if(MbedTLS_FOUND AND NOT TARGET MbedTLS::mbedtls)
add_library(MbedTLS::mbedtls UNKNOWN IMPORTED)
set_target_properties(MbedTLS::mbedtls PROPERTIES
IMPORTED_LOCATION "${MBEDTLS_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${MBEDTLS_INCLUDE_DIR}"
)
endif()

if(MbedTLS_FOUND AND NOT TARGET MbedTLS::mbedx509)
add_library(MbedTLS::mbedx509 UNKNOWN IMPORTED)
set_target_properties(MbedTLS::mbedx509 PROPERTIES
IMPORTED_LOCATION "${MBEDX509_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${MBEDTLS_INCLUDE_DIR}"
)
endif()

mark_as_advanced(MBEDTLS_INCLUDE_DIR MBEDCRYPTO_LIBRARY MBEDTLS_LIBRARY MBEDX509_LIBRARY)
2 changes: 2 additions & 0 deletions include/dislocker/accesses/rp/recovery_password.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,7 @@ int prompt_rp(uint8_t** rp);

void print_intermediate_key(uint8_t *result_key);

int extract_recovery_password_from_vmk(dis_metadata_t dis_meta, uint8_t* vmk, char* password);


#endif // RECOVERY_PASSWORD_H
1 change: 1 addition & 0 deletions include/dislocker/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ typedef enum {
DIS_OPT_VOLUME_OFFSET,
DIS_OPT_READ_ONLY,
DIS_OPT_DONT_CHECK_VOLUME_STATE,
DIS_OPT_SHOW_RECOVERY_PASSWORD,

/* Below are options for users of the library (i.e: developers) */
DIS_OPT_INITIALIZE_STATE
Expand Down
2 changes: 2 additions & 0 deletions include/dislocker/config.priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ typedef enum {
* if mounted using fuse
*/
DIS_FLAG_DONT_CHECK_VOLUME_STATE = (1 << 1),
/* Show recovery password after successful VMK decryption */
DIS_FLAG_SHOW_RECOVERY_PASSWORD = (1 << 2),
} dis_flags_e;


Expand Down
2 changes: 1 addition & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ endif()
# Libraries
set (CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)

find_package (MbedTLS 3 REQUIRED)
find_package (MbedTLS REQUIRED)
set (LIB ${LIB} MbedTLS::mbedcrypto)

# Ruby bindings
Expand Down
29 changes: 26 additions & 3 deletions src/accesses/accesses.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

#include "dislocker/metadata/vmk.h"
#include "dislocker/metadata/fvek.h"
#include "dislocker/metadata/datums.h"

#include "dislocker/return_values.h"

Expand Down Expand Up @@ -171,11 +172,33 @@ int dis_get_access(dis_context_t dis_ctx)
* NOTE -- We could here validate the information buffer in a more precise
* way using the VMK and the validations structure (the one after the
* information one, see bitlocker_validations_t in metadata/metadata.h)
*
* NOTE -- We could here get all of the other key a user could use
* using the VMK and the reverse encrypted data
*/

/*
* If requested, extract and display the recovery password using the VMK
*/
if(dis_ctx->cfg.flags & DIS_FLAG_SHOW_RECOVERY_PASSWORD)
{
char recovery_password[56]; /* 8*6 + 7 + 1 */
uint8_t* vmk_key = (uint8_t*)vmk_datum + sizeof(datum_key_t);

if(extract_recovery_password_from_vmk(dis_ctx->metadata, vmk_key, recovery_password))
{
dis_printf(L_INFO, "\n");
dis_printf(L_INFO, "============================================================\n");
dis_printf(L_INFO, "BitLocker Recovery Password:\n");
dis_printf(L_INFO, "\n");
dis_printf(L_INFO, " %s\n", recovery_password);
dis_printf(L_INFO, "\n");
dis_printf(L_INFO, "============================================================\n");
dis_printf(L_INFO, "\n");
}
else
{
dis_printf(L_WARNING, "Could not extract recovery password from VMK\n");
}
}


/*
* And then, use the VMK to decrypt the FVEK
Expand Down
158 changes: 158 additions & 0 deletions src/accesses/rp/recovery_password.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@

#include "dislocker/accesses/rp/recovery_password.h"
#include "dislocker/metadata/vmk.h"
#include "dislocker/metadata/datums.h"
#include "dislocker/encryption/decrypt.h"
#include "dislocker/xstd/xsys_select.h"


Expand Down Expand Up @@ -594,3 +596,159 @@ void print_intermediate_key(uint8_t *result_key)

dis_printf(L_INFO, "Intermediate recovery key:\n\t%s\n", s);
}


#define VMK_SIZE 32
#define RECOVERY_KEY_SIZE 16

/**
* Convert 16-byte recovery key material to recovery password string
*
* @param key_material 16 bytes of recovery key material
* @param password Output buffer (must be at least 56 bytes: 8*6 digits + 7 hyphens + null)
* @return TRUE on success, FALSE on failure
*/
static int recovery_key_to_password(const uint8_t* key_material, char* password)
{
int i;
char* p = password;

for (i = 0; i < NB_RP_BLOCS; i++)
{
/* Extract 16-bit little-endian value */
uint16_t value = (uint16_t)(key_material[i * 2] | (key_material[i * 2 + 1] << 8));

/* Multiply by 11 to get the 6-digit recovery password group */
uint32_t digit_group = (uint32_t)value * 11;

/* Format as 6-digit group */
int written = snprintf(p, 7, "%06u", digit_group);
if (written != 6)
{
dis_printf(L_ERROR, "Error formatting recovery password block %d\n", i + 1);
return FALSE;
}
p += 6;

/* Add hyphen separator (except after last block) */
if (i < NB_RP_BLOCS - 1)
{
*p++ = '-';
}
}

*p = '\0';
return TRUE;
}


/**
* Extract recovery password from VMK
*
* Given a decrypted VMK, this function finds the recovery password protector
* datum, decrypts it using the VMK, and converts the result to the standard
* 8x6-digit recovery password format.
*
* @param dis_meta The metadata structure
* @param vmk The 32-byte Volume Master Key
* @param password Output buffer for recovery password (at least 56 bytes)
* @return TRUE on success, FALSE on failure
*/
int extract_recovery_password_from_vmk(dis_metadata_t dis_meta, uint8_t* vmk, char* password)
{
void* vmk_datum = NULL;
void* stretch_datum = NULL;
void* aesccm_datum = NULL;
datum_aes_ccm_t* aesccm = NULL;
void* decrypted = NULL;
uint8_t* key_material = NULL;
unsigned int header_size;
unsigned int payload_size;

if (!dis_meta || !vmk || !password)
return FALSE;

/*
* Find VMK datum for recovery password protector
* Recovery password protectors have priority range 0x800-0xfff
*/
if (!get_vmk_datum_from_range(dis_meta, 0x800, 0xfff, &vmk_datum, NULL))
{
dis_printf(L_DEBUG, "No recovery password protector found in metadata\n");
return FALSE;
}

dis_printf(L_DEBUG, "Found VMK datum for recovery password protector\n");

/*
* Get the nested STRETCH_KEY datum
* This contains the salt and nested AES-CCM data
*/
if (!get_nested_datumvaluetype(vmk_datum, DATUMS_VALUE_STRETCH_KEY, &stretch_datum) ||
!stretch_datum)
{
dis_printf(L_DEBUG, "Cannot find STRETCH_KEY datum in VMK datum\n");
return FALSE;
}

/*
* Get the nested AES-CCM datum inside the STRETCH_KEY
* This contains the recovery key material encrypted by the VMK
*/
if (!get_nested_datumvaluetype(stretch_datum, DATUMS_VALUE_AES_CCM, &aesccm_datum) ||
!aesccm_datum)
{
dis_printf(L_DEBUG, "Cannot find AES-CCM datum in STRETCH_KEY datum\n");
return FALSE;
}

aesccm = (datum_aes_ccm_t*)aesccm_datum;

/* Calculate payload size */
header_size = datum_value_types_prop[aesccm->header.value_type].size_header;
payload_size = aesccm->header.datum_size - header_size;

dis_printf(L_DEBUG, "AES-CCM payload size: %u bytes\n", payload_size);

/* Decrypt the recovery key material using the VMK */
if (!decrypt_key(
(unsigned char*)aesccm_datum + header_size,
payload_size,
aesccm->mac,
aesccm->nonce,
vmk,
VMK_SIZE * 8, /* key size in bits */
&decrypted))
{
dis_printf(L_DEBUG, "Failed to decrypt recovery key material\n");
return FALSE;
}

/*
* The decrypted data has the following structure:
* - 4 bytes: size
* - 4 bytes: type
* - 4 bytes: algorithm
* - 16 bytes: recovery key material
*/
if (payload_size < 12 + RECOVERY_KEY_SIZE)
{
dis_printf(L_DEBUG, "Decrypted data too small (%u bytes)\n", payload_size);
dis_free(decrypted);
return FALSE;
}

/* Skip the 12-byte header to get to the recovery key material */
key_material = (uint8_t*)decrypted + 12;

/* Convert to recovery password format */
if (!recovery_key_to_password(key_material, password))
{
dis_printf(L_ERROR, "Failed to convert recovery key to password format\n");
dis_free(decrypted);
return FALSE;
}

dis_free(decrypted);
return TRUE;
}
Loading