Skip to content
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

Add support for VeraCrypt volumes and extend support for TCRYPT volumes #320

Merged
merged 11 commits into from
Mar 20, 2018
2 changes: 2 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ LIBBLOCKDEV_PKG_CHECK_MODULES([KMOD], [libkmod >= 19])

AS_IF([test "x$with_crypto" != "xno"],
[LIBBLOCKDEV_PKG_CHECK_MODULES([CRYPTSETUP], [libcryptsetup >= 1.6.7])
AS_IF([$PKG_CONFIG --atleast-version=2.0 libcryptsetup],
[AC_DEFINE([LIBCRYPTSETUP_PIM_SUPPORT])], [])
LIBBLOCKDEV_PKG_CHECK_MODULES([NSS], [nss >= 3.18.0])
LIBBLOCKDEV_CHECK_HEADER([volume_key/libvolume_key.h], [$GLIB_CFLAGS $NSS_CFLAGS], [libvolume_key.h not available])
],
Expand Down
2 changes: 2 additions & 0 deletions docs/libblockdev-sections.txt
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ DEFAULT_LUKS_KEYSIZE_BITS
DEFAULT_LUKS_CIPHER
bd_crypto_generate_backup_passphrase
bd_crypto_device_is_luks
bd_crypto_device_seems_encrypted
bd_crypto_luks_uuid
bd_crypto_luks_status
bd_crypto_luks_format
Expand All @@ -82,6 +83,7 @@ bd_crypto_luks_change_key
bd_crypto_luks_change_key_blob
bd_crypto_luks_resize
bd_crypto_tc_open
bd_crypto_tc_open_full
bd_crypto_tc_close
bd_crypto_escrow_device
BDCryptoTech
Expand Down
45 changes: 45 additions & 0 deletions src/lib/plugin_apis/crypto.api
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,31 @@ gboolean bd_crypto_luks_change_key_blob (const gchar *device, const guint8 *pass
*/
gboolean bd_crypto_luks_resize (const gchar *luks_device, guint64 size, GError **error);

/**
* bd_crypto_device_seems_encrypted:
* @device: the queried device
* @error: (out): place to store error (if any)
*
* Determines whether a block device seems to be encrypted.
*
* TCRYPT volumes are not easily identifiable, because they have no
* cleartext header, but are completely encrypted. This function is
* used to determine whether a block device is a candidate for being
* TCRYPT encrypted.
*
* To achieve this, we calculate the chi square value of the first
* 512 Bytes and treat devices with a chi square value between 136
* and 426 as candidates for being encrypted.
* For the reasoning, see: https://tails.boum.org/blueprint/veracrypt/
*
* Returns: %TRUE if the given @device seems to be encrypted or %FALSE if not or
* failed to determine (the @error) is populated with the error in such
* cases)
*
* Tech category: %BD_CRYPTO_TECH_TRUECRYPT-%BD_CRYPTO_TECH_MODE_QUERY
*/
gboolean bd_crypto_device_seems_encrypted (const gchar *device, GError **error);
Copy link
Member

Choose a reason for hiding this comment

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

Please add the new functions to docs/libblockdev-sections.txt too.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in b8d8bd7.


/**
* bd_crypto_tc_open:
* @device: the device to open
Expand All @@ -309,6 +334,26 @@ gboolean bd_crypto_luks_resize (const gchar *luks_device, guint64 size, GError *
*/
gboolean bd_crypto_tc_open (const gchar *device, const gchar *name, const guint8* pass_data, gsize data_len, gboolean read_only, GError **error);

/**
* bd_crypto_tc_open_full:
* @device: the device to open
* @name: name for the TrueCrypt/VeraCrypt device
* @pass_data: (array length=data_len): a passphrase for the TrueCrypt/VeraCrypt volume (may contain arbitrary binary data)
* @data_len: length of the @pass_data buffer
* @read_only: whether to open as read-only or not (meaning read-write)
* @keyfiles: (allow-none) (array zero-terminated=1): paths to the keyfiles for the TrueCrypt/VeraCrypt volume
* @hidden: whether a hidden volume inside the volume should be opened
* @system: whether to try opening as an encrypted system (with boot loader)
* @veracrypt: whether to try VeraCrypt modes (TrueCrypt modes are tried anyway)
* @veracrypt_pim: VeraCrypt PIM value
* @error: (out): place to store error (if any)
*
* Returns: whether the @device was successfully opened or not
*
* Tech category: %BD_CRYPTO_TECH_TRUECRYPT-%BD_CRYPTO_TECH_MODE_OPEN_CLOSE
*/
gboolean bd_crypto_tc_open_full (const gchar *device, const gchar *name, const guint8* pass_data, gsize data_len, const gchar **keyfiles, gboolean hidden, gboolean system, gboolean veracrypt, guint32 veracrypt_pim, gboolean read_only, GError **error);

/**
* bd_crypto_tc_close:
* @tc_device: TrueCrypt/VeraCrypt device to close
Expand Down
119 changes: 118 additions & 1 deletion src/plugins/crypto.c
Original file line number Diff line number Diff line change
Expand Up @@ -958,6 +958,71 @@ gboolean bd_crypto_luks_resize (const gchar *luks_device, guint64 size, GError *
return TRUE;
}

/**
* bd_crypto_device_seems_encrypted:
* @device: the queried device
* @error: (out): place to store error (if any)
*
* Determines whether a block device seems to be encrypted.
*
* TCRYPT volumes are not easily identifiable, because they have no
* cleartext header, but are completely encrypted. This function is
* used to determine whether a block device is a candidate for being
* TCRYPT encrypted.
*
* To achieve this, we calculate the chi square value of the first
* 512 Bytes and treat devices with a chi square value between 136
* and 426 as candidates for being encrypted.
* For the reasoning, see: https://tails.boum.org/blueprint/veracrypt/#detection
*
* Returns: %TRUE if the given @device seems to be encrypted or %FALSE if not or
* failed to determine (the @error) is populated with the error in such
* cases)
*
* Tech category: %BD_CRYPTO_TECH_TRUECRYPT-%BD_CRYPTO_TECH_MODE_QUERY
*/
gboolean bd_crypto_device_seems_encrypted (const gchar *device, GError **error) {
gint fd = -1;
guchar buf[BD_CRYPTO_CHI_SQUARE_BYTES_TO_CHECK];
guint symbols[256] = {0};
gfloat chi_square = 0.0;
gfloat e = (gfloat) sizeof(buf) / (gfloat) 256.0;
guint i;
guint64 progress_id = 0;
gchar *msg = NULL;

msg = g_strdup_printf ("Started determining if device '%s' seems to be encrypted", device);
progress_id = bd_utils_report_started (msg);
g_free (msg);

fd = open (device, O_RDONLY);
if (fd == -1) {
g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to open device");
bd_utils_report_finished (progress_id, (*error)->message);
return FALSE;
}

if (read (fd, buf, sizeof(buf)) != sizeof(buf)) {
g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to read device");
bd_utils_report_finished (progress_id, (*error)->message);
close(fd);
return FALSE;
}

close(fd);

/* Calculate Chi Square */
for (i = 0; i < sizeof(buf); i++)
/* This is safe because the max value of buf[i] is < sizeof(symbols). */
symbols[buf[i]]++;
for (i = 0; i < 256; i++)
chi_square += (symbols[i] - e) * (symbols[i] - e);
chi_square /= e;

bd_utils_report_finished (progress_id, "Completed");
return BD_CRYPTO_CHI_SQUARE_LOWER_LIMIT < chi_square && chi_square < BD_CRYPTO_CHI_SQUARE_UPPER_LIMIT;
}

/**
* bd_crypto_tc_open:
* @device: the device to open
Expand All @@ -972,17 +1037,47 @@ gboolean bd_crypto_luks_resize (const gchar *luks_device, guint64 size, GError *
* Tech category: %BD_CRYPTO_TECH_TRUECRYPT-%BD_CRYPTO_TECH_MODE_OPEN_CLOSE
*/
gboolean bd_crypto_tc_open (const gchar *device, const gchar *name, const guint8* pass_data, gsize data_len, gboolean read_only, GError **error) {
return bd_crypto_tc_open_full (device, name, pass_data, data_len, NULL, FALSE, FALSE, FALSE, 0, read_only, error);
}

/**
* bd_crypto_tc_open_full:
* @device: the device to open
* @name: name for the TrueCrypt/VeraCrypt device
* @pass_data: (array length=data_len): a passphrase for the TrueCrypt/VeraCrypt volume (may contain arbitrary binary data)
* @data_len: length of the @pass_data buffer
* @read_only: whether to open as read-only or not (meaning read-write)
* @keyfiles: (allow-none) (array zero-terminated=1): paths to the keyfiles for the TrueCrypt/VeraCrypt volume
* @hidden: whether a hidden volume inside the volume should be opened
* @system: whether to try opening as an encrypted system (with boot loader)
* @veracrypt: whether to try VeraCrypt modes (TrueCrypt modes are tried anyway)
* @veracrypt_pim: VeraCrypt PIM value (only used if @veracrypt is %TRUE; only supported if compiled against libcryptsetup >= 2.0)
* @error: (out): place to store error (if any)
*
* Returns: whether the @device was successfully opened or not
*
* Tech category: %BD_CRYPTO_TECH_TRUECRYPT-%BD_CRYPTO_TECH_MODE_OPEN_CLOSE
*/
gboolean bd_crypto_tc_open_full (const gchar *device, const gchar *name, const guint8* pass_data, gsize data_len, const gchar **keyfiles, gboolean hidden, gboolean system, gboolean veracrypt, guint32 veracrypt_pim, gboolean read_only, GError **error) {
struct crypt_device *cd = NULL;
gint ret = 0;
guint64 progress_id = 0;
gchar *msg = NULL;
struct crypt_params_tcrypt params = ZERO_INIT;
gsize keyfiles_count = 0;
guint i;

msg = g_strdup_printf ("Started opening '%s' TrueCrypt/VeraCrypt device", device);
progress_id = bd_utils_report_started (msg);
g_free (msg);

if (data_len == 0) {
if (keyfiles) {
for (i=0; *(keyfiles + i); i++) {
keyfiles_count = i;
}
}

if ((data_len == 0) && (keyfiles_count == 0)) {
g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_NO_KEY,
"No passphrase nor key file specified, cannot open.");
bd_utils_report_finished (progress_id, (*error)->message);
Expand All @@ -999,6 +1094,28 @@ gboolean bd_crypto_tc_open (const gchar *device, const gchar *name, const guint8

params.passphrase = (const char*) pass_data;
params.passphrase_size = data_len;
params.keyfiles = keyfiles;
params.keyfiles_count = keyfiles_count;

if (veracrypt)
params.flags |= CRYPT_TCRYPT_VERA_MODES;
if (hidden)
params.flags |= CRYPT_TCRYPT_HIDDEN_HEADER;
if (system)
params.flags |= CRYPT_TCRYPT_SYSTEM_HEADER;

#ifndef LIBCRYPTSETUP_PIM_SUPPORT
if (veracrypt && veracrypt_pim != 0) {
g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_TECH_UNAVAIL,
"Compiled against a version of libcryptsetup that does not support the VeraCrypt PIM setting.");
bd_utils_report_finished (progress_id, (*error)->message);
return FALSE;
}
#else
if (veracrypt && veracrypt_pim != 0)
params.veracrypt_pim = veracrypt_pim;
#endif

ret = crypt_load (cd, CRYPT_TCRYPT, &params);
if (ret != 0) {
g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE,
Expand Down
6 changes: 6 additions & 0 deletions src/plugins/crypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@

#define BD_CRYPTO_LUKS_METADATA_SIZE (2 MiB)

#define BD_CRYPTO_CHI_SQUARE_LOWER_LIMIT 136
#define BD_CRYPTO_CHI_SQUARE_UPPER_LIMIT 426
#define BD_CRYPTO_CHI_SQUARE_BYTES_TO_CHECK 512
Copy link
Member

Choose a reason for hiding this comment

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

This looks like something that might be better as parameters for bd_crypto_device_seems_encrypted. I don't know if there is a change that these will change in feature, but it might be better to not have to recompile libblockdev just because you want to change these limits.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Mmh, I don't really see the use case for changing these values - especially not in the higher level applications which will use this function. Why do you think they should be changeable without recompiling?

Copy link
Member

Choose a reason for hiding this comment

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

I don't know, but the limits just look like something that users of libblockdev might want to change eventually. But if you think this isn't something that makes sense to change from e.g. udisks, we can keep it this way.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't see why these values should be changed from udisks. We explained at https://tails.boum.org/blueprint/veracrypt/#detection why, with these values, the chi-squared test should only produce false negatives with negligible probability (1 in 10 billion). And devices with (non-encrypted) filesystem headers will never pass the test. The only reason I can think of to change these values is to handle strange filesystems, which don't have their header at least partly in the first 512 Bytes (I don't know of any such filesystem), but then I think we should handle this in libblockdev and not udisks or similar.

Copy link
Member

Choose a reason for hiding this comment

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

Ok, you convinced me, we can keep it this way.


GQuark bd_crypto_error_quark (void);
#define BD_CRYPTO_ERROR bd_crypto_error_quark ()
typedef enum {
Expand Down Expand Up @@ -80,7 +84,9 @@ gboolean bd_crypto_luks_change_key (const gchar *device, const gchar *pass, cons
gboolean bd_crypto_luks_change_key_blob (const gchar *device, const guint8 *pass_data, gsize data_len, const guint8 *npass_data, gsize ndata_len, GError **error);
gboolean bd_crypto_luks_resize (const gchar *device, guint64 size, GError **error);

gboolean bd_crypto_device_seems_encrypted (const gchar *device, GError **error);
gboolean bd_crypto_tc_open (const gchar *device, const gchar *name, const guint8* pass_data, gsize data_len, gboolean read_only, GError **error);
gboolean bd_crypto_tc_open_full (const gchar *device, const gchar *name, const guint8* pass_data, gsize data_len, const gchar **keyfiles, gboolean hidden, gboolean system, gboolean veracrypt, guint32 veracrypt_pim, gboolean read_only, GError **error);
gboolean bd_crypto_tc_close (const gchar *tc_device, GError **error);

gboolean bd_crypto_escrow_device (const gchar *device, const gchar *passphrase, const gchar *cert_data, const gchar *directory, const gchar *backup_passphrase, GError **error);
Expand Down