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
7 changes: 6 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ set (AUTHOR "Romain Coltel")

find_program (GIT_EXE NAMES git PATHS /usr/bin /usr/local/bin)

include(CTest)
enable_testing()

if(GIT_EXE)
execute_process (
COMMAND ${GIT_EXE} rev-parse --abbrev-ref HEAD
Expand All @@ -47,7 +50,9 @@ set (VERSION_RELEASE 3)
set (VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_RELEASE}")

add_subdirectory (${PROJECT_SOURCE_DIR}/src)

if(BUILD_TESTING)
add_subdirectory (${PROJECT_SOURCE_DIR}/test)
endif()
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
Expand Down
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,20 @@ instance, if you want to compile dislocker-fuse only, you'd simply run:
$ cmake .
$ make dislocker-fuse
```

## Crypto Backends

One can also change the cryptographic libraries used by using the CMake option "CRYPTO_BACKEND".
The current options are "mbedtls" the default, and "openssl". You MUST specify those strings exactly.
For example, to enable the openssl cryptographic backend, do this in your `cmake` command:
```bash
cmake -DCRYPTO_BACKEND=openssl <rest of command here>
```

## Testing

You can enable (the default behavior) or disable testing using the CMake option "BUILD_TESTING".
For example, to disable testing add `-DBUILD_TESTING=OFF` to your `cmake` command like so:
```bash
cmake -DBUILD_TESTING=OFF <rest of command here>
```
2 changes: 1 addition & 1 deletion include/dislocker/accesses/stretch_key.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

#include "dislocker/common.h"

#include "dislocker/ssl_bindings.h"
#include "ssl_bindings.h"



Expand Down
2 changes: 1 addition & 1 deletion include/dislocker/encryption/aes-xts.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
#ifndef DIS_AES_XTS_H
#define DIS_AES_XTS_H

#include "dislocker/ssl_bindings.h"
#include "ssl_bindings.h"


/*
Expand Down
2 changes: 1 addition & 1 deletion include/dislocker/encryption/encommon.priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

#include "dislocker/encryption/encommon.h"

#include "dislocker/ssl_bindings.h"
#include "ssl_bindings.h"



Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
#define SHA256(input, len, output) mbedtls_sha256(input, len, output, 0)

/* Here stand the bindings for AES functions and contexts */
#if defined(MBEDTLS_AES_H)
# define AES_CONTEXT mbedtls_aes_context
# define AES_SETENC_KEY(ctx, key, size) mbedtls_aes_setkey_enc(ctx, key, size)
# define AES_SETDEC_KEY(ctx, key, size) mbedtls_aes_setkey_dec(ctx, key, size)
Expand All @@ -41,15 +40,6 @@
mbedtls_aes_crypt_cbc(ctx, mode, size, iv, in, out);
# define AES_ENCRYPT MBEDTLS_AES_ENCRYPT
# define AES_DECRYPT MBEDTLS_AES_DECRYPT
#else
# define AES_CONTEXT aes_context
# define AES_SETENC_KEY(ctx, key, size) aes_setkey_enc(ctx, key, size)
# define AES_SETDEC_KEY(ctx, key, size) aes_setkey_dec(ctx, key, size)
# define AES_ECB_ENC(ctx, mode, in, out) aes_crypt_ecb(ctx, mode, in, out)
# define AES_CBC(ctx, mode, size, iv, in, out) \
aes_crypt_cbc(ctx, mode, size, iv, in, out);
#endif /* defined(MBEDTLS_AES_H) */


#include "dislocker/encryption/aes-xts.h"
#if defined(MBEDTLS_CIPHER_MODE_XEX)
Expand Down
139 changes: 139 additions & 0 deletions include/dislocker/encryption/openssl/ssl_bindings.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/* -*- coding: utf-8 -*- */
/* -*- mode: c -*- */
/*
* Dislocker -- enables to read/write on BitLocker encrypted partitions under
* Linux
* Copyright (C) 2026 Arm Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*/
#ifndef SSL_BINDINGS_H
#define SSL_BINDINGS_H

#include <openssl/aes.h>
#include <openssl/evp.h>
#include <openssl/sha.h>

#define DIS_OSSL_MAX_AES_KEY_BYTES 512/8

typedef struct dis_ossl_aes_ctx dis_ossl_aes_ctx;
struct dis_ossl_aes_ctx {
unsigned char key[DIS_OSSL_MAX_AES_KEY_BYTES];
size_t len;
int enc_dec;
};

#define AES_CONTEXT dis_ossl_aes_ctx

#define DIS_OSSL_CIPHER_ECB 0
#define DIS_OSSL_CIPHER_CBC 1

static inline const EVP_CIPHER *dis_ossle_get_cipher(size_t key_len, int ossl_mode)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Typo: dis_ossle_get_cipherdis_ossl_get_cipher (stray e)

{
if (ossl_mode == DIS_OSSL_CIPHER_ECB)
{
switch (key_len)
{
case 16: return EVP_aes_128_ecb();
case 24: return EVP_aes_192_ecb();
case 32: return EVP_aes_256_ecb();
default: return NULL;
}
}
else if (ossl_mode == DIS_OSSL_CIPHER_CBC)
{
switch (key_len)
{
case 16: return EVP_aes_128_cbc();
case 24: return EVP_aes_192_cbc();
case 32: return EVP_aes_256_cbc();
default: return NULL;
}
}
return NULL;
}

static inline int dis_ossl_set_key(AES_CONTEXT *ctx, const unsigned char *key, size_t key_bits, int enc_dec)
{
size_t key_len = key_bits / 8;
if (key_len > sizeof(ctx->key))
return 1;

memcpy(ctx->key, key, key_len);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

memcpy is used here but this header doesn't include <string.h>. It happens to work because callers include it first, but the header should be self-contained.

ctx->len = key_bits / 8;
ctx->enc_dec = enc_dec;

return 0;
}

static inline int dis_ossl_aes_crypt(
AES_CONTEXT *ctx,
int mode,
int size,
const unsigned char *iv,
const unsigned char *input,
unsigned char *output,
int cipher)
{
int rc = 1; /* fail unless otherwise changed */
int out_len = 0;

const EVP_CIPHER *ossl_cipher = dis_ossle_get_cipher(ctx->len, cipher);
if (!ossl_cipher)
return rc;

EVP_CIPHER_CTX *ossl_ctx = EVP_CIPHER_CTX_new();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This allocates and tears down an EVP context on every call. AES_ECB_ENC is called per 16-byte block from the XTS/XEX inner loops, so for a 512-byte sector you're doing ~33 malloc/init/free cycles. Over a full disk decryption that's tens of millions of unnecessary allocations — going to be dramatically slower than the mbedtls path.

Worth either caching the EVP_CIPHER_CTX in the dis_ossl_aes_ctx struct or initializing it once at set-key time.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

This allocates and tears down an EVP context on every call. AES_ECB_ENC is called per 16-byte block from the XTS/XEX inner loops, so for a 512-byte sector you're doing ~33 malloc/init/free cycles. Over a full disk decryption that's tens of millions of unnecessary allocations — going to be dramatically slower than the mbedtls path.

Probably not as much as one thinks, since alloc and free are fairly cheap unless the heap needs to grow. I didn't notice a decryption speed difference. But I didn't like this approach either because of what you state.

Worth either caching the EVP_CIPHER_CTX in the dis_ossl_aes_ctx struct or initializing it once at set-key time.

I think it would be cleaner if I just refactored the crypto interface, where we have an init, use, free pattern by the underlying crypto routines.

if (!ossl_ctx)
return rc;

if (mode == AES_ENCRYPT)
{
if (!EVP_EncryptInit(ossl_ctx, ossl_cipher, ctx->key, iv)) goto error;
if (!EVP_CIPHER_CTX_set_padding(ossl_ctx, 0)) goto error;
if (!EVP_EncryptUpdate(ossl_ctx, output, &out_len, input, size)) goto error;
if (!EVP_EncryptFinal(ossl_ctx, &output[out_len], &out_len)) goto error;
} else {
if (!EVP_DecryptInit(ossl_ctx, ossl_cipher, ctx->key, iv)) goto error;
if (!EVP_CIPHER_CTX_set_padding(ossl_ctx, 0)) goto error;
if (!EVP_DecryptUpdate(ossl_ctx, output, &out_len, input, size)) goto error;
if (!EVP_DecryptFinal(ossl_ctx, &output[out_len], &out_len)) goto error;
}

/* success */
rc = 0;

error:
EVP_CIPHER_CTX_free(ossl_ctx);
return rc;
}

#define AES_SETENC_KEY(ctx, key, size) dis_ossl_set_key(ctx, key, size, AES_ENCRYPT)
#define AES_SETDEC_KEY(ctx, key, size) dis_ossl_set_key(ctx, key, size, AES_DECRYPT)
#define AES_ECB_ENC(ctx, mode, in, out) dis_ossl_aes_crypt(ctx, mode, 16, NULL, in, out, DIS_OSSL_CIPHER_ECB)
#define AES_CBC(ctx, mode, size, iv, in, out) dis_ossl_aes_crypt(ctx, mode, size, iv, in, out, DIS_OSSL_CIPHER_CBC)

/*
* OpenSSL doesn't provide XEX nor XTS, so just use the dislocker implementations. Note that

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This isn't quite right — OpenSSL does provide XTS (EVP_aes_128_xts() / EVP_aes_256_xts()). It doesn't have XEX, which is correct. Using OpenSSL's native XTS would also get you hardware acceleration (AES-NI) for free.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Ahh I see that it has XTS, I didn't see that before when I looked.

* the dislocker implementations use the OpenSSL primitives already defined, these are optional
*/
#include "dislocker/encryption/aes-xts.h"
#define AES_XEX(ctx1, ctx2, mode, size, iv, in, out) \
dis_aes_crypt_xex(ctx1, ctx2, mode, size, iv, in, out)

#define AES_XTS(ctx1, ctx2, mode, size, iv, in, out) \
dis_aes_crypt_xts(ctx1, ctx2, mode, size, iv, in, out)

#endif /* SSL_BINDINGS_H */
42 changes: 36 additions & 6 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ endif()
if( NOT CMAKE_CROSSCOMPILING )
include_directories (SYSTEM /usr/local/include)
endif()
include_directories (${PROJECT_SOURCE_DIR}/include)

set (LIB pthread)
set (SOURCES
Expand Down Expand Up @@ -118,9 +117,6 @@ endif()
# Libraries
set (CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)

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

# Ruby bindings
set(WITH_RUBY "AUTO" CACHE STRING "Enable Ruby bindings. Valid values are ON, OFF, or AUTO")
if (NOT "${WITH_RUBY}" STREQUAL "OFF")
Expand Down Expand Up @@ -201,7 +197,8 @@ endif()

# Targets
add_library (${PROJECT_NAME} SHARED ${SOURCES})
target_link_libraries (${PROJECT_NAME} ${LIB})
target_include_directories (${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/include)
target_link_libraries (${PROJECT_NAME} PRIVATE ${LIB})
set_target_properties (${PROJECT_NAME} PROPERTIES VERSION "${VERSION}" SOVERSION "${VERSION_MAJOR}.${VERSION_MINOR}")
install (TARGETS ${PROJECT_NAME} LIBRARY DESTINATION "${libdir}")

Expand All @@ -213,12 +210,45 @@ if (${APPLE})
install (TARGETS ${BUNDLE_NAME} DESTINATION "${libdir}")
endif()

set(CRYPTO_BACKEND "mbedtls" CACHE STRING "Crypto library backend")
set_property(CACHE CRYPTO_BACKEND PROPERTY STRINGS "mbedtls" "openssl")

add_library(dislocker_crypto_backend INTERFACE)

message(STATUS "Crypto backend: ${CRYPTO_BACKEND}")
if(CRYPTO_BACKEND STREQUAL "mbedtls")
find_package (MbedTLS 3 REQUIRED)
target_link_libraries(${PROJECT_NAME}
PRIVATE
MbedTLS::mbedcrypto
)
target_link_libraries(dislocker_crypto_backend INTERFACE MbedTLS::mbedcrypto)
target_include_directories(${PROJECT_NAME}
PUBLIC
${CMAKE_SOURCE_DIR}/include/${PROJECT_NAME}/encryption/mbedtls
)
elseif(CRYPTO_BACKEND STREQUAL "openssl")
find_package(OpenSSL REQUIRED)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The mbedtls path specifies a minimum version (find_package(MbedTLS 3 REQUIRED)). Should probably do the same here — something like find_package(OpenSSL 1.1.0 REQUIRED) given the EVP API usage.

target_link_libraries(${PROJECT_NAME}
PRIVATE
OpenSSL::Crypto
)
target_link_libraries(dislocker_crypto_backend INTERFACE OpenSSL::Crypto)
target_include_directories(${PROJECT_NAME}
PUBLIC
${CMAKE_SOURCE_DIR}/include/${PROJECT_NAME}/encryption/openssl
)
else()
message(FATAL_ERROR "Unknown CRYPTO_BACKEND='${CRYPTO_BACKEND}'.")
endif()


set (CLEAN_FILES "")

if(FUSE_FOUND)
set (BIN_FUSE ${PROJECT_NAME}-fuse)
add_executable (${BIN_FUSE} ${BIN_FUSE}.c)
target_link_libraries (${BIN_FUSE} ${FUSE_LIBBRARIES} ${PROJECT_NAME})
target_link_libraries (${BIN_FUSE} PRIVATE ${FUSE_LIBBRARIES} ${LIB} ${PROJECT_NAME})
set_target_properties (${BIN_FUSE} PROPERTIES COMPILE_DEFINITIONS FUSE_USE_VERSION=314)
set_target_properties (${BIN_FUSE} PROPERTIES LINK_FLAGS "-pie -fPIE")
add_custom_command (TARGET ${BIN_FUSE} POST_BUILD
Expand Down
2 changes: 1 addition & 1 deletion src/encryption/aes-xts.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
#include <stdint.h>
#include <string.h>

#include "dislocker/ssl_bindings.h"
#include "ssl_bindings.h"

#ifndef GET_UINT64_LE
#define GET_UINT64_LE(n,b,i) \
Expand Down
27 changes: 27 additions & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Dislocker -- enables to read/write on BitLocker encrypted partitions under
# Linux
# Copyright (C) 2026 Arm Ltd
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
# USA.

add_executable(crypto_tests
test-crypto.c
)

target_link_libraries(crypto_tests PRIVATE ${PROJECT_NAME} dislocker_crypto_backend)

add_test(NAME cyrpto_tests COMMAND crypto_tests)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Typo: cyrpto_testscrypto_tests


Loading