Skip to content

crypto: Add support for LUKS Reencryption. #1065

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
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
11 changes: 11 additions & 0 deletions docs/libblockdev-sections.txt
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,17 @@ bd_crypto_luks_header_restore
bd_crypto_luks_set_label
bd_crypto_luks_set_uuid
bd_crypto_luks_convert
BDCryptoLUKSReencryptParams
bd_crypto_luks_reencrypt_params_copy
bd_crypto_luks_reencrypt_params_free
bd_crypto_luks_reencrypt_params_new
bd_crypto_luks_reencrypt
bd_crypto_luks_encrypt
bd_crypto_luks_reencrypt_status
bd_crypto_luks_reencrypt_resume
BDCryptoLUKSReencryptProgFunc
BDCryptoLUKSReencryptStatus
BDCryptoLUKSReencryptMode
BDCryptoLUKSInfo
bd_crypto_luks_info_free
bd_crypto_luks_info_copy
Expand Down
211 changes: 211 additions & 0 deletions src/lib/plugin_apis/crypto.api
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ typedef enum {
BD_CRYPTO_ERROR_KEYRING,
BD_CRYPTO_ERROR_KEYFILE_FAILED,
BD_CRYPTO_ERROR_INVALID_CONTEXT,
BD_CRYPTO_ERROR_REENCRYPT_FAILED,
} BDCryptoError;

typedef enum {
Expand Down Expand Up @@ -1111,6 +1112,216 @@ gboolean bd_crypto_luks_set_uuid (const gchar *device, const gchar *uuid, GError
*/
gboolean bd_crypto_luks_convert (const gchar *device, BDCryptoLUKSVersion target_version, GError **error);


#define BD_CRYPTO_TYPE_LUKS_REENCRYPT_PARAMS (bd_crypto_luks_reencrypt_params_get_type ())
GType bd_crypto_luks_reencrypt_params_get_type();

/**
* BDCryptoLUKSReencryptParams:
* @key_size new volume key size if @new_volume_key is true. Ignored otherwise
* @cipher new cipher
* @cipher_mode new cipher mode
* @resilience resilience mode to be used during reencryption
* @hash used hash for "checksum" resilience type, ignored otherwise
* @max_hotzone_size max hotzone size
* @sector_size sector size. Note that 0 is not a valid value
* @new_volume_key whether to generate a new volume key or keep the existing one
* @offline whether to perform an offline or online reencryption,
* i.e. whether a device is active in the time of reencryption or not
* @pbkdf PBDKF function parameters for a new keyslot
*/
typedef struct BDCryptoLUKSReencryptParams {
guint32 key_size;
gchar *cipher;
gchar *cipher_mode;
gchar *resilience;
gchar *hash;
guint64 max_hotzone_size;
guint32 sector_size;
gboolean new_volume_key;
gboolean offline;
BDCryptoLUKSPBKDF *pbkdf;
} BDCryptoLUKSReencryptParams;

/**
* bd_crypto_luks_reencrypt_params_copy: (skip)
* @params: (nullable): %BDCryptoLUKSReencryptParams to copy
*
* Creates a copy of @params.
*/
BDCryptoLUKSReencryptParams* bd_crypto_luks_reencrypt_params_copy (BDCryptoLUKSReencryptParams* params) {
if (params == NULL)
return NULL;

BDCryptoLUKSReencryptParams *new_params = g_new0 (BDCryptoLUKSReencryptParams, 1);
new_params->key_size = params->key_size;
new_params->cipher = g_strdup (params->cipher);
new_params->cipher_mode = g_strdup (params->cipher_mode);
new_params->resilience = g_strdup (params->resilience);
new_params->hash = g_strdup (params->hash);
new_params->max_hotzone_size = params->max_hotzone_size;
new_params->sector_size = params->sector_size;
new_params->new_volume_key = params->new_volume_key;
new_params->offline = params->offline;
new_params->pbkdf = bd_crypto_luks_pbkdf_copy(params->pbkdf);

return new_params;
}

/**
* bd_crypto_luks_reencrypt_params_free: (skip)
* @params: (nullable): %BDCryptoLUKSReencryptParams to free
*
* Frees @params.
*/
void bd_crypto_luks_reencrypt_params_free (BDCryptoLUKSReencryptParams* params) {
if (params == NULL)
return;

g_free (params->cipher);
g_free (params->cipher_mode);
g_free (params->resilience);
g_free (params->hash);
bd_crypto_luks_pbkdf_free(params->pbkdf);
}
Copy link
Member

Choose a reason for hiding this comment

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

You forgot to free params itself ;-)


/**
* bd_crypto_luks_reencrypt_params_new: (constructor)
* @key_size new volume key size if @new_volume_key is true. Ignored otherwise
* @cipher: (nullable): new cipher
* @cipher_mode: (nullable): new cipher mode
* @resilience: (nullable): resilience mode to be used during reencryption
* @hash: (nullable): used hash for "checksum" resilience type, ignored otherwise
* @max_hotzone_size max hotzone size
* @sector_size sector size. Note that 0 is not a valid value
* @new_volume_key whether to generate a new volume key or keep the existing one
* @offline whether to perform an offline or online reencryption,
* i.e. whether a device is active in the time of reencryption or not
* @pbkdf: (nullable): PBDKF function parameters for a new keyslot
*/
BDCryptoLUKSReencryptParams* bd_crypto_luks_reencrypt_params_new (guint32 key_size, gchar *cipher, gchar *cipher_mode, gchar *resilience, gchar *hash, guint64 max_hotzone_size, guint32 sector_size, gboolean new_volume_key, gboolean offline, BDCryptoLUKSPBKDF *pbkdf) {
BDCryptoLUKSReencryptParams *ret = g_new0 (BDCryptoLUKSReencryptParams, 1);
ret->key_size = key_size;
ret->cipher = g_strdup (cipher);
ret->cipher_mode = g_strdup (cipher_mode);
ret->resilience = g_strdup (resilience);
ret->hash = g_strdup (hash);
ret->max_hotzone_size = max_hotzone_size;
ret->sector_size = sector_size;
ret->new_volume_key = new_volume_key;
ret->offline = offline;
ret->pbkdf = bd_crypto_luks_pbkdf_copy(pbkdf);

return ret;
}

GType bd_crypto_luks_reencrypt_params_get_type () {
static GType type = 0;

if (G_UNLIKELY(type == 0)) {
type = g_boxed_type_register_static("BDCryptoLUKSReencryptParams",
(GBoxedCopyFunc) bd_crypto_luks_reencrypt_params_copy,
(GBoxedFreeFunc) bd_crypto_luks_reencrypt_params_free);
}

return type;
}

/**
* BDCryptoLUKSReencryptProgFunc:
* @size size of the device being reencrypted
* @offset current offset
*
* A callback function called during reencryption to report progress. Also used to possibly stop reencryption.
*
* Returns: 0, if the reencryption should continue.
* A non-zero value to stop the reencryption
*/
typedef int (*BDCryptoLUKSReencryptProgFunc) (guint64 size, guint64 offset);
Copy link
Member

Choose a reason for hiding this comment

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

Please provide another argument - gpointer user_data. This is a good practice with Glib callbacks to pass through user data that the caller will use for a back-reference. Nothing special, just pass the value. All the functions that take BDCryptoLUKSReencryptProgFunc in will need to be extended with this gpointer user_data argument as well.

I wasn't able to find any nice writeup, this is perhaps close to what I'm talking about: https://www.geany.org/manual/gtk/gtk-tutorial/x159.html

Better to talk to @vojtechtrefny first, I don't remember exactly what is the style of callbacks in libblockdev.


typedef enum {
BD_CRYPTO_LUKS_REENCRYPT_NONE = 0,
BD_CRYPTO_LUKS_REENCRYPT_CLEAN,
BD_CRYPTO_LUKS_REENCRYPT_CRASH,
BD_CRYPTO_LUKS_REENCRYPT_INVALID
Comment on lines +1243 to +1246
Copy link
Member

Choose a reason for hiding this comment

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

Please also document enum values.

} BDCryptoLUKSReencryptStatus;
Copy link
Member

Choose a reason for hiding this comment

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

Looking at bd_crypto_luks_reencrypt_status(), does it make sense to include status values like "running" or "finished"?


typedef enum {
BD_CRYPTO_LUKS_REENCRYPT = 0,
BD_CRYPTO_LUKS_ENCRYPT,
BD_CRYPTO_LUKS_DECRYPT,
} BDCryptoLUKSReencryptMode;

/**
* bd_crypto_luks_reencrypt:
* @device: device to reencrypt. Either an active device name for online reencryption, or a block device for offline reencryption.
* Must match the @params's "offline" parameter
* @params: reencryption parameters
* @context: key slot context to unlock @device. The newly created keyslot will use the same context
* @prog_func: (scope call) (nullable): progress function. Also used to possibly stop reencryption
* @error: (out) (optional): place to store error (if any)
*
* Reencrypts @device. This could mean a change of cipher, cipher mode, or volume key, based on @params
*
* Returns: true, if the reencryption was successful or gracefully stopped with @prog_func.
* false, if an error occurred.
Comment on lines +1266 to +1267
Copy link
Member

Choose a reason for hiding this comment

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

As this is a long-running operation, I (as an user) would like to know whether this is a blocking call or not. I.e. whether this is a synchronous operation or just a trigger to start an operation in the background. If this is a blocking call, would be good to warn user that this may take hours or days to complete.

*
* Supported @context types for this function: passphrase
*
* Tech category: %BD_CRYPTO_TECH_LUKS-%BD_CRYPTO_TECH_MODE_MODIFY
*/
gboolean bd_crypto_luks_reencrypt(const gchar *device, BDCryptoLUKSReencryptParams *params, BDCryptoKeyslotContext *context, BDCryptoLUKSReencryptProgFunc prog_func, GError **error);

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

/**
* bd_crypto_luks_reencrypt_status:
* @device: an active device name or a block device
* @mode: (out): the exact operation in the "reencryption family"
* Has no meaning if the return value is BD_CRYPTO_LUKS_REENCRYPT_NONE
* @error: (out) (optional): place to store error (if any)
*
* Returns: state of @device's reencryption
*
* Tech category: %BD_CRYPTO_TECH_LUKS-%BD_CRYPTO_TECH_MODE_QUERY
*/
BDCryptoLUKSReencryptStatus bd_crypto_luks_reencrypt_status (const gchar *device, BDCryptoLUKSReencryptMode *mode, GError **error);

/**
* bd_crypto_luks_reencrypt_resume:
* @device: device with a stopped reencryption. An active device name or a block device
* @context: key slot context to unlock @device
* @prog_func: (scope call) (nullable): progress function. Also used to possibly stop reencryption
* @error: (out) (optional): place to store error (if any)
*
* Returns: true, if the reencryption finished successfully or was gracefully stopped with @prog_func.
* false, if an error occurred.
Comment on lines +1318 to +1319
Copy link
Member

Choose a reason for hiding this comment

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

Just like above, would be good to know if this call blocks until the operation is finished or if it just resumes an operation in the background.

*
* Tech category: %BD_CRYPTO_TECH_LUKS-%BD_CRYPTO_TECH_MODE_MODIFY
*/
gboolean bd_crypto_luks_reencrypt_resume (const gchar *device, BDCryptoKeyslotContext *context, BDCryptoLUKSReencryptProgFunc prog_func, GError **error);

/**
* bd_crypto_luks_info:
* @device: a device to get information about
Expand Down
Loading
Loading