Skip to content

Commit 8edea28

Browse files
committed
crypto: Add support for LUKS Encryption.
1 parent 47ae95d commit 8edea28

File tree

6 files changed

+406
-4
lines changed

6 files changed

+406
-4
lines changed

docs/libblockdev-sections.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ bd_crypto_luks_reencrypt_params_copy
9999
bd_crypto_luks_reencrypt_params_free
100100
bd_crypto_luks_reencrypt_params_new
101101
bd_crypto_luks_reencrypt
102+
bd_crypto_luks_encrypt
102103
bd_crypto_luks_reencrypt_status
103104
bd_crypto_luks_reencrypt_resume
104105
BDCryptoLUKSReencryptProgFunc

src/lib/plugin_apis/crypto.api

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1272,6 +1272,29 @@ typedef enum {
12721272
*/
12731273
gboolean bd_crypto_luks_reencrypt(const gchar *device, BDCryptoLUKSReencryptParams *params, BDCryptoKeyslotContext *context, BDCryptoLUKSReencryptProgFunc prog_func, GError **error);
12741274

1275+
/**
1276+
* bd_crypto_luks_encrypt:
1277+
* @device: device to encrypt. Either an active device name for online encryption, or a block device for offline encryption.
1278+
* Must match the @params's "offline" parameter
1279+
* @params: encryption parameters
1280+
* @context: key slot context to unlock @device. The newly created keyslot will use the same context
1281+
* @prog_func: (scope call) (nullable): progress function. Also used to possibly stop encryption
1282+
* @error: (out) (optional): place to store error (if any)
1283+
*
1284+
* Encrypts @device. In contrast to %bd_crypto_luks_format, possible existent data on @device is not destroyed,
1285+
* but encrypted, i.e., is usable after activating device.
1286+
*
1287+
* Important: you need to ensure that there is enough free (unallocated) space on @device for a LUKS header (recomended 16 to 32 MiB).
1288+
*
1289+
* Returns: true, if the encryption was successful or gracefully stopped with @prog_func.
1290+
* false, if an error occurred.
1291+
*
1292+
* Supported @context types for this function: passphrase
1293+
*
1294+
* Tech category: %BD_CRYPTO_TECH_LUKS-%BD_CRYPTO_TECH_MODE_MODIFY
1295+
*/
1296+
gboolean bd_crypto_luks_encrypt (const gchar *device, BDCryptoLUKSReencryptParams *params, BDCryptoKeyslotContext *context, BDCryptoLUKSReencryptProgFunc prog_func, GError **error);
1297+
12751298
/**
12761299
* bd_crypto_luks_reencrypt_status:
12771300
* @device: an active device name or a block device

src/plugins/crypto.c

Lines changed: 226 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2483,7 +2483,12 @@ gboolean bd_crypto_luks_reencrypt (const gchar *device, BDCryptoLUKSReencryptPar
24832483
paramsReencrypt.luks2 = &paramsLuks2;
24842484

24852485
paramsLuks2.sector_size = params->sector_size;
2486-
paramsLuks2.pbkdf = get_pbkdf_params (params->pbkdf, error);
2486+
if (params->pbkdf == NULL) {
2487+
paramsLuks2.pbkdf = crypt_get_pbkdf_default (CRYPT_LUKS2);
2488+
} else {
2489+
paramsLuks2.pbkdf = get_pbkdf_params (params->pbkdf, error);
2490+
}
2491+
24872492
if (paramsLuks2.pbkdf == NULL) {
24882493
/* get info to log */
24892494
if (params->pbkdf != NULL && params->pbkdf->type != NULL) {
@@ -2530,6 +2535,226 @@ gboolean bd_crypto_luks_reencrypt (const gchar *device, BDCryptoLUKSReencryptPar
25302535
return TRUE;
25312536
}
25322537

2538+
/**
2539+
* bd_crypto_luks_encrypt:
2540+
* @device: device to encrypt. Either an active device name for online encryption, or a block device for offline encryption.
2541+
* Must match the @params's "offline" parameter
2542+
* @params: encryption parameters
2543+
* @context: key slot context to unlock @device. The newly created keyslot will use the same context
2544+
* @prog_func: (scope call) (nullable): progress function. Also used to possibly stop encryption
2545+
* @error: (out) (optional): place to store error (if any)
2546+
*
2547+
* Encrypts @device. In contrast to %bd_crypto_luks_format, possible existent data on @device is not destroyed,
2548+
* but encrypted, i.e., is usable after activating device.
2549+
*
2550+
* Important: you need to ensure that there is enough free (unallocated) space on @device for a LUKS header (recomended 16 to 32 MiB).
2551+
*
2552+
* Returns: true, if the encryption was successful or gracefully stopped with @prog_func.
2553+
* false, if an error occurred.
2554+
*
2555+
* Supported @context types for this function: passphrase
2556+
*
2557+
* Tech category: %BD_CRYPTO_TECH_LUKS-%BD_CRYPTO_TECH_MODE_MODIFY
2558+
*/
2559+
gboolean bd_crypto_luks_encrypt (const gchar *device, BDCryptoLUKSReencryptParams *params, BDCryptoKeyslotContext *context, BDCryptoLUKSReencryptProgFunc prog_func, GError **error) {
2560+
struct crypt_device *cd = NULL;
2561+
struct crypt_params_reencrypt paramsReencrypt = {};
2562+
struct crypt_params_luks2 paramsLuks2 = {};
2563+
struct reencryption_progress_struct usrptr;
2564+
2565+
guint key_size = params->key_size / 8; /* convert bits to bytes */
2566+
const gchar *HEADER_FILENAME_TEMPLATE = "libblockdev-crypto-luks-encrypt-XXXXXX";
2567+
gchar *header_file_path = NULL;
2568+
int allocated_keyslot;
2569+
gint ret, fd = 0;
2570+
guint64 progress_id = 0;
2571+
gchar *msg = NULL;
2572+
GError *l_error = NULL;
2573+
2574+
msg = g_strdup_printf ("Started encryption of LUKS device '%s'", device);
2575+
progress_id = bd_utils_report_started (msg);
2576+
g_free (msg);
2577+
2578+
if (context->type != BD_CRYPTO_KEYSLOT_CONTEXT_TYPE_PASSPHRASE) {
2579+
g_set_error (&l_error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_INVALID_CONTEXT,
2580+
"Only the 'passphrase' context type is supported for LUKS encrypt.");
2581+
bd_utils_report_finished (progress_id, l_error->message);
2582+
g_propagate_error (error, l_error);
2583+
crypt_free (cd);
2584+
return FALSE;
2585+
}
2586+
2587+
fd = g_file_open_tmp (HEADER_FILENAME_TEMPLATE, &header_file_path, &l_error);
2588+
if (fd == -1) {
2589+
g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_REENCRYPT_FAILED,
2590+
"Failed to create temporary header file: %s", l_error->message);
2591+
bd_utils_report_finished (progress_id, (*error)->message);
2592+
g_free (header_file_path);
2593+
crypt_free (cd);
2594+
return FALSE;
2595+
}
2596+
2597+
ret = posix_fallocate (fd, 0, 4096);
2598+
close (fd);
2599+
if (ret != 0) {
2600+
g_set_error (&l_error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_REENCRYPT_FAILED,
2601+
"Failed to allocate enough space for temporary header file.");
2602+
bd_utils_report_finished (progress_id, l_error->message);
2603+
g_propagate_error (error, l_error);
2604+
unlink (header_file_path);
2605+
g_free (header_file_path);
2606+
crypt_free (cd);
2607+
return FALSE;
2608+
}
2609+
2610+
ret = crypt_init (&cd, header_file_path);
2611+
if (ret < 0) {
2612+
g_set_error (&l_error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE,
2613+
"Failed to initialize device with detached header: %s", strerror_l (-ret, c_locale));
2614+
bd_utils_report_finished (progress_id, l_error->message);
2615+
g_propagate_error (error, l_error);
2616+
unlink (header_file_path);
2617+
g_free (header_file_path);
2618+
return FALSE;
2619+
}
2620+
2621+
paramsLuks2.data_device = device;
2622+
paramsLuks2.sector_size = params->sector_size;
2623+
paramsLuks2.pbkdf = get_pbkdf_params (params->pbkdf, error);
2624+
2625+
crypt_set_data_offset (cd, 16 MiB / SECTOR_SIZE);
2626+
ret = crypt_format (cd, CRYPT_LUKS2, params->cipher, params->cipher_mode, NULL, NULL, key_size, &paramsLuks2);
2627+
if (ret < 0) {
2628+
g_set_error (&l_error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_REENCRYPT_FAILED,
2629+
"Failed to format a header file: %s", strerror_l (-ret, c_locale));
2630+
bd_utils_report_finished (progress_id, l_error->message);
2631+
g_propagate_error (error, l_error);
2632+
unlink (header_file_path);
2633+
g_free (header_file_path);
2634+
crypt_free (cd);
2635+
return FALSE;
2636+
}
2637+
2638+
ret = crypt_keyslot_add_by_key (cd,
2639+
CRYPT_ANY_SLOT,
2640+
NULL,
2641+
key_size,
2642+
(const char*) context->u.passphrase.pass_data,
2643+
context->u.passphrase.data_len,
2644+
0);
2645+
if (ret < 0) {
2646+
g_set_error (&l_error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_ADD_KEY,
2647+
"Failed to add key: %s", strerror_l (-ret, c_locale));
2648+
bd_utils_report_finished (progress_id, l_error->message);
2649+
g_propagate_error (error, l_error);
2650+
unlink (header_file_path);
2651+
g_free (header_file_path);
2652+
crypt_free (cd);
2653+
return FALSE;
2654+
}
2655+
allocated_keyslot = ret;
2656+
bd_utils_report_progress (progress_id, 10, "Added new keyslot");
2657+
2658+
paramsReencrypt.mode = CRYPT_REENCRYPT_ENCRYPT;
2659+
paramsReencrypt.direction = CRYPT_REENCRYPT_BACKWARD;
2660+
paramsReencrypt.resilience = params->resilience;
2661+
paramsReencrypt.hash = params->hash;
2662+
paramsReencrypt.data_shift = 16 MiB / SECTOR_SIZE;
2663+
paramsReencrypt.max_hotzone_size = params->max_hotzone_size;
2664+
paramsReencrypt.device_size = 0;
2665+
paramsReencrypt.flags = CRYPT_REENCRYPT_INITIALIZE_ONLY;
2666+
paramsReencrypt.flags |= CRYPT_REENCRYPT_MOVE_FIRST_SEGMENT;
2667+
paramsReencrypt.luks2 = &paramsLuks2;
2668+
2669+
/* Initialize reencryption */
2670+
ret = crypt_reencrypt_init_by_passphrase (cd,
2671+
params->offline ? NULL : device,
2672+
(const char *) context->u.passphrase.pass_data,
2673+
context->u.passphrase.data_len,
2674+
CRYPT_ANY_SLOT,
2675+
allocated_keyslot,
2676+
params->cipher,
2677+
params->cipher_mode,
2678+
&paramsReencrypt);
2679+
if (ret < 0) {
2680+
g_set_error (&l_error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_REENCRYPT_FAILED,
2681+
"Failed to initialize encryption: %s", strerror_l (-ret, c_locale));
2682+
bd_utils_report_finished (progress_id, l_error->message);
2683+
g_propagate_error (error, l_error);
2684+
unlink (header_file_path);
2685+
g_free (header_file_path);
2686+
crypt_free (cd);
2687+
return FALSE;
2688+
}
2689+
2690+
/* Set header from temporary file to disk */
2691+
/* 1/2: Re-init without detached header */
2692+
crypt_free (cd);
2693+
cd = NULL;
2694+
ret = crypt_init (&cd, device);
2695+
if (ret < 0) {
2696+
g_set_error (&l_error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE,
2697+
"Failed to re-initialize device: %s", strerror_l (-ret, c_locale));
2698+
bd_utils_report_finished (progress_id, l_error->message);
2699+
g_propagate_error (error, l_error);
2700+
unlink (header_file_path);
2701+
g_free (header_file_path);
2702+
return FALSE;
2703+
}
2704+
2705+
/* 2/2: Set header */
2706+
ret = crypt_header_restore (cd, CRYPT_LUKS2, header_file_path);
2707+
unlink (header_file_path);
2708+
g_free (header_file_path);
2709+
if (ret < 0) {
2710+
g_set_error (&l_error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE,
2711+
"Failed to re-initialize device: %s", strerror_l (-ret, c_locale));
2712+
bd_utils_report_finished (progress_id, l_error->message);
2713+
g_propagate_error (error, l_error);
2714+
crypt_free (cd);
2715+
return FALSE;
2716+
}
2717+
2718+
paramsReencrypt.flags &= ~CRYPT_REENCRYPT_INITIALIZE_ONLY;
2719+
paramsReencrypt.flags |= CRYPT_REENCRYPT_RESUME_ONLY;
2720+
2721+
ret = crypt_reencrypt_init_by_passphrase (cd,
2722+
params->offline ? NULL : device,
2723+
(const char *) context->u.passphrase.pass_data,
2724+
context->u.passphrase.data_len,
2725+
CRYPT_ANY_SLOT,
2726+
allocated_keyslot,
2727+
params->cipher,
2728+
params->cipher_mode,
2729+
&paramsReencrypt);
2730+
if (ret < 0) {
2731+
g_set_error (&l_error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_REENCRYPT_FAILED,
2732+
"Failed to re-initialize encryption: %s", strerror_l (-ret, c_locale));
2733+
bd_utils_report_finished (progress_id, l_error->message);
2734+
g_propagate_error (error, l_error);
2735+
crypt_free (cd);
2736+
return FALSE;
2737+
}
2738+
2739+
/* marshal to usrptr */
2740+
usrptr.progress_id = progress_id;
2741+
usrptr.usr_func = prog_func;
2742+
2743+
ret = crypt_reencrypt_run (cd, reencryption_progress, &usrptr);
2744+
if (ret != 0) {
2745+
g_set_error (&l_error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_REENCRYPT_FAILED,
2746+
"Reencryption failed: %s", strerror_l (-ret, c_locale));
2747+
bd_utils_report_finished (progress_id, l_error->message);
2748+
g_propagate_error (error, l_error);
2749+
crypt_free (cd);
2750+
return FALSE;
2751+
}
2752+
2753+
crypt_free (cd);
2754+
bd_utils_report_finished (progress_id, "Completed.");
2755+
return TRUE;
2756+
}
2757+
25332758
/**
25342759
* bd_crypto_luks_reencrypt_status:
25352760
* @device: an active device name or a block device

src/plugins/crypto.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,8 @@ gboolean bd_crypto_luks_convert (const gchar *device, BDCryptoLUKSVersion target
304304
* @hash used hash for "checksum" resilience type, ignored otherwise
305305
* @max_hotzone_size max hotzone size
306306
* @sector_size sector size. Note that 0 is not a valid value
307-
* @new_volume_key whether to generate a new volume key or keep the existing one
307+
* @new_volume_key whether to generate a new volume key or keep the existing one.
308+
* Makes sense only for reencryption (not encryption or decryption).
308309
* @offline whether to perform an offline or online reencryption,
309310
* i.e. whether a device is active in the time of reencryption or not
310311
* @pbkdf PBDKF function parameters for a new keyslot
@@ -352,6 +353,7 @@ typedef enum {
352353
} BDCryptoLUKSReencryptMode;
353354

354355
gboolean bd_crypto_luks_reencrypt(const gchar *device, BDCryptoLUKSReencryptParams *params, BDCryptoKeyslotContext *context, BDCryptoLUKSReencryptProgFunc prog_func, GError **error);
356+
gboolean bd_crypto_luks_encrypt(const gchar *device, BDCryptoLUKSReencryptParams *params, BDCryptoKeyslotContext *context, BDCryptoLUKSReencryptProgFunc prog_func, GError **error);
355357
BDCryptoLUKSReencryptStatus bd_crypto_luks_reencrypt_status (const gchar *device, BDCryptoLUKSReencryptMode *mode, GError **error);
356358
gboolean bd_crypto_luks_reencrypt_resume (const gchar *device, BDCryptoKeyslotContext *context, BDCryptoLUKSReencryptProgFunc prog_func, GError **error);
357359

src/python/gi/overrides/BlockDev.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -303,8 +303,6 @@ def __init__(self, *args, **kwargs): # pylint: disable=unused-argument
303303

304304
class CryptoLUKSReencryptParams(BlockDev.CryptoLUKSReencryptParams):
305305
def __new__(cls, key_size, cipher, cipher_mode, resilience="checksum" , hash="sha256", max_hotzone_size=0, sector_size=512, new_volume_key=True, offline=False, pbkdf=None):
306-
if pbkdf is None:
307-
pbkdf = CryptoLUKSPBKDF()
308306
ret = BlockDev.CryptoLUKSReencryptParams.new(key_size=key_size, cipher=cipher, cipher_mode=cipher_mode, resilience=resilience, hash=hash, max_hotzone_size=max_hotzone_size, sector_size=sector_size, new_volume_key=new_volume_key, offline=offline, pbkdf=pbkdf)
309307
ret.__class__ = cls
310308
return ret

0 commit comments

Comments
 (0)