Add libLUKS2.

This commit is contained in:
Milan Broz
2017-08-25 20:26:20 +02:00
parent 00b103c85c
commit 9f2727bb77
27 changed files with 9281 additions and 404 deletions

View File

@@ -34,6 +34,8 @@ AC_ENABLE_STATIC(no)
LT_INIT
PKG_PROG_PKG_CONFIG
AC_C_RESTRICT
AC_HEADER_DIRENT
AC_HEADER_STDC
AC_CHECK_HEADERS(fcntl.h malloc.h inttypes.h sys/ioctl.h sys/mman.h \
@@ -328,6 +330,9 @@ if test "x$enable_udev" = xyes; then
fi
LIBS=$saved_LIBS
dnl Check for JSON-C used in LUKS2
PKG_CHECK_MODULES([JSON_C], [json-c])
dnl Crypto backend configuration.
AC_ARG_WITH([crypto_backend],
AS_HELP_STRING([--with-crypto_backend=BACKEND], [crypto backend (gcrypt/openssl/nss/kernel/nettle) [gcrypt]]),
@@ -363,8 +368,8 @@ AM_CONDITIONAL(CRYPTO_BACKEND_NETTLE, test $with_crypto_backend = nettle)
AM_CONDITIONAL(CRYPTO_INTERNAL_PBKDF2, test $use_internal_pbkdf2 = 1)
AC_DEFINE_UNQUOTED(USE_INTERNAL_PBKDF2, [$use_internal_pbkdf2], [Use internal PBKDF2])
AC_ARG_ENABLE(argon2, AS_HELP_STRING([--enable-argon2],
[enable internal implementation of Argon2 PBKDF]))
AC_ARG_ENABLE(argon2, AS_HELP_STRING([--disable-argon2],
[disable internal implementation of Argon2 PBKDF]),[], [enable_argon2=yes])
AM_CONDITIONAL(CRYPTO_INTERNAL_ARGON2, test x$enable_argon2 = xyes)
if test x$enable_argon2 = xyes ; then
AC_DEFINE(USE_INTERNAL_ARGON2, 1, [Use internal Argon2])
@@ -414,6 +419,8 @@ AC_SUBST([CRYPTO_CFLAGS])
AC_SUBST([CRYPTO_LIBS])
AC_SUBST([CRYPTO_STATIC_LIBS])
AC_SUBST([JSON_C_LIBS])
AC_SUBST([LIBCRYPTSETUP_VERSION])
AC_SUBST([LIBCRYPTSETUP_VERSION_INFO])
@@ -480,7 +487,12 @@ CS_STR_WITH([luks1-hash], [hash function for LUKS1 header], [sha256])
CS_STR_WITH([luks1-cipher], [cipher for LUKS1], [aes])
CS_STR_WITH([luks1-mode], [cipher mode for LUKS1], [xts-plain64])
CS_NUM_WITH([luks1-keybits],[key length in bits for LUKS1], [256])
CS_NUM_WITH([luks1-iter-time],[PBKDF2 iteration time for LUKS1 (in ms)], [2000])
CS_STR_WITH([luks2-pbkdf], [Default PBKDF algorithm (pbkdf2 or argon2i/argon2id) for LUKS2], [argon2i])
CS_NUM_WITH([luks1-iter-time], [PBKDF2 iteration time for LUKS1 (in ms)], [2000])
CS_NUM_WITH([luks2-iter-time], [Argon2 PBKDF iteration time for LUKS2 (in ms)], [800])
CS_NUM_WITH([luks2-memory-kb], [Argon2 PBKDF memory cost for LUKS2 (in kB)], [1024])
CS_NUM_WITH([luks2-parallel-threads],[Argon2 PBKDF max parallel cost for LUKS2 (if CPUs available)], [4])
CS_STR_WITH([loopaes-cipher], [cipher for loop-AES mode], [aes])
CS_NUM_WITH([loopaes-keybits],[key length in bits for loop-AES mode], [256])
@@ -513,6 +525,7 @@ lib/libcryptsetup.pc
lib/crypto_backend/Makefile
lib/crypto_backend/argon2/Makefile
lib/luks1/Makefile
lib/luks2/Makefile
lib/loopaes/Makefile
lib/verity/Makefile
lib/tcrypt/Makefile

View File

@@ -1,4 +1,4 @@
SUBDIRS = crypto_backend luks1 loopaes verity tcrypt integrity
SUBDIRS = crypto_backend luks1 luks2 loopaes verity tcrypt integrity
moduledir = $(libdir)/cryptsetup
@@ -9,6 +9,7 @@ AM_CPPFLAGS = -include config.h \
-I$(top_srcdir) \
-I$(top_srcdir)/lib/crypto_backend \
-I$(top_srcdir)/lib/luks1 \
-I$(top_srcdir)/lib/luks2 \
-I$(top_srcdir)/lib/loopaes \
-I$(top_srcdir)/lib/verity \
-I$(top_srcdir)/lib/tcrypt \
@@ -24,6 +25,7 @@ lib_LTLIBRARIES = libcryptsetup.la
common_ldadd = \
crypto_backend/libcrypto_backend.la \
luks1/libluks1.la \
luks2/libluks2.la \
loopaes/libloopaes.la \
verity/libverity.la \
tcrypt/libtcrypt.la \
@@ -41,6 +43,7 @@ libcryptsetup_la_LIBADD = \
@UUID_LIBS@ \
@DEVMAPPER_LIBS@ \
@CRYPTO_LIBS@ \
@JSON_C_LIBS@ \
$(common_ldadd)

View File

@@ -253,9 +253,9 @@ int INTEGRITY_format(struct crypt_device *cd,
dmdi.u.integrity.journal_commit_time = params->journal_commit_time;
dmdi.u.integrity.interleave_sectors = params->interleave_sectors;
dmdi.u.integrity.buffer_sectors = params->buffer_sectors;
dmdi.u.integrity.integrity = params->integrity;
dmdi.u.integrity.journal_integrity = params->journal_integrity;
dmdi.u.integrity.journal_crypt = params->journal_crypt;
dmdi.u.integrity.integrity = params->integrity;
}
uuid_generate(tmp_uuid_bin);

View File

@@ -323,6 +323,8 @@ int crypt_volume_key_keyring(struct crypt_device *cd, int enable);
#define CRYPT_PLAIN "PLAIN"
/** LUKS version 1 header on-disk */
#define CRYPT_LUKS1 "LUKS1"
/** LUKS version 2 header on-disk */
#define CRYPT_LUKS2 "LUKS2"
/** loop-AES compatibility mode */
#define CRYPT_LOOPAES "LOOPAES"
/** dm-verity mode */
@@ -464,8 +466,8 @@ struct crypt_params_integrity {
uint32_t tag_size; /**< tag size per-sector in bytes */
uint32_t sector_size; /**< sector size in bytes */
uint32_t buffer_sectors; /**< number of sectors in one buffer */
const char *integrity; /**< integrity algorithm */
uint32_t integrity_key_size; /**< integrity key size, info only */
const char *integrity; /**< integrity algorithm, NULL for LUKS2 */
uint32_t integrity_key_size; /**< integrity key size, info only, 0 for LUKS2 */
const char *journal_integrity; /**< journal integrity algorithm */
const char *journal_integrity_key; /**< journal integrity key, only for crypt_load */
@@ -476,6 +478,26 @@ struct crypt_params_integrity {
uint32_t journal_crypt_key_size; /**< journal crypt key size, only for crypt_load */
};
/**
* Structure used as parameter for LUKS2 device type.
*
* @see crypt_format, crypt_load
*
* @note during crypt_format @e data_device attribute determines
* if the LUKS2 header is separated from encrypted payload device
*
*/
struct crypt_params_luks2 {
const struct crypt_pbkdf_type *pbkdf; /**< PBKDF (and hash) parameters or @e NULL*/
const char *integrity; /**< integrity algorithm or @e NULL */
const struct crypt_params_integrity *integrity_params; /**< Data integrity parameters or @e NULL*/
size_t data_alignment; /**< data alignment in sectors, data offset is multiple of this */
const char *data_device; /**< detached encrypted data device or @e NULL */
uint32_t sector_size; /**< encryption sector size */
const char *label; /**< header label or @e NULL*/
const char *subsystem; /**< header subsystem label or @e NULL*/
};
/** @} */
/**
@@ -508,6 +530,21 @@ int crypt_format(struct crypt_device *cd,
size_t volume_key_size,
void *params);
/**
* Convert to new type for already existing device.
*
* @param cd crypt device handle
* @param type type of device (optional params struct must be of this type)
* @param params crypt type specific parameters (see @link crypt_type @endlink)
*
* @returns 0 on success or negative errno value otherwise.
*
* @note Currently, only LUKS1->LUKS2 conversion is supported
*/
int crypt_convert(struct crypt_device *cd,
const char *type,
void *params);
/**
* Set new UUID for already existing device.
*
@@ -521,6 +558,21 @@ int crypt_format(struct crypt_device *cd,
int crypt_set_uuid(struct crypt_device *cd,
const char *uuid);
/**
* Set new labels (label and subsystem) for already existing device.
*
* @param cd crypt device handle
* @param label requested label or @e NULL
* @param subsystem requested subsystem label or @e NULL
*
* @returns 0 on success or negative errno value otherwise.
*
* @note Currently, only LUKS2 device type is supported
*/
int crypt_set_label(struct crypt_device *cd,
const char *label,
const char *subsystem);
/**
* Load crypt device parameters from on-disk header.
*
@@ -754,6 +806,37 @@ int crypt_keyslot_add_by_volume_key(struct crypt_device *cd,
const char *passphrase,
size_t passphrase_size);
/* create keyslot with volume key not associated with current dm-crypt segment */
#define CRYPT_VOLUME_KEY_NO_SEGMENT (1 << 0)
/**
* Add key slot using provided key.
*
* @pre @e cd contains initialized and formatted LUKS2 device context
*
* @param cd crypt device handle
* @param keyslot requested keyslot or CRYPT_ANY_SLOT
* @param volume_key provided volume key or @e NULL (see note below)
* @param volume_key_size size of volume_key
* @param passphrase passphrase for new keyslot, @e NULL for query
* @param passphrase_size size of passphrase
*
* @return allocated key slot number or negative errno otherwise.
*
* @note in case volume_key is @e NULL following first matching rule will apply:
* a) if cd is device handle used in crypt_format() by current process, the volume
* key generated (passed) to crypt_format() will be stored in keyslot.
* b) if CRYPT_VOLUME_KEY_NO_SEGMENT flag is raised the new volume_key will be
* generated and stored in keyslot.
*/
int crypt_keyslot_add_by_key(struct crypt_device *cd,
int keyslot,
const char *volume_key,
size_t volume_key_size,
const char *passphrase,
size_t passphrase_size,
uint32_t flags);
/**
* Destroy (and disable) key slot.
*
@@ -770,6 +853,20 @@ int crypt_keyslot_destroy(struct crypt_device *cd, int keyslot);
/** @} */
/**
* @defgroup areqs LUKS2 header requirements
*
* LUKS2 requirements flags
*
* @addtogroup areqs
* @{
*/
#define CRYPT_REQUIREMENT_UNKNOWN (1 << 31)
/** unknown requirement in header (output only) */
/* @} */
/**
* @defgroup aflags Device runtime attributes
*
@@ -807,6 +904,8 @@ int crypt_keyslot_destroy(struct crypt_device *cd, int keyslot);
#define CRYPT_ACTIVATE_NO_JOURNAL (1 << 12)
/** dm-integrity: recovery mode - no journal, no integrity checks */
#define CRYPT_ACTIVATE_RECOVERY (1 << 13)
/** ignore persistently stored flags */
#define CRYPT_ACTIVATE_IGNORE_PERSISTENT (1 << 14)
/**
* Active device runtime attributes
@@ -832,6 +931,49 @@ int crypt_get_active_device(struct crypt_device *cd,
const char *name,
struct crypt_active_device *cad);
/**
* Persistent flags type
*/
typedef enum {
CRYPT_FLAGS_ACTIVATION, /**< activation flags */
CRYPT_FLAGS_REQUIREMENTS /**< requirements flags */
} crypt_flags_type;
/**
* Set persistent flags.
*
* @param cd crypt device handle (can be @e NULL)
* @param type type to set (CRYPT_FLAGS_ACTIVATION or CRYPT_FLAGS_REQUIREMENTS)
* @param flags flags to set
*
* @return @e 0 on success or negative errno value otherwise
*
* @note Valid only for LUKS2.
*
* @note Not all activation flags can be stored. Only ALLOW_DISCARD,
* SAME_CPU_CRYPT, SUBMIT_FROM_CRYPT_CPU and NO_JOURNAL can be
* stored persistently.
*
* @note Only requirements flags recognised by current library may be set.
* CRYPT_REQUIREMENT_FLAG is illegal (output only) in set operation.
*/
int crypt_persistent_flags_set(struct crypt_device *cd,
crypt_flags_type type,
uint32_t flags);
/**
* Get persistent flags stored in header.
*
* @param cd crypt device handle (can be @e NULL)
* @param type flags type to retrieve (CRYPT_FLAGS_ACTIVATION or CRYPT_FLAGS_REQUIREMENTS)
* @param flags reference to output variable
*
* @return @e 0 on success or negative errno value otherwise
*/
int crypt_persistent_flags_get(struct crypt_device *cd,
crypt_flags_type type,
uint32_t *flags);
/** @} */
/**
@@ -1226,7 +1368,37 @@ typedef enum {
*
*/
crypt_keyslot_info crypt_keyslot_status(struct crypt_device *cd, int keyslot);
/** @} */
/**
* Crypt keyslot priority
*/
typedef enum {
CRYPT_SLOT_PRIORITY_INVALID =-1, /**< no such slot */
CRYPT_SLOT_PRIORITY_IGNORE = 0, /**< CRYPT_ANY_SLOT will ignore it for open */
CRYPT_SLOT_PRIORITY_NORMAL = 1, /**< default priority, tried after preferred */
CRYPT_SLOT_PRIORITY_PREFER = 2, /**< will try to open first */
} crypt_keyslot_priority;
/**
* Get keyslot priority (LUKS2)
*
* @param cd crypt device handle
* @param keyslot keyslot number
*
* @return value defined by crypt_keyslot_priority
*/
crypt_keyslot_priority crypt_keyslot_get_priority(struct crypt_device *cd, int keyslot);
/**
* Set keyslot priority (LUKS2)
*
* @param cd crypt device handle
* @param keyslot keyslot number
* @param priority priority defined in crypt_keyslot_priority
*
* @return @e 0 on success or negative errno value otherwise.
*/
int crypt_keyslot_set_priority(struct crypt_device *cd, int keyslot, crypt_keyslot_priority priority);
/**
* Get number of keyslots supported for device type.
@@ -1253,6 +1425,8 @@ int crypt_keyslot_area(struct crypt_device *cd,
int keyslot,
uint64_t *offset,
uint64_t *length);
/** @} */
/**
* Backup header and keyslots to file.
@@ -1314,6 +1488,16 @@ void crypt_set_debug_level(int level);
/** @} */
/**
* @defgroup keyfiles Keyfile utilities
*
* Utilities to handle keyfiles
*
* @addtogroup keyfiles
* @{
*
*/
/**
* Read keyfile
*
@@ -1383,6 +1567,238 @@ int crypt_wipe(struct crypt_device *cd,
/** @} */
/**
* @defgroup tokens LUKS2 token wrapper access
*
* Utilities for handling tokens LUKS2
* Token is a device or a method how to read password for particular keyslot
* automatically. It can be chunk of data stored on hardware token or
* just a metadata how to generate password.
*
* @addtogroup tokens
* @{
*/
/** iterate through all tokens */
#define CRYPT_ANY_TOKEN -1
/**
* Get content of a token definition in JSON format.
*
* @param cd crypt device handle
* @param token token id
* @param json buffer with JSON
*
* @return allocated token id or negative errno otherwise.
*/
int crypt_token_json_get(struct crypt_device *cd,
int token,
const char **json);
/**
* Store content of a token definition in JSON format.
*
* @param cd crypt device handle
* @param token token id or @e CRYPT_ANY_TOKEN to allocate new one
* @param json buffer with JSON or @e NULL to remove token
*
* @return allocated token id or negative errno otherwise.
*
* @note The buffer must be in proper JSON format and must contain at least
* string "type" with slot type and array of string names "keyslots".
* Keyslots array contains assignments to particular slots and can be empty.
*/
int crypt_token_json_set(struct crypt_device *cd,
int token,
const char *json);
typedef enum {
CRYPT_TOKEN_INVALID, /**< token is invalid */
CRYPT_TOKEN_INACTIVE, /**< token is empty (free) */
CRYPT_TOKEN_INTERNAL, /**< active internal token with driver */
CRYPT_TOKEN_INTERNAL_UNKNOWN, /**< active internal token (reserved name) with missing token driver */
CRYPT_TOKEN_EXTERNAL, /**< active external (user defined) token with driver */
CRYPT_TOKEN_EXTERNAL_UNKNOWN, /**< active external (user defined) token with missing token driver */
} crypt_token_info;
/**
* Get info for specific token.
*
* @param cd crypt device handle
* @param token existing token id
* @param type pointer for returned type string
*
* @return token status info. For any returned status (besides CRYPT_TOKEN_INVALID
* and CRYPT_TOKEN_INACTIVE) and if type parameter is not NULL it will
* contain address of type string.
*
* @note if required, create a copy of string referenced in *type before calling next
* libcryptsetup API function. The reference may become invalid.
*/
crypt_token_info crypt_token_status(struct crypt_device *cd, int token, const char **type);
/**
* LUKS2 keyring token paramaters.
*
* @see crypt_token_builtin_set
*
*/
struct crypt_token_params_luks2_keyring {
const char *key_description;
};
/*
* Create a new luks2 keyring token.
*
* @param cd crypt device handle
* @param token token id or @e CRYPT_ANY_TOKEN to allocate new one
* @param params luks2 keyring token params
*
* @return allocated token id or negative errno otherwise.
*
*/
int crypt_token_luks2_keyring_set(struct crypt_device *cd,
int token,
const struct crypt_token_params_luks2_keyring *params);
/*
* Get LUKS2 keyring token params
*
* @param cd crypt device handle
* @param token existing luks2 keyring token id
* @param params returned luks2 keyring token params
*
* @return allocated token id or negative errno otherwise.
*
* @note do not call free() on params members. Members are valid only
* until next libcryptsetup function is called.
*/
int crypt_token_luks2_keyring_get(struct crypt_device *cd,
int token,
struct crypt_token_params_luks2_keyring *params);
/**
* Assign a token to particular keyslot.
* (There can be more keyslots assigned to one token id.)
*
* @param cd crypt device handle
* @param token token id
* @param keyslot keyslot to be assigned to token (CRYPT_ANY SLOT
* assigns all active keyslots to token)
*
* @return allocated token id or negative errno otherwise.
*/
int crypt_token_assign_keyslot(struct crypt_device *cd,
int token,
int keyslot);
/**
* Unassign a token from particular keyslot.
* (There can be more keyslots assigned to one token id.)
*
* @param cd crypt device handle
* @param token token id
* @param keyslot keyslot to be unassigned from token (CRYPT_ANY SLOT
* unassigns all active keyslots from token)
*
* @return allocated token id or negative errno otherwise.
*/
int crypt_token_unassign_keyslot(struct crypt_device *cd,
int token,
int keyslot);
/**
* Token handler open function prototype.
* This fuction retrieves password from a token and return allocated buffer
* containing this password. This buffer has to be deallocated by calling
* free() function and content should be wiped before deallocation.
*
* @param cd crypt device handle
* @param token token id
* @param buffer returned allocated buffer with password
* @param buffer_len length of the buffer
* @param usrptr user data in @link crypt_activate_by_token @endlink
*/
typedef int (*crypt_token_open_func) (
struct crypt_device *cd,
int token,
char **buffer,
size_t *buffer_len,
void *usrptr);
/**
* Token handler buffer free function prototype.
* This function is used by library to free the buffer with keyslot
* passphrase when it's no longer needed. If not defined the library
* overwrites buffer with zeroes and call free().
*
* @param buffer the buffer with keyslot passphrase
* @param buffer_len the buffer length
*/
typedef void (*crypt_token_buffer_free_func) (void *buffer, size_t buffer_len);
/**
* Token handler validate function prototype.
* This fuction validates JSON representation of user defined token for additional data
* specific for its token type. If defined in the handler, it's called
* during @link crypt_activate_by_token @endlink. It may also be called during
* @link crypt_token_set @endlink when appropriate token handler was registered before
* with @link crypt_token_register @endlink.
*
* @param cd crypt device handle
* @param json buffer with JSON
*/
typedef int (*crypt_token_validate_func) (struct crypt_device *cd, const char *json);
/**
* Token handler dump function prototype.
* This fuction is supposed to print token implementation specific details. It gets
* called during @link crypt_dump @endlink if token handler was registered before.
*
* @param cd crypt device handle
* @param json buffer with token JSON
*
* @note dump implementations are advised to use @link crypt_log @endlink function
* to dump token details.
*/
typedef void (*crypt_token_dump_func) (struct crypt_device *cd, const char *json);
typedef struct {
const char *name; /**< token handler name */
crypt_token_open_func open; /**< token handler open function */
crypt_token_buffer_free_func buffer_free; /**< token handler buffer_free function (optional) */
crypt_token_validate_func validate; /**< token handler validate function (optional) */
crypt_token_dump_func dump; /**< token handler dump function (optional) */
} crypt_token_handler;
/**
* Register token handler
*
* @param handler token handler to register
*
* @return @e 0 on success or negative errno value otherwise.
*/
int crypt_token_register(const crypt_token_handler *handler);
/**
* Activate device or check key using a token.
*
* @param cd crypt device handle
* @param name name of device to create, if @e NULL only check token
* @param token requested token to check or CRYPT_ANY_TOKEN to check all
* @param usrptr provided identification in callback
* @param flags activation flags
*
* @return unlocked key slot number or negative errno otherwise.
*/
int crypt_activate_by_token(struct crypt_device *cd,
const char *name,
int token,
void *usrptr,
uint32_t flags);
/** @} */
#ifdef __cplusplus
}
#endif

View File

@@ -8,11 +8,13 @@ CRYPTSETUP_2.0 {
crypt_set_confirm_callback;
crypt_set_iteration_time;
crypt_set_uuid;
crypt_set_label;
crypt_set_data_device;
crypt_memory_lock;
crypt_metadata_locking;
crypt_format;
crypt_convert;
crypt_load;
crypt_repair;
crypt_resize;
@@ -27,6 +29,22 @@ CRYPTSETUP_2.0 {
crypt_keyslot_add_by_keyfile;
crypt_keyslot_add_by_keyfile_offset;
crypt_keyslot_add_by_volume_key;
crypt_keyslot_add_by_key;
crypt_keyslot_set_priority;
crypt_keyslot_get_priority;
crypt_token_json_get;
crypt_token_json_set;
crypt_token_status;
crypt_token_luks2_keyring_get;
crypt_token_luks2_keyring_set;
crypt_token_assign_keyslot;
crypt_token_unassign_keyslot;
crypt_token_register;
crypt_activate_by_token;
crypt_keyslot_destroy;
crypt_activate_by_passphrase;
crypt_activate_by_keyfile;
@@ -55,6 +73,8 @@ CRYPTSETUP_2.0 {
crypt_get_type;
crypt_get_active_device;
crypt_persistent_flags_set;
crypt_persistent_flags_get;
crypt_set_rng_type;
crypt_get_rng_type;

23
lib/luks2/Makefile.am Normal file
View File

@@ -0,0 +1,23 @@
moduledir = $(libdir)/cryptsetup
noinst_LTLIBRARIES = libluks2.la
libluks2_la_CFLAGS = -Wall $(AM_CFLAGS) @CRYPTO_CFLAGS@
libluks2_la_SOURCES = \
luks2_disk_metadata.c \
luks2_json_format.c \
luks2_json_metadata.c \
luks2_luks1_convert.c \
luks2_digest.c \
luks2_digest_pbkdf2.c \
luks2_keyslot.c \
luks2_keyslot_luks2.c \
luks2_token_keyring.c \
luks2_token.c \
luks2_internal.h \
luks2.h
AM_CPPFLAGS = -include config.h \
-I$(top_srcdir)/lib \
-I$(top_srcdir)/lib/crypto_backend

353
lib/luks2/luks2.h Normal file
View File

@@ -0,0 +1,353 @@
/*
* LUKS - Linux Unified Key Setup v2
*
* Copyright (C) 2015-2017, Red Hat, Inc. All rights reserved.
* Copyright (C) 2015-2017, Milan Broz. All rights reserved.
*
* 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 _CRYPTSETUP_LUKS2_ONDISK_H
#define _CRYPTSETUP_LUKS2_ONDISK_H
#include <stdint.h>
#define LUKS2_MAGIC_1ST "LUKS\xba\xbe"
#define LUKS2_MAGIC_2ND "SKUL\xba\xbe"
#define LUKS2_MAGIC_L 6
#define LUKS2_UUID_L 40
#define LUKS2_LABEL_L 48
#define LUKS2_SALT_L 64
#define LUKS2_CHECKSUM_ALG_L 32
#define LUKS2_CHECKSUM_L 64
#define LUKS2_KEYSLOTS_MAX 32
#define LUKS2_TOKENS_MAX 32
#define LUKS2_BUILTIN_TOKEN_PREFIX "luks2-"
#define LUKS2_BUILTIN_TOKEN_PREFIX_LEN 6
#define LUKS2_TOKEN_KEYRING LUKS2_BUILTIN_TOKEN_PREFIX "keyring"
#define LUKS2_DIGEST_MAX 8
typedef int digests_t[LUKS2_DIGEST_MAX];
#define CRYPT_ANY_SEGMENT -1
#define CRYPT_DEFAULT_SEGMENT 0
#define CRYPT_DEFAULT_SEGMENT_STR "0"
/*
* LUKS2 header on-disk.
*
* Binary header is followed by JSON area.
* JSON area is followed by keyslot area and data area,
* these are described in JSON metadata.
*
* Note: uuid, csum_alg are intentionally on the same offset as LUKS1
* (checksum alg replaces hash in LUKS1)
*
* String (char) should be zero terminated.
* Padding should be wiped.
* Checksum is calculated with csum zeroed (+ full JSON area).
*/
struct luks2_hdr_disk {
char magic[LUKS2_MAGIC_L];
uint16_t version; /* Version 2 */
uint64_t hdr_size; /* in bytes, including JSON area */
uint64_t seqid; /* increased on every update */
char label[LUKS2_LABEL_L];
char checksum_alg[LUKS2_CHECKSUM_ALG_L];
uint8_t salt[LUKS2_SALT_L]; /* unique for every header/offset */
char uuid[LUKS2_UUID_L];
char subsystem[LUKS2_LABEL_L]; /* owner subsystem label */
uint64_t hdr_offset; /* offset from device start in bytes */
char _padding[184];
uint8_t csum[LUKS2_CHECKSUM_L];
char _padding4096[7*512];
/* JSON area starts here */
} __attribute__ ((packed));
/*
* LUKS2 header in-memory.
*/
typedef struct json_object json_object;
struct luks2_hdr {
size_t hdr_size;
uint64_t seqid;
unsigned int version;
char label[LUKS2_LABEL_L];
char subsystem[LUKS2_LABEL_L];
char checksum_alg[LUKS2_CHECKSUM_ALG_L];
uint8_t salt1[LUKS2_SALT_L];
uint8_t salt2[LUKS2_SALT_L];
char uuid[LUKS2_UUID_L];
json_object *jobj;
};
/*
* Supportable header sizes (hdr_disk + JSON area)
* Also used as offset for the 2nd header.
*/
#define LUKS2_HDR_16K_LEN 0x4000
#define LUKS2_HDR_BIN_LEN sizeof(struct luks2_hdr_disk)
#define LUKS2_HDR_DEFAULT_LEN 0x400000 /* 4 MiB */
#define LUKS2_MAX_KEYSLOTS_SIZE 0x8000000 /* 128 MiB */
int LUKS2_hdr_version_unlocked(struct crypt_device *cd);
int LUKS2_hdr_read(struct crypt_device *cd, struct luks2_hdr *hdr);
int LUKS2_hdr_write(struct crypt_device *cd, struct luks2_hdr *hdr);
int LUKS2_hdr_dump(struct crypt_device *cd, struct luks2_hdr *hdr);
int LUKS2_hdr_uuid(struct crypt_device *cd,
struct luks2_hdr *hdr,
const char *uuid);
int LUKS2_hdr_labels(struct crypt_device *cd,
struct luks2_hdr *hdr,
const char *label,
const char *subsystem,
int commit);
void LUKS2_hdr_free(struct luks2_hdr *hdr);
int LUKS2_hdr_backup(struct crypt_device *cd,
struct luks2_hdr *hdr,
const char *backup_file);
int LUKS2_hdr_restore(struct crypt_device *cd,
struct luks2_hdr *hdr,
const char *backup_file);
uint64_t LUKS2_hdr_and_areas_size(json_object *jobj);
uint64_t LUKS2_keyslots_size(json_object *jobj);
/*
* Generic LUKS2 keyslot
*/
int LUKS2_keyslot_open(struct crypt_device *cd,
int keyslot,
int segment,
const char *password,
size_t password_len,
struct volume_key **vk);
int LUKS2_keyslot_store(struct crypt_device *cd,
struct luks2_hdr *hdr,
int keyslot,
const char *password,
size_t password_len,
const struct volume_key *vk);
int LUKS2_keyslot_wipe(struct crypt_device *cd,
struct luks2_hdr *hdr,
int keyslot,
int wipe_area_only);
int LUKS2_keyslot_dump(struct crypt_device *cd,
int keyslot);
crypt_keyslot_priority LUKS2_keyslot_priority_get(struct crypt_device *cd,
struct luks2_hdr *hdr,
int keyslot);
int LUKS2_keyslot_priority_set(struct crypt_device *cd,
struct luks2_hdr *hdr,
int keyslot,
crypt_keyslot_priority priority,
int commit);
/*
* Generic LUKS2 token
*/
int LUKS2_token_json_get(struct crypt_device *cd,
struct luks2_hdr *hdr,
int token,
const char **json);
int LUKS2_token_assign(struct crypt_device *cd,
struct luks2_hdr *hdr,
int keyslot,
int token,
int assign,
int commit);
int LUKS2_token_create(struct crypt_device *cd,
struct luks2_hdr *hdr,
int token,
const char *json,
int commit);
crypt_token_info LUKS2_token_status(struct crypt_device *cd,
struct luks2_hdr *hdr,
int token,
const char **type);
int LUKS2_builtin_token_get(struct crypt_device *cd,
struct luks2_hdr *hdr,
int token,
const char *type,
void *params);
int LUKS2_builtin_token_create(struct crypt_device *cd,
struct luks2_hdr *hdr,
int token,
const char *type,
const void *params,
int commit);
int LUKS2_token_open_and_activate(struct crypt_device *cd,
struct luks2_hdr *hdr,
int token,
const char *name,
uint32_t flags,
void *usrptr);
int LUKS2_token_open_and_activate_any(struct crypt_device *cd,
struct luks2_hdr *hdr,
const char *name,
uint32_t flags);
/*
* Generic LUKS2 digest
*/
int LUKS2_digests_verify_by_segment(struct crypt_device *cd,
struct luks2_hdr *hdr,
int segment,
const struct volume_key *vk,
digests_t digests);
void LUKS2_digests_erase_unused(struct crypt_device *cd,
struct luks2_hdr *hdr);
int LUKS2_digest_verify(struct crypt_device *cd,
struct luks2_hdr *hdr,
struct volume_key *vk,
int keyslot);
int LUKS2_digest_dump(struct crypt_device *cd,
int digest);
int LUKS2_digest_json_get(struct crypt_device *cd,
struct luks2_hdr *hdr,
int digest,
const char **json);
int LUKS2_digest_json_set(struct crypt_device *cd,
struct luks2_hdr *hdr,
int digest,
const char *json);
int LUKS2_digests_assign(struct crypt_device *cd,
struct luks2_hdr *hdr,
int keyslot,
digests_t digests,
int assign,
int commit);
int LUKS2_digest_assign(struct crypt_device *cd,
struct luks2_hdr *hdr,
int keyslot,
int digest,
int assign,
int commit);
int LUKS2_digest_segment_assign(struct crypt_device *cd,
struct luks2_hdr *hdr,
int segment,
int digest,
int assign,
int commit);
int LUKS2_digests_by_keyslot(struct crypt_device *cd,
struct luks2_hdr *hdr,
int keyslot,
digests_t digests);
int LUKS2_digest_create(struct crypt_device *cd,
const char *type,
struct luks2_hdr *hdr,
const struct volume_key *vk);
/*
* LUKS2 generic
*/
int LUKS2_activate(struct crypt_device *cd,
const char *name,
struct volume_key *vk,
uint32_t flags);
int LUKS2_keyslot_luks2_format(struct crypt_device *cd,
struct luks2_hdr *hdr,
int keyslot,
const char *cipher,
size_t keylength);
int LUKS2_generate_hdr(
struct crypt_device *cd,
struct luks2_hdr *hdr,
const struct volume_key *vk,
const char *cipherName,
const char *cipherMode,
const char *integrity,
const char *uuid,
unsigned int sector_size,
unsigned int alignPayload,
unsigned int alignOffset,
int detached_metadata_device);
uint64_t LUKS2_get_data_offset(struct luks2_hdr *hdr);
int LUKS2_get_sector_size(struct luks2_hdr *hdr);
const char *LUKS2_get_cipher(struct luks2_hdr *hdr, int segment);
const char *LUKS2_get_integrity(struct luks2_hdr *hdr, int segment);
int LUKS2_get_volume_key_size(struct luks2_hdr *hdr, int segment);
int LUKS2_get_keyslot_key_size(struct luks2_hdr *hdr, int keyslot);
int LUKS2_keyslot_find_empty(struct luks2_hdr *hdr, const char *type);
int LUKS2_keyslot_active_count(struct luks2_hdr *hdr, int segment);
int LUKS2_keyslot_for_segment(struct luks2_hdr *hdr, int keyslot, int segment);
crypt_keyslot_info LUKS2_keyslot_info(struct luks2_hdr *hdr, int keyslot);
int LUKS2_keyslot_area(struct luks2_hdr *hdr,
int keyslot,
uint64_t *offset,
uint64_t *length);
/*
* Permanent activation flags stored in header
*/
int LUKS2_config_get_flags(struct crypt_device *cd, struct luks2_hdr *hdr, uint32_t *flags);
int LUKS2_config_set_flags(struct crypt_device *cd, struct luks2_hdr *hdr, uint32_t flags);
/*
* Requirements for device activation or header modification
*/
int LUKS2_config_get_requirements(struct crypt_device *cd, struct luks2_hdr *hdr, uint32_t *requirements);
int LUKS2_config_set_requirements(struct crypt_device *cd, struct luks2_hdr *hdr, uint32_t requirements);
int LUKS2_unmet_requirements(struct crypt_device *cd, struct luks2_hdr *hdr, int quiet);
int crypt_use_keyring_for_vk(const struct crypt_device *cd);
int crypt_volume_key_load_in_keyring(struct crypt_device *cd, struct volume_key *vk);
struct luks_phdr;
int LUKS2_luks1_to_luks2(struct crypt_device *cd,
struct luks_phdr *hdr1,
struct luks2_hdr *hdr2);
int LUKS2_luks2_to_luks1(struct crypt_device *cd,
struct luks2_hdr *hdr2,
struct luks_phdr *hdr1);
#endif

391
lib/luks2/luks2_digest.c Normal file
View File

@@ -0,0 +1,391 @@
/*
* LUKS - Linux Unified Key Setup v2, digest handling
*
* Copyright (C) 2015-2017, Red Hat, Inc. All rights reserved.
* Copyright (C) 2015-2017, Milan Broz. All rights reserved.
*
* 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.
*/
#include "luks2_internal.h"
extern const digest_handler PBKDF2_digest;
static const digest_handler *digest_handlers[LUKS2_DIGEST_MAX] = {
&PBKDF2_digest,
NULL
};
int crypt_digest_register(const digest_handler *handler)
{
int i;
for (i = 0; i < LUKS2_DIGEST_MAX && digest_handlers[i]; i++) {
if (!strcmp(digest_handlers[i]->name, handler->name))
return -EINVAL;
}
if (i == LUKS2_DIGEST_MAX)
return -EINVAL;
digest_handlers[i] = handler;
return 0;
}
const digest_handler *LUKS2_digest_handler_type(struct crypt_device *cd, const char *type)
{
int i;
for (i = 0; i < LUKS2_DIGEST_MAX && digest_handlers[i]; i++) {
if (!strcmp(digest_handlers[i]->name, type))
return digest_handlers[i];
}
return NULL;
}
static const digest_handler *LUKS2_digest_handler(struct crypt_device *cd, int digest)
{
struct luks2_hdr *hdr;
json_object *jobj1, *jobj2;
if (digest < 0)
return NULL;
if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2)))
return NULL;
if (!(jobj1 = LUKS2_get_digest_jobj(hdr, digest)))
return NULL;
if (!json_object_object_get_ex(jobj1, "type", &jobj2))
return NULL;
return LUKS2_digest_handler_type(cd, json_object_get_string(jobj2));
}
static int LUKS2_digest_find_free(struct crypt_device *cd, struct luks2_hdr *hdr)
{
int digest = 0;
while (LUKS2_get_digest_jobj(hdr, digest) && digest < LUKS2_DIGEST_MAX)
digest++;
return digest < LUKS2_DIGEST_MAX ? digest : -1;
}
int LUKS2_digest_create(struct crypt_device *cd,
const char *type,
struct luks2_hdr *hdr,
const struct volume_key *vk)
{
int digest;
const digest_handler *dh;
dh = LUKS2_digest_handler_type(cd, type);
if (!dh)
return -EINVAL;
digest = LUKS2_digest_find_free(cd, hdr);
if (digest < 0)
return -EINVAL;
log_dbg("Creating new digest %d (%s).", digest, type);
return dh->store(cd, digest, vk->key, vk->keylength) ?: digest;
}
int LUKS2_digests_by_keyslot(struct crypt_device *cd,
struct luks2_hdr *hdr,
int keyslot,
digests_t digests)
{
char keyslot_name[16];
int i = 0;
json_object *jobj_digests, *jobj_digest_keyslots;
if (snprintf(keyslot_name, sizeof(keyslot_name), "%u", keyslot) < 1)
return -ENOMEM;
json_object_object_get_ex(hdr->jobj, "digests", &jobj_digests);
json_object_object_foreach(jobj_digests, key, val) {
json_object_object_get_ex(val, "keyslots", &jobj_digest_keyslots);
if (LUKS2_array_jobj(jobj_digest_keyslots, keyslot_name))
digests[i++] = atoi(key);
}
if (i < LUKS2_DIGEST_MAX)
digests[i] = -1;
return i ? 0 : -ENOENT;
}
int LUKS2_digest_verify(struct crypt_device *cd,
struct luks2_hdr *hdr,
struct volume_key *vk,
int keyslot)
{
const digest_handler *h;
digests_t digests;
int i, r;
r = LUKS2_digests_by_keyslot(cd, hdr, keyslot, digests);
if (r == -ENOENT)
return 0;
if (r < 0)
return r;
for (i = 0; i < LUKS2_DIGEST_MAX && digests[i] != -1 ; i++) {
log_dbg("Verifying key from keyslot %d, digest %d.",
keyslot, digests[i]);
h = LUKS2_digest_handler(cd, digests[i]);
if (!h)
return -EINVAL;
r = h->verify(cd, digests[i], vk->key, vk->keylength);
if (r < 0) {
log_dbg("Digest %d (%s) verify failed with %d.",
digests[i], h->name, r);
return r;
}
}
return 0;
}
int LUKS2_digest_dump(struct crypt_device *cd, int digest)
{
const digest_handler *h;
if (!(h = LUKS2_digest_handler(cd, digest)))
return -EINVAL;
return h->dump(cd, digest);
}
int LUKS2_digests_verify_by_segment(struct crypt_device *cd,
struct luks2_hdr *hdr,
int segment,
const struct volume_key *vk,
digests_t digests)
{
char segment_name[16];
const digest_handler *h;
json_object *jobj_digests, *jobj_digest_segments;
int digest, r, i = 0;
json_object_object_get_ex(hdr->jobj, "digests", &jobj_digests);
if (snprintf(segment_name, sizeof(segment_name), "%u", segment) < 1)
return -EINVAL;
json_object_object_foreach(jobj_digests, key, val) {
json_object_object_get_ex(val, "segments", &jobj_digest_segments);
if (!LUKS2_array_jobj(jobj_digest_segments, segment_name))
continue;
digest = atoi(key);
log_dbg("Verifying key digest %d.", digest);
h = LUKS2_digest_handler(cd, digest);
if (!h)
return -EINVAL;
r = h->verify(cd, digest, vk->key, vk->keylength);
if (r < 0) {
log_dbg("Digest %d (%s) verify failed with %d.", digest, h->name, r);
return r;
}
if (digests)
digests[i] = digest;
i++;
}
if (digests && i < LUKS2_DIGEST_MAX)
digests[i] = -1;
return i ? 0 : -ENOENT;
}
int LUKS2_digest_json_get(struct crypt_device *cd, struct luks2_hdr *hdr,
int digest, const char **json)
{
json_object *jobj_digest;
jobj_digest = LUKS2_get_digest_jobj(hdr, digest);
if (!jobj_digest)
return -EINVAL;
*json = json_object_to_json_string_ext(jobj_digest, JSON_C_TO_STRING_PLAIN);
return 0;
}
static int assign_one_digest(struct crypt_device *cd, struct luks2_hdr *hdr,
int keyslot, int digest, int assign)
{
json_object *jobj1, *jobj_digest, *jobj_digest_keyslots;
char num[16];
log_dbg("Keyslot %i %s digest %i.", keyslot, assign ? "assigned to" : "unassigned from", digest);
jobj_digest = LUKS2_get_digest_jobj(hdr, digest);
if (!jobj_digest)
return -EINVAL;
json_object_object_get_ex(jobj_digest, "keyslots", &jobj_digest_keyslots);
if (!jobj_digest_keyslots)
return -EINVAL;
snprintf(num, sizeof(num), "%d", keyslot);
if (assign) {
jobj1 = LUKS2_array_jobj(jobj_digest_keyslots, num);
if (!jobj1)
json_object_array_add(jobj_digest_keyslots, json_object_new_string(num));
} else {
jobj1 = LUKS2_array_remove(jobj_digest_keyslots, num);
if (jobj1)
json_object_object_add(jobj_digest, "keyslots", jobj1);
}
return 0;
}
int LUKS2_digests_assign(struct crypt_device *cd, struct luks2_hdr *hdr,
int keyslot, digests_t digests, int assign, int commit)
{
int i, r;
for (i = 0; i < LUKS2_DIGEST_MAX && digests[i] != -1; i++) {
r = LUKS2_digest_assign(cd, hdr, keyslot, digests[i], assign, 0);
if (r < 0)
return r;
}
return commit ? LUKS2_hdr_write(cd, hdr) : 0;
}
int LUKS2_digest_assign(struct crypt_device *cd, struct luks2_hdr *hdr,
int keyslot, int digest, int assign, int commit)
{
json_object *jobj_digests;
int r = 0;
if (digest == CRYPT_ANY_DIGEST) {
json_object_object_get_ex(hdr->jobj, "digests", &jobj_digests);
json_object_object_foreach(jobj_digests, key, val) {
UNUSED(val);
r = assign_one_digest(cd, hdr, keyslot, atoi(key), assign);
if (r < 0)
break;
}
} else
r = assign_one_digest(cd, hdr, keyslot, digest, assign);
if (r < 0)
return r;
// FIXME: do not write header in nothing changed
return commit ? LUKS2_hdr_write(cd, hdr) : 0;
}
static int assign_one_segment(struct crypt_device *cd, struct luks2_hdr *hdr,
int segment, int digest, int assign)
{
json_object *jobj1, *jobj_digest, *jobj_digest_segments;
char num[16];
log_dbg("Segment %i %s digest %i.", segment, assign ? "assigned to" : "unassigned from", digest);
jobj_digest = LUKS2_get_digest_jobj(hdr, digest);
if (!jobj_digest)
return -EINVAL;
json_object_object_get_ex(jobj_digest, "segments", &jobj_digest_segments);
if (!jobj_digest_segments)
return -EINVAL;
snprintf(num, sizeof(num), "%d", segment);
if (assign) {
jobj1 = LUKS2_array_jobj(jobj_digest_segments, num);
if (!jobj1)
json_object_array_add(jobj_digest_segments, json_object_new_string(num));
} else {
jobj1 = LUKS2_array_remove(jobj_digest_segments, num);
if (jobj1)
json_object_object_add(jobj_digest, "segments", jobj1);
}
return 0;
}
int LUKS2_digest_segment_assign(struct crypt_device *cd, struct luks2_hdr *hdr,
int segment, int digest, int assign, int commit)
{
json_object *jobj_digests;
int r = 0;
if (digest == CRYPT_ANY_DIGEST) {
json_object_object_get_ex(hdr->jobj, "digests", &jobj_digests);
json_object_object_foreach(jobj_digests, key, val) {
UNUSED(val);
r = assign_one_segment(cd, hdr, segment, atoi(key), assign);
if (r < 0)
break;
}
} else
r = assign_one_segment(cd, hdr, segment, digest, assign);
if (r < 0)
return r;
// FIXME: do not write header in nothing changed
return commit ? LUKS2_hdr_write(cd, hdr) : 0;
}
static int digest_unused(json_object *jobj_digest)
{
json_object *jobj;
json_object_object_get_ex(jobj_digest, "segments", &jobj);
if (!jobj || !json_object_is_type(jobj, json_type_array) || json_object_array_length(jobj))
return 0;
json_object_object_get_ex(jobj_digest, "keyslots", &jobj);
if (!jobj || !json_object_is_type(jobj, json_type_array))
return 0;
return json_object_array_length(jobj) ? 0 : 1;
}
void LUKS2_digests_erase_unused(struct crypt_device *cd,
struct luks2_hdr *hdr)
{
json_object *jobj_digests;
json_object_object_get_ex(hdr->jobj, "digests", &jobj_digests);
if (!jobj_digests || !json_object_is_type(jobj_digests, json_type_object))
return;
json_object_object_foreach(jobj_digests, key, val) {
if (digest_unused(val)) {
log_dbg("Erasing unused digest %d.", atoi(key));
json_object_object_del(jobj_digests, key);
}
}
}

View File

@@ -0,0 +1,197 @@
/*
* LUKS - Linux Unified Key Setup v2, PBKDF2 digest handler (LUKS1 compatible)
*
* Copyright (C) 2015-2017, Red Hat, Inc. All rights reserved.
* Copyright (C) 2015-2017, Milan Broz. All rights reserved.
*
* 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.
*/
#include "luks2_internal.h"
#define LUKS_DIGESTSIZE 20 // since SHA1
#define LUKS_SALTSIZE 32
#define LUKS_MKD_ITERATIONS_MS 125
static int PBKDF2_digest_verify(struct crypt_device *cd,
int digest,
const char *volume_key,
size_t volume_key_len)
{
char checkHashBuf[64];
json_object *jobj_digest, *jobj1;
const char *hashSpec;
char *mkDigest = NULL, mkDigestSalt[LUKS_SALTSIZE];
unsigned int mkDigestIterations;
size_t len;
int r;
/* This can be done only for internally linked digests */
jobj_digest = LUKS2_get_digest_jobj(crypt_get_hdr(cd, CRYPT_LUKS2), digest);
if (!jobj_digest)
return -EINVAL;
if (!json_object_object_get_ex(jobj_digest, "hash", &jobj1))
return -EINVAL;
hashSpec = json_object_get_string(jobj1);
if (!json_object_object_get_ex(jobj_digest, "iterations", &jobj1))
return -EINVAL;
mkDigestIterations = json_object_get_int64(jobj1);
if (!json_object_object_get_ex(jobj_digest, "salt", &jobj1))
return -EINVAL;
len = sizeof(mkDigestSalt);
if (!base64_decode(json_object_get_string(jobj1),
json_object_get_string_len(jobj1), mkDigestSalt, &len))
return -EINVAL;
if (len != LUKS_SALTSIZE)
return -EINVAL;
if (!json_object_object_get_ex(jobj_digest, "digest", &jobj1))
return -EINVAL;
len = 0;
if (!base64_decode_alloc(json_object_get_string(jobj1),
json_object_get_string_len(jobj1), &mkDigest, &len))
return -EINVAL;
if (len < LUKS_DIGESTSIZE ||
len > sizeof(checkHashBuf) ||
(len != LUKS_DIGESTSIZE && len != (size_t)crypt_hash_size(hashSpec))) {
free(mkDigest);
return -EINVAL;
}
r = -EPERM;
if (crypt_pbkdf(CRYPT_KDF_PBKDF2, hashSpec, volume_key, volume_key_len,
mkDigestSalt, LUKS_SALTSIZE,
checkHashBuf, len,
mkDigestIterations, 0, 0) < 0) {
r = -EINVAL;
} else {
if (memcmp(checkHashBuf, mkDigest, len) == 0)
r = 0;
}
free(mkDigest);
return r;
}
static int PBKDF2_digest_store(struct crypt_device *cd,
int digest,
const char *volume_key,
size_t volume_key_len)
{
json_object *jobj_digest, *jobj_digests;
char salt[LUKS_SALTSIZE], digest_raw[128], num[16];
int r;
char *base64_str;
struct luks2_hdr *hdr;
struct crypt_pbkdf_type pbkdf = {
.type = CRYPT_KDF_PBKDF2,
.hash = "sha256",
.time_ms = LUKS_MKD_ITERATIONS_MS,
};
log_dbg("Setting PBKDF2 type key digest %d.", digest);
r = crypt_random_get(cd, salt, LUKS_SALTSIZE, CRYPT_RND_SALT);
if (r < 0)
return r;
if (crypt_get_pbkdf(cd)->flags & CRYPT_PBKDF_NO_BENCHMARK)
pbkdf.iterations = MIN_PBKDF2_ITERATIONS;
else {
r = crypt_benchmark_pbkdf_internal(cd, &pbkdf, volume_key_len);
if (r < 0)
return r;
}
r = crypt_pbkdf(CRYPT_KDF_PBKDF2, pbkdf.hash, volume_key, volume_key_len,
salt, LUKS_SALTSIZE, digest_raw, crypt_hmac_size(pbkdf.hash),
pbkdf.iterations, 0, 0);
if (r < 0)
return r;
jobj_digest = LUKS2_get_digest_jobj(crypt_get_hdr(cd, CRYPT_LUKS2), digest);
jobj_digests = NULL;
if (!jobj_digest) {
hdr = crypt_get_hdr(cd, CRYPT_LUKS2);
jobj_digest = json_object_new_object();
json_object_object_get_ex(hdr->jobj, "digests", &jobj_digests);
}
json_object_object_add(jobj_digest, "type", json_object_new_string("pbkdf2"));
json_object_object_add(jobj_digest, "keyslots", json_object_new_array());
json_object_object_add(jobj_digest, "segments", json_object_new_array());
json_object_object_add(jobj_digest, "hash", json_object_new_string(pbkdf.hash));
json_object_object_add(jobj_digest, "iterations", json_object_new_int(pbkdf.iterations));
base64_encode_alloc(salt, LUKS_SALTSIZE, &base64_str);
if (!base64_str) {
json_object_put(jobj_digest);
return -ENOMEM;
}
json_object_object_add(jobj_digest, "salt", json_object_new_string(base64_str));
free(base64_str);
base64_encode_alloc(digest_raw, crypt_hmac_size(pbkdf.hash), &base64_str);
if (!base64_str) {
json_object_put(jobj_digest);
return -ENOMEM;
}
json_object_object_add(jobj_digest, "digest", json_object_new_string(base64_str));
free(base64_str);
if (jobj_digests) {
snprintf(num, sizeof(num), "%d", digest);
json_object_object_add(jobj_digests, num, jobj_digest);
}
JSON_DBG(jobj_digest, "Digest JSON");
return 0;
}
static int PBKDF2_digest_dump(struct crypt_device *cd, int digest)
{
json_object *jobj_digest, *jobj1;
/* This can be done only for internally linked digests */
jobj_digest = LUKS2_get_digest_jobj(crypt_get_hdr(cd, CRYPT_LUKS2), digest);
if (!jobj_digest)
return -EINVAL;
json_object_object_get_ex(jobj_digest, "hash", &jobj1);
log_std(cd, "\tHash: %s\n", json_object_get_string(jobj1));
json_object_object_get_ex(jobj_digest, "iterations", &jobj1);
log_std(cd, "\tIterations: %" PRIu64 "\n", json_object_get_int64(jobj1));
json_object_object_get_ex(jobj_digest, "salt", &jobj1);
log_std(cd, "\tSalt: ");
hexprint_base64(cd, jobj1, " ", " ");
json_object_object_get_ex(jobj_digest, "digest", &jobj1);
log_std(cd, "\tDigest: ");
hexprint_base64(cd, jobj1, " ", " ");
return 0;
}
const digest_handler PBKDF2_digest = {
.name = "pbkdf2",
.verify = PBKDF2_digest_verify,
.store = PBKDF2_digest_store,
.dump = PBKDF2_digest_dump,
};

View File

@@ -0,0 +1,695 @@
/*
* LUKS - Linux Unified Key Setup v2
*
* Copyright (C) 2015-2017, Red Hat, Inc. All rights reserved.
* Copyright (C) 2015-2017, Milan Broz. All rights reserved.
*
* 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.
*/
#include <assert.h>
#include "luks2_internal.h"
/*
* Helper functions
*/
json_object *parse_json_len(const char *json_area, int length, int *end_offset)
{
json_object *jobj;
struct json_tokener *jtok;
if (!json_area || length <= 0)
return NULL;
jtok = json_tokener_new();
if (!jtok) {
log_dbg("ERROR: Failed to init json tokener");
return NULL;
}
jobj = json_tokener_parse_ex(jtok, json_area, length);
if (!jobj)
log_dbg("ERROR: Failed to parse json data (%d): %s",
json_tokener_get_error(jtok),
json_tokener_error_desc(json_tokener_get_error(jtok)));
else
*end_offset = jtok->char_offset;
json_tokener_free(jtok);
return jobj;
}
static void log_dbg_checksum(const uint8_t *csum, const char *csum_alg, const char *info)
{
char csum_txt[2*LUKS2_CHECKSUM_L+1];
int i;
for (i = 0; i < crypt_hash_size(csum_alg); i++)
snprintf(&csum_txt[i*2], 3, "%02hhx", (const char)csum[i]);
csum_txt[i*2+1] = '\0'; /* Just to be safe, sprintf should write \0 there. */
log_dbg("Checksum:%s (%s)", &csum_txt[0], info);
}
/*
* Calculate hash (checksum) of |LUKS2_bin|LUKS2_JSON_area| from in-memory structs.
* LUKS2 on-disk header contains uniques salt both for primary and secondary header.
* Checksum is always calculated with zeroed checksum field in binary header.
*/
static int hdr_checksum_calculate(const char *alg, struct luks2_hdr_disk *hdr_disk,
const char *json_area, size_t json_len)
{
struct crypt_hash *hd = NULL;
int r;
if (crypt_hash_init(&hd, alg))
return -EINVAL;
/* Binary header, csum zeroed. */
r = crypt_hash_write(hd, (char*)hdr_disk, LUKS2_HDR_BIN_LEN);
/* JSON area (including unused space) */
if (!r)
r = crypt_hash_write(hd, json_area, json_len);
if (!r)
r = crypt_hash_final(hd, (char*)hdr_disk->csum, crypt_hash_size(alg));
crypt_hash_destroy(hd);
return r;
}
/*
* Compare hash (checksum) of on-disk and in-memory header.
*/
static int hdr_checksum_check(const char *alg, struct luks2_hdr_disk *hdr_disk,
const char *json_area, size_t json_len)
{
struct luks2_hdr_disk hdr_tmp;
int r;
/* Copy header and zero checksum. */
memcpy(&hdr_tmp, hdr_disk, LUKS2_HDR_BIN_LEN);
memset(&hdr_tmp.csum, 0, sizeof(hdr_tmp.csum));
r = hdr_checksum_calculate(alg, &hdr_tmp, json_area, json_len);
if (r < 0)
return r;
log_dbg_checksum(hdr_disk->csum, alg, "on-disk");
log_dbg_checksum(hdr_tmp.csum, alg, "in-memory");
if (memcmp(hdr_tmp.csum, hdr_disk->csum, crypt_hash_size(alg)))
return -EINVAL;
return 0;
}
/*
* Convert header from on-disk format to in-memory struct
*/
static void hdr_from_disk(struct luks2_hdr_disk *hdr_disk1,
struct luks2_hdr_disk *hdr_disk2,
struct luks2_hdr *hdr,
int secondary)
{
hdr->version = be16_to_cpu(hdr_disk1->version);
hdr->hdr_size = be64_to_cpu(hdr_disk1->hdr_size);
hdr->seqid = be64_to_cpu(hdr_disk1->seqid);
memcpy(hdr->label, hdr_disk1->label, LUKS2_LABEL_L);
hdr->label[LUKS2_LABEL_L - 1] = '\0';
memcpy(hdr->subsystem, hdr_disk1->subsystem, LUKS2_LABEL_L);
hdr->subsystem[LUKS2_LABEL_L - 1] = '\0';
memcpy(hdr->checksum_alg, hdr_disk1->checksum_alg, LUKS2_CHECKSUM_ALG_L);
hdr->checksum_alg[LUKS2_CHECKSUM_ALG_L - 1] = '\0';
memcpy(hdr->uuid, hdr_disk1->uuid, LUKS2_UUID_L);
hdr->uuid[LUKS2_UUID_L - 1] = '\0';
if (secondary) {
memcpy(hdr->salt1, hdr_disk2->salt, LUKS2_SALT_L);
memcpy(hdr->salt2, hdr_disk1->salt, LUKS2_SALT_L);
} else {
memcpy(hdr->salt1, hdr_disk1->salt, LUKS2_SALT_L);
memcpy(hdr->salt2, hdr_disk2->salt, LUKS2_SALT_L);
}
}
/*
* Convert header from in-memory struct to on-disk format
*/
static void hdr_to_disk(struct luks2_hdr *hdr,
struct luks2_hdr_disk *hdr_disk,
int secondary, uint64_t offset)
{
assert(((char*)&(hdr_disk->_padding4096) - (char*)&(hdr_disk->magic)) == 512);
memset(hdr_disk, 0, LUKS2_HDR_BIN_LEN);
memcpy(&hdr_disk->magic, secondary ? LUKS2_MAGIC_2ND : LUKS2_MAGIC_1ST, LUKS2_MAGIC_L);
hdr_disk->version = cpu_to_be16(hdr->version);
hdr_disk->hdr_size = cpu_to_be64(hdr->hdr_size);
hdr_disk->hdr_offset = cpu_to_be64(offset);
hdr_disk->seqid = cpu_to_be64(hdr->seqid);
strncpy(hdr_disk->label, hdr->label, LUKS2_LABEL_L);
hdr_disk->label[LUKS2_LABEL_L - 1] = '\0';
strncpy(hdr_disk->subsystem, hdr->subsystem, LUKS2_LABEL_L);
hdr_disk->subsystem[LUKS2_LABEL_L - 1] = '\0';
strncpy(hdr_disk->checksum_alg, hdr->checksum_alg, LUKS2_CHECKSUM_ALG_L);
hdr_disk->checksum_alg[LUKS2_CHECKSUM_ALG_L - 1] = '\0';
strncpy(hdr_disk->uuid, hdr->uuid, LUKS2_UUID_L);
hdr_disk->uuid[LUKS2_UUID_L - 1] = '\0';
memcpy(hdr_disk->salt, secondary ? hdr->salt2 : hdr->salt1, LUKS2_SALT_L);
}
/*
* Sanity checks before checkum is validated
*/
static int hdr_disk_sanity_check_pre(struct luks2_hdr_disk *hdr,
size_t *hdr_json_size, int secondary,
uint64_t offset)
{
if (memcmp(hdr->magic, secondary ? LUKS2_MAGIC_2ND : LUKS2_MAGIC_1ST, LUKS2_MAGIC_L))
return -EINVAL;
if (be16_to_cpu(hdr->version) != 2) {
log_dbg("Unsupported LUKS2 header version %u.", be16_to_cpu(hdr->version));
return -EINVAL;
}
if (offset != be64_to_cpu(hdr->hdr_offset)) {
log_dbg("LUKS2 offset 0x%04x on device differs to expected offset 0x%04x.",
(unsigned)be64_to_cpu(hdr->hdr_offset), (unsigned)offset);
return -EINVAL;
}
/* FIXME: sanity check checksum alg. */
log_dbg("LUKS2 header version %u of size %u bytes, checksum %s.",
(unsigned)be16_to_cpu(hdr->version), (unsigned)be64_to_cpu(hdr->hdr_size),
hdr->checksum_alg);
*hdr_json_size = be64_to_cpu(hdr->hdr_size) - LUKS2_HDR_BIN_LEN;
return 0;
}
/*
* Read LUKS2 header from disk at specific offset.
*/
static int hdr_read_disk(struct device *device, struct luks2_hdr_disk *hdr_disk,
char **json_area, uint64_t offset, int secondary)
{
size_t hdr_json_size = 0;
int devfd = -1, r;
log_dbg("Trying to read %s LUKS2 header at offset %" PRIu64 ".",
secondary ? "secondary" : "primary", offset);
devfd = device_open_locked(device, O_RDONLY);
if (devfd < 0)
return devfd == -1 ? -EIO : devfd;
/*
* Read binary header and run sanity check before reading
* JSON area and validating checksum.
*/
if (read_lseek_blockwise(devfd, device_block_size(device),
device_alignment(device), hdr_disk,
LUKS2_HDR_BIN_LEN, offset) != LUKS2_HDR_BIN_LEN) {
close(devfd);
return -EIO;
}
r = hdr_disk_sanity_check_pre(hdr_disk, &hdr_json_size, secondary, offset);
if (r < 0) {
close(devfd);
return r;
}
/*
* Allocate and read JSON area. Always the whole area must be read.
*/
*json_area = malloc(hdr_json_size);
if (!*json_area) {
close(devfd);
return -ENOMEM;
}
if (read_lseek_blockwise(devfd, device_block_size(device),
device_alignment(device), *json_area, hdr_json_size,
offset + LUKS2_HDR_BIN_LEN) != (ssize_t)hdr_json_size) {
close(devfd);
free(*json_area);
*json_area = NULL;
return -EIO;
}
close(devfd);
/*
* Calculate and validate checksum and zero it afterwards.
*/
if (hdr_checksum_check(hdr_disk->checksum_alg, hdr_disk,
*json_area, hdr_json_size)) {
log_dbg("LUKS2 header checksum error (offset %" PRIu64 ").", offset);
r = -EINVAL;
}
memset(hdr_disk->csum, 0, LUKS2_CHECKSUM_L);
return r;
}
/*
* Write LUKS2 header to disk at specific offset.
*/
static int hdr_write_disk(struct device *device, struct luks2_hdr *hdr,
const char *json_area, int secondary)
{
struct luks2_hdr_disk hdr_disk;
uint64_t offset = secondary ? hdr->hdr_size : 0;
size_t hdr_json_len;
int devfd = -1, r;
log_dbg("Trying to write LUKS2 header (%zu bytes) at offset %" PRIu64 ".",
hdr->hdr_size, offset);
/* FIXME: read-only device silent fail? */
devfd = device_open_locked(device, O_RDWR);
if (devfd < 0)
return devfd == -1 ? -EINVAL : devfd;
hdr_json_len = hdr->hdr_size - LUKS2_HDR_BIN_LEN;
hdr_to_disk(hdr, &hdr_disk, secondary, offset);
/*
* Write header without checksum but with proper seqid.
*/
if (write_lseek_blockwise(devfd, device_block_size(device),
device_alignment(device), (char *)&hdr_disk,
LUKS2_HDR_BIN_LEN, offset) < (ssize_t)LUKS2_HDR_BIN_LEN) {
close(devfd);
return -EIO;
}
/*
* Write json area.
*/
if (write_lseek_blockwise(devfd, device_block_size(device),
device_alignment(device),
CONST_CAST(char*)json_area, hdr_json_len,
LUKS2_HDR_BIN_LEN + offset) < (ssize_t)hdr_json_len) {
close(devfd);
return -EIO;
}
/*
* Calculate checksum and write header with checkum.
*/
r = hdr_checksum_calculate(hdr_disk.checksum_alg, &hdr_disk,
json_area, hdr_json_len);
if (r < 0) {
close(devfd);
return r;
}
log_dbg_checksum(hdr_disk.csum, hdr_disk.checksum_alg, "in-memory");
if (write_lseek_blockwise(devfd, device_block_size(device),
device_alignment(device), (char *)&hdr_disk,
LUKS2_HDR_BIN_LEN, offset) < (ssize_t)LUKS2_HDR_BIN_LEN)
r = -EIO;
close(devfd);
return r;
}
static int LUKS2_check_device_size(struct crypt_device *cd, struct device *device, uint64_t hdr_size)
{
uint64_t dev_size;
if (device_size(device, &dev_size)) {
log_dbg("Cannot get device size for device %s.", device_path(device));
return -EIO;
}
log_dbg("Device size %" PRIu64 ", header size %"
PRIu64 ".", dev_size, hdr_size);
if (hdr_size > dev_size) {
log_err(cd, _("Device %s is too small. (LUKS2 requires at least %" PRIu64 " bytes.)\n"),
device_path(device), hdr_size);
return -EINVAL;
}
return 0;
}
/*
* Convert in-memory LUKS2 header and write it to disk.
* This will increase sequence id, write both header copies and calculate checksum.
*/
int LUKS2_disk_hdr_write(struct crypt_device *cd, struct luks2_hdr *hdr, struct device *device)
{
char *json_area;
const char *json_text;
size_t json_area_len;
int r;
if (hdr->version != 2) {
log_dbg("Unsupported LUKS2 header version (%u).", hdr->version);
return -EINVAL;
}
if (hdr->hdr_size != LUKS2_HDR_16K_LEN) {
log_dbg("Unsupported LUKS2 header size (%zu).", hdr->hdr_size);
return -EINVAL;
}
r = LUKS2_check_device_size(cd, crypt_metadata_device(cd), LUKS2_hdr_and_areas_size(hdr->jobj));
if (r)
return r;
/*
* Allocate and zero JSON area (of proper header size).
*/
json_area_len = hdr->hdr_size - LUKS2_HDR_BIN_LEN;
json_area = malloc(json_area_len);
if (!json_area)
return -ENOMEM;
memset(json_area, 0, json_area_len);
/*
* Generate text space-efficient JSON representation to json area.
*/
json_text = json_object_to_json_string_ext(hdr->jobj, JSON_C_TO_STRING_PLAIN);
if (!json_text || !*json_text) {
log_dbg("Cannot parse JSON object to text representation.");
free(json_area);
return -ENOMEM;
}
if (strlen(json_text) > (json_area_len - 1)) {
log_dbg("JSON is too large (%zu > %zu).", strlen(json_text), json_area_len);
free(json_area);
return -EINVAL;
}
strncpy(json_area, json_text, json_area_len);
/* Increase sequence id before writing it to disk. */
hdr->seqid++;
r = device_write_lock(cd, device);
if (r) {
log_err(cd, _("Failed to acquire write device lock.\n"));
free(json_area);
return r;
}
/* Write primary and secondary header */
r = hdr_write_disk(device, hdr, json_area, 0);
if (!r)
r = hdr_write_disk(device, hdr, json_area, 1);
if (r)
log_dbg("LUKS2 header write failed (%d).", r);
device_write_unlock(device);
/* FIXME: try recovery here? */
free(json_area);
return r;
}
static int validate_json_area(const char *json_area, int start, int length)
{
char c;
/* Enforce there are no needless opening bytes */
if (*json_area != '{') {
log_dbg("ERROR: Opening character must be left curly bracket: '{'.");
return -EINVAL;
}
if (start >= length) {
log_dbg("ERROR: Missing trailing null byte beyond parsed json data string.");
return -EINVAL;
}
/*
* TODO:
* validate there are legal json format characters between
* 'json_area' and 'json_area + start'
*/
do {
c = *(json_area + start);
if (c != '\0') {
log_dbg("ERROR: Forbidden ascii code 0x%02hhx found beyond json data string at offset %d.",
c, start);
return -EINVAL;
}
} while (++start < length);
return 0;
}
static int validate_luks2_json_object(json_object *jobj_hdr)
{
int r;
/* we require top level object to be of json_type_object */
r = !json_object_is_type(jobj_hdr, json_type_object);
if (r) {
log_dbg("ERROR: Resulting object is not a json object type");
return r;
}
r = LUKS2_hdr_validate(jobj_hdr);
if (r)
log_dbg("ERROR: LUKS2 validation failed");
return r;
}
static json_object *parse_and_validate_json(const char *json_area, int length)
{
int offset, r;
json_object *jobj = parse_json_len(json_area, length, &offset);
if (!jobj)
return NULL;
/* successfull parse_json_len must not return offset <= 0 */
assert(offset > 0);
r = validate_json_area(json_area, offset, length);
if (!r)
r = validate_luks2_json_object(jobj);
if (r) {
json_object_put(jobj);
jobj = NULL;
}
return jobj;
}
/*
* Read and convert on-disk LUKS2 header to in-memory representation..
* Try to do recovery if on-disk state is not consistent.
*/
int LUKS2_disk_hdr_read(struct crypt_device *cd, struct luks2_hdr *hdr,
struct device *device, int do_recovery)
{
enum { HDR_OK, HDR_OBSOLETE, HDR_FAIL, HDR_FAIL_IO } state_hdr1, state_hdr2;
struct luks2_hdr_disk hdr_disk1, hdr_disk2;
char *json_area1 = NULL, *json_area2 = NULL;
json_object *jobj_hdr1 = NULL, *jobj_hdr2 = NULL;
int i, r;
uint64_t hdr_size;
if (do_recovery && !crypt_metadata_locking_enabled()) {
do_recovery = 0;
log_dbg("Disabling header auto-recovery due to locking being disabled.");
}
/*
* Read primary LUKS2 header (offset 0).
*/
state_hdr1 = HDR_FAIL;
r = hdr_read_disk(device, &hdr_disk1, &json_area1, 0, 0);
if (r == 0) {
jobj_hdr1 = parse_and_validate_json(json_area1, be64_to_cpu(hdr_disk1.hdr_size) - LUKS2_HDR_BIN_LEN);
state_hdr1 = jobj_hdr1 ? HDR_OK : HDR_OBSOLETE;
} else if (r == -EIO)
state_hdr1 = HDR_FAIL_IO;
/*
* Read secondary LUKS2 header (follows primary).
*/
state_hdr2 = HDR_FAIL;
if (state_hdr1 != HDR_FAIL && state_hdr1 != HDR_FAIL_IO) {
r = hdr_read_disk(device, &hdr_disk2, &json_area2, be64_to_cpu(hdr_disk1.hdr_size), 1);
if (r == 0) {
jobj_hdr2 = parse_and_validate_json(json_area2, be64_to_cpu(hdr_disk2.hdr_size) - LUKS2_HDR_BIN_LEN);
state_hdr2 = jobj_hdr2 ? HDR_OK : HDR_OBSOLETE;
} else if (r == -EIO)
state_hdr2 = HDR_FAIL_IO;
} else {
/*
* No header size, check all known offsets.
*/
for (r = -EINVAL,i = 2; r < 0 && i <= 1024; i <<= 1)
r = hdr_read_disk(device, &hdr_disk2, &json_area2, i * 4096, 1);
if (r == 0) {
jobj_hdr2 = parse_and_validate_json(json_area2, be64_to_cpu(hdr_disk2.hdr_size) - LUKS2_HDR_BIN_LEN);
state_hdr2 = jobj_hdr2 ? HDR_OK : HDR_OBSOLETE;
} else if (r == -EIO)
state_hdr2 = HDR_FAIL_IO;
}
/*
* Check sequence id if both headers are read correctly.
*/
if (state_hdr1 == HDR_OK && state_hdr2 == HDR_OK) {
if (be64_to_cpu(hdr_disk1.seqid) > be64_to_cpu(hdr_disk2.seqid))
state_hdr2 = HDR_OBSOLETE;
else if (be64_to_cpu(hdr_disk1.seqid) < be64_to_cpu(hdr_disk2.seqid))
state_hdr1 = HDR_OBSOLETE;
}
/* check header with keyslots fit the device */
if (state_hdr1 == HDR_OK)
hdr_size = LUKS2_hdr_and_areas_size(jobj_hdr1);
else if (state_hdr2 == HDR_OK)
hdr_size = LUKS2_hdr_and_areas_size(jobj_hdr2);
else {
r = (state_hdr1 == HDR_FAIL_IO && state_hdr2 == HDR_FAIL_IO) ? -EIO : -EINVAL;
goto err;
}
r = LUKS2_check_device_size(cd, device, hdr_size);
if (r)
goto err;
/*
* Try to rewrite (recover) bad header. Always regenerate salt for bad header.
*/
if (state_hdr1 == HDR_OK && state_hdr2 != HDR_OK) {
log_dbg("Secondary LUKS2 header requires recovery.");
if (do_recovery) {
memcpy(&hdr_disk2, &hdr_disk1, LUKS2_HDR_BIN_LEN);
r = crypt_random_get(NULL, (char*)hdr_disk2.salt, sizeof(hdr_disk2.salt), CRYPT_RND_SALT);
if (r)
log_dbg("Cannot generate master salt.");
else {
hdr_from_disk(&hdr_disk1, &hdr_disk2, hdr, 0);
r = hdr_write_disk(device, hdr, json_area1, 1);
}
if (r)
log_dbg("Secondary LUKS2 header recovery failed.");
}
} else if (state_hdr1 != HDR_OK && state_hdr2 == HDR_OK) {
log_dbg("Primary LUKS2 header requires recovery.");
if (do_recovery) {
memcpy(&hdr_disk1, &hdr_disk2, LUKS2_HDR_BIN_LEN);
r = crypt_random_get(NULL, (char*)hdr_disk1.salt, sizeof(hdr_disk1.salt), CRYPT_RND_SALT);
if (r)
log_dbg("Cannot generate master salt.");
else {
hdr_from_disk(&hdr_disk2, &hdr_disk1, hdr, 1);
r = hdr_write_disk(device, hdr, json_area2, 0);
}
if (r)
log_dbg("Primary LUKS2 header recovery failed.");
}
}
free(json_area1);
json_area1 = NULL;
free(json_area2);
json_area2 = NULL;
/* wrong lock for write mode during recovery attempt */
if (r == -EAGAIN)
goto err;
/*
* Even if status is failed, the second header includes salt.
*/
if (state_hdr1 == HDR_OK) {
hdr_from_disk(&hdr_disk1, &hdr_disk2, hdr, 0);
hdr->jobj = jobj_hdr1;
json_object_put(jobj_hdr2);
return 0;
} else if (state_hdr2 == HDR_OK) {
hdr_from_disk(&hdr_disk2, &hdr_disk1, hdr, 1);
hdr->jobj = jobj_hdr2;
json_object_put(jobj_hdr1);
return 0;
}
r = (state_hdr1 == HDR_FAIL_IO || state_hdr2 == HDR_FAIL_IO) ? -EIO : -EINVAL;
err:
log_dbg("LUKS2 header read failed (%d).", r);
free(json_area1);
free(json_area2);
json_object_put(jobj_hdr1);
json_object_put(jobj_hdr2);
hdr->jobj = NULL;
return r;
}
int LUKS2_hdr_version_unlocked(struct crypt_device *cd)
{
struct {
char magic[LUKS2_MAGIC_L];
uint16_t version;
} __attribute__ ((packed)) hdr;
struct device *device = crypt_metadata_device(cd);
int r, devfd, flags;
if (!device)
return 0;
flags = O_RDONLY;
if (device_direct_io(device))
flags |= O_DIRECT;
devfd = open(device_path(device), flags);
if (devfd < 0)
return 0;
if ((read_lseek_blockwise(devfd, device_block_size(device),
device_alignment(device), &hdr, sizeof(hdr), 0)
!= sizeof(hdr)) ||
memcmp(hdr.magic, LUKS2_MAGIC_1ST, LUKS2_MAGIC_L))
r = 0;
else
r = (int)be16_to_cpu(hdr.version);
close(devfd);
return r;
}

154
lib/luks2/luks2_internal.h Normal file
View File

@@ -0,0 +1,154 @@
/*
* LUKS - Linux Unified Key Setup v2
*
* Copyright (C) 2015-2017, Red Hat, Inc. All rights reserved.
* Copyright (C) 2015-2017, Milan Broz. All rights reserved.
*
* 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 _CRYPTSETUP_LUKS2_INTERNAL_H
#define _CRYPTSETUP_LUKS2_INTERNAL_H
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <json-c/json.h>
#include "internal.h"
#include "base64.h"
#include "luks2.h"
#define UNUSED(x) (void)(x)
/*
* On-disk access function prototypes
*/
int LUKS2_disk_hdr_read(struct crypt_device *cd, struct luks2_hdr *hdr,
struct device *device, int do_recovery);
int LUKS2_disk_hdr_write(struct crypt_device *cd, struct luks2_hdr *hdr,
struct device *device);
/*
* JSON struct access helpers
*/
json_object *LUKS2_get_keyslot_jobj(struct luks2_hdr *hdr, int keyslot);
json_object *LUKS2_get_token_jobj(struct luks2_hdr *hdr, int token);
json_object *LUKS2_get_digest_jobj(struct luks2_hdr *hdr, int keyslot);
json_object *LUKS2_get_segment_jobj(struct luks2_hdr *hdr, int segment);
void hexprint_base64(struct crypt_device *cd, json_object *jobj,
const char *sep, const char *line_sep);
json_object *parse_json_len(const char *json_area, int length, int *end_offset);
uint64_t json_object_get_uint64(json_object *jobj);
uint32_t json_object_get_uint32(json_object *jobj);
void JSON_DBG(json_object *jobj, const char *desc);
/*
* LUKS2 JSON validation
*/
int LUKS2_hdr_validate(json_object *hdr_jobj);
int LUKS2_keyslot_validate(json_object *hdr_jobj, json_object *hdr_keyslot, const char *key);
int LUKS2_check_json_size(const struct luks2_hdr *hdr);
int LUKS2_token_validate(json_object *hdr_jobj, json_object *jobj_token, const char *key);
void LUKS2_token_dump(struct crypt_device *cd, int token);
/*
* JSON array helpers
*/
struct json_object *LUKS2_array_jobj(struct json_object *array, const char *num);
struct json_object *LUKS2_array_remove(struct json_object *array, const char *num);
/*
* Plugins API
*/
/**
* LUKS2 keyslots handlers (EXPERIMENTAL)
*/
typedef int (*keyslot_alloc_func)(struct crypt_device *cd, int keyslot,
size_t volume_key_len);
typedef int (*keyslot_open_func) (struct crypt_device *cd, int keyslot,
const char *password, size_t password_len,
char *volume_key, size_t volume_key_len);
typedef int (*keyslot_store_func)(struct crypt_device *cd, int keyslot,
const char *password, size_t password_len,
const char *volume_key, size_t volume_key_len);
typedef int (*keyslot_wipe_func) (struct crypt_device *cd, int keyslot);
typedef int (*keyslot_dump_func) (struct crypt_device *cd, int keyslot);
typedef int (*keyslot_validate_func) (struct crypt_device *cd, int keyslot);
int luks2_keyslot_alloc(struct crypt_device *cd,
int keyslot,
size_t volume_key_len);
typedef struct {
const char *name;
keyslot_alloc_func alloc;
keyslot_open_func open;
keyslot_store_func store;
keyslot_wipe_func wipe;
keyslot_dump_func dump;
keyslot_validate_func validate;
} keyslot_handler;
/**
* LUKS2 digest handlers (EXPERIMENTAL)
*/
typedef int (*digest_verify_func)(struct crypt_device *cd, int digest,
const char *volume_key, size_t volume_key_len);
typedef int (*digest_store_func) (struct crypt_device *cd, int digest,
const char *volume_key, size_t volume_key_len);
typedef int (*digest_dump_func) (struct crypt_device *cd, int digest);
typedef struct {
const char *name;
digest_verify_func verify;
digest_store_func store;
digest_dump_func dump;
} digest_handler;
int crypt_digest_register(const digest_handler *handler);
const digest_handler *LUKS2_digest_handler_type(struct crypt_device *cd, const char *type);
#define CRYPT_ANY_DIGEST -1
int crypt_keyslot_assign_digest(struct crypt_device *cd, int keyslot, int digest);
int crypt_keyslot_unassign_digest(struct crypt_device *cd, int keyslot, int digest);
/**
* LUKS2 token handlers (internal use only)
*/
typedef int (*builtin_token_get_func) (json_object *jobj_token, void *params);
typedef int (*builtin_token_set_func) (json_object **jobj_token, const void *params);
typedef struct {
/* internal only section used by builtin tokens */
builtin_token_get_func get;
builtin_token_set_func set;
/* public token handler */
const crypt_token_handler *h;
} token_handler;
int token_keyring_set(json_object **, const void *);
int token_keyring_get(json_object *, void *);
#define CRYPT_ANY_TOKEN -1
int LUKS2_find_area_gap(struct crypt_device *cd, struct luks2_hdr *hdr,
size_t keylength, uint64_t *area_offset, uint64_t *area_length);
#endif

View File

@@ -0,0 +1,233 @@
/*
* LUKS - Linux Unified Key Setup v2, LUKS2 header format code
*
* Copyright (C) 2015-2017, Red Hat, Inc. All rights reserved.
* Copyright (C) 2015-2017, Milan Broz. All rights reserved.
*
* 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.
*/
#include "luks2_internal.h"
#include <uuid/uuid.h>
struct area {
uint64_t offset;
uint64_t length;
};
static size_t get_area_size(size_t keylength)
{
//FIXME: calculate this properly, for now it is AF_split_sectors
return size_round_up(keylength * 4000, 4096);
}
static size_t get_min_offset(struct luks2_hdr *hdr)
{
return 2 * hdr->hdr_size;
}
static size_t get_max_offset(struct crypt_device *cd)
{
return crypt_get_data_offset(cd) * SECTOR_SIZE;
}
int LUKS2_find_area_gap(struct crypt_device *cd, struct luks2_hdr *hdr,
size_t keylength, uint64_t *area_offset, uint64_t *area_length)
{
struct area areas[LUKS2_KEYSLOTS_MAX], sorted_areas[LUKS2_KEYSLOTS_MAX] = {};
int i, j, k, area_i;
size_t offset, length;
/* fill area offset + length table */
for (i = 0; i < LUKS2_KEYSLOTS_MAX; i++) {
if (!LUKS2_keyslot_area(hdr, i, &areas[i].offset, &areas[i].length))
continue;
areas[i].length = 0;
areas[i].offset = 0;
}
/* sort table */
k = 0; /* index in sorted table */
for (i = 0; i < LUKS2_KEYSLOTS_MAX; i++) {
offset = get_max_offset(cd) ?: UINT64_MAX;
area_i = -1;
/* search for the smallest offset in table */
for (j = 0; j < LUKS2_KEYSLOTS_MAX; j++)
if (areas[j].offset && areas[j].offset <= offset) {
area_i = j;
offset = areas[j].offset;
}
if (area_i >= 0) {
sorted_areas[k].length = areas[area_i].length;
sorted_areas[k].offset = areas[area_i].offset;
areas[area_i].length = 0;
areas[area_i].offset = 0;
k++;
}
}
/* search for the gap we can use */
offset = get_min_offset(hdr);
length = get_area_size(keylength);
for (i = 0; i < LUKS2_KEYSLOTS_MAX; i++) {
/* skip empty */
if (sorted_areas[i].offset == 0 || sorted_areas[i].length == 0)
continue;
/* enough space before the used area */
if ((offset < sorted_areas[i].offset) && ((offset + length) <= sorted_areas[i].offset))
break;
/* both offset and length are already aligned to 4096 bytes */
offset = sorted_areas[i].offset + sorted_areas[i].length;
}
if (get_max_offset(cd) && (offset + length) > get_max_offset(cd)) {
log_err(cd, _("No space for new keyslot.\n"));
return -EINVAL;
}
log_dbg("Found area %zu -> %zu", offset, length + offset);
/*
log_dbg("Area offset min: %zu, max %zu, slots max %u",
get_min_offset(hdr), get_max_offset(cd), LUKS2_KEYSLOTS_MAX);
for (i = 0; i < LUKS2_KEYSLOTS_MAX; i++)
log_dbg("SLOT[%02i]: %-8" PRIu64 " -> %-8" PRIu64, i,
sorted_areas[i].offset,
sorted_areas[i].length + sorted_areas[i].offset);
*/
*area_offset = offset;
*area_length = length;
return 0;
}
int LUKS2_generate_hdr(
struct crypt_device *cd,
struct luks2_hdr *hdr,
const struct volume_key *vk,
const char *cipherName,
const char *cipherMode,
const char *integrity,
const char *uuid,
unsigned int sector_size,
unsigned int alignPayload,
unsigned int alignOffset,
int detached_metadata_device)
{
struct json_object *jobj_segment, *jobj_integrity, *jobj_keyslots, *jobj_segments, *jobj_config;
char num[24], cipher[128];
uint64_t offset, json_size, keyslots_size;
uuid_t partitionUuid;
int digest;
hdr->hdr_size = LUKS2_HDR_16K_LEN;
hdr->seqid = 1;
hdr->version = 2;
memset(hdr->label, 0, LUKS2_LABEL_L);
strcpy(hdr->checksum_alg, "sha256");
crypt_random_get(NULL, (char*)hdr->salt1, LUKS2_SALT_L, CRYPT_RND_SALT);
crypt_random_get(NULL, (char*)hdr->salt2, LUKS2_SALT_L, CRYPT_RND_SALT);
if (uuid && uuid_parse(uuid, partitionUuid) == -1) {
log_err(cd, _("Wrong LUKS UUID format provided.\n"));
return -EINVAL;
}
if (!uuid)
uuid_generate(partitionUuid);
uuid_unparse(partitionUuid, hdr->uuid);
if (*cipherMode != '\0')
snprintf(cipher, sizeof(cipher), "%s-%s", cipherName, cipherMode);
else
snprintf(cipher, sizeof(cipher), "%s", cipherName);
hdr->jobj = json_object_new_object();
jobj_keyslots = json_object_new_object();
json_object_object_add(hdr->jobj, "keyslots", jobj_keyslots);
json_object_object_add(hdr->jobj, "tokens", json_object_new_object());
jobj_segments = json_object_new_object();
json_object_object_add(hdr->jobj, "segments", jobj_segments);
json_object_object_add(hdr->jobj, "digests", json_object_new_object());
jobj_config = json_object_new_object();
json_object_object_add(hdr->jobj, "config", jobj_config);
digest = LUKS2_digest_create(cd, "pbkdf2", hdr, vk);
if (digest < 0) {
json_object_put(hdr->jobj);
hdr->jobj = NULL;
return -EINVAL;
}
if (LUKS2_digest_segment_assign(cd, hdr, CRYPT_DEFAULT_SEGMENT, digest, 1, 0) < 0) {
json_object_put(hdr->jobj);
hdr->jobj = NULL;
return -EINVAL;
}
jobj_segment = json_object_new_object();
json_object_object_add(jobj_segment, "type", json_object_new_string("crypt"));
if (detached_metadata_device)
offset = alignPayload * sector_size;
else {
//FIXME
//offset = size_round_up(areas[7].offset + areas[7].length, alignPayload * SECTOR_SIZE);
offset = size_round_up(LUKS2_HDR_DEFAULT_LEN, alignPayload * sector_size);
offset += alignOffset;
}
json_object_object_add(jobj_segment, "offset", json_object_new_string(uint64_to_str(num, sizeof(num), &offset)));
json_object_object_add(jobj_segment, "iv_tweak", json_object_new_string("0"));
json_object_object_add(jobj_segment, "size", json_object_new_string("dynamic"));
json_object_object_add(jobj_segment, "encryption", json_object_new_string(cipher));
json_object_object_add(jobj_segment, "sector_size", json_object_new_int(sector_size));
if (integrity) {
jobj_integrity = json_object_new_object();
json_object_object_add(jobj_integrity, "type", json_object_new_string(integrity));
json_object_object_add(jobj_integrity, "journal_encryption", json_object_new_string("none"));
json_object_object_add(jobj_integrity, "journal_integrity", json_object_new_string("none"));
json_object_object_add(jobj_segment, "integrity", jobj_integrity);
}
snprintf(num, sizeof(num), "%u", CRYPT_DEFAULT_SEGMENT);
json_object_object_add(jobj_segments, num, jobj_segment);
json_size = hdr->hdr_size - LUKS2_HDR_BIN_LEN;
json_object_object_add(jobj_config, "json_size",
json_object_new_string(uint64_to_str(num, sizeof(num), &json_size)));
/* for detached metadata device compute reasonable keyslot areas size */
// FIXME: this is coupled with FIXME above
if (detached_metadata_device)
keyslots_size = LUKS2_HDR_DEFAULT_LEN - get_min_offset(hdr);
else
keyslots_size = offset - get_min_offset(hdr);
/* keep keyslots_size reasonable for custom data alignments */
if (keyslots_size > LUKS2_MAX_KEYSLOTS_SIZE)
keyslots_size = LUKS2_MAX_KEYSLOTS_SIZE;
/* keyslots size has to be 4 KiB aligned */
keyslots_size -= (keyslots_size % 4096);
json_object_object_add(jobj_config, "keyslots_size",
json_object_new_string(uint64_to_str(num, sizeof(num), &keyslots_size)));
JSON_DBG(hdr->jobj, "Header JSON");
return 0;
}

File diff suppressed because it is too large Load Diff

477
lib/luks2/luks2_keyslot.c Normal file
View File

@@ -0,0 +1,477 @@
/*
* LUKS - Linux Unified Key Setup v2, keyslot handling
*
* Copyright (C) 2015-2017, Red Hat, Inc. All rights reserved.
* Copyright (C) 2015-2017, Milan Broz. All rights reserved.
*
* 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.
*/
#include "luks2_internal.h"
/* Internal implementations */
extern const keyslot_handler luks2_keyslot;
static const keyslot_handler *keyslot_handlers[LUKS2_KEYSLOTS_MAX] = {
&luks2_keyslot,
NULL
};
static const keyslot_handler
*LUKS2_keyslot_handler_type(struct crypt_device *cd, const char *type)
{
int i;
for (i = 0; i < LUKS2_KEYSLOTS_MAX && keyslot_handlers[i]; i++) {
if (!strcmp(keyslot_handlers[i]->name, type))
return keyslot_handlers[i];
}
return NULL;
}
static const keyslot_handler
*LUKS2_keyslot_handler(struct crypt_device *cd, int keyslot)
{
struct luks2_hdr *hdr;
json_object *jobj1, *jobj2;
if (keyslot < 0)
return NULL;
if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2)))
return NULL;
if (!(jobj1 = LUKS2_get_keyslot_jobj(hdr, keyslot)))
return NULL;
if (!json_object_object_get_ex(jobj1, "type", &jobj2))
return NULL;
return LUKS2_keyslot_handler_type(cd, json_object_get_string(jobj2));
}
static crypt_keyslot_info LUKS2_keyslot_active(struct luks2_hdr *hdr, int keyslot)
{
if (keyslot >= LUKS2_KEYSLOTS_MAX)
return CRYPT_SLOT_INVALID;
return LUKS2_get_keyslot_jobj(hdr, keyslot) ? CRYPT_SLOT_ACTIVE : CRYPT_SLOT_INACTIVE;
}
int LUKS2_keyslot_find_empty(struct luks2_hdr *hdr, const char *type)
{
int i;
for (i = 0; i < LUKS2_KEYSLOTS_MAX; i++)
if (!LUKS2_get_keyslot_jobj(hdr, i))
return i;
return -EINVAL;
}
static int digests_by_segment(json_object *jobj_digests, const char *segment,
digests_t digests)
{
json_object *jobj_segs;
int i = 0;
json_object_object_foreach(jobj_digests, dig, val) {
json_object_object_get_ex(val, "segments", &jobj_segs);
if (LUKS2_array_jobj(jobj_segs, segment))
digests[i++] = atoi(dig);
}
if (i < LUKS2_DIGEST_MAX)
digests[i] = -1;
return i ? 0 : -ENOENT;
}
static int is_in(const int super[], int super_size, int elem)
{
int i;
for (i = 0; i < super_size && super[i] != -1; i++)
if (super[i] == elem)
return 1;
return 0;
}
static int is_subset(const int super[], const int sub[], int super_size)
{
int i;
for (i = 0; i < super_size && sub[i] != -1; i++)
if (!is_in(super, super_size, sub[i]))
return 0;
return 1;
}
int LUKS2_keyslot_for_segment(struct luks2_hdr *hdr, int keyslot, int segment)
{
char keyslot_name[16], segment_name[16];
digests_t keyslot_digests, segment_digests;
json_object *jobj_digests;
int r = -ENOENT;
/* no need to check anything */
if (segment == CRYPT_ANY_SEGMENT)
return 0;
if (snprintf(segment_name, sizeof(segment_name), "%u", segment) < 1 ||
snprintf(keyslot_name, sizeof(keyslot_name), "%u", keyslot) < 1)
return -EINVAL;
/* empty set is subset of any set and it'd be wrong */
json_object_object_get_ex(hdr->jobj, "digests", &jobj_digests);
r = LUKS2_digests_by_keyslot(NULL, hdr, keyslot, keyslot_digests);
if (r)
return r;
/* empty set can't be superset of non-empty one */
if (digests_by_segment(jobj_digests, segment_name, segment_digests))
return r;
/*
* keyslot may activate segment if set of digests for keyslot
* is actually subset of set of digests for segment
*/
return is_subset(segment_digests, keyslot_digests, LUKS2_DIGEST_MAX) ? 0 : -ENOENT;
}
int LUKS2_keyslot_active_count(struct luks2_hdr *hdr, int segment)
{
int num = 0;
json_object *jobj_keyslots;
json_object_object_get_ex(hdr->jobj, "keyslots", &jobj_keyslots);
/* keyslot digests must be subset of segment digests */
json_object_object_foreach(jobj_keyslots, slot, val) {
UNUSED(val);
if (!LUKS2_keyslot_for_segment(hdr, atoi(slot), segment))
num++;
}
return num;
}
crypt_keyslot_info LUKS2_keyslot_info(struct luks2_hdr *hdr, int keyslot)
{
crypt_keyslot_info ki;
if(keyslot >= LUKS2_KEYSLOTS_MAX || keyslot < 0)
return CRYPT_SLOT_INVALID;
ki = LUKS2_keyslot_active(hdr, keyslot);
if (ki != CRYPT_SLOT_ACTIVE)
return ki;
if (LUKS2_keyslot_active_count(hdr, CRYPT_DEFAULT_SEGMENT) == 1 && !LUKS2_keyslot_for_segment(hdr, keyslot, CRYPT_DEFAULT_SEGMENT))
return CRYPT_SLOT_ACTIVE_LAST;
return CRYPT_SLOT_ACTIVE;
}
int LUKS2_keyslot_area(struct luks2_hdr *hdr,
int keyslot,
uint64_t *offset,
uint64_t *length)
{
json_object *jobj_keyslot, *jobj_area, *jobj;
if(LUKS2_keyslot_info(hdr, keyslot) == CRYPT_SLOT_INVALID)
return -EINVAL;
jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot);
if (!jobj_keyslot)
return -ENOENT;
if (!json_object_object_get_ex(jobj_keyslot, "area", &jobj_area))
return -EINVAL;
if (!json_object_object_get_ex(jobj_area, "offset", &jobj))
return -EINVAL;
*offset = json_object_get_int64(jobj);
if (!json_object_object_get_ex(jobj_area, "size", &jobj))
return -EINVAL;
*length = json_object_get_int64(jobj);
return 0;
}
static int LUKS2_open_and_verify(struct crypt_device *cd,
struct luks2_hdr *hdr,
int keyslot,
int segment,
const char *password,
size_t password_len,
struct volume_key **vk)
{
const keyslot_handler *h;
int key_size, r;
if (!(h = LUKS2_keyslot_handler(cd, keyslot)))
return -ENOENT;
r = LUKS2_keyslot_for_segment(hdr, keyslot, segment);
if (r) {
if (r == -ENOENT)
log_dbg("Keyslot %d unusable for segment %d.", keyslot, segment);
return r;
}
key_size = LUKS2_get_volume_key_size(hdr, segment);
if (key_size < 0)
key_size = LUKS2_get_keyslot_key_size(hdr, keyslot);
if (key_size < 0)
return -EINVAL;
*vk = crypt_alloc_volume_key(key_size, NULL);
if (!*vk)
return -ENOMEM;
r = h->open(cd, keyslot, password, password_len, (*vk)->key, (*vk)->keylength);
if (r < 0)
log_dbg("Keyslot %d (%s) open failed with %d.", keyslot, h->name, r);
else
r = LUKS2_digest_verify(cd, hdr, *vk, keyslot);
if (r < 0) {
crypt_free_volume_key(*vk);
*vk = NULL;
}
return r < 0 ? r : keyslot;
}
static int LUKS2_keyslot_open_priority(struct crypt_device *cd,
struct luks2_hdr *hdr,
crypt_keyslot_priority priority,
const char *password,
size_t password_len,
int segment,
struct volume_key **vk)
{
json_object *jobj_keyslots, *jobj;
crypt_keyslot_priority slot_priority;
int keyslot, r = -ENOENT;
json_object_object_get_ex(hdr->jobj, "keyslots", &jobj_keyslots);
json_object_object_foreach(jobj_keyslots, slot, val) {
if (!json_object_object_get_ex(val, "priority", &jobj))
slot_priority = CRYPT_SLOT_PRIORITY_NORMAL;
else
slot_priority = json_object_get_int(jobj);
keyslot = atoi(slot);
if (slot_priority != priority) {
log_dbg("Keyslot %d priority %d != %d (required), skipped.",
keyslot, slot_priority, priority);
continue;
}
r = LUKS2_open_and_verify(cd, hdr, keyslot, segment, password, password_len, vk);
/* Do not retry for errors that are no -EPERM or -ENOENT,
former meaning password wrong, latter key slot unusable for segment */
if ((r != -EPERM) && (r != -ENOENT))
break;
}
return r;
}
int LUKS2_keyslot_open(struct crypt_device *cd,
int keyslot,
int segment,
const char *password,
size_t password_len,
struct volume_key **vk)
{
struct luks2_hdr *hdr;
int r_prio, r = -EINVAL;
hdr = crypt_get_hdr(cd, CRYPT_LUKS2);
if (keyslot == CRYPT_ANY_SLOT) {
r_prio = LUKS2_keyslot_open_priority(cd, hdr, CRYPT_SLOT_PRIORITY_PREFER,
password, password_len, segment, vk);
if (r_prio >= 0)
r = r_prio;
else if (r_prio < 0 && (r_prio != -EPERM) && (r_prio != -ENOENT))
r = r_prio;
else
r = LUKS2_keyslot_open_priority(cd, hdr, CRYPT_SLOT_PRIORITY_NORMAL,
password, password_len, segment, vk);
/* Prefer password wrong to no entry from priority slot */
if (r_prio == -EPERM && r == -ENOENT)
r = r_prio;
} else
r = LUKS2_open_and_verify(cd, hdr, keyslot, segment, password, password_len, vk);
return r;
}
int LUKS2_keyslot_store(struct crypt_device *cd,
struct luks2_hdr *hdr,
int keyslot,
const char *password,
size_t password_len,
const struct volume_key *vk)
{
const keyslot_handler *h;
int r;
if (keyslot == CRYPT_ANY_SLOT)
return -EINVAL;
if (!LUKS2_get_keyslot_jobj(hdr, keyslot)) {
/* Try to allocate default and empty keyslot type */
h = LUKS2_keyslot_handler_type(cd, "luks2");
if (!h)
return -EINVAL;
r = h->alloc(cd, keyslot, vk->keylength);
if (r)
return r;
} else if (!(h = LUKS2_keyslot_handler(cd, keyslot)))
return -EINVAL;
r = h->validate(cd, keyslot);
if (r) {
log_dbg("Keyslot validation failed.");
return r;
}
return h->store(cd, keyslot, password, password_len,
vk->key, vk->keylength);
}
int LUKS2_keyslot_wipe(struct crypt_device *cd,
struct luks2_hdr *hdr,
int keyslot,
int wipe_area_only)
{
struct device *device = crypt_metadata_device(cd);
uint64_t area_offset, area_length;
char num[16];
int r;
json_object *jobj_keyslot, *jobj_keyslots;
const keyslot_handler *h;
h = LUKS2_keyslot_handler(cd, keyslot);
if (!json_object_object_get_ex(hdr->jobj, "keyslots", &jobj_keyslots))
return -EINVAL;
jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot);
if (!jobj_keyslot)
return -ENOENT;
if (wipe_area_only)
log_dbg("Wiping keyslot %d area only.", keyslot);
/* Just check that nobody uses the metadata now */
r = device_write_lock(cd, device);
if (r) {
log_err(cd, _("Failed to acquire write lock on device %s.\n"),
device_path(device));
return r;
}
device_write_unlock(device);
/* secure deletion of possible key material in keyslot area */
r = crypt_keyslot_area(cd, keyslot, &area_offset, &area_length);
if (r && r != -ENOENT)
return r;
/* We can destroy the binary keyslot area now without lock */
if (!r) {
r = crypt_wipe_device(cd, device, CRYPT_WIPE_SPECIAL, area_offset,
area_length, area_length, NULL, NULL);
if (r) {
if (r == -EACCES) {
log_err(cd, _("Cannot write to device %s, permission denied.\n"),
device_path(device));
r = -EINVAL;
} else
log_err(cd, _("Cannot wipe device %s.\n"), device_path(device));
return r;
}
}
if (wipe_area_only)
return r;
/* Slot specific wipe */
if (h) {
r = h->wipe(cd, keyslot);
if (r < 0)
return r;
} else
log_dbg("Wiping keyslot %d without specific-slot handler loaded.", keyslot);
snprintf(num, sizeof(num), "%d", keyslot);
json_object_object_del(jobj_keyslots, num);
return LUKS2_hdr_write(cd, hdr);
}
int LUKS2_keyslot_dump(struct crypt_device *cd, int keyslot)
{
const keyslot_handler *h;
if (!(h = LUKS2_keyslot_handler(cd, keyslot)))
return -EINVAL;
return h->dump(cd, keyslot);
}
crypt_keyslot_priority LUKS2_keyslot_priority_get(struct crypt_device *cd,
struct luks2_hdr *hdr, int keyslot)
{
json_object *jobj_keyslot, *jobj_priority;
jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot);
if (!jobj_keyslot)
return CRYPT_SLOT_PRIORITY_INVALID;
if (!json_object_object_get_ex(jobj_keyslot, "priority", &jobj_priority))
return CRYPT_SLOT_PRIORITY_NORMAL;
return json_object_get_int(jobj_priority);
}
int LUKS2_keyslot_priority_set(struct crypt_device *cd, struct luks2_hdr *hdr,
int keyslot, crypt_keyslot_priority priority, int commit)
{
json_object *jobj_keyslot;
jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot);
if (!jobj_keyslot)
return -EINVAL;
if (priority == CRYPT_SLOT_PRIORITY_NORMAL)
json_object_object_del(jobj_keyslot, "priority");
else
json_object_object_add(jobj_keyslot, "priority", json_object_new_int(priority));
return commit ? LUKS2_hdr_write(cd, hdr) : 0;
}

View File

@@ -0,0 +1,687 @@
/*
* LUKS - Linux Unified Key Setup v2, LUKS2 type keyslot handler
*
* Copyright (C) 2015-2017, Red Hat, Inc. All rights reserved.
* Copyright (C) 2015-2017, Milan Broz. All rights reserved.
*
* 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.
*/
#include "luks2_internal.h"
/* FIXME: move keyslot encryption to crypto backend */
#include "../luks1/af.h"
#define LUKS_SALTSIZE 32
#define LUKS_SLOT_ITERATIONS_MIN 1000
#define LUKS_STRIPES 4000
static int luks2_encrypt_to_storage(char *src, size_t srcLength,
const char *cipher, const char *cipher_mode,
struct volume_key *vk, unsigned int sector,
struct crypt_device *cd)
{
struct device *device = crypt_metadata_device(cd);
#ifndef ENABLE_AF_ALG /* Support for old kernel without Crypto API */
int r = device_write_lock(cd, device);
if (r) {
log_err(cd, _("Failed to acquire write lock on device %s.\n"), device_path(device));
return r;
}
r = LUKS_encrypt_to_storage(src, srcLength, cipher, cipher_mode, vk, sector, cd);
device_write_unlock(crypt_metadata_device(cd));
return r;
#else
struct crypt_storage *s;
int devfd = -1, r;
/* Only whole sector writes supported */
if (srcLength % SECTOR_SIZE)
return -EINVAL;
/* Encrypt buffer */
r = crypt_storage_init(&s, 0, cipher, cipher_mode, vk->key, vk->keylength);
if (r) {
log_dbg("Userspace crypto wrapper cannot use %s-%s (%d).",
cipher, cipher_mode, r);
return r;
}
r = crypt_storage_encrypt(s, 0, srcLength / SECTOR_SIZE, src);
crypt_storage_destroy(s);
if (r)
return r;
r = device_write_lock(cd, device);
if (r) {
log_err(cd, _("Failed to acquire write lock on device %s.\n"),
device_path(device));
return r;
}
devfd = device_open_locked(device, O_RDWR);
if (devfd >= 0) {
if (lseek(devfd, sector * SECTOR_SIZE, SEEK_SET) == -1 ||
write_blockwise(devfd, device_block_size(device),
device_alignment(device), src,
srcLength) == -1)
r = -EIO;
else
r = 0;
close(devfd);
} else
r = -EIO;
device_write_unlock(device);
if (r)
log_err(cd, _("IO error while encrypting keyslot.\n"));
return r;
#endif
}
static int luks2_decrypt_from_storage(char *dst, size_t dstLength,
const char *cipher, const char *cipher_mode, struct volume_key *vk,
unsigned int sector, struct crypt_device *cd)
{
struct device *device = crypt_metadata_device(cd);
#ifndef ENABLE_AF_ALG /* Support for old kernel without Crypto API */
int r = device_read_lock(cd, device);
if (r) {
log_err(cd, _("Failed to acquire read lock on device %s.\n"), device_path(device));
return r;
}
r = LUKS_decrypt_from_storage(dst, dstLength, cipher, cipher_mode, vk, sector, cd);
device_read_unlock(crypt_metadata_device(cd));
return r;
#else
struct crypt_storage *s;
int devfd = -1, r;
/* Only whole sector writes supported */
if (dstLength % SECTOR_SIZE)
return -EINVAL;
r = crypt_storage_init(&s, 0, cipher, cipher_mode, vk->key, vk->keylength);
if (r) {
log_dbg("Userspace crypto wrapper cannot use %s-%s (%d).",
cipher, cipher_mode, r);
return r;
}
r = device_read_lock(cd, device);
if (r) {
log_err(cd, _("Failed to acquire read lock on device %s.\n"),
device_path(device));
return r;
}
devfd = device_open_locked(device, O_RDONLY);
if (devfd >= 0) {
if (lseek(devfd, sector * SECTOR_SIZE, SEEK_SET) == -1 ||
read_blockwise(devfd, device_block_size(device),
device_alignment(device), dst, dstLength) == -1)
r = -EIO;
else
r = 0;
close(devfd);
} else
r = -EIO;
device_read_unlock(device);
if (r) {
log_err(cd, _("IO error while decrypting keyslot.\n"));
return r;
}
/* Decrypt buffer */
r = crypt_storage_decrypt(s, 0, dstLength / SECTOR_SIZE, dst);
crypt_storage_destroy(s);
return r;
#endif
}
static int luks2_keyslot_set_key(struct crypt_device *cd,
json_object *jobj_keyslot,
const char *password, size_t passwordLen,
const char *volume_key, size_t volume_key_len)
{
struct volume_key *derived_key;
char salt[LUKS_SALTSIZE], cipher[MAX_CIPHER_LEN], cipher_mode[MAX_CIPHER_LEN];
char *AfKey = NULL, *salt_base64 = NULL;
size_t AFEKSize, keyslot_key_len;
json_object *jobj2, *jobj_kdf, *jobj_af, *jobj_area;
uint64_t area_offset;
const struct crypt_pbkdf_type *pbkdf;
int r;
if (!json_object_object_get_ex(jobj_keyslot, "kdf", &jobj_kdf) ||
!json_object_object_get_ex(jobj_keyslot, "af", &jobj_af) ||
!json_object_object_get_ex(jobj_keyslot, "area", &jobj_area))
return -EINVAL;
if (!json_object_object_get_ex(jobj_area, "offset", &jobj2))
return -EINVAL;
area_offset = json_object_get_uint64(jobj2);
if (!json_object_object_get_ex(jobj_area, "encryption", &jobj2))
return -EINVAL;
r = crypt_parse_name_and_mode(json_object_get_string(jobj2), cipher, NULL, cipher_mode);
if (r < 0)
return r;
if (!json_object_object_get_ex(jobj_area, "key_size", &jobj2))
return -EINVAL;
keyslot_key_len = json_object_get_int(jobj2);
pbkdf = crypt_get_pbkdf_type(cd);
if (!pbkdf)
return -EINVAL;
r = crypt_benchmark_pbkdf_internal(cd, CONST_CAST(struct crypt_pbkdf_type *)pbkdf, volume_key_len);
if (r < 0)
return r;
if (!strcmp(pbkdf->type, CRYPT_KDF_PBKDF2)) {
json_object_object_add(jobj_kdf, "hash", json_object_new_string(pbkdf->hash));
json_object_object_add(jobj_kdf, "iterations", json_object_new_int(pbkdf->iterations));
} else {
json_object_object_add(jobj_kdf, "time", json_object_new_int(pbkdf->iterations));
json_object_object_add(jobj_kdf, "memory", json_object_new_int(pbkdf->max_memory_kb));
json_object_object_add(jobj_kdf, "cpus", json_object_new_int(pbkdf->parallel_threads));
}
json_object_object_add(jobj_kdf, "type", json_object_new_string(pbkdf->type));
/*
* Get salt and allocate derived key storage.
*/
r = crypt_random_get(cd, salt, LUKS_SALTSIZE, CRYPT_RND_SALT);
if (r < 0)
return r;
base64_encode_alloc(salt, LUKS_SALTSIZE, &salt_base64);
if (!salt_base64)
return -ENOMEM;
json_object_object_add(jobj_kdf, "salt", json_object_new_string(salt_base64));
free(salt_base64);
json_object_object_add(jobj_kdf, "type", json_object_new_string(pbkdf->type));
json_object_object_add(jobj_af, "hash", json_object_new_string(pbkdf->hash));
derived_key = crypt_alloc_volume_key(keyslot_key_len, NULL);
if (!derived_key)
return -ENOMEM;
/*
* Calculate keyslot content, split and store it to keyslot area.
*/
r = crypt_pbkdf(pbkdf->type, pbkdf->hash, password, passwordLen,
salt, LUKS_SALTSIZE,
derived_key->key, derived_key->keylength,
pbkdf->iterations, pbkdf->max_memory_kb,
pbkdf->parallel_threads);
if (r < 0) {
crypt_free_volume_key(derived_key);
return r;
}
// FIXME: verity key_size to AFEKSize
AFEKSize = AF_split_sectors(volume_key_len, LUKS_STRIPES) * SECTOR_SIZE;
AfKey = crypt_safe_alloc(AFEKSize);
if (!AfKey) {
crypt_free_volume_key(derived_key);
return -ENOMEM;
}
r = AF_split(volume_key, AfKey, volume_key_len, LUKS_STRIPES, pbkdf->hash);
if (r == 0) {
log_dbg("Updating keyslot area [0x%04x].", (unsigned)area_offset);
/* FIXME: sector_offset should be size_t, fix LUKS_encrypt... accordingly */
r = luks2_encrypt_to_storage(AfKey, AFEKSize, cipher, cipher_mode,
derived_key, (unsigned)(area_offset / SECTOR_SIZE), cd);
}
crypt_safe_free(AfKey);
crypt_free_volume_key(derived_key);
if (r < 0)
return r;
JSON_DBG(jobj_keyslot, "Keyslot JSON");
return 0;
}
static int luks2_keyslot_get_key(struct crypt_device *cd,
json_object *jobj_keyslot,
const char *password, size_t passwordLen,
char *volume_key, size_t volume_key_len)
{
struct volume_key *derived_key;
char *AfKey;
size_t AFEKSize;
const char *hash = NULL, *af_hash = NULL, *kdf;
char salt[LUKS_SALTSIZE], cipher[MAX_CIPHER_LEN], cipher_mode[MAX_CIPHER_LEN];
json_object *jobj1, *jobj2, *jobj_kdf, *jobj_af, *jobj_area;
uint32_t iterations, memory, parallel;
uint64_t area_offset;
size_t salt_len, keyslot_key_len;
int r;
if (!json_object_object_get_ex(jobj_keyslot, "kdf", &jobj_kdf) ||
!json_object_object_get_ex(jobj_keyslot, "af", &jobj_af) ||
!json_object_object_get_ex(jobj_keyslot, "area", &jobj_area))
return -EINVAL;
if (!json_object_object_get_ex(jobj_kdf, "type", &jobj1))
return -EINVAL;
kdf = json_object_get_string(jobj1);
if (!strcmp(kdf, CRYPT_KDF_PBKDF2)) {
if (!json_object_object_get_ex(jobj_kdf, "hash", &jobj2))
return -EINVAL;
hash = json_object_get_string(jobj2);
if (!json_object_object_get_ex(jobj_kdf, "iterations", &jobj2))
return -EINVAL;
iterations = json_object_get_int(jobj2);
memory = 0;
parallel = 0;
} else {
if (!json_object_object_get_ex(jobj_kdf, "time", &jobj2))
return -EINVAL;
iterations = json_object_get_int(jobj2);
if (!json_object_object_get_ex(jobj_kdf, "memory", &jobj2))
return -EINVAL;
memory = json_object_get_int(jobj2);
if (!json_object_object_get_ex(jobj_kdf, "cpus", &jobj2))
return -EINVAL;
parallel = json_object_get_int(jobj2);
}
if (!json_object_object_get_ex(jobj_kdf, "salt", &jobj2))
return -EINVAL;
salt_len = LUKS_SALTSIZE;
base64_decode(json_object_get_string(jobj2),
json_object_get_string_len(jobj2),
salt, &salt_len);
if (salt_len != LUKS_SALTSIZE)
return -EINVAL;
if (!json_object_object_get_ex(jobj_af, "hash", &jobj2))
return -EINVAL;
af_hash = json_object_get_string(jobj2);
if (!json_object_object_get_ex(jobj_area, "offset", &jobj2))
return -EINVAL;
area_offset = json_object_get_uint64(jobj2);
if (!json_object_object_get_ex(jobj_area, "encryption", &jobj2))
return -EINVAL;
r = crypt_parse_name_and_mode(json_object_get_string(jobj2), cipher, NULL, cipher_mode);
if (r < 0)
return r;
if (!json_object_object_get_ex(jobj_area, "key_size", &jobj2))
return -EINVAL;
keyslot_key_len = json_object_get_int(jobj2);
/*
* Allocate derived key storage space.
*/
derived_key = crypt_alloc_volume_key(keyslot_key_len, NULL);
if (!derived_key)
return -ENOMEM;
AFEKSize = AF_split_sectors(volume_key_len, LUKS_STRIPES) * SECTOR_SIZE;
AfKey = crypt_safe_alloc(AFEKSize);
if (!AfKey) {
crypt_free_volume_key(derived_key);
return -ENOMEM;
}
/*
* Calculate derived key, decrypt keyslot content and merge it.
*/
r = crypt_pbkdf(kdf, hash, password, passwordLen,
salt, LUKS_SALTSIZE,
derived_key->key, derived_key->keylength,
iterations, memory, parallel);
if (r == 0) {
log_dbg("Reading keyslot area [0x%04x].", (unsigned)area_offset);
/* FIXME: sector_offset should be size_t, fix LUKS_decrypt... accordingly */
r = luks2_decrypt_from_storage(AfKey, AFEKSize, cipher, cipher_mode,
derived_key, (unsigned)(area_offset / SECTOR_SIZE), cd);
}
if (r == 0)
r = AF_merge(AfKey, volume_key, volume_key_len, LUKS_STRIPES, af_hash);
crypt_free_volume_key(derived_key);
crypt_safe_free(AfKey);
return r;
}
int luks2_keyslot_alloc(struct crypt_device *cd,
int keyslot,
size_t volume_key_len)
{
struct luks2_hdr *hdr;
const struct crypt_pbkdf_type *pbkdf;
char area_offset_string[24], area_length_string[24];
char cipher[2 * MAX_CIPHER_LEN + 1], num[16];
uint64_t area_offset, area_length;
json_object *jobj_keyslots, *jobj_keyslot, *jobj_kdf, *jobj_af, *jobj_area;
size_t keyslot_key_len;
int r;
log_dbg("Trying to allocate LUKS2 keyslot %d.", keyslot);
if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2)))
return -EINVAL;
if (keyslot == CRYPT_ANY_SLOT)
keyslot = LUKS2_keyslot_find_empty(hdr, "luks2");
if (keyslot < 0 || keyslot > LUKS2_KEYSLOTS_MAX)
return -ENOMEM;
if (LUKS2_get_keyslot_jobj(hdr, keyslot)) {
log_dbg("Cannot modify already active keyslot %d.", keyslot);
return -EINVAL;
}
if (!json_object_object_get_ex(hdr->jobj, "keyslots", &jobj_keyslots))
return -EINVAL;
r = LUKS2_find_area_gap(cd, hdr, volume_key_len, &area_offset, &area_length);
if (r < 0)
return r;
pbkdf = crypt_get_pbkdf_type(cd);
if (!pbkdf)
return -EINVAL;
r = crypt_benchmark_pbkdf_internal(cd, CONST_CAST(struct crypt_pbkdf_type *)pbkdf, volume_key_len);
if (r < 0)
return r;
jobj_keyslot = json_object_new_object();
json_object_object_add(jobj_keyslot, "type", json_object_new_string("luks2"));
json_object_object_add(jobj_keyslot, "key_size", json_object_new_int(volume_key_len));
/* PBKDF object */
jobj_kdf = json_object_new_object();
json_object_object_add(jobj_kdf, "type", json_object_new_string(pbkdf->type));
if (!strcmp(pbkdf->type, CRYPT_KDF_PBKDF2)) {
json_object_object_add(jobj_kdf, "iterations", json_object_new_int(pbkdf->iterations));
json_object_object_add(jobj_kdf, "hash", json_object_new_string(pbkdf->hash));
json_object_object_add(jobj_kdf, "salt", json_object_new_string(""));
} else {
json_object_object_add(jobj_kdf, "time", json_object_new_int(pbkdf->iterations));
json_object_object_add(jobj_kdf, "memory", json_object_new_int(pbkdf->max_memory_kb));
json_object_object_add(jobj_kdf, "cpus", json_object_new_int(pbkdf->parallel_threads));
json_object_object_add(jobj_kdf, "salt", json_object_new_string(""));
}
json_object_object_add(jobj_keyslot, "kdf", jobj_kdf);
/* AF object */
jobj_af = json_object_new_object();
json_object_object_add(jobj_af, "type", json_object_new_string("luks1"));
json_object_object_add(jobj_af, "hash", json_object_new_string(pbkdf->hash));
json_object_object_add(jobj_af, "stripes", json_object_new_int(4000));
json_object_object_add(jobj_keyslot, "af", jobj_af);
/* Area object */
jobj_area = json_object_new_object();
json_object_object_add(jobj_area, "type", json_object_new_string("raw"));
/* Slot encryption tries to use the same key size as fot the main algorithm */
keyslot_key_len = volume_key_len - crypt_get_integrity_key_size(cd);
/* Cannot use metadata tags in keyslot */
if (crypt_get_integrity_tag_size(cd)) {
snprintf(cipher, sizeof(cipher), "aes-xts-plain64"); // FIXME: fixed cipher and key size can be wrong
keyslot_key_len = 32;
} else if (crypt_get_cipher_mode(cd))
snprintf(cipher, sizeof(cipher), "%s-%s", crypt_get_cipher(cd), crypt_get_cipher_mode(cd));
else
snprintf(cipher, sizeof(cipher), "%s", crypt_get_cipher(cd));
json_object_object_add(jobj_area, "encryption", json_object_new_string(cipher));
json_object_object_add(jobj_area, "key_size", json_object_new_int(keyslot_key_len));
uint64_to_str(area_offset_string, sizeof(area_offset_string), &area_offset);
json_object_object_add(jobj_area, "offset", json_object_new_string(area_offset_string));
uint64_to_str(area_length_string, sizeof(area_length_string), &area_length);
json_object_object_add(jobj_area, "size", json_object_new_string(area_length_string));
json_object_object_add(jobj_keyslot, "area", jobj_area);
snprintf(num, sizeof(num), "%d", keyslot);
json_object_object_add(jobj_keyslots, num, jobj_keyslot);
if (LUKS2_check_json_size(hdr)) {
log_dbg("Not enough space in header json area for new keyslot.");
json_object_object_del(jobj_keyslots, num);
return -ENOSPC;
}
return 0;
}
static int luks2_keyslot_open(struct crypt_device *cd,
int keyslot,
const char *password,
size_t password_len,
char *volume_key,
size_t volume_key_len)
{
struct luks2_hdr *hdr;
json_object *jobj_keyslot;
log_dbg("Trying to open LUKS2 keyslot %d.", keyslot);
if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2)))
return -EINVAL;
jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot);
if (!jobj_keyslot)
return -EINVAL;
return luks2_keyslot_get_key(cd, jobj_keyslot,
password, password_len,
volume_key, volume_key_len);
}
static int luks2_keyslot_store(struct crypt_device *cd,
int keyslot,
const char *password,
size_t password_len,
const char *volume_key,
size_t volume_key_len)
{
struct luks2_hdr *hdr;
json_object *jobj_keyslot;
int r;
log_dbg("Calculating attributes for LUKS2 keyslot %d.", keyslot);
if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2)))
return -EINVAL;
jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot);
if (!jobj_keyslot)
return -EINVAL;
r = luks2_keyslot_set_key(cd, jobj_keyslot,
password, password_len,
volume_key, volume_key_len);
if (r < 0)
return r;
r = LUKS2_hdr_write(cd, hdr);
if (r < 0)
return r;
return keyslot;
}
static int luks2_keyslot_wipe(struct crypt_device *cd, int keyslot)
{
struct luks2_hdr *hdr;
if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2)))
return -EINVAL;
/* Remove any reference of deleted keyslot from digests and tokens */
LUKS2_digest_assign(cd, hdr, keyslot, CRYPT_ANY_DIGEST, 0, 0);
LUKS2_token_assign(cd, hdr, keyslot, CRYPT_ANY_TOKEN, 0, 0);
return 0;
}
static int luks2_keyslot_dump(struct crypt_device *cd, int keyslot)
{
json_object *jobj_keyslot, *jobj1, *jobj_kdf, *jobj_af, *jobj_area;
jobj_keyslot = LUKS2_get_keyslot_jobj(crypt_get_hdr(cd, CRYPT_LUKS2), keyslot);
if (!jobj_keyslot)
return -EINVAL;
if (!json_object_object_get_ex(jobj_keyslot, "kdf", &jobj_kdf) ||
!json_object_object_get_ex(jobj_keyslot, "af", &jobj_af) ||
!json_object_object_get_ex(jobj_keyslot, "area", &jobj_area))
return -EINVAL;
json_object_object_get_ex(jobj_area, "encryption", &jobj1);
log_std(cd, "\tCipher: %s\n", json_object_get_string(jobj1));
json_object_object_get_ex(jobj_kdf, "type", &jobj1);
log_std(cd, "\tPBKDF: %s\n", json_object_get_string(jobj1));
if (!strcmp(json_object_get_string(jobj1), CRYPT_KDF_PBKDF2)) {
json_object_object_get_ex(jobj_kdf, "hash", &jobj1);
log_std(cd, "\tHash: %s\n", json_object_get_string(jobj1));
json_object_object_get_ex(jobj_kdf, "iterations", &jobj1);
log_std(cd, "\tIterations: %" PRIu64 "\n", json_object_get_uint64(jobj1));
} else {
json_object_object_get_ex(jobj_kdf, "time", &jobj1);
log_std(cd, "\tTime: %" PRIu64 "\n", json_object_get_int64(jobj1));
json_object_object_get_ex(jobj_kdf, "memory", &jobj1);
log_std(cd, "\tMemory: %" PRIu64 "\n", json_object_get_int64(jobj1));
json_object_object_get_ex(jobj_kdf, "cpus", &jobj1);
log_std(cd, "\tThreads: %" PRIu64 "\n", json_object_get_int64(jobj1));
}
json_object_object_get_ex(jobj_kdf, "salt", &jobj1);
log_std(cd, "\tSalt: ");
hexprint_base64(cd, jobj1, " ", " ");
json_object_object_get_ex(jobj_af, "stripes", &jobj1);
log_std(cd, "\tAF stripes: %u\n", json_object_get_int(jobj1));
json_object_object_get_ex(jobj_area, "offset", &jobj1);
log_std(cd, "\tArea offset:%" PRIu64 " [bytes]\n", json_object_get_uint64(jobj1));
json_object_object_get_ex(jobj_area, "size", &jobj1);
log_std(cd, "\tArea length:%" PRIu64 " [bytes]\n", json_object_get_uint64(jobj1));
return 0;
}
static int contains(json_object *jobj, const char *key, json_type type)
{
json_object *sobj;
if (!json_object_object_get_ex(jobj, key, &sobj) ||
!json_object_is_type(sobj, type))
return 0;
return 1;
}
static int luks2_keyslot_validate(struct crypt_device *cd, int keyslot)
{
struct luks2_hdr *hdr;
json_object *jobj_keyslot, *jobj_kdf, *jobj_af, *jobj_area, *jobj1;
char num[16];
hdr = crypt_get_hdr(cd, CRYPT_LUKS2);
jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot);
if (!jobj_keyslot)
return -EINVAL;
snprintf(num, sizeof(num), "%d", keyslot);
if (LUKS2_keyslot_validate(hdr->jobj, jobj_keyslot, num))
return -EINVAL;
if (!json_object_object_get_ex(jobj_keyslot, "kdf", &jobj_kdf) ||
!json_object_object_get_ex(jobj_keyslot, "af", &jobj_af) ||
!json_object_object_get_ex(jobj_keyslot, "area", &jobj_area))
return -EINVAL;
if (!json_object_object_get_ex(jobj_kdf, "type", &jobj1))
return -EINVAL;
if (!strcmp(json_object_get_string(jobj1), CRYPT_KDF_PBKDF2)) {
if (!contains(jobj_kdf, "hash", json_type_string) ||
!contains(jobj_kdf, "iterations", json_type_int) ||
!contains(jobj_kdf, "salt", json_type_string))
return -EINVAL;
} else {
if (!contains(jobj_kdf, "time", json_type_int) ||
!contains(jobj_kdf, "memory", json_type_int) ||
!contains(jobj_kdf, "cpus", json_type_int) ||
!contains(jobj_kdf, "salt", json_type_string))
return -EINVAL;
}
if (!json_object_object_get_ex(jobj_af, "type", &jobj1))
return -EINVAL;
if (!strcmp(json_object_get_string(jobj1), "luks1")) {
if (!contains(jobj_af, "hash", json_type_string) ||
!contains(jobj_af, "stripes", json_type_int))
return -EINVAL;
} else
return -EINVAL;
// FIXME check numbered
if (!json_object_object_get_ex(jobj_area, "type", &jobj1))
return -EINVAL;
if (!strcmp(json_object_get_string(jobj1), "raw")) {
if (!contains(jobj_area, "encryption", json_type_string) ||
!contains(jobj_area, "key_size", json_type_int) ||
!contains(jobj_area, "offset", json_type_string) ||
!contains(jobj_area, "size", json_type_string))
return -EINVAL;
} else
return -EINVAL;
return 0;
}
const keyslot_handler luks2_keyslot = {
.name = "luks2",
.alloc = luks2_keyslot_alloc,
.open = luks2_keyslot_open,
.store = luks2_keyslot_store,
.wipe = luks2_keyslot_wipe,
.dump = luks2_keyslot_dump,
.validate = luks2_keyslot_validate,
};

View File

@@ -0,0 +1,794 @@
/*
* LUKS - Linux Unified Key Setup v2, LUKS1 conversion code
*
* Copyright (C) 2015-2017, Red Hat, Inc. All rights reserved.
* Copyright (C) 2015-2017, Ondrej Kozina. All rights reserved.
* Copyright (C) 2015-2017, Milan Broz. All rights reserved.
*
* 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.
*/
#include "luks2_internal.h"
#include "../luks1/luks.h"
#include "../luks1/af.h"
static int json_luks1_keyslot(const struct luks_phdr *hdr_v1, int keyslot, struct json_object **keyslot_object)
{
char *base64_str, cipher[LUKS_CIPHERNAME_L+LUKS_CIPHERMODE_L], num[24];
size_t base64_len;
struct json_object *keyslot_obj, *field, *jobj_kdf, *jobj_af, *jobj_area;
uint64_t offset, area_size, offs_a, offs_b, length;
keyslot_obj = json_object_new_object();
json_object_object_add(keyslot_obj, "type", json_object_new_string("luks2"));
json_object_object_add(keyslot_obj, "key_size", json_object_new_int64(hdr_v1->keyBytes));
/* KDF */
jobj_kdf = json_object_new_object();
json_object_object_add(jobj_kdf, "type", json_object_new_string(CRYPT_KDF_PBKDF2));
json_object_object_add(jobj_kdf, "hash", json_object_new_string(hdr_v1->hashSpec));
json_object_object_add(jobj_kdf, "iterations", json_object_new_int64(hdr_v1->keyblock[keyslot].passwordIterations));
/* salt field */
base64_len = base64_encode_alloc(hdr_v1->keyblock[keyslot].passwordSalt, LUKS_SALTSIZE, &base64_str);
if (!base64_str) {
json_object_put(keyslot_obj);
json_object_put(jobj_kdf);
if (!base64_len)
return -EINVAL;
return -ENOMEM;
}
field = json_object_new_string_len(base64_str, base64_len);
free(base64_str);
json_object_object_add(jobj_kdf, "salt", field);
json_object_object_add(keyslot_obj, "kdf", jobj_kdf);
/* AF */
jobj_af = json_object_new_object();
json_object_object_add(jobj_af, "type", json_object_new_string("luks1"));
json_object_object_add(jobj_af, "hash", json_object_new_string(hdr_v1->hashSpec));
/* stripes field ignored, fixed to LUKS_STRIPES (4000) */
json_object_object_add(jobj_af, "stripes", json_object_new_int(4000));
json_object_object_add(keyslot_obj, "af", jobj_af);
/* Area */
jobj_area = json_object_new_object();
json_object_object_add(jobj_area, "type", json_object_new_string("raw"));
/* encryption algorithm field */
if (*hdr_v1->cipherMode != '\0') {
(void) snprintf(cipher, sizeof(cipher), "%s-%s", hdr_v1->cipherName, hdr_v1->cipherMode);
json_object_object_add(jobj_area, "encryption", json_object_new_string(cipher));
} else
json_object_object_add(jobj_area, "encryption", json_object_new_string(hdr_v1->cipherName));
/* area */
if (LUKS_keyslot_area(hdr_v1, 0, &offs_a, &length) ||
LUKS_keyslot_area(hdr_v1, 1, &offs_b, &length) ||
LUKS_keyslot_area(hdr_v1, keyslot, &offset, &length)) {
json_object_put(keyslot_obj);
json_object_put(jobj_area);
return -EINVAL;
}
area_size = offs_b - offs_a;
json_object_object_add(jobj_area, "key_size", json_object_new_int(hdr_v1->keyBytes));
json_object_object_add(jobj_area, "offset", json_object_new_string(uint64_to_str(num, sizeof(num), &offset)));
json_object_object_add(jobj_area, "size", json_object_new_string(uint64_to_str(num, sizeof(num), &area_size)));
json_object_object_add(keyslot_obj, "area", jobj_area);
*keyslot_object = keyslot_obj;
return 0;
}
static int json_luks1_keyslots(const struct luks_phdr *hdr_v1, struct json_object **keyslots_object)
{
char keyslot_str[2];
int key_slot, r;
struct json_object *keyslot_obj, *field;
keyslot_obj = json_object_new_object();
if (!keyslot_obj)
return -ENOMEM;
for (key_slot = 0; key_slot < LUKS_NUMKEYS; key_slot++) {
if (hdr_v1->keyblock[key_slot].active != LUKS_KEY_ENABLED)
continue;
r = json_luks1_keyslot(hdr_v1, key_slot, &field);
if (r) {
json_object_put(keyslot_obj);
return r;
}
(void) snprintf(keyslot_str, sizeof(keyslot_str), "%d", key_slot);
json_object_object_add(keyslot_obj, keyslot_str, field);
}
*keyslots_object = keyslot_obj;
return 0;
}
static int json_luks1_segment(const struct luks_phdr *hdr_v1, struct json_object **segment_object)
{
const char *c;
char cipher[LUKS_CIPHERNAME_L+LUKS_CIPHERMODE_L];
char num[24]; /* uint64_t in string */
struct json_object *segment_obj, *field;
uint64_t number;
segment_obj = json_object_new_object();
if (!segment_obj)
return -ENOMEM;
/* type field */
field = json_object_new_string("crypt");
if (!field) {
json_object_put(segment_obj);
return -ENOMEM;
}
json_object_object_add(segment_obj, "type", field);
/* offset field */
number = hdr_v1->payloadOffset * SECTOR_SIZE;
field = json_object_new_string(uint64_to_str(num, sizeof(num), &number));
if (!field) {
json_object_put(segment_obj);
return -ENOMEM;
}
json_object_object_add(segment_obj, "offset", field);
/* iv_tweak field */
field = json_object_new_string("0");
if (!field) {
json_object_put(segment_obj);
return -ENOMEM;
}
json_object_object_add(segment_obj, "iv_tweak", field);
/* length field */
field = json_object_new_string("dynamic");
if (!field) {
json_object_put(segment_obj);
return -ENOMEM;
}
json_object_object_add(segment_obj, "size", field);
/* cipher field */
if (*hdr_v1->cipherMode != '\0') {
(void) snprintf(cipher, sizeof(cipher), "%s-%s", hdr_v1->cipherName, hdr_v1->cipherMode);
c = cipher;
} else
c = hdr_v1->cipherName;
field = json_object_new_string(c);
if (!field) {
json_object_put(segment_obj);
return -ENOMEM;
}
json_object_object_add(segment_obj, "encryption", field);
/* block field */
field = json_object_new_int(SECTOR_SIZE);
if (!field) {
json_object_put(segment_obj);
return -ENOMEM;
}
json_object_object_add(segment_obj, "sector_size", field);
*segment_object = segment_obj;
return 0;
}
static int json_luks1_segments(const struct luks_phdr *hdr_v1, struct json_object **segments_object)
{
char num[16];
int r;
struct json_object *segments_obj, *field;
segments_obj = json_object_new_object();
if (!segments_obj)
return -ENOMEM;
r = json_luks1_segment(hdr_v1, &field);
if (r) {
json_object_put(segments_obj);
return r;
}
snprintf(num, sizeof(num), "%u", CRYPT_DEFAULT_SEGMENT);
json_object_object_add(segments_obj, num, field);
*segments_object = segments_obj;
return 0;
}
static int json_luks1_digest(const struct luks_phdr *hdr_v1, struct json_object **digest_object)
{
char keyslot_str[2], *base64_str;
int ks;
size_t base64_len;
struct json_object *digest_obj, *array, *field;
digest_obj = json_object_new_object();
if (!digest_obj)
return -ENOMEM;
/* type field */
field = json_object_new_string("pbkdf2");
if (!field) {
json_object_put(digest_obj);
return -ENOMEM;
}
json_object_object_add(digest_obj, "type", field);
/* keyslots array */
array = json_object_new_array();
if (!array) {
json_object_put(digest_obj);
return -ENOMEM;
}
json_object_object_add(digest_obj, "keyslots", json_object_get(array));
for (ks = 0; ks < LUKS_NUMKEYS; ks++) {
if (hdr_v1->keyblock[ks].active != LUKS_KEY_ENABLED)
continue;
(void) snprintf(keyslot_str, sizeof(keyslot_str), "%d", ks);
field = json_object_new_string(keyslot_str);
if (!field || json_object_array_add(array, field) < 0) {
json_object_put(field);
json_object_put(array);
json_object_put(digest_obj);
return -ENOMEM;
}
}
json_object_put(array);
/* segments array */
array = json_object_new_array();
if (!array) {
json_object_put(digest_obj);
return -ENOMEM;
}
json_object_object_add(digest_obj, "segments", json_object_get(array));
field = json_object_new_string("0");
if (!field || json_object_array_add(array, field) < 0) {
json_object_put(field);
json_object_put(array);
json_object_put(digest_obj);
return -ENOMEM;
}
json_object_put(array);
/* hash field */
field = json_object_new_string(hdr_v1->hashSpec);
if (!field) {
json_object_put(digest_obj);
return -ENOMEM;
}
json_object_object_add(digest_obj, "hash", field);
/* salt field */
base64_len = base64_encode_alloc(hdr_v1->mkDigestSalt, LUKS_SALTSIZE, &base64_str);
if (!base64_str) {
json_object_put(digest_obj);
if (!base64_len)
return -EINVAL;
return -ENOMEM;
}
field = json_object_new_string_len(base64_str, base64_len);
free(base64_str);
if (!field) {
json_object_put(digest_obj);
return -ENOMEM;
}
json_object_object_add(digest_obj, "salt", field);
/* digest field */
base64_len = base64_encode_alloc(hdr_v1->mkDigest, LUKS_DIGESTSIZE, &base64_str);
if (!base64_str) {
json_object_put(digest_obj);
if (!base64_len)
return -EINVAL;
return -ENOMEM;
}
field = json_object_new_string_len(base64_str, base64_len);
free(base64_str);
if (!field) {
json_object_put(digest_obj);
return -ENOMEM;
}
json_object_object_add(digest_obj, "digest", field);
/* iterations field */
field = json_object_new_int64(hdr_v1->mkDigestIterations);
if (!field) {
json_object_put(digest_obj);
return -ENOMEM;
}
json_object_object_add(digest_obj, "iterations", field);
*digest_object = digest_obj;
return 0;
}
static int json_luks1_digests(const struct luks_phdr *hdr_v1, struct json_object **digests_object)
{
int r;
struct json_object *digests_obj, *field;
digests_obj = json_object_new_object();
if (!digests_obj)
return -ENOMEM;
r = json_luks1_digest(hdr_v1, &field);
if (r) {
json_object_put(digests_obj);
return r;
}
json_object_object_add(digests_obj, "0", field);
*digests_object = digests_obj;
return 0;
}
static int json_luks1_object(struct luks_phdr *hdr_v1, struct json_object **luks1_object, uint64_t keyslots_size)
{
char num[24];
int r;
struct json_object *luks1_obj, *field;
uint64_t json_size;
luks1_obj = json_object_new_object();
if (!luks1_obj)
return -ENOMEM;
/* keyslots field */
r = json_luks1_keyslots(hdr_v1, &field);
if (r) {
json_object_put(luks1_obj);
return r;
}
json_object_object_add(luks1_obj, "keyslots", field);
/* tokens field */
field = json_object_new_object();
if (!field) {
json_object_put(luks1_obj);
return -ENOMEM;
}
json_object_object_add(luks1_obj, "tokens", field);
/* segments field */
r = json_luks1_segments(hdr_v1, &field);
if (r) {
json_object_put(luks1_obj);
return r;
}
json_object_object_add(luks1_obj, "segments", field);
/* digests field */
r = json_luks1_digests(hdr_v1, &field);
if (r) {
json_object_put(luks1_obj);
return r;
}
json_object_object_add(luks1_obj, "digests", field);
/* config field */
/* anything else? */
field = json_object_new_object();
if (!field) {
json_object_put(luks1_obj);
return -ENOMEM;
}
json_object_object_add(luks1_obj, "config", field);
json_size = LUKS2_HDR_16K_LEN - LUKS2_HDR_BIN_LEN;
json_object_object_add(field, "json_size",
json_object_new_string(uint64_to_str(num, sizeof(num), &json_size)));
json_object_object_add(field, "keyslots_size",
json_object_new_string(uint64_to_str(num, sizeof(num), &keyslots_size)));
*luks1_object = luks1_obj;
return 0;
}
static void move_keyslot_offset(json_object *jobj, int offset_add)
{
char num[24];
json_object *jobj1, *jobj2, *jobj_area;
uint64_t offset = 0;
json_object_object_get_ex(jobj, "keyslots", &jobj1);
json_object_object_foreach(jobj1, key, val) {
UNUSED(key);
json_object_object_get_ex(val, "area", &jobj_area);
json_object_object_get_ex(jobj_area, "offset", &jobj2);
offset = json_object_get_uint64(jobj2) + offset_add;
json_object_object_add(jobj_area, "offset", json_object_new_string(uint64_to_str(num, sizeof(num), &offset)));
}
}
/* FIXME: return specific error code for partial write error (aka keyslots are gone) */
static int move_keyslot_areas(struct crypt_device *cd, off_t offset_from,
off_t offset_to, size_t buf_size)
{
struct device *device = crypt_metadata_device(cd);
void *buf = NULL;
int devfd = -1;
log_dbg("Moving keyslot areas of size %zu from %jd to %jd.",
buf_size, (intmax_t)offset_from, (intmax_t)offset_to);
// FIXME: export aligned_malloc from utils
if (posix_memalign(&buf, crypt_getpagesize(), buf_size))
return -ENOMEM;
devfd = device_open(device, O_RDWR);
if (devfd == -1) {
log_dbg("Cannot open device %s.", device_path(device));
return -EIO;
}
if (read_lseek_blockwise(devfd, device_block_size(device),
device_alignment(device), buf, buf_size,
offset_from)!= (ssize_t)buf_size) {
close(devfd);
free(buf);
return -EIO;
}
if (write_lseek_blockwise(devfd, device_block_size(device),
device_alignment(device), buf, buf_size,
offset_to) != (ssize_t)buf_size) {
close(devfd);
free(buf);
return -EIO;
}
close(devfd);
crypt_memzero(buf, buf_size);
free(buf);
return 0;
}
static int luks_header_in_use(struct crypt_device *cd)
{
int r;
r = lookup_dm_dev_by_uuid(crypt_get_uuid(cd), crypt_get_type(cd));
if (r < 0)
log_err(cd, _("Can not check status of device with uuid: %s.\n"), crypt_get_uuid(cd));
return r;
}
/* Convert LUKS1 -> LUKS2 */
int LUKS2_luks1_to_luks2(struct crypt_device *cd, struct luks_phdr *hdr1, struct luks2_hdr *hdr2)
{
int r;
json_object *jobj = NULL;
size_t buf_size, buf_offset, luks1_size, luks1_shift = 2 * LUKS2_HDR_16K_LEN - LUKS_ALIGN_KEYSLOTS;
uint64_t max_size = crypt_get_data_offset(cd) * SECTOR_SIZE;
/* for detached headers max size == device size */
if (!max_size && (r = device_size(crypt_metadata_device(cd), &max_size)))
return r;
luks1_size = LUKS_device_sectors(hdr1) << SECTOR_SHIFT;
if (!luks1_size)
return -EINVAL;
if (LUKS_keyslots_offset(hdr1) != (LUKS_ALIGN_KEYSLOTS / SECTOR_SIZE)) {
log_dbg("Unsupported keyslots material offset: %zu.", LUKS_keyslots_offset(hdr1));
return -EINVAL;
}
log_dbg("Max size: %" PRIu64 ", LUKS1 (full) header size %zu , required shift: %zu",
max_size, luks1_size, luks1_shift);
if ((max_size - luks1_size) < luks1_shift) {
log_err(cd, _("Unable to move keyslot materials. Not enough space\n"));
return -EINVAL;
}
r = json_luks1_object(hdr1, &jobj, max_size - 2 * LUKS2_HDR_16K_LEN);
if (r < 0)
return r;
move_keyslot_offset(jobj, luks1_shift);
// fill hdr2
memset(hdr2, 0, sizeof(*hdr2));
hdr2->hdr_size = LUKS2_HDR_16K_LEN;
hdr2->seqid = 1;
hdr2->version = 2;
strncpy(hdr2->checksum_alg, "sha256", LUKS2_CHECKSUM_ALG_L);
crypt_random_get(cd, (char*)hdr2->salt1, sizeof(hdr2->salt1), CRYPT_RND_SALT);
crypt_random_get(cd, (char*)hdr2->salt2, sizeof(hdr2->salt2), CRYPT_RND_SALT);
strncpy(hdr2->uuid, crypt_get_uuid(cd), LUKS2_UUID_L);
hdr2->jobj = jobj;
/*
* It duplicates check in LUKS2_hdr_write() but we don't want to move
* keyslot areas in case it would fail later
*/
if (max_size < LUKS2_hdr_and_areas_size(hdr2->jobj)) {
r = -EINVAL;
goto out;
}
if ((r = luks_header_in_use(cd))) {
if (r > 0)
r = -EBUSY;
goto out;
}
// move keyslots 4k -> 32k offset
buf_offset = 2 * LUKS2_HDR_16K_LEN;
buf_size = luks1_size - LUKS_ALIGN_KEYSLOTS;
if ((r = move_keyslot_areas(cd, 8 * SECTOR_SIZE, buf_offset, buf_size)) < 0)
goto out;
// Write JSON hdr2
r = LUKS2_hdr_write(cd, hdr2);
out:
LUKS2_hdr_free(hdr2);
return r;
}
static int keyslot_LUKS1_compatible(struct luks2_hdr *hdr, int keyslot, uint32_t key_size)
{
json_object *jobj_keyslot, *jobj, *jobj_kdf, *jobj_af;
uint64_t l2_offset, l2_length;
int ks_key_size;
jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot);
if (!jobj_keyslot)
return 1;
if (!json_object_object_get_ex(jobj_keyslot, "type", &jobj) ||
strcmp(json_object_get_string(jobj), "luks2"))
return 0;
/* Using PBKDF2, this implies memory and parallel is not used. */
jobj = NULL;
if (!json_object_object_get_ex(jobj_keyslot, "kdf", &jobj_kdf) ||
!json_object_object_get_ex(jobj_kdf, "type", &jobj) ||
strcmp(json_object_get_string(jobj), CRYPT_KDF_PBKDF2))
return 0;
jobj = NULL;
if (!json_object_object_get_ex(jobj_keyslot, "af", &jobj_af) ||
!json_object_object_get_ex(jobj_af, "stripes", &jobj) ||
json_object_get_int(jobj) != LUKS_STRIPES)
return 0;
jobj = NULL;
if (!json_object_object_get_ex(jobj_af, "hash", &jobj) ||
crypt_hash_size(json_object_get_string(jobj)) < 0)
return 0;
/* FIXME: should this go to validation code instead (aka invalid luks2 header if assigned to segment 0)? */
ks_key_size = LUKS2_get_keyslot_key_size(hdr, keyslot);
if (ks_key_size < 0 || (int)key_size != LUKS2_get_keyslot_key_size(hdr, keyslot)) {
log_dbg("Key length in keyslot %d is different from volume key length", keyslot);
return 0;
}
if (LUKS2_keyslot_area(hdr, keyslot, &l2_offset, &l2_length))
return 0;
if (l2_length != (size_round_up(AF_split_sectors(key_size, LUKS_STRIPES) * SECTOR_SIZE, 4096))) {
log_dbg("Area length in LUKS2 keyslot (%d) is not compatible with LUKS1", keyslot);
return 0;
}
return 1;
}
/* Convert LUKS2 -> LUKS1 */
int LUKS2_luks2_to_luks1(struct crypt_device *cd, struct luks2_hdr *hdr2, struct luks_phdr *hdr1)
{
size_t buf_size, buf_offset;
char cipher[LUKS_CIPHERNAME_L], cipher_mode[LUKS_CIPHERMODE_L];
char digest[LUKS_DIGESTSIZE], digest_salt[LUKS_SALTSIZE];
size_t len;
json_object *jobj_keyslot, *jobj_digest, *jobj_segment, *jobj_kdf, *jobj_area, *jobj1, *jobj2;
uint32_t key_size;
int i, r, last_active = 0;
uint64_t offset, area_length;
char buf[256], luksMagic[] = LUKS_MAGIC;
jobj_digest = LUKS2_get_digest_jobj(hdr2, 0);
if (!jobj_digest)
return -EINVAL;
jobj_segment = LUKS2_get_segment_jobj(hdr2, CRYPT_DEFAULT_SEGMENT);
if (!jobj_segment)
return -EINVAL;
json_object_object_get_ex(hdr2->jobj, "digests", &jobj1);
if (!json_object_object_get_ex(jobj_digest, "type", &jobj2) ||
strcmp(json_object_get_string(jobj2), "pbkdf2") ||
json_object_object_length(jobj1) != 1) {
log_err(cd, _("Cannot convert to LUKS1 format - key slot digests are not LUKS1 compatible.\n"));
return -EINVAL;
}
key_size = r = LUKS2_get_volume_key_size(hdr2, 0);
if (r < 0)
return -EINVAL;
for (i = 0; i < LUKS2_KEYSLOTS_MAX; i++) {
if (LUKS2_keyslot_info(hdr2, i) == CRYPT_SLOT_INACTIVE)
continue;
if (LUKS2_keyslot_info(hdr2, i) == CRYPT_SLOT_INVALID) {
log_err(cd, _("Cannot convert to LUKS1 format - keyslot %u is in invalid state.\n"), i);
return -EINVAL;
}
if (i >= LUKS_NUMKEYS) {
log_err(cd, _("Cannot convert to LUKS1 format - slot %u (over maximum slots) is still active .\n"), i);
return -EINVAL;
}
if (!keyslot_LUKS1_compatible(hdr2, i, key_size)) {
log_err(cd, _("Cannot convert to LUKS1 format - keyslot %u is not LUKS1 compatible.\n"), i);
return -EINVAL;
}
}
memset(hdr1, 0, sizeof(*hdr1));
for (i = 0; i < LUKS_NUMKEYS; i++) {
hdr1->keyblock[i].active = LUKS_KEY_DISABLED;
hdr1->keyblock[i].stripes = LUKS_STRIPES;
jobj_keyslot = LUKS2_get_keyslot_jobj(hdr2, i);
if (jobj_keyslot) {
if (!json_object_object_get_ex(jobj_keyslot, "area", &jobj_area))
return -EINVAL;
if (!json_object_object_get_ex(jobj_area, "offset", &jobj1))
return -EINVAL;
offset = json_object_get_uint64(jobj1);
} else {
if (LUKS2_find_area_gap(cd, hdr2, key_size, &offset, &area_length))
return -EINVAL;
/* FIXME: luks2 reload is required! */
if (luks2_keyslot_alloc(cd, i, key_size))
return -EINVAL;
}
offset /= SECTOR_SIZE;
if (offset > UINT32_MAX)
return -EINVAL;
hdr1->keyblock[i].keyMaterialOffset = offset;
hdr1->keyblock[i].keyMaterialOffset -=
((2 * LUKS2_HDR_16K_LEN - LUKS_ALIGN_KEYSLOTS) / SECTOR_SIZE);
if (!jobj_keyslot)
continue;
hdr1->keyblock[i].active = LUKS_KEY_ENABLED;
last_active = i;
if (!json_object_object_get_ex(jobj_keyslot, "kdf", &jobj_kdf))
continue;
if (!json_object_object_get_ex(jobj_kdf, "iterations", &jobj1))
continue;
hdr1->keyblock[i].passwordIterations = json_object_get_uint32(jobj1);
if (!json_object_object_get_ex(jobj_kdf, "salt", &jobj1))
continue;
len = sizeof(buf);
memset(buf, 0, len);
if (!base64_decode(json_object_get_string(jobj1),
json_object_get_string_len(jobj1), buf, &len))
continue;
if (len > 0 && len != LUKS_SALTSIZE)
continue;
memcpy(hdr1->keyblock[i].passwordSalt, buf, LUKS_SALTSIZE);
}
if (!jobj_keyslot) {
jobj_keyslot = LUKS2_get_keyslot_jobj(hdr2, last_active);
if (!jobj_keyslot)
return -EINVAL;
}
if (!json_object_object_get_ex(jobj_keyslot, "area", &jobj_area))
return -EINVAL;
if (!json_object_object_get_ex(jobj_area, "encryption", &jobj1))
return -EINVAL;
r = crypt_parse_name_and_mode(json_object_get_string(jobj1), cipher, NULL, cipher_mode);
if (r < 0)
return r;
strncpy(hdr1->cipherName, cipher, sizeof(hdr1->cipherName));
strncpy(hdr1->cipherMode, cipher_mode, sizeof(hdr1->cipherMode));
if (!json_object_object_get_ex(jobj_keyslot, "kdf", &jobj_kdf))
return -EINVAL;
if (!json_object_object_get_ex(jobj_kdf, "hash", &jobj1))
return -EINVAL;
strncpy(hdr1->hashSpec, json_object_get_string(jobj1), sizeof(hdr1->hashSpec));
hdr1->keyBytes = key_size;
if (!json_object_object_get_ex(jobj_digest, "iterations", &jobj1))
return -EINVAL;
hdr1->mkDigestIterations = json_object_get_uint32(jobj1);
if (!json_object_object_get_ex(jobj_digest, "digest", &jobj1))
return -EINVAL;
len = sizeof(digest);
if (!base64_decode(json_object_get_string(jobj1),
json_object_get_string_len(jobj1), digest, &len))
return -EINVAL;
/* We can store full digest here, not only sha1 length */
if (len < LUKS_DIGESTSIZE)
return -EINVAL;
memcpy(hdr1->mkDigest, digest, LUKS_DIGESTSIZE);
if (!json_object_object_get_ex(jobj_digest, "salt", &jobj1))
return -EINVAL;
len = sizeof(digest_salt);
if (!base64_decode(json_object_get_string(jobj1),
json_object_get_string_len(jobj1), digest_salt, &len))
return -EINVAL;
if (len != LUKS_SALTSIZE)
return -EINVAL;
memcpy(hdr1->mkDigestSalt, digest_salt, LUKS_SALTSIZE);
if (!json_object_object_get_ex(jobj_segment, "offset", &jobj1))
return -EINVAL;
offset = json_object_get_uint64(jobj1) / SECTOR_SIZE;
if (offset > UINT32_MAX)
return -EINVAL;
/* FIXME: LUKS1 requires offset == 0 || offset >= luks1_hdr_size */
hdr1->payloadOffset = offset;
strncpy(hdr1->uuid, hdr2->uuid, UUID_STRING_L);
memcpy(hdr1->magic, luksMagic, LUKS_MAGIC_L);
hdr1->version = 1;
r = luks_header_in_use(cd);
if (r)
return r > 0 ? -EBUSY : r;
// move keyslots 32k -> 4k offset
buf_offset = 2 * LUKS2_HDR_16K_LEN;
buf_size = LUKS2_keyslots_size(hdr2->jobj);
r = move_keyslot_areas(cd, buf_offset, 8 * SECTOR_SIZE, buf_size);
if (r < 0)
return r;
crypt_wipe_device(cd, crypt_metadata_device(cd), CRYPT_WIPE_ZERO, 0,
8 * SECTOR_SIZE, 8 * SECTOR_SIZE, NULL, NULL);
// Write LUKS1 hdr
return LUKS_write_phdr(hdr1, cd);
}

560
lib/luks2/luks2_token.c Normal file
View File

@@ -0,0 +1,560 @@
/*
* LUKS - Linux Unified Key Setup v2, token handling
*
* Copyright (C) 2016-2017, Red Hat, Inc. All rights reserved.
* Copyright (C) 2016-2017, Milan Broz. All rights reserved.
*
* 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.
*/
#include <assert.h>
#include "luks2_internal.h"
/* Builtin tokens */
extern const crypt_token_handler keyring_handler;
static token_handler token_handlers[LUKS2_TOKENS_MAX] = {
/* keyring builtin token */
{
.get = token_keyring_get,
.set = token_keyring_set,
.h = &keyring_handler
},
};
static int is_builtin_candidate(const char *type)
{
return !strncmp(type, LUKS2_BUILTIN_TOKEN_PREFIX, LUKS2_BUILTIN_TOKEN_PREFIX_LEN);
}
int crypt_token_register(const crypt_token_handler *handler)
{
int i;
if (is_builtin_candidate(handler->name)) {
log_dbg("'" LUKS2_BUILTIN_TOKEN_PREFIX "' is reserved prefix for builtin tokens.");
return -EINVAL;
}
for (i = 0; i < LUKS2_TOKENS_MAX && token_handlers[i].h; i++) {
if (!strcmp(token_handlers[i].h->name, handler->name)) {
log_dbg("Keyslot handler %s is already registered.", handler->name);
return -EINVAL;
}
}
if (i == LUKS2_TOKENS_MAX) {
log_dbg("No more space for another token handler.");
return -EINVAL;
}
token_handlers[i].h = handler;
return 0;
}
static const token_handler
*LUKS2_token_handler_type_internal(struct crypt_device *cd, const char *type)
{
int i;
for (i = 0; i < LUKS2_TOKENS_MAX && token_handlers[i].h; i++)
if (!strcmp(token_handlers[i].h->name, type))
return token_handlers + i;
return NULL;
}
static const crypt_token_handler
*LUKS2_token_handler_type(struct crypt_device *cd, const char *type)
{
const token_handler *th = LUKS2_token_handler_type_internal(cd, type);
return th ? th->h : NULL;
}
static const token_handler
*LUKS2_token_handler_internal(struct crypt_device *cd, int token)
{
struct luks2_hdr *hdr;
json_object *jobj1, *jobj2;
if (token < 0)
return NULL;
if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2)))
return NULL;
if (!(jobj1 = LUKS2_get_token_jobj(hdr, token)))
return NULL;
if (!json_object_object_get_ex(jobj1, "type", &jobj2))
return NULL;
return LUKS2_token_handler_type_internal(cd, json_object_get_string(jobj2));
}
static const crypt_token_handler
*LUKS2_token_handler(struct crypt_device *cd, int token)
{
const token_handler *th = LUKS2_token_handler_internal(cd, token);
return th ? th->h : NULL;
}
static int LUKS2_token_find_free(struct luks2_hdr *hdr)
{
int i;
for (i = 0; i < LUKS2_TOKENS_MAX; i++)
if (!LUKS2_get_token_jobj(hdr, i))
return i;
return -EINVAL;
}
int LUKS2_token_create(struct crypt_device *cd,
struct luks2_hdr *hdr,
int token,
const char *json,
int commit)
{
const crypt_token_handler *h;
json_object *jobj_tokens, *jobj_type, *jobj;
enum json_tokener_error jerr;
char num[16];
if (token == CRYPT_ANY_TOKEN) {
if (!json)
return -EINVAL;
token = LUKS2_token_find_free(hdr);
}
if (token < 0 || token >= LUKS2_TOKENS_MAX)
return -EINVAL;
if (!json_object_object_get_ex(hdr->jobj, "tokens", &jobj_tokens))
return -EINVAL;
/* Remove token */
if (!json) {
snprintf(num, sizeof(num), "%d", token);
json_object_object_del(jobj_tokens, num);
} else {
jobj = json_tokener_parse_verbose(json, &jerr);
if (!jobj) {
log_dbg("Token JSON parse failed.");
return -EINVAL;
}
snprintf(num, sizeof(num), "%d", token);
if (LUKS2_token_validate(hdr->jobj, jobj, num)) {
json_object_put(jobj);
return -EINVAL;
}
json_object_object_get_ex(jobj, "type", &jobj_type);
if (is_builtin_candidate(json_object_get_string(jobj_type))) {
log_dbg("%s is builtin token candidate", json_object_get_string(jobj_type));
json_object_put(jobj);
return -EINVAL;
}
h = LUKS2_token_handler_type(cd, json_object_get_string(jobj_type));
if (h && h->validate && h->validate(cd, json)) {
json_object_put(jobj);
return -EINVAL;
}
json_object_object_add(jobj_tokens, num, jobj);
if (LUKS2_check_json_size(hdr)) {
log_dbg("Not enough space in header json area for new token.");
json_object_object_del(jobj_tokens, num);
return -ENOSPC;
}
}
if (commit)
return LUKS2_hdr_write(cd, hdr) ?: token;
return token;
}
crypt_token_info LUKS2_token_status(struct crypt_device *cd,
struct luks2_hdr *hdr,
int token,
const char **type)
{
const char *tmp;
const token_handler *th;
json_object *jobj_type, *jobj_token;
if (token < 0 || token >= LUKS2_TOKENS_MAX)
return CRYPT_TOKEN_INVALID;
if (!(jobj_token = LUKS2_get_token_jobj(hdr, token)))
return CRYPT_TOKEN_INACTIVE;
json_object_object_get_ex(jobj_token, "type", &jobj_type);
tmp = json_object_get_string(jobj_type);
if ((th = LUKS2_token_handler_type_internal(cd, tmp))) {
if (type)
*type = th->h->name;
return th->set ? CRYPT_TOKEN_INTERNAL : CRYPT_TOKEN_EXTERNAL;
}
if (type)
*type = tmp;
return is_builtin_candidate(tmp) ? CRYPT_TOKEN_INTERNAL_UNKNOWN : CRYPT_TOKEN_EXTERNAL_UNKNOWN;
}
int LUKS2_builtin_token_get(struct crypt_device *cd,
struct luks2_hdr *hdr,
int token,
const char *type,
void *params)
{
const token_handler *th = LUKS2_token_handler_type_internal(cd, type);
// internal error
assert(th && th->get);
return th->get(LUKS2_get_token_jobj(hdr, token), params) ?: token;
}
int LUKS2_builtin_token_create(struct crypt_device *cd,
struct luks2_hdr *hdr,
int token,
const char *type,
const void *params,
int commit)
{
const token_handler *th;
char num[16];
int r;
json_object *jobj_token, *jobj_tokens;
th = LUKS2_token_handler_type_internal(cd, type);
// at this point all builtin handlers must exist and have validate fn defined
assert(th && th->set && th->h->validate);
if (token == CRYPT_ANY_TOKEN) {
if ((token = LUKS2_token_find_free(hdr)) < 0)
log_err(cd, _("No free token slot\n"));
}
if (token < 0 || token >= LUKS2_TOKENS_MAX)
return -EINVAL;
snprintf(num, sizeof(num), "%u", token);
r = th->set(&jobj_token, params);
if (r) {
log_err(cd, _("Failed to create builtin token %s\n"), type);
return r;
}
// builtin tokens must produce valid json
r = LUKS2_token_validate(hdr->jobj, jobj_token, "new");
assert(!r);
r = th->h->validate(cd, json_object_to_json_string_ext(jobj_token, JSON_C_TO_STRING_PLAIN));
assert(!r);
json_object_object_get_ex(hdr->jobj, "tokens", &jobj_tokens);
json_object_object_add(jobj_tokens, num, jobj_token);
if (LUKS2_check_json_size(hdr)) {
log_dbg("Not enough space in header json area for new %s token.", type);
json_object_object_del(jobj_tokens, num);
return -ENOSPC;
}
if (commit)
return LUKS2_hdr_write(cd, hdr) ?: token;
return token;
}
static int LUKS2_token_open(struct crypt_device *cd,
struct luks2_hdr *hdr,
int token,
char **buffer,
size_t *buffer_len,
void *usrptr)
{
const char *json;
const crypt_token_handler *h;
int r;
if (!(h = LUKS2_token_handler(cd, token)))
return -ENOENT;
if (h->validate) {
if (LUKS2_token_json_get(cd, hdr, token, &json))
return -EINVAL;
if (h->validate(cd, json)) {
log_dbg("Token %d (%s) validation failed.", token, h->name);
return -EINVAL;
}
}
r = h->open(cd, token, buffer, buffer_len, usrptr);
if (r < 0)
log_dbg("Token %d (%s) open failed with %d.", token, h->name, r);
return r;
}
static void LUKS2_token_buffer_free(struct crypt_device *cd,
int token,
void *buffer,
size_t buffer_len)
{
const crypt_token_handler *h = LUKS2_token_handler(cd, token);
if (h->buffer_free)
h->buffer_free(buffer, buffer_len);
else {
crypt_memzero(buffer, buffer_len);
free(buffer);
}
}
static int LUKS2_keyslot_open_by_token(struct crypt_device *cd,
struct luks2_hdr *hdr,
int token,
int segment,
const char *buffer,
size_t buffer_len,
struct volume_key **vk)
{
const crypt_token_handler *h;
json_object *jobj_token, *jobj_token_keyslots, *jobj;
const char *num;
int i, r;
if (!(h = LUKS2_token_handler(cd, token)))
return -ENOENT;
jobj_token = LUKS2_get_token_jobj(hdr, token);
if (!jobj_token)
return -EINVAL;
json_object_object_get_ex(jobj_token, "keyslots", &jobj_token_keyslots);
if (!jobj_token_keyslots)
return -EINVAL;
/* Try to open keyslot referenced in token */
r = -EINVAL;
for (i = 0; i < json_object_array_length(jobj_token_keyslots) && r < 0; i++) {
jobj = json_object_array_get_idx(jobj_token_keyslots, i);
num = json_object_get_string(jobj);
log_dbg("Trying to open keyslot %s with token %d (type %s).", num, token, h->name);
r = LUKS2_keyslot_open(cd, atoi(num), segment, buffer, buffer_len, vk);
}
return r < 0 ? r : atoi(num);
}
int LUKS2_token_open_and_activate(struct crypt_device *cd,
struct luks2_hdr *hdr,
int token,
const char *name,
uint32_t flags,
void *usrptr)
{
int keyslot, r;
char *buffer;
size_t buffer_len;
struct volume_key *vk = NULL;
r = LUKS2_token_open(cd, hdr, token, &buffer, &buffer_len, usrptr);
if (r < 0)
return r;
r = LUKS2_keyslot_open_by_token(cd, hdr, token,
name ? CRYPT_DEFAULT_SEGMENT : CRYPT_ANY_SEGMENT,
buffer, buffer_len, &vk);
LUKS2_token_buffer_free(cd, token, buffer, buffer_len);
if (r < 0)
return r;
keyslot = r;
if ((name || (flags & CRYPT_ACTIVATE_KEYRING_KEY)) && crypt_use_keyring_for_vk(cd))
r = crypt_volume_key_load_in_keyring(cd, vk);
if (r >= 0 && name)
r = LUKS2_activate(cd, name, vk, flags);
crypt_free_volume_key(vk);
return r < 0 ? r : keyslot;
}
int LUKS2_token_open_and_activate_any(struct crypt_device *cd,
struct luks2_hdr *hdr,
const char *name,
uint32_t flags)
{
char *buffer;
json_object *tokens_jobj;
size_t buffer_len;
int keyslot, token, r = -EINVAL;
struct volume_key *vk = NULL;
json_object_object_get_ex(hdr->jobj, "tokens", &tokens_jobj);
json_object_object_foreach(tokens_jobj, slot, val) {
UNUSED(val);
token = atoi(slot);
r = LUKS2_token_open(cd, hdr, token, &buffer, &buffer_len, NULL);
if (r < 0)
continue;
r = LUKS2_keyslot_open_by_token(cd, hdr, token,
name ? CRYPT_DEFAULT_SEGMENT : CRYPT_ANY_SEGMENT,
buffer, buffer_len, &vk);
LUKS2_token_buffer_free(cd, token, buffer, buffer_len);
if (r >= 0)
break;
}
keyslot = r;
if (r >= 0 && (name || (flags & CRYPT_ACTIVATE_KEYRING_KEY)) && crypt_use_keyring_for_vk(cd))
r = crypt_volume_key_load_in_keyring(cd, vk);
if (r >= 0 && name)
r = LUKS2_activate(cd, name, vk, flags);
crypt_free_volume_key(vk);
return r < 0 ? r : keyslot;
}
void LUKS2_token_dump(struct crypt_device *cd, int token)
{
const crypt_token_handler *h;
json_object *jobj_token;
h = LUKS2_token_handler(cd, token);
if (h && h->dump) {
jobj_token = LUKS2_get_token_jobj(crypt_get_hdr(cd, CRYPT_LUKS2), token);
if (jobj_token)
h->dump(cd, json_object_to_json_string_ext(jobj_token, JSON_C_TO_STRING_PLAIN));
}
}
int LUKS2_token_json_get(struct crypt_device *cd, struct luks2_hdr *hdr,
int token, const char **json)
{
json_object *jobj_token;
jobj_token = LUKS2_get_token_jobj(hdr, token);
if (!jobj_token)
return -EINVAL;
*json = json_object_to_json_string_ext(jobj_token, JSON_C_TO_STRING_PLAIN);
return 0;
}
static int assign_one_keyslot(struct crypt_device *cd, struct luks2_hdr *hdr,
int token, int keyslot, int assign)
{
json_object *jobj1, *jobj_token, *jobj_token_keyslots;
char num[16];
log_dbg("Keyslot %i %s token %i.", keyslot, assign ? "assigned to" : "unassigned from", token);
jobj_token = LUKS2_get_token_jobj(hdr, token);
if (!jobj_token)
return -EINVAL;
json_object_object_get_ex(jobj_token, "keyslots", &jobj_token_keyslots);
if (!jobj_token_keyslots)
return -EINVAL;
snprintf(num, sizeof(num), "%d", keyslot);
if (assign) {
jobj1 = LUKS2_array_jobj(jobj_token_keyslots, num);
if (!jobj1)
json_object_array_add(jobj_token_keyslots, json_object_new_string(num));
} else {
jobj1 = LUKS2_array_remove(jobj_token_keyslots, num);
if (jobj1)
json_object_object_add(jobj_token, "keyslots", jobj1);
}
return 0;
}
static int assign_one_token(struct crypt_device *cd, struct luks2_hdr *hdr,
int keyslot, int token, int assign)
{
json_object *jobj_keyslots;
int r = 0;
if (!LUKS2_get_token_jobj(hdr, token))
return -EINVAL;
if (keyslot == CRYPT_ANY_SLOT) {
json_object_object_get_ex(hdr->jobj, "keyslots", &jobj_keyslots);
json_object_object_foreach(jobj_keyslots, key, val) {
UNUSED(val);
r = assign_one_keyslot(cd, hdr, token, atoi(key), assign);
if (r < 0)
break;
}
} else
r = assign_one_keyslot(cd, hdr, token, keyslot, assign);
return r;
}
int LUKS2_token_assign(struct crypt_device *cd, struct luks2_hdr *hdr,
int keyslot, int token, int assign, int commit)
{
json_object *jobj_tokens;
int r = 0;
if (token == CRYPT_ANY_TOKEN) {
json_object_object_get_ex(hdr->jobj, "tokens", &jobj_tokens);
json_object_object_foreach(jobj_tokens, key, val) {
UNUSED(val);
r = assign_one_token(cd, hdr, keyslot, atoi(key), assign);
if (r < 0)
break;
}
} else
r = assign_one_token(cd, hdr, keyslot, token, assign);
if (r < 0)
return r;
// FIXME: do not write header in nothing changed
if (commit)
return LUKS2_hdr_write(cd, hdr) ?: token;
return token;
}

View File

@@ -0,0 +1,159 @@
/*
* LUKS - Linux Unified Key Setup v2, kernel keyring token
*
* Copyright (C) 2016-2017, Red Hat, Inc. All rights reserved.
* Copyright (C) 2016-2017, Ondrej Kozina. All rights reserved.
*
* 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.
*/
#include <assert.h>
#include "luks2_internal.h"
static int keyring_open(struct crypt_device *cd,
int token,
char **buffer,
size_t *buffer_len,
void *usrptr __attribute__((unused)))
{
json_object *jobj_token, *jobj_key;
struct luks2_hdr *hdr;
if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2)))
return -EINVAL;
jobj_token = LUKS2_get_token_jobj(hdr, token);
if (!jobj_token)
return -EINVAL;
json_object_object_get_ex(jobj_token, "key_description", &jobj_key);
/* TODO: if r == -ENOKEY then instantiate the key? */
if (keyring_get_passphrase(json_object_get_string(jobj_key), buffer, buffer_len))
return -EINVAL;
return 0;
}
static int keyring_validate(struct crypt_device *cd __attribute__((unused)),
const char *json)
{
enum json_tokener_error jerr;
json_object *jobj_token, *jobj_key;
int r = 1;
log_dbg("Validating keyring token json");
jobj_token = json_tokener_parse_verbose(json, &jerr);
if (!jobj_token) {
log_dbg("Keyring token JSON parse failed.");
return r;
}
if (!json_object_object_get_ex(jobj_token, "key_description", &jobj_key)) {
log_dbg("missing key_description field.");
goto out;
}
if (!json_object_is_type(jobj_key, json_type_string)) {
log_dbg("key_description is not a string.");
goto out;
}
/* TODO: perhaps check that key description is in '%s:%s'
* format where both strings are not empty */
r = !strlen(json_object_get_string(jobj_key));
out:
json_object_put(jobj_token);
return r;
}
static void keyring_dump(struct crypt_device *cd, const char *json)
{
enum json_tokener_error jerr;
json_object *jobj_token, *jobj_key;
jobj_token = json_tokener_parse_verbose(json, &jerr);
if (!jobj_token)
return;
if (!json_object_object_get_ex(jobj_token, "key_description", &jobj_key)) {
json_object_put(jobj_token);
return;
}
log_std(cd, "\tKey description: %s\n", json_object_get_string(jobj_key));
json_object_put(jobj_token);
}
int token_keyring_set(json_object **jobj_builtin_token,
const void *params)
{
json_object *jobj_token, *jobj;
const struct crypt_token_params_luks2_keyring *keyring_params = (const struct crypt_token_params_luks2_keyring *) params;
jobj_token = json_object_new_object();
if (!jobj_token)
return -ENOMEM;
jobj = json_object_new_string(LUKS2_TOKEN_KEYRING);
if (!jobj) {
json_object_put(jobj_token);
return -ENOMEM;
}
json_object_object_add(jobj_token, "type", jobj);
jobj = json_object_new_array();
if (!jobj) {
json_object_put(jobj_token);
return -ENOMEM;
}
json_object_object_add(jobj_token, "keyslots", jobj);
jobj = json_object_new_string(keyring_params->key_description);
if (!jobj) {
json_object_put(jobj_token);
return -ENOMEM;
}
json_object_object_add(jobj_token, "key_description", jobj);
*jobj_builtin_token = jobj_token;
return 0;
}
int token_keyring_get(json_object *jobj_token,
void *params)
{
json_object *jobj;
struct crypt_token_params_luks2_keyring *keyring_params = (struct crypt_token_params_luks2_keyring *) params;
json_object_object_get_ex(jobj_token, "type", &jobj);
assert(!strcmp(json_object_get_string(jobj), LUKS2_TOKEN_KEYRING));
json_object_object_get_ex(jobj_token, "key_description", &jobj);
keyring_params->key_description = json_object_get_string(jobj);
return 0;
}
const crypt_token_handler keyring_handler = {
.name = LUKS2_TOKEN_KEYRING,
.open = keyring_open,
.validate = keyring_validate,
.dump = keyring_dump
};

File diff suppressed because it is too large Load Diff

View File

@@ -403,6 +403,9 @@ int crypt_keyfile_read(struct crypt_device *cd, const char *keyfile,
size_t buflen, i, file_read_size;
struct stat st;
if (!key || !key_size_read)
return -EINVAL;
*key = NULL;
*key_size_read = 0;

View File

@@ -197,7 +197,7 @@ int crypt_benchmark(struct crypt_device *cd,
char *c;
int r;
if (!cipher || !cipher_mode || !volume_key_size)
if (!cipher || !cipher_mode || !volume_key_size || !encryption_mbs || !decryption_mbs)
return -EINVAL;
r = init_crypto(cd);
@@ -244,6 +244,9 @@ int crypt_benchmark_pbkdf(struct crypt_device *cd,
int r;
const char *kdf_opt;
if (!pbkdf || (!password && password_size))
return -EINVAL;
r = init_crypto(cd);
if (r < 0)
return r;

View File

@@ -24,6 +24,14 @@
#include "internal.h"
const struct crypt_pbkdf_type default_luks2 = {
.type = DEFAULT_LUKS2_PBKDF,
.hash = DEFAULT_LUKS1_HASH,
.time_ms = DEFAULT_LUKS2_ITER_TIME,
.max_memory_kb = DEFAULT_LUKS2_MEMORY_KB,
.parallel_threads = DEFAULT_LUKS2_PARALLEL_THREADS
};
const struct crypt_pbkdf_type default_luks1 = {
.type = CRYPT_KDF_PBKDF2,
.hash = DEFAULT_LUKS1_HASH,
@@ -95,7 +103,9 @@ int init_pbkdf_type(struct crypt_device *cd,
uint32_t old_flags;
int r;
if (!pbkdf)
if (!pbkdf && dev_type && !strcmp(dev_type, CRYPT_LUKS2))
pbkdf = &default_luks2;
else if (!pbkdf)
pbkdf = &default_luks1;
r = verify_pbkdf_params(cd, pbkdf);

View File

@@ -1,4 +1,4 @@
.TH CRYPTSETUP-REENCRYPT "8" "March 2017" "cryptsetup-reencrypt" "Maintenance Commands"
.TH CRYPTSETUP-REENCRYPT "8" "September 2017" "cryptsetup-reencrypt" "Maintenance Commands"
.SH NAME
cryptsetup-reencrypt - tool for offline LUKS device re-encryption
.SH SYNOPSIS
@@ -14,6 +14,8 @@ unclocked by passphrase), \fBcipher\fR, \fBcipher mode\fR.
Cryptsetup-reencrypt reencrypts data on LUKS device in-place. During
reencryption process the LUKS device is marked unavailable.
\fBNOTE:\fR LUKS2 format is not yet supported.
\fIWARNING\fR: The cryptsetup-reencrypt program is not resistant to hardware
or kernel failures during reencryption (you can lose you data in this case).

View File

@@ -1,4 +1,4 @@
.TH CRYPTSETUP "8" "March 2017" "cryptsetup" "Maintenance Commands"
.TH CRYPTSETUP "8" "September 2017" "cryptsetup" "Maintenance Commands"
.SH NAME
cryptsetup - manage plain dm-crypt and LUKS encrypted volumes
.SH SYNOPSIS
@@ -12,7 +12,7 @@ and can hence offer more features than plain dm-crypt. On the other
hand, the header is visible and vulnerable to damage.
In addition, cryptsetup provides limited support for the use of
historic loopaes volumes and for TrueCrypt compatible volumes.
loop-AES volumes and for TrueCrypt compatible volumes.
.SH PLAIN DM-CRYPT OR LUKS?
.PP
@@ -29,12 +29,12 @@ to be mentioned here.
\fBBackup:\fR Storage media die. Encryption has no influence on that.
Backup is mandatory for encrypted data as well, if the data has any
worth. See the Cryptsetup FAQ for advice on how to do backup of an
worth. See the Cryptsetup FAQ for advice on how to do a backup of an
encrypted volume.
\fBCharacter encoding:\fR If you enter a
passphrase with special symbols, the passphrase can change
depending character encoding. Keyboard settings can also change,
depending on character encoding. Keyboard settings can also change,
which can make blind input hard or impossible. For
example, switching from some ASCII 8-bit variant to UTF-8
can lead to a different binary encoding and hence different
@@ -57,10 +57,10 @@ secure wiping by just overwriting header and key-slot area.
it is a very good idea to wipe filesystem signatures, data, etc. before
creating a LUKS or plain dm-crypt container on it.
For a quick removal of filesystem signatures, use "wipefs". Take care
though that this may not remove everything. In particular md (RAID)
though that this may not remove everything. In particular, MD RAID
signatures at the end of a device may survive. It also does not
remove data. For a full wipe, overwrite the whole partition before
container creation. If you do not know how to to that, the
container creation. If you do not know how to do that, the
cryptsetup FAQ describes several options.
.SH BASIC COMMANDS
@@ -98,6 +98,9 @@ For backward compatibility there are \fBclose\fR command aliases:
\fBremove\fR, \fBplainClose\fR, \fBluksClose\fR, \fBloopaesClose\fR,
\fBtcryptClose\fR (all behaves exactly the same, device type is
determined automatically from active device).
\fB<options>\fR can be [\-\-deferred]
.PP
\fIstatus\fR <name>
.IP
@@ -111,11 +114,22 @@ If \-\-size (in sectors) is not specified, the size is computed
from the underlying device. For LUKS it is the size of the
underlying device without the area reserved for LUKS header
(see data payload offset in \fBluksDump\fR command).
For plain crypt device the whole device size is used.
For plain crypt device, the whole device size is used.
Note that this does not change the raw device geometry, it just
changes how many sectors of the raw device are represented
in the mapped device.
If cryptsetup detected volume key for active device loaded in kernel keyring
service, resize action would first try to retrieve
the key using a token and only if it failed it'd ask for a passphrase
to unlock a keyslot (LUKS) or to derive a volume key again (plain mode).
The kernel keyring is used by default for LUKS2 devices.
With LUKS2 device additional \fB<options>\fR can be [\-\-token\-id, \-\-token\-only,
\-\-key\-slot, \-\-key\-file, \-\-keyfile\-size, \-\-keyfile\-offset, \-\-timeout,
\-\-disable\-locks, \-\-disable\-keyring].
.SH PLAIN MODE
Plain dm-crypt encrypts the device sector-by-sector with a
single, non-salted hash of the passphrase. No checks
@@ -134,7 +148,7 @@ Opens (creates a mapping with) <name> backed by device <device>.
\fB<options>\fR can be [\-\-hash, \-\-cipher, \-\-verify-passphrase,
\-\-key-file, \-\-keyfile-offset, \-\-key-size, \-\-offset, \-\-skip, \-\-size,
\-\-readonly, \-\-shared, \-\-allow-discards]
\-\-readonly, \-\-shared, \-\-allow\-discards]
Example: 'cryptsetup open \-\-type plain /dev/sda10 e1' maps the raw
encrypted device /dev/sda10 to the mapped (decrypted) device
@@ -146,7 +160,7 @@ It adds a standardized header at the start of the device,
a key-slot area directly behind the header and the bulk
data area behind that. The whole set is called a 'LUKS container'.
The device that a LUKS container resides on is called a 'LUKS device'.
For most purposes both terms can be used interchangeably. But
For most purposes, both terms can be used interchangeably. But
note that when the LUKS header is at a nonzero offset
in a device, then the device is not a LUKS device anymore, but
has a LUKS container stored in it at an offset.
@@ -158,6 +172,12 @@ are protected against brute-force and dictionary
attacks by PBKDF2, which implements hash iteration and salting
in one function.
LUKS2 is a new version of header format that allows additional
extensions like different PBKDF algorithm or authenticated encryption.
You can format device with LUKS2 header if you specify
\fI\-\-type luks2\fR in \fIluksFormat\fR command.
For activation, the format is already recognized automatically.
Each passphrase, also called a
.B key
in this document, is associated with one of 8 key-slots.
@@ -165,13 +185,13 @@ Key operations that do not specify a slot affect the first slot
that matches the supplied passphrase or the first empty slot if
a new passphrase is added.
The \fB<device>\fR parameter can be also specified by a LUKS UUID in the
The \fB<device>\fR parameter can also be specified by a LUKS UUID in the
format UUID=<uuid>. Translation to real device name uses symlinks
in /dev/disk/by-uuid directory.
To specify a detached header, the \fB\-\-header\fR parameter can be used
in all LUKS commands and always takes precedence over positional \fB<device>\fR
parameter.
in all LUKS commands and always takes precedence over the positional
\fB<device>\fR parameter.
The following are valid LUKS actions:
@@ -183,21 +203,30 @@ either via prompting or via <key file>. Note that
if the second argument is present, then the passphrase
is taken from the file given there, without the need
to use the \-\-key-file option. Also note that for both forms
of reading the passphrase from file you can
of reading the passphrase from a file you can
give '-' as file name, which results in the passphrase being read
from stdin and the safety-question being skipped.
You can only call luksFormat on a LUKS device that is not mapped.
To use LUKS2, specify \fI\-\-type luks2\fR.
\fB<options>\fR can be [\-\-hash, \-\-cipher, \-\-verify\-passphrase,
\-\-key\-size, \-\-key\-slot,
\-\-key\-file (takes precedence over optional second argument),
\-\-keyfile\-offset, \-\-keyfile\-size, \-\-use\-random | \-\-use\-urandom,
\-\-uuid, \-\-master\-key\-file, \-\-iter\-time, \-\-header,
\-\-force\-password].
\-\-pbkdf\-force\-iterations,
\-\-force\-password, \-\-disable-locks].
For LUKS2, additional \fB<options>\fR can be
[\-\-integrity, \-\-integrity\-no\-wipe, \-\-sector\-size,
\-\-label, \-\-subsystem,
\-\-pbkdf, \-\-pbkdf\-memory, \-\-pbkdf\-parallel,
\-\-disable\-locks, \-\-disable\-keyring].
\fBWARNING:\fR Doing a luksFormat on an existing LUKS container will
make all data the old container permanently irretrievable, unless
make all data the old container permanently irretrievable unless
you have a header backup.
.PP
\fIopen\fR \-\-type luks <device> <name>
@@ -206,16 +235,19 @@ you have a header backup.
.IP
Opens the LUKS device <device> and sets up a mapping <name> after
successful verification of the supplied passphrase.
If the passphrase is not supplied via \-\-key-file, the command
prompts for it interactively.
First, the passphrase is searched in LUKS tokens. If it's not
found in any token and also the passphrase is not supplied via \-\-key-file,
the command prompts for it interactively.
\fB<options>\fR can be [\-\-key\-file, \-\-keyfile\-offset,
\-\-keyfile\-size, \-\-readonly, \-\-test\-passphrase,
\-\-allow\-discards, \-\-header, \-\-key-slot, \-\-master\-key\-file].
\-\-allow\-discards, \-\-header, \-\-key-slot, \-\-master\-key\-file, \-\-token\-id,
\-\-token\-only, \-\-disable\-keyring, \-\-disable\-locks].
.PP
\fIluksSuspend\fR <name>
.IP
Suspends an active device (all IO operations will blocked
Suspends an active device (all IO operations will block
and accesses to the device will wait indefinitely)
and wipes the encryption
key from kernel memory. Needs kernel 2.6.19 or later.
@@ -226,14 +258,15 @@ the mapped device.
\fBWARNING:\fR never suspend the device on which the cryptsetup binary resides.
\fB<options>\fR can be [\-\-header].
\fB<options>\fR can be [\-\-header, \-\-disable\-locks].
.PP
\fIluksResume\fR <name>
.IP
Resumes a suspended device and reinstates the encryption key.
Prompts interactively for a passphrase if \-\-key-file is not given.
\fB<options>\fR can be [\-\-key\-file, \-\-keyfile\-size, \-\-header]
\fB<options>\fR can be [\-\-key\-file, \-\-keyfile\-size, \-\-header,
\-\-disable\-keyring,\-\-disable\-locks]
.PP
\fIluksAddKey\fR <device> [<key file with new key>]
.IP
@@ -245,19 +278,19 @@ or read from the file given as positional argument.
\fB<options>\fR can be [\-\-key\-file, \-\-keyfile\-offset,
\-\-keyfile\-size, \-\-new\-keyfile\-offset,
\-\-new\-keyfile\-size, \-\-key\-slot, \-\-master\-key\-file,
\-\-iter\-time, \-\-force\-password, \-\-header].
\-\-iter\-time, \-\-force\-password, \-\-header, \-\-disable\-locks].
.PP
\fIluksRemoveKey\fR <device> [<key file with passphrase to be removed>]
.IP
Removes the supplied passphrase from the LUKS device. The
passphrase to be removed can be specified interactively,
as positional argument or via \-\-key-file.
as the positional argument or via \-\-key-file.
\fB<options>\fR can be [\-\-key\-file, \-\-keyfile\-offset,
\-\-keyfile\-size, \-\-header]
\-\-keyfile\-size, \-\-header, \-\-disable\-locks]
\fBWARNING:\fR If you read the passphrase from stdin
(without further argument or with '-' as argument
(without further argument or with '-' as an argument
to \-\-key\-file), batch-mode (\-q) will be implicitly
switched on and no warning will be given when you remove the
last remaining passphrase from a LUKS container. Removing
@@ -287,7 +320,8 @@ inaccessible.
\fB<options>\fR can be [\-\-key\-file, \-\-keyfile\-offset,
\-\-keyfile\-size, \-\-new\-keyfile\-offset,
\-\-new\-keyfile\-size, \-\-key\-slot, \-\-force\-password, \-\-header].
\-\-new\-keyfile\-size, \-\-key\-slot, \-\-force\-password, \-\-header,
\-\-disable\-locks].
.PP
\fIluksKillSlot\fR <device> <key slot number>
.IP
@@ -299,10 +333,10 @@ an interactive confirmation when doing so. Removing the last
passphrase makes a LUKS container permanently inaccessible.
\fB<options>\fR can be [\-\-key\-file, \-\-keyfile\-offset,
\-\-keyfile\-size, \-\-header].
\-\-keyfile\-size, \-\-header, \-\-disable\-locks].
\fBWARNING:\fR If you read the passphrase from stdin
(without further argument or with '-' as argument
(without further argument or with '-' as an argument
to \-\-key-file), batch-mode (\-q) will be implicitly
switched on and no warning will be given when you remove the
last remaining passphrase from a LUKS container. Removing
@@ -346,11 +380,11 @@ without a passphrase and even without the LUKS header. This means
that if the master key is compromised, the whole device has to be
erased to prevent further access. Use this option carefully.
In order to dump the master key, a passphrase has to be supplied,
To dump the master key, a passphrase has to be supplied,
either interactively or via \-\-key\-file.
\fB<options>\fR can be [\-\-dump\-master\-key, \-\-key\-file,
\-\-keyfile\-offset, \-\-keyfile\-size, \-\-header].
\-\-keyfile\-offset, \-\-keyfile\-size, \-\-header, \-\-disable\-locks].
\fBWARNING:\fR If \-\-dump\-master\-key is used with \-\-key\-file
and the argument to \-\-key\-file is '-', no validation question
@@ -381,12 +415,56 @@ from the specified file.
Note: Using '-' as filename reads the header backup from a file named '-'.
\fBWARNING:\fR Header and keyslots will be replaced, only
the passphrases from the backup will work afterwards.
the passphrases from the backup will work afterward.
This command requires that the master key size and data offset
of the LUKS header already on the device and of the header backup
match. Alternatively, if there is no LUKS header on the device,
the backup will also be written to it.
.PP
\fItoken\fR <add|remove> <device>
.IP
Adds a new keyring token to enable auto-activation of the device.
For the auto-activation, the passphrase must be stored in keyring with the specified
description. Usually, the passphrase should be stored in \fIuser\fR or
\fIuser-session\fR keyring.
The \fItoken\fR command is supported only for LUKS2.
For adding new keyring token, option \-\-key\-description is mandatory.
Also, new token is assigned to key slot specified with \-\-key\-slot option or to all
active key slots in the case \-\-key\-slot option is ommited.
To remove existing token, specify the token ID which should be removed with
\-\-token\-id option.
\fBWARNING:\fR The action \fItoken remove\fR removes any token type, not just \fIkeyring\fR
type from token slot specified by \-\-token\-id option.
\fB<options>\fR can be [\-\-header, \-\-token\-id, \-\-key-slot, \-\-key\-description,
\-\-disable\-locks, \-\-disable\-keyring].
.PP
\fIconvert\fR <device> \-\-type <format>
.IP
Converts the device between LUKS and LUKS2 format (if possible).
The conversion will not be performed if there is an additional LUKS2 feature or LUKS has
unsupported header size.
\fBWARNING:\fR The \fIconvert\fR action can destroy the LUKS header in the case of a crash
during conversion or if a media error occurs.
Always create a header backup before performing this operation!
\fB<options>\fR can be [\-\-header, \-\-type].
.PP
\fIconfig\fR <device>
.IP
Set permanent configuration options (store to LUKS header).
The \fIconfig\fR command is supported only for LUKS2.
The permanent options can be \fI\-\-priority\fR to set priority (normal, prefer, ignore)
for keyslot (specified by \fI\-\-key\-slot\fR) or \fI\-\-label\fR and \fI\-\-subsystem\fR.
\fB<options>\fR can be [\-\-priority, \-\-label, \-\-subsystem, \-\-key\-slot, \-\-header].
.SH loop-AES EXTENSION
cryptsetup supports mapping loop-AES encrypted partition using
a compatibility mode.
@@ -403,7 +481,7 @@ If the key file is encrypted with GnuPG, then you have to use
gpg \-\-decrypt <keyfile> | cryptsetup loopaesOpen \-\-key\-file=\-
<device> <name>
\fBWARNING:\fR The loop-AES extension cannot use direct input of key file
\fBWARNING:\fR The loop-AES extension cannot use the direct input of key file
on real terminal because the keys are separated by end-of-line and only part
of the multi-key file would be read.
.br
@@ -456,16 +534,14 @@ iteration count so unlocking can take quite a lot of time (in comparison
with TCRYPT device).
To open a VeraCrypt device with a custom Personal Iteration Multiplier (PIM)
value, \fBadditionally to --veracrypt \fR use either the
\fB--veracrypt-pim=<PIM>\fR option to directly specify the PIM on the command-
line or use \fB--veracrypt-query-pim\fR to be prompted for the PIM.
value, \fBadditionally to \-\-veracrypt \fR use either the
\fB\-\-veracrypt\-pim=<PIM>\fR option to directly specify the PIM on the command-
line or use \fB\-\-veracrypt\-query\-pim\fR to be prompted for the PIM.
The PIM value affects the number of iterations applied during key derivation. Please refer to
\fBhttps://veracrypt.codeplex.com/wikipage?title=Personal%20Iterations%20Multiplier%20(PIM)\fR
\fBhttps://www.veracrypt.fr/en/Personal%20Iterations%20Multiplier%20(PIM).html\fR
for more detailed information.
\fBNOTE:\fR Activation with \fBtcryptOpen\fR is supported only for cipher chains
using LRW or XTS encryption modes.
@@ -475,17 +551,17 @@ and doesn't require superuser privilege.
To map system device (device with boot loader where the whole encrypted
system resides) use \fB\-\-tcrypt\-system\fR option.
You can use partition device as the parameter (parameter must be real partition
device, not image in file), then only this partition is mapped.
device, not an image in a file), then only this partition is mapped.
If you have whole TCRYPT device as a file image and you want to map multiple
If you have the whole TCRYPT device as a file image and you want to map multiple
partition encrypted with system encryption, please create loopback mapping
with partitions first (\fBlosetup \-P\fR, see \fPlosetup(8)\fR man page for more info),
and use loop partition as the device parameter.
If you use whole base device as parameter, one device for the whole system
If you use the whole base device as a parameter, one device for the whole system
encryption is mapped. This mode is available only for backward compatibility
with older cryptsetup versions which mapped TCRYPT system encryption
using whole device.
using the whole device.
To use hidden header (and map hidden device, if available),
use \fB\-\-tcrypt\-hidden\fR option.
@@ -509,15 +585,15 @@ a mapping <name>.
\fB<options>\fR can be [\-\-key\-file, \-\-tcrypt\-hidden,
\-\-tcrypt\-system, \-\-tcrypt\-backup, \-\-readonly, \-\-test\-passphrase,
\-\-allow-discards].
\-\-allow-discards, \-\-vracrypt, \-\-veracrypt\-pim, \-\-veracrypt\-querry\-pim].
The keyfile parameter allows combination of file content with the
The keyfile parameter allows a combination of file content with the
passphrase and can be repeated. Note that using keyfiles is compatible
with TCRYPT and is different from LUKS keyfile logic.
\fBWARNING:\fR Option \fB\-\-allow\-discards\fR cannot be combined with
option \fB\-\-tcrypt\-hidden\fR. For normal mapping it can cause
\fBdestruction of hidden volume\fR (hidden volume appears as unused space
option \fB\-\-tcrypt\-hidden\fR. For normal mapping, it can cause
the \fBdestruction of hidden volume\fR (hidden volume appears as unused space
for outer volume so this space can be discarded).
.PP
@@ -536,14 +612,14 @@ to be erased to prevent further access. Use this option carefully.
\fB<options>\fR can be [\-\-dump\-master\-key, \-\-key\-file,
\-\-tcrypt\-hidden, \-\-tcrypt\-system, \-\-tcrypt\-backup].
The keyfile parameter allows combination of file content with the
The keyfile parameter allows a combination of file content with the
passphrase and can be repeated.
.PP
See also \fBhttps://en.wikipedia.org/wiki/TrueCrypt\fR for more information regarding
TrueCrypt.
Please note that cryptsetup does not use TrueCrypt code, please report
all problems related to this compatibility extension to cryptsetup project.
all problems related to this compatibility extension to the cryptsetup project.
.SH MISCELLANEOUS
.PP
\fIrepair\fR <device>
@@ -562,7 +638,7 @@ header before calling this command.
\fIbenchmark\fR <options>
.IP
Benchmarks ciphers and KDF (key derivation function).
Without parameters it tries to measure few common configurations.
Without parameters, it tries to measure few common configurations.
To benchmark other ciphers or modes, you need to specify \fB\-\-cipher\fR
and \fB\-\-key\-size\fR options or \fB\-\-hash\fR for KDF test.
@@ -586,6 +662,10 @@ Print more information on command execution.
Run in debug mode with full diagnostic logs. Debug output
lines are always prefixed by '#'.
.TP
.B "\-\-type <device-type>
Specifies required device type, for more info
read \fIBASIC COMMANDS\fR section.
.TP
.B "\-\-hash, \-h \fI<hash\-spec>\fR"
Specifies the passphrase hash for \fIopen\fR (for plain and
loopaes device types).
@@ -652,18 +732,18 @@ See section \fBNOTES ON PASSPHRASE PROCESSING\fR for more information.
.TP
.B "\-\-keyfile\-offset \fIvalue\fR"
Skip \fIvalue\fR bytes at the beginning of the key file.
Works with all commands that accepts key files.
Works with all commands that accept key files.
.TP
.B "\-\-keyfile\-size, \-l \fIvalue\fR"
Read a maximum of \fIvalue\fR bytes from the key file.
Default is to read the whole file up to the compiled-in
The default is to read the whole file up to the compiled-in
maximum that can be queried with \-\-help. Supplying more
data than the compiled-in maximum aborts the operation.
This option is useful
to cut trailing newlines, for example. If \-\-keyfile\-offset
is also given, the size count starts after the offset.
Works with all commands that accepts key files.
Works with all commands that accept key files.
.TP
.B "\-\-new\-keyfile\-offset \fIvalue\fR"
Skip \fIvalue\fR bytes at the start when
@@ -673,7 +753,7 @@ adding a new passphrase from key file with
.B "\-\-new\-keyfile\-size \fIvalue\fR"
Read a maximum of \fIvalue\fR bytes when adding
a new passphrase from key file with \fIluksAddKey\fR.
Default is to read the whole file up to the compiled-in
The default is to read the whole file up to the compiled-in
maximum length that can be queried with \-\-help.
Supplying more than the compiled in maximum aborts the
operation.
@@ -691,7 +771,7 @@ then the new header decrypts the data encrypted with the
header the master key was taken from.
\fBWARNING:\fR If you create your own master key, you
need to make sure to do it right. Otherwise you can end
need to make sure to do it right. Otherwise, you can end
up with a low-entropy or otherwise partially predictable
master key which will compromise security.
@@ -764,7 +844,7 @@ This option is only relevant for the \fIopen\fR action with plain
or loopaes device types.
Hence, if \-\-offset \fIn\fR, and \-\-skip \fIs\fR, sector \fIn\fR
(the first sector of encrypted device) will get a sector number
(the first sector of the encrypted device) will get a sector number
of \fIs\fR for the IV calculation.
.TP
.B "\-\-readonly, \-r"
@@ -777,12 +857,67 @@ This option is only relevant for the
\fIopen \-\-type plain\fR action. Use \-\-offset, \-\-size and \-\-skip to
specify the mapped area.
.TP
.B "\-\-pbkdf <PBKDF spec>"
Set Password-Based Key Derivation Function (PBKDF) algorithm for LUKS keyslot.
The PBKDF can be: \fIpbkdf2\fR (for PBKDF2 according to RFC2898),
\fIargon2i\fR for Argon2i or \fIargon2id\fR for Argon2id
(see https://www.cryptolux.org/index.php/Argon2 for more info).
For LUKS1, only PBKDF2 is accepted (no need to use this option).
The default PBKDF2 for LUKS2 is set during compilation time
and is available in \fIcryptsetup \-\-help\fR output.
A PBKDF is used for increasing dictionary and brute-force attack cost
for keyslot passwords. The parameters can be time, memory and parallel cost.
For PBKDF2, only time cost (number of iterations) applies.
For Argon2i/id, there is also memory cost (memory required during
the process of key derivation) and parallel cost (number of threads
that run in parallel during the key derivation.
Note that increasing memory cost also increases time, so the final
parameter values are measured by a benchmark. The benchmark
tries to find iteration time (\fI\-\-iter\-time\fR) with required
memory cost \fI\-\-pbkdf\-memory\fR. If it is not possible,
the memory cost is decreased as well.
The parallel cost \fI\-\-pbkdf\-parallel\fR is constant, is is checked
against available CPU cores (if not available, it is decreased) and the maximum
parallel cost is 4.
You can see all PBKDF parameters for particular LUKS2 keyslot with
\fIluksDump\fR command.
\fBNOTE:\fR If you do not want to use benchmark and want to specify
all parameters directly, use \fI\-\-pbkdf\-force\-iterations\fR with
\fI\-\-pbkdf\-memory\fR and \fI\-\-pbkdf\-parallel\fR.
This will override the values without benchmarking.
Note it can cause extremely long unlocking time. Use only is specified
cases, for example, if you know that the formatted device will
be used on some small embedded system.
In this case, the LUKS PBKDF2 digest will be set to the minimum iteration count.
.TP
.B "\-\-iter\-time, \-i <number of milliseconds>"
The number of milliseconds to spend with PBKDF2 passphrase processing.
The number of milliseconds to spend with PBKDF passphrase processing.
This option is only relevant for LUKS operations that set or change
passphrases, such as \fIluksFormat\fR or \fIluksAddKey\fR.
Specifying 0 as parameter selects the compiled-in default.
.TP
.B "\-\-pbkdf\-memory <number>"
Set the memory cost for PBKDF (for Argon2i/id the number represents kilobytes).
Note that it is maximal value, PBKDF benchmark can decrease it.
This option is not available for PBKDF2.
.TP
.B "\-\-pbkdf\-paralell <number>"
Set the parallel cost for PBKDF (number of threads, up to 4).
Note that it is maximal value, it is decreased automatically if
CPU online count is lower.
This option is not available for PBKDF2.
.TP
.B "\-\-pbkdf\-force\-iterations <num>"
Avoid PBKDF benchmark and set time cost (iterations) directly.
It can be used for LUKS/LUKS2 device only.
See \fI\-\-pbkdf\fR option for more info.
.TP
.B "\-\-batch\-mode, \-q"
Suppresses all confirmation questions. Use with care!
@@ -812,23 +947,23 @@ Align payload at a boundary of \fIvalue\fR 512-byte sectors.
This option is relevant for \fIluksFormat\fR.
If not specified, cryptsetup tries to use the topology info
provided by kernel for the underlying device to get optimal alignment.
provided by the kernel for the underlying device to get the optimal alignment.
If not available (or the calculated value is a multiple of the default)
data is by default aligned to a 1MiB boundary (i.e. 2048 512-byte sectors).
For a detached LUKS header this option specifies the offset on the
For a detached LUKS header, this option specifies the offset on the
data device. See also the \-\-header option.
.TP
.B "\-\-uuid=\fIUUID\fR"
Use the provided \fIUUID\fR for the \fIluksFormat\fR command
instead of generating new one. Changes the existing UUID when
instead of generating a new one. Changes the existing UUID when
used with the \fIluksUUID\fR command.
The UUID must be provided in the standard UUID format,
e.g. 12345678-1234-1234-1234-123456789abc.
.TP
.B "\-\-allow\-discards\fR"
Allow the use of discard (TRIM) requests for device.
Allow the use of discard (TRIM) requests for the device.
This option is only relevant for \fIopen\fR action.
\fBWARNING:\fR This command can have a negative security impact
@@ -838,7 +973,7 @@ filesystem type, used space, etc. may be extractable from
the physical device if the discarded blocks can be located
later. If in doubt, do not use it.
A kernel version of 3.1 or later is needed. For earlier kernels
A kernel version of 3.1 or later is needed. For earlier kernels,
this option is ignored.
.TP
.B "\-\-perf\-same_cpu_crypt\fR"
@@ -864,20 +999,20 @@ performance tuning, use only if you need a change to default dm-crypt
behaviour. Needs kernel 4.0 or later.
.TP
.B "\-\-test\-passphrase\fR"
Do not activate device, just verify passphrase.
Do not activate the device, just verify passphrase.
This option is only relevant for \fIopen\fR action (the device
mapping name is not mandatory if this option is used).
.TP
.B "\-\-header\fR <device or file storing the LUKS header>"
Use a detached (separated) metadata device or file where the
LUKS header is stored. This options allows one to store ciphertext
LUKS header is stored. This option allows one to store ciphertext
and LUKS header on different devices.
This option is only relevant for LUKS devices and can be
used with the \fIluksFormat\fR, \fIopen\fR, \fIluksSuspend\fR,
\fIluksResume\fR, \fIstatus\fR and \fIresize\fR commands.
For \fIluksFormat\fR with a file name as argument to \-\-header,
For \fIluksFormat\fR with a file name as the argument to \-\-header,
it has to exist and be large enough to contain the LUKS header.
See the cryptsetup FAQ for header size calculation.
@@ -889,23 +1024,129 @@ If used with \fIluksFormat\fR, the \-\-align\-payload option is taken
as absolute sector alignment on ciphertext device and can be zero.
\fBWARNING:\fR There is no check whether the ciphertext device specified
actually belongs to the header given. In fact you can specify an
actually belongs to the header given. In fact, you can specify an
arbitrary device as the ciphertext device for \fIopen\fR
with the \-\-header option. Use with care.
.TP
.B "\-\-force\-password\fR"
.B "\-\-header\-backup\-file <file>"
Specify file with header backup for \fIluksHeaderBackup\fR or
\fIluksHeaderBackup\fR actions.
.TP
.B "\-\-force\-password"
Do not use password quality checking for new LUKS passwords.
This option applies only to \fIluksFormat\fR, \fIluksAddKey\fR and
\fIluksChangeKey\fR and is ignored if cryptsetup is built without
password quality checking support.
For more info about password quality check, see manual page
For more info about password quality check, see the manual page
for \fBpwquality.conf(5)\fR and \fBpasswdqc.conf(5)\fR.
.TP
.B "\-\-deferred"
Defers device removal in \fIclose\fR command until the last user closes it.
.TP
.B "\-\-disable\-locks"
Disable lock protection for metadata on disk.
This option is valid only for LUKS2 and ignored for other formats.
\fBWARNING:\fR Do not use this option unless you run cryptsetup in
a restricted environment where locking is impossible to perform
(where /run directory cannot be used).
.TP
.B "\-\-disable\-keyring"
Do not load volume key in kernel keyring but use store key directly
in the dm-crypt target.
This option is supported only for the LUKS2 format.
.TP
.B "\-\-key\-description <text>"
Set key description in keyring for use with \fItoken\fR command.
.TP
.B "\-\-priority <normal|prefer|ignore>"
Set a priority for LUKS2 keyslot.
The \fIprefer\fR priority marked slots are tried before \fInormal\fR priority.
The \fIignored\fR priority means, that slot is never used, if not explicitly
requested by \fI\-\-key\-slot\fR option.
.TP
.B "\-\-token\-id"
Specify what token to use in actions \fItoken\fR, \fIopen\fR or \fIresize\fR.
If omitted, all available tokens will be checked before proceeding further with
passphrase prompt.
.TP
.B "\-\-token\-only"
Do not proceed further with action (any of \fItoken\fR, \fIopen\fR or
\fIresize\fR) if token activation failed. Without the option,
action asks for passphrase to proceed further.
.TP
.B "\-\-sector\-size <bytes>"
Set sector size for use with disk encryption. It must be power of two
and in range 512 - 4096 bytes. The default is 512 bytes sectors.
This option is available only in the LUKS2 mode.
Note that if sector size is higher than underlying device hardware sector
and there is not integrity protection that uses data journal, using
this option can increase risk on incomplete sector writes during a power fail.
If used together with \fI\-\-integrity\fR option and dm-integrity journal,
the atomicity of writes is guaranteed in all cases (but it cost write
performance - data has to be written twice).
Increasing sector size from 512 bytes to 4096 bytes can provide better
performance on most of the modern storage devices and also with some
hw encryption accelerators.
.TP
.B "\-\-persistent"
If used with LUKS2 devices and activation commands like \fIopen\fR,
the specified activation flags are persistently written into metadata
and used next time automatically even for normal activation.
(No need to use cryptab or other system configuration files.)
Only \fI\-\-allow-discards\fR, \fI\-\-perf\-same_cpu_crypt\fR,
\fI\-\-perf\-submit_from_crypt_cpus\fR and \fI\-\-integrity\-no\-journal\fR
can be stored persistently.
.TP
.B "\-\-label <LABEL>"
.B "\-\-subsystem <SUBSYSTEM>"
Set label and subsystem description for LUKS2 device, can be used
in \fIconfig\fR and \fIformat\fR actions.
The label and subsystem are optional fields and can be later used in udev scripts
for triggering user actions once device marked by these labels is detected.
.TP
.B "\-\-integrity <integrity algorithm>"
Specify integrity algorithm to be used for authenticated disk encryption in LUKS2.
\fBWARNING: This extension is EXPERIMENTAL\fR and requires dm-integrity
kernel target (available since kernel version 4.12).
For more info, see \fIAUTHENTICATED DISK ENCRYPTION\fR section.
.TP
.B "\-\-integrity\-\no\-journal"
Activate device with integrity protection without using data journal (direct
write of data and integrity tags).
Note that without journal power fail can cause non-atomic write and data corruption.
Use only if journalling is performed on a different storage layer.
.TP
.B "\-\-integrity\-\no\-wipe"
Skip wiping of device authentication (integrity) tags. If you skip this
step, sectors will report invalid integrity tag until an application write
to the sector.
\fBNOTE:\fR Even some writes to the device can fail if the write is not
aligned to page size and page-cache initiates read of a sector with invalid
integrity tag.
.TP
.B "\-\-tcrypt\-hidden"
.B "\-\-tcrypt\-system"
.B "\-\-tcrypt\-backup"
Specify which TrueCrypt on-disk header will be used to open the device.
See \fITCRYPT\fR section for more info.
.TP
.B "\-\-veracrypt"
Allow VeraCrypt compatible mode. Only for TCRYPT extension.
See \fITCRYPT\fR section for more info.
.TP
.B "\-\-veracrypt\-pim"
.B "\-\-veracrypt\-query\-pim"
Use a custom Personal Iteration Multiplier (PIM) for VeraCrypt device.
See \fITCRYPT\fR section for more info.
.TP
.B "\-\-version"
Show the program version.
.TP
@@ -956,7 +1197,7 @@ less than the key size.
\fBFrom a key file\fR: It will be truncated to the
key size of the used cipher or the size given by \-s
and directly used as binary key.
and directly used as a binary key.
\fBWARNING\fR: The \-\-hash argument is being ignored.
The \-\-hash option is usable only for stdin input in plain mode.
@@ -998,10 +1239,10 @@ low-entropy passphrases, but open will take longer to
complete. For passphrases that have entropy higher than the
used key length, higher iteration times will not increase security.
The default setting of one second is sufficient for most
The default setting of one or two seconds is sufficient for most
practical cases. The only exception is a low-entropy
passphrase used on a device with a slow CPU, as this will
result in a low iteration count. On a slow device it may
result in a low iteration count. On a slow device, it may
be advisable to increase the iteration time using the
\-\-iter\-time option in order to obtain a higher
iteration count. This does slow down all later luksOpen
@@ -1021,7 +1262,7 @@ in order to get more options.
For the \-\-hash option, if the crypto backend is libgcrypt,
then all algorithms supported by the gcrypt library are available.
For other crypto backends some algorithms may be missing.
For other crypto backends, some algorithms may be missing.
.SH NOTES ON PASSPHRASES
Mathematics can't be bribed. Make sure you keep your passphrases safe.
There are a few nice tricks for constructing a fallback, when suddenly
@@ -1050,13 +1291,49 @@ time, using /dev/urandom in a low-entropy situation will
produce low-quality keys. This is a serious problem, but solving
it is out of scope for a mere man-page.
See \fPurandom(4)\fR for more information.
.SH AUTHENTICATED DISK ENCRYPTION (EXPERIMENTAL)
Since Linux kernel version 4.12 dm-crypt supports authenticated
disk encryption.
Normal disk encryption modes are length-preserving (plaintext sector
is of the same size as a ciphertext sector) and can provide only
confidentiality protection, but not cryptographically sound
data integrity protection.
Authenticated modes require additional space per-sector for
authentication tag and use Authenticated Encryption with Additional
Data (AEAD) algorithms.
If you configure LUKS2 device with data integrity protection,
there will be an underlying dm-integrity device, which provides
additional per-sector metadata space and also provide data
journal protection to ensure atomicity of data and metadata update.
Because there must be additional space for metadata and journal,
the available space for the device will be smaller than for
length-preserving modes.
The dm-crypt device then resides on top of such a dm-integrity device.
All activation and deactivation of this device stack is performed
by cryptsetup, there is no difference in using \fIluksOpen\fR
for integrity protected devices.
If you want to format LUKS2 device with data integrity protection,
use \fI\-\-integrity\fR option.
Some integrity modes requires two independent keys (key for encryption
and for authentication). Both these keys are stored in one LUKS keyslot.
\fBWARNING:\fR All support for authenticated modes is experimental
and there are only some modes available for now. Note that there
are a very few authenticated encryption algorithms that are suitable
for disk encryption.
.SH NOTES ON LOOPBACK DEVICE USE
Cryptsetup is usually used directly on a block device (disk
partition or LVM volume). However, if the device argument is a
file, cryptsetup tries to allocate a loopback device
and map it into this file. This mode requires Linux kernel 2.6.25
or more recent which supports the loop autoclear flag (loop device is
cleared on last close automatically). Of course, you can
cleared on the last close automatically). Of course, you can
always map a file to a loop-device manually. See the
cryptsetup FAQ for an example.

View File

@@ -23,6 +23,16 @@ lib/verity/verity.c
lib/verity/verity_hash.c
lib/verity/verity_fec.c
lib/integrity/integrity.c
lib/luks2/luks2_digest.c
lib/luks2/luks2_digest_pbkdf2.c
lib/luks2/luks2_disk_metadata.c
lib/luks2/luks2_json_format.c
lib/luks2/luks2_json_metadata.c
lib/luks2/luks2_keyslot.c
lib/luks2/luks2_keyslot_luks2.c
lib/luks2/luks2_luks1_convert.c
lib/luks2/luks2_token.c
lib/luks2/luks2_token_keyring.c
src/cryptsetup.c
src/veritysetup.c
src/integritysetup.c

View File

@@ -21,7 +21,8 @@ cryptsetup_LDADD = -lm \
$(top_builddir)/lib/libcryptsetup.la \
@POPT_LIBS@ \
@PWQUALITY_LIBS@ \
@PASSWDQC_LIBS@
@PASSWDQC_LIBS@ \
@UUID_LIBS@
cryptsetup_CFLAGS = $(AM_CFLAGS) -Wall

View File

@@ -22,6 +22,7 @@
*/
#include "cryptsetup.h"
#include <uuid/uuid.h>
static const char *opt_cipher = NULL;
static const char *opt_hash = NULL;
@@ -43,6 +44,8 @@ static long opt_new_keyfile_size = 0;
static long opt_keyfile_offset = 0;
static long opt_new_keyfile_offset = 0;
static int opt_key_slot = CRYPT_ANY_SLOT;
static int opt_token = CRYPT_ANY_TOKEN;
static int opt_token_only = 0;
static uint64_t opt_size = 0;
static uint64_t opt_offset = 0;
static uint64_t opt_skip = 0;
@@ -69,10 +72,21 @@ static int opt_veracrypt_query_pim = 0;
static int opt_deferred_remove = 0;
//FIXME: check uint32 overflow for long type
static const char *opt_pbkdf = NULL;
static long opt_pbkdf_memory = 1024;
static long opt_pbkdf_parallel = 2;
static long opt_pbkdf_memory = DEFAULT_LUKS2_MEMORY_KB;
static long opt_pbkdf_parallel = DEFAULT_LUKS2_PARALLEL_THREADS;
static long opt_pbkdf_iterations = 0;
static int opt_iteration_time = 0;
static int opt_disable_locks = 0;
static int opt_disable_keyring = 0;
static const char *opt_priority = NULL; /* normal */
static const char *opt_integrity = NULL; /* none */
static int opt_integrity_nojournal = 0;
static int opt_integrity_no_wipe = 0;
static const char *opt_key_description = NULL;
static int opt_sector_size = 512;
static int opt_persistent = 0;
static const char *opt_label = NULL;
static const char *opt_subsystem = NULL;
static const char **action_argv;
static int action_argc;
@@ -86,6 +100,23 @@ static const char *uuid_or_device_header(const char **data_device)
return uuid_or_device(opt_header_device ?: action_argv[0]);
}
static const char *luksType(const char *type)
{
if (type && !strcmp(type, "luks2"))
return CRYPT_LUKS2;
if (type && !strcmp(type, "luks1"))
return CRYPT_LUKS1;
if (type && !strcmp(type, "luks"))
return CRYPT_LUKS; /* NULL */
if (type && *type)
return type;
return CRYPT_LUKS; /* NULL */
}
static int _verify_passphrase(int def)
{
/* Batch mode switch off verify - if not overrided by -y */
@@ -117,6 +148,13 @@ static void _set_activation_flags(uint32_t *flags)
if (opt_perf_submit_from_crypt_cpus)
*flags |= CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS;
if (opt_integrity_nojournal)
*flags |= CRYPT_ACTIVATE_NO_JOURNAL;
/* In persistent mode, we use what is set on command line */
if (opt_persistent)
*flags |= CRYPT_ACTIVATE_IGNORE_PERSISTENT;
}
static int action_open_plain(void)
@@ -449,12 +487,45 @@ static int action_close(void)
static int action_resize(void)
{
struct crypt_device *cd = NULL;
int r;
size_t passwordLen;
struct crypt_active_device cad;
char *password = NULL;
struct crypt_device *cd = NULL;
r = crypt_init_by_name_and_header(&cd, action_argv[0], opt_header_device);
if (r == 0)
r = crypt_resize(cd, action_argv[0], opt_size);
if (r == 0) {
r = crypt_get_active_device(cd, action_argv[0], &cad);
if (!r && (cad.flags & CRYPT_ACTIVATE_KEYRING_KEY)) {
if (opt_disable_keyring) {
r = -EINVAL;
log_err(_("Resize of active device requires volume key in keyring but --disable-keyring option is set.\n"));
goto out;
}
/* try load VK in kernel keyring using token */
r = crypt_activate_by_token(cd, NULL, opt_token, NULL, CRYPT_ACTIVATE_KEYRING_KEY);
if (r < 0) {
if (opt_token_only)
goto out;
r = tools_get_key(NULL, &password, &passwordLen,
opt_keyfile_offset, opt_keyfile_size, opt_key_file,
opt_timeout, _verify_passphrase(0), 0, cd);
if (r < 0)
goto out;
r = crypt_activate_by_passphrase(cd, NULL, opt_key_slot, password, passwordLen, CRYPT_ACTIVATE_KEYRING_KEY);
crypt_safe_free(password);
}
}
if (r >= 0)
r = crypt_resize(cd, action_argv[0], opt_size);
}
out:
crypt_free(cd);
return r;
@@ -464,6 +535,7 @@ static int action_status(void)
{
crypt_status_info ci;
struct crypt_active_device cad;
struct crypt_params_integrity ip = {};
struct crypt_device *cd = NULL;
char *backing_file;
const char *device;
@@ -504,8 +576,17 @@ static int action_status(void)
if (r < 0)
goto out;
r = crypt_get_integrity_info(cd, &ip);
if (r < 0 && r != -ENOTSUP)
goto out;
log_std(" cipher: %s-%s\n", crypt_get_cipher(cd), crypt_get_cipher_mode(cd));
log_std(" keysize: %d bits\n", crypt_get_volume_key_size(cd) * 8);
log_std(" key location: %s\n", (cad.flags & CRYPT_ACTIVATE_KEYRING_KEY) ? "keyring" : "dm-crypt");
if (ip.integrity)
log_std(" integrity: %s\n", ip.integrity);
if (ip.integrity_key_size)
log_std(" integrity keysize: %d bits\n", ip.integrity_key_size * 8);
device = crypt_get_device_name(cd);
log_std(" device: %s\n", device);
if (crypt_loop_device(device)) {
@@ -513,6 +594,7 @@ static int action_status(void)
log_std(" loop: %s\n", backing_file);
free(backing_file);
}
log_std(" sector size: %d\n", crypt_get_sector_size(cd));
log_std(" offset: %" PRIu64 " sectors\n", cad.offset);
log_std(" size: %" PRIu64 " sectors\n", cad.size);
if (cad.iv_offset)
@@ -569,7 +651,7 @@ static int action_benchmark_kdf(const char *kdf, const char *hash, size_t key_si
} else {
struct crypt_pbkdf_type pbkdf = {
.type = kdf,
.time_ms = opt_iteration_time ?: 800,
.time_ms = opt_iteration_time ?: DEFAULT_LUKS2_ITER_TIME,
.max_memory_kb = opt_pbkdf_memory,
.parallel_threads = opt_pbkdf_parallel,
};
@@ -765,6 +847,14 @@ static int set_pbkdf_params(struct crypt_device *cd, const char *dev_type)
pbkdf.type = CRYPT_KDF_PBKDF2;
pbkdf.hash = opt_hash ?: DEFAULT_LUKS1_HASH;
pbkdf.time_ms = opt_iteration_time ?: DEFAULT_LUKS1_ITER_TIME;
} else if (!strcmp(dev_type, CRYPT_LUKS2)) {
pbkdf.type = opt_pbkdf ?: DEFAULT_LUKS2_PBKDF;
pbkdf.hash = opt_hash ?: DEFAULT_LUKS1_HASH;
pbkdf.time_ms = opt_iteration_time ?: DEFAULT_LUKS2_ITER_TIME;
if (strcmp(pbkdf.type, CRYPT_KDF_PBKDF2)) {
pbkdf.max_memory_kb = opt_pbkdf_memory;
pbkdf.parallel_threads = opt_pbkdf_parallel;
}
} else
return 0;
@@ -784,9 +874,8 @@ static int action_luksRepair(void)
if ((r = crypt_init(&cd, action_argv[0])))
goto out;
/* Currently only LUKS1 allows repair */
crypt_set_log_callback(cd, quiet_log, NULL);
r = crypt_load(cd, CRYPT_LUKS1, NULL);
r = crypt_load(cd, luksType(opt_type), NULL);
crypt_set_log_callback(cd, tool_log, NULL);
if (r == 0) {
log_verbose(_("No known problems detected for LUKS header.\n"));
@@ -796,18 +885,53 @@ static int action_luksRepair(void)
r = yesDialog(_("Really try to repair LUKS device header?"),
NULL) ? 0 : -EINVAL;
if (r == 0)
r = crypt_repair(cd, CRYPT_LUKS1, NULL);
r = crypt_repair(cd, luksType(opt_type), NULL);
out:
crypt_free(cd);
return r;
}
static int _wipe_data_device(struct crypt_device *cd)
{
char tmp_name[64], tmp_path[128], tmp_uuid[40];
uuid_t tmp_uuid_bin;
int r;
if (!opt_batch_mode)
log_std(_("Wiping device to initialize integrity checksum.\n"
"You can interrupt this by pressing CTRL+c "
"(rest of not wiped device will contain invalid checksum).\n"));
/* Activate the device a temporary one */
uuid_generate(tmp_uuid_bin);
uuid_unparse(tmp_uuid_bin, tmp_uuid);
if (snprintf(tmp_name, sizeof(tmp_name), "temporary-cryptsetup-%s", tmp_uuid) < 0)
return -EINVAL;
if (snprintf(tmp_path, sizeof(tmp_path), "%s/%s", crypt_get_dir(), tmp_name) < 0)
return -EINVAL;
r = crypt_activate_by_volume_key(cd, tmp_name, NULL, 0,
CRYPT_ACTIVATE_PRIVATE | CRYPT_ACTIVATE_NO_JOURNAL);
if (r < 0)
return r;
/* Wipe the device */
set_int_handler(0);
r = crypt_wipe(cd, tmp_path, CRYPT_WIPE_ZERO, 0, 0, DEFAULT_WIPE_BLOCK,
0, &tools_wipe_progress, NULL);
if (crypt_deactivate(cd, tmp_name))
log_err(_("Cannot deactivate temporary device %s.\n"), tmp_path);
set_int_block(0);
return r;
}
static int action_luksFormat(void)
{
int r = -EINVAL, keysize;
int r = -EINVAL, keysize, integrity_keysize = 0, luks_version;
const char *header_device;
char *msg = NULL, *key = NULL, cipher [MAX_CIPHER_LEN], cipher_mode[MAX_CIPHER_LEN];
char *password = NULL;
char *msg = NULL, *key = NULL, *password = NULL;
char cipher [MAX_CIPHER_LEN], cipher_mode[MAX_CIPHER_LEN], integrity[MAX_CIPHER_LEN];
size_t passwordLen;
struct crypt_device *cd = NULL;
struct crypt_params_luks1 params = {
@@ -815,6 +939,25 @@ static int action_luksFormat(void)
.data_alignment = opt_align_payload,
.data_device = opt_header_device ? action_argv[0] : NULL,
};
struct crypt_params_luks2 params2 = {
.data_alignment = params.data_alignment,
.data_device = params.data_device,
.sector_size = opt_sector_size,
.label = opt_label,
.subsystem = opt_subsystem
};
if (!opt_type)
return -EINVAL;
else if (!strcmp(opt_type, "luks2"))
luks_version = 2;
else
luks_version = 1;
if (opt_sector_size > 512 && luks_version == 1) {
log_err(_("Unsupported encryption sector size.\n"));
return -EINVAL;
}
header_device = opt_header_device ?: action_argv[0];
@@ -836,6 +979,16 @@ static int action_luksFormat(void)
goto out;
}
if (luks_version == 2 && opt_integrity) {
r = crypt_parse_integrity_mode(opt_integrity, integrity, &integrity_keysize);
if (r < 0) {
log_err(_("No known integrity specification pattern detected.\n"));
goto out;
}
params2.integrity = integrity;
/* FIXME: we use default integrity_params (set to NULL) */
}
/* Never call pwquality if using null cipher */
if (tools_is_cipher_null(cipher))
opt_force_password = 1;
@@ -846,7 +999,7 @@ static int action_luksFormat(void)
goto out;
}
keysize = (opt_key_size ?: DEFAULT_LUKS1_KEYBITS) / 8;
keysize = (opt_key_size ?: DEFAULT_LUKS1_KEYBITS) / 8 + integrity_keysize;
if (opt_random)
crypt_set_rng_type(cd, CRYPT_RNG_RANDOM);
@@ -865,14 +1018,21 @@ static int action_luksFormat(void)
goto out;
}
r = set_pbkdf_params(cd, CRYPT_LUKS1);
if (luks_version == 1)
r = set_pbkdf_params(cd, CRYPT_LUKS1);
else
r = set_pbkdf_params(cd, CRYPT_LUKS2);
if (r) {
log_err(_("Failed to set pbkdf parameters.\n"));
goto out;
}
r = crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode,
opt_uuid, key, keysize, &params);
if (luks_version == 1)
r = crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode,
opt_uuid, key, keysize, &params);
else
r = crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode,
opt_uuid, key, keysize, &params2);
check_signal(&r);
if (r < 0)
goto out;
@@ -880,6 +1040,11 @@ static int action_luksFormat(void)
r = crypt_keyslot_add_by_volume_key(cd, opt_key_slot,
key, keysize,
password, passwordLen);
if (r < 0)
goto out;
if (opt_integrity && !opt_integrity_no_wipe)
r = _wipe_data_device(cd);
out:
crypt_free(cd);
crypt_safe_free(key);
@@ -905,7 +1070,7 @@ static int action_open_luks(void)
if ((r = crypt_init(&cd, header_device)))
goto out;
if ((r = crypt_load(cd, CRYPT_LUKS1, NULL)))
if ((r = crypt_load(cd, luksType(opt_type), NULL)))
goto out;
if (data_device &&
@@ -928,6 +1093,10 @@ static int action_open_luks(void)
r = crypt_activate_by_volume_key(cd, activated_name,
key, keysize, activate_flags);
} else {
r = crypt_activate_by_token(cd, activated_name, opt_token, NULL, activate_flags);
if (r >= 0 || opt_token_only)
goto out;
tries = (opt_key_file && !tools_is_stdin(opt_key_file)) ? 1 : opt_tries;
do {
r = tools_get_key(NULL, &password, &passwordLen,
@@ -945,6 +1114,11 @@ static int action_open_luks(void)
} while ((r == -EPERM || r == -ERANGE) && (--tries > 0));
}
out:
if (r >= 0 && opt_persistent &&
crypt_persistent_flags_set(cd, CRYPT_FLAGS_ACTIVATION, activate_flags))
log_err(_("Device activated but cannot make flags persistent.\n"));
crypt_safe_free(key);
crypt_safe_free(password);
crypt_free(cd);
@@ -959,7 +1133,7 @@ static int verify_keyslot(struct crypt_device *cd, int key_slot,
crypt_keyslot_info ki;
char *password = NULL;
size_t passwordLen;
int i, r;
int i, max, r;
ki = crypt_keyslot_status(cd, key_slot);
if (ki == CRYPT_SLOT_ACTIVE_LAST && !opt_batch_mode && !key_file &&
@@ -969,7 +1143,7 @@ static int verify_keyslot(struct crypt_device *cd, int key_slot,
r = tools_get_key(msg_pass, &password, &passwordLen,
keyfile_offset, keyfile_size, key_file, opt_timeout,
_verify_passphrase(0), 0, cd);
if(r < 0)
if (r < 0)
goto out;
if (ki == CRYPT_SLOT_ACTIVE_LAST) {
@@ -978,13 +1152,18 @@ static int verify_keyslot(struct crypt_device *cd, int key_slot,
password, passwordLen, 0);
} else {
/* try all other keyslots */
for (i = 0; i < crypt_keyslot_max(CRYPT_LUKS1); i++) {
r = crypt_keyslot_max(crypt_get_type(cd));
if (r < 0)
goto out;
max = r;
for (i = 0; i < max ; i++) {
if (i == key_slot)
continue;
ki = crypt_keyslot_status(cd, key_slot);
if (ki == CRYPT_SLOT_ACTIVE)
r = crypt_activate_by_passphrase(cd, NULL, i,
password, passwordLen, 0);
ki = crypt_keyslot_status(cd, i);
if (ki == CRYPT_SLOT_ACTIVE || ki == CRYPT_SLOT_ACTIVE_LAST)
r = crypt_activate_by_passphrase(cd, NULL, i,
password, passwordLen, 0);
if (r == i)
break;
}
@@ -1011,21 +1190,9 @@ static int action_luksKillSlot(void)
crypt_set_confirm_callback(cd, yesDialog, NULL);
if ((r = crypt_load(cd, CRYPT_LUKS1, NULL)))
if ((r = crypt_load(cd, luksType(opt_type), NULL)))
goto out;
switch (crypt_keyslot_status(cd, opt_key_slot)) {
case CRYPT_SLOT_ACTIVE_LAST:
case CRYPT_SLOT_ACTIVE:
log_verbose(_("Key slot %d selected for deletion.\n"), opt_key_slot);
break;
case CRYPT_SLOT_INACTIVE:
log_err(_("Key %d not active. Can't wipe.\n"), opt_key_slot);
case CRYPT_SLOT_INVALID:
r = -EINVAL;
goto out;
}
if (!opt_batch_mode || opt_key_file || !isatty(STDIN_FILENO)) {
r = verify_keyslot(cd, opt_key_slot,
_("This is the last keyslot. Device will become unusable after purging this key."),
@@ -1059,7 +1226,7 @@ static int action_luksRemoveKey(void)
crypt_set_confirm_callback(cd, yesDialog, NULL);
if ((r = crypt_load(cd, CRYPT_LUKS1, NULL)))
if ((r = crypt_load(cd, luksType(opt_type), NULL)))
goto out;
r = tools_get_key(_("Enter passphrase to be deleted: "),
@@ -1109,7 +1276,7 @@ static int action_luksAddKey(void)
crypt_set_confirm_callback(cd, yesDialog, NULL);
if ((r = crypt_load(cd, CRYPT_LUKS1, NULL)))
if ((r = crypt_load(cd, luksType(opt_type), NULL)))
goto out;
/* Never call pwquality if using null cipher */
@@ -1194,7 +1361,7 @@ static int action_luksChangeKey(void)
if ((r = crypt_init(&cd, uuid_or_device_header(NULL))))
goto out;
if ((r = crypt_load(cd, CRYPT_LUKS1, NULL)))
if ((r = crypt_load(cd, luksType(opt_type), NULL)))
goto out;
/* Never call pwquality if using null cipher */
@@ -1253,7 +1420,7 @@ static int action_isLuks(void)
goto out;
crypt_set_log_callback(cd, quiet_log, NULL);
r = crypt_load(cd, CRYPT_LUKS1, NULL);
r = crypt_load(cd, luksType(opt_type), NULL);
out:
crypt_free(cd);
return r;
@@ -1270,7 +1437,7 @@ static int action_luksUUID(void)
crypt_set_confirm_callback(cd, yesDialog, NULL);
if ((r = crypt_load(cd, CRYPT_LUKS1, NULL)))
if ((r = crypt_load(cd, luksType(opt_type), NULL)))
goto out;
if (opt_uuid)
@@ -1347,7 +1514,7 @@ static int action_luksDump(void)
if ((r = crypt_init(&cd, uuid_or_device_header(NULL))))
goto out;
if ((r = crypt_load(cd, CRYPT_LUKS1, NULL)))
if ((r = crypt_load(cd, luksType(opt_type), NULL)))
goto out;
if (opt_dump_master_key)
@@ -1382,7 +1549,7 @@ static int action_luksResume(void)
if ((r = crypt_init_by_name_and_header(&cd, action_argv[0], uuid_or_device(opt_header_device))))
goto out;
if ((r = crypt_load(cd, CRYPT_LUKS1, NULL)))
if ((r = crypt_load(cd, luksType(opt_type), NULL)))
goto out;
tries = (opt_key_file && !tools_is_stdin(opt_key_file)) ? 1 : opt_tries;
@@ -1421,7 +1588,7 @@ static int action_luksBackup(void)
crypt_set_confirm_callback(cd, yesDialog, NULL);
r = crypt_header_backup(cd, CRYPT_LUKS1, opt_header_backup_file);
r = crypt_header_backup(cd, NULL, opt_header_backup_file);
out:
crypt_free(cd);
return r;
@@ -1441,7 +1608,7 @@ static int action_luksRestore(void)
goto out;
crypt_set_confirm_callback(cd, yesDialog, NULL);
r = crypt_header_restore(cd, CRYPT_LUKS1, opt_header_backup_file);
r = crypt_header_restore(cd, NULL, opt_header_backup_file);
out:
crypt_free(cd);
return r;
@@ -1452,7 +1619,9 @@ static int action_open(void)
if (!opt_type)
return -EINVAL;
if (!strcmp(opt_type, "luks") || !strcmp(opt_type, "luks1")) {
if (!strcmp(opt_type, "luks") ||
!strcmp(opt_type, "luks1") ||
!strcmp(opt_type, "luks2")) {
if (action_argc < 2 && !opt_test_passphrase)
goto args;
return action_open_luks();
@@ -1482,14 +1651,14 @@ static int action_luksErase(void)
struct crypt_device *cd = NULL;
crypt_keyslot_info ki;
char *msg = NULL;
int i, r;
int i, max, r;
if ((r = crypt_init(&cd, uuid_or_device_header(NULL))))
goto out;
crypt_set_confirm_callback(cd, yesDialog, NULL);
if ((r = crypt_load(cd, CRYPT_LUKS1, NULL)))
if ((r = crypt_load(cd, luksType(opt_type), NULL)))
goto out;
if(asprintf(&msg, _("This operation will erase all keyslots on device %s.\n"
@@ -1504,7 +1673,12 @@ static int action_luksErase(void)
goto out;
}
for (i = 0; i < crypt_keyslot_max(CRYPT_LUKS1); i++) {
/* Safety check */
max = crypt_keyslot_max(crypt_get_type(cd));
if (max <= 0)
return -EINVAL;
for (i = 0; i < max; i++) {
ki = crypt_keyslot_status(cd, i);
if (ki == CRYPT_SLOT_ACTIVE || ki == CRYPT_SLOT_ACTIVE_LAST) {
r = crypt_keyslot_destroy(cd, i);
@@ -1518,6 +1692,182 @@ out:
return r;
}
static int action_luksConvert(void)
{
struct crypt_device *cd = NULL;
char *msg = NULL;
const char *to_type, *from_type;
int r;
if (!strcmp(opt_type, "luks2")) {
to_type = CRYPT_LUKS2;
} else if (!strcmp(opt_type, "luks1")) {
to_type = CRYPT_LUKS1;
} else {
log_err(_("Missing LUKS target type, option --type is required.\n"));
return -EINVAL;
}
if ((r = crypt_init(&cd, uuid_or_device_header(NULL))))
return r;
crypt_set_confirm_callback(cd, yesDialog, NULL);
if ((r = crypt_load(cd, CRYPT_LUKS, NULL)) ||
!(from_type = crypt_get_type(cd))) {
crypt_free(cd);
return r;
}
if (!strcmp(from_type, to_type)) {
log_err(_("Device is already %s type.\n"), to_type);
crypt_free(cd);
return -EINVAL;
}
if (asprintf(&msg, _("This operation will convert %s to %s format.\n"),
uuid_or_device_header(NULL), to_type) == -1) {
crypt_free(cd);
return -ENOMEM;
}
if (yesDialog(msg, NULL))
r = crypt_convert(cd, to_type, NULL);
else
r = -EPERM;
free(msg);
crypt_free(cd);
return r;
}
static int _config_priority(struct crypt_device *cd)
{
crypt_keyslot_info cs;
crypt_keyslot_priority priority = CRYPT_SLOT_PRIORITY_INVALID;
if (!strcmp("normal", opt_priority))
priority = CRYPT_SLOT_PRIORITY_NORMAL;
else if (!strcmp("prefer", opt_priority))
priority = CRYPT_SLOT_PRIORITY_PREFER;
else if (!strcmp("ignore", opt_priority))
priority = CRYPT_SLOT_PRIORITY_IGNORE;
cs = crypt_keyslot_status(cd, opt_key_slot);
if (cs != CRYPT_SLOT_INVALID)
return crypt_keyslot_set_priority(cd, opt_key_slot, priority);
return -EINVAL;
}
static int _config_labels(struct crypt_device *cd)
{
return crypt_set_label(cd, opt_label, opt_subsystem);
}
static int action_luksConfig(void)
{
struct crypt_device *cd = NULL;
int r;
if (!opt_priority && !opt_label && !opt_subsystem) {
log_err(_("Option --priority, --label or --subsystem is missing.\n"));
return -EINVAL;
}
if ((r = crypt_init(&cd, uuid_or_device_header(NULL))))
return r;
if ((r = crypt_load(cd, CRYPT_LUKS2, NULL)))
goto out;
if (opt_priority && (r = _config_priority(cd)))
goto out;
if ((opt_label || opt_subsystem) && (r = _config_labels(cd)))
goto out;
out:
crypt_free(cd);
return r;
}
static int _token_add(struct crypt_device *cd)
{
int r, token;
crypt_token_info token_info;
const struct crypt_token_params_luks2_keyring params = {
.key_description = opt_key_description
};
if (opt_token != CRYPT_ANY_TOKEN) {
token_info = crypt_token_status(cd, opt_token, NULL);
if (token_info < CRYPT_TOKEN_INACTIVE) {
log_err(_("Token %d is invalid.\n"), opt_token);
return -EINVAL;
} else if (token_info > CRYPT_TOKEN_INACTIVE) {
log_err(_("Token %d in use.\n"), opt_token);
return -EINVAL;
}
}
r = crypt_token_luks2_keyring_set(cd, opt_token, &params);
if (r < 0)
return r;
token = r;
r = crypt_token_assign_keyslot(cd, token, opt_key_slot);
if (r < 0) {
log_err(_("Failed to assign token %d to keyslot %d.\n"), token, opt_key_slot);
(void) crypt_token_json_set(cd, token, NULL);
}
return r;
}
static int action_token(void)
{
int add, r;
struct crypt_device *cd = NULL;
if (!strcmp(action_argv[0], "add")) {
if (!opt_key_description) {
log_err(_("--key-description parameter is mandatory for token add action.\n"));
return -EINVAL;
}
add = 1;
} else if (!strcmp(action_argv[0], "remove")) {
if (opt_token == CRYPT_ANY_TOKEN) {
log_err(_("Missing --token option specifying token for removal.\n"));
return -EINVAL;
}
add = 0;
} else {
log_err(_("Invalid token operation %s.\n"), action_argv[0]);
return -EINVAL;
}
if ((r = crypt_init(&cd, uuid_or_device(opt_header_device ?: action_argv[1]))))
return r;
if ((r = crypt_load(cd, CRYPT_LUKS2, NULL))) {
crypt_free(cd);
return r;
}
r = add ? _token_add(cd) : crypt_token_json_set(cd, opt_token, NULL);
if (r < 0) {
if (add)
log_err(_("Failed to add keyring token %d.\n"), opt_token);
else
log_err(_("Failed to remove token %d.\n"), opt_token);
}
crypt_free(cd);
return r;
}
static struct action_type {
const char *type;
int (*handler)(void);
@@ -1533,6 +1883,8 @@ static struct action_type {
{ "benchmark", action_benchmark, 0, 0, N_("[--cipher <cipher>]"), N_("benchmark cipher") },
{ "repair", action_luksRepair, 1, 1, N_("<device>"), N_("try to repair on-disk metadata") },
{ "erase", action_luksErase , 1, 1, N_("<device>"), N_("erase all keyslots (remove encryption key)") },
{ "convert", action_luksConvert, 1, 1, N_("<device>"), N_("convert LUKS from/to LUKS2 format") },
{ "config", action_luksConfig, 1, 1, N_("<device>"), N_("set permanent configuration options for LUKS2") },
{ "luksFormat", action_luksFormat, 1, 1, N_("<device> [<new key file>]"), N_("formats a LUKS device") },
{ "luksAddKey", action_luksAddKey, 1, 1, N_("<device> [<new key file>]"), N_("add key to LUKS device") },
{ "luksRemoveKey",action_luksRemoveKey,1, 1, N_("<device> [<key file>]"), N_("removes supplied key or key file from LUKS device") },
@@ -1546,6 +1898,7 @@ static struct action_type {
{ "luksResume", action_luksResume, 1, 1, N_("<device>"), N_("Resume suspended LUKS device.") },
{ "luksHeaderBackup", action_luksBackup,1,1, N_("<device>"), N_("Backup LUKS device header and keyslots") },
{ "luksHeaderRestore",action_luksRestore,1,1,N_("<device>"), N_("Restore LUKS device header and keyslots") },
{ "token", action_token, 2, 0, N_("<add|remove> <device>"), N_("Add or remove keyring token") },
{}
};
@@ -1582,9 +1935,12 @@ static void help(poptContext popt_context,
log_std(_("\nDefault compiled-in key and passphrase parameters:\n"
"\tMaximum keyfile size: %dkB, "
"Maximum interactive passphrase length %d (characters)\n"
"Default PBKDF2 iteration time for LUKS: %d (ms)\n"),
"Default PBKDF2 iteration time for LUKS: %d (ms)\n"
"Default PBKDF for LUKS2: %s\n"
"\tIteration time: %d, Memory required: %dkB, Parallel threads: %d\n"),
DEFAULT_KEYFILE_SIZE_MAXKB, DEFAULT_PASSPHRASE_SIZE_MAX,
DEFAULT_LUKS1_ITER_TIME);
DEFAULT_LUKS1_ITER_TIME, DEFAULT_LUKS2_PBKDF, DEFAULT_LUKS2_ITER_TIME,
DEFAULT_LUKS2_MEMORY_KB, DEFAULT_LUKS2_PARALLEL_THREADS);
log_std(_("\nDefault compiled-in device cipher parameters:\n"
"\tloop-AES: %s, Key %d bits\n"
@@ -1689,6 +2045,19 @@ int main(int argc, const char **argv)
{ "pbkdf-memory", '\0', POPT_ARG_LONG, &opt_pbkdf_memory, 0, N_("PBKDF memory cost limit"), N_("kilobytes") },
{ "pbkdf-parallel", '\0', POPT_ARG_LONG, &opt_pbkdf_parallel, 0, N_("PBKDF parallel cost "), N_("threads") },
{ "pbkdf-force-iterations",'\0',POPT_ARG_LONG, &opt_pbkdf_iterations, 0, N_("PBKDF iterations cost (forced, disables benchmark)"), NULL },
{ "priority", '\0', POPT_ARG_STRING, &opt_priority, 0, N_("Keyslot priority (ignore/normal/prefer)"), NULL },
{ "disable-locks", '\0', POPT_ARG_NONE, &opt_disable_locks, 0, N_("Disable locking of on-disk metadata"), NULL },
{ "disable-keyring", '\0', POPT_ARG_NONE, &opt_disable_keyring, 0, N_("Disable loading volume keys via kernel keyring"), NULL },
{ "integrity", 'I', POPT_ARG_STRING, &opt_integrity, 0, N_("Data integrity algorithm (LUKS2 only)"), NULL },
{ "integrity-no-journal",'\0',POPT_ARG_NONE, &opt_integrity_nojournal, 0, N_("Disable journal for integrity device."), NULL },
{ "integrity-no-wipe", '\0', POPT_ARG_NONE, &opt_integrity_no_wipe, 0, N_("Do not wipe device after format"), NULL },
{ "token-only", '\0', POPT_ARG_NONE, &opt_token_only, 0, N_("Do not ask for passphrase if activation by token fails"), NULL },
{ "token-id", '\0', POPT_ARG_INT, &opt_token, 0, N_("Token number (default: any)"), NULL },
{ "key-description", '\0', POPT_ARG_STRING, &opt_key_description, 0, N_("Key description"), NULL },
{ "sector-size", '\0', POPT_ARG_INT, &opt_sector_size, 0, N_("Encryption sector size (default: 512 bytes)"), NULL },
{ "persistent", '\0', POPT_ARG_NONE, &opt_persistent, 0, N_("Set activation flags persistent for device."), NULL },
{ "label", '\0', POPT_ARG_STRING, &opt_label, 0, N_("Set label for the LUKS2 device."), NULL },
{ "subsystem", '\0', POPT_ARG_STRING, &opt_subsystem, 0, N_("Set subsystem label for the LUKS2 device."), NULL },
POPT_TABLEEND
};
poptContext popt_context;
@@ -1801,6 +2170,9 @@ int main(int argc, const char **argv)
} else if (!strcmp(aname, "luksErase")) {
aname = "erase";
opt_type = "luks";
} else if (!strcmp(aname, "luksConfig")) {
aname = "config";
opt_type = "luks2";
}
for(action = action_types; action->type; action++)
@@ -1831,6 +2203,11 @@ int main(int argc, const char **argv)
_("Option --allow-discards is allowed only for open operation.\n"),
poptGetInvocationName(popt_context));
if (opt_persistent && strcmp(aname, "open"))
usage(popt_context, EXIT_FAILURE,
_("Option --persistent is allowed only for open operation.\n"),
poptGetInvocationName(popt_context));
if (opt_key_size &&
strcmp(aname, "luksFormat") &&
strcmp(aname, "open") &&
@@ -1840,8 +2217,24 @@ int main(int argc, const char **argv)
"To limit read from keyfile use --keyfile-size=(bytes)."),
poptGetInvocationName(popt_context));
if (opt_integrity && strcmp(aname, "luksFormat"))
usage(popt_context, EXIT_FAILURE,
_("Option --integrity is allowed only for luksFormat (LUKS2).\n"),
poptGetInvocationName(popt_context));
if (opt_integrity_no_wipe && !opt_integrity)
usage(popt_context, EXIT_FAILURE,
_("Option --integrity-no-wipe"
" can be used only for format action with integrity extension.\n"),
poptGetInvocationName(popt_context));
if ((opt_label || opt_subsystem) && strcmp(aname, "luksFormat") && strcmp(aname, "config"))
usage(popt_context, EXIT_FAILURE,
_("Options --label and --subsystem are allowed only for luksFormat and config LUKS2 operations.\n"),
poptGetInvocationName(popt_context));
if (opt_test_passphrase && (strcmp(aname, "open") ||
(strcmp(opt_type, "luks") && strcmp(opt_type, "tcrypt"))))
(strncmp(opt_type, "luks", 4) && strcmp(opt_type, "tcrypt"))))
usage(popt_context, EXIT_FAILURE,
_("Option --test-passphrase is allowed only for open of LUKS and TCRYPT devices.\n"),
poptGetInvocationName(popt_context));
@@ -1853,8 +2246,7 @@ int main(int argc, const char **argv)
if (!strcmp(aname, "luksKillSlot") && action_argc > 1)
opt_key_slot = atoi(action_argv[1]);
if (opt_key_slot != CRYPT_ANY_SLOT &&
(opt_key_slot < 0 || opt_key_slot >= crypt_keyslot_max(CRYPT_LUKS1)))
if (opt_key_slot != CRYPT_ANY_SLOT && opt_key_slot < 0)
usage(popt_context, EXIT_FAILURE, _("Key slot is invalid."),
poptGetInvocationName(popt_context));
@@ -1945,9 +2337,19 @@ int main(int argc, const char **argv)
}
}
if (opt_priority && strcmp(opt_priority, "normal") && strcmp(opt_priority, "prefer") && strcmp(opt_priority, "ignore"))
usage(popt_context, EXIT_FAILURE,
_("Option --priority can be only ignore/normal/prefer.\n"),
poptGetInvocationName(popt_context));
if (!strcmp(aname, "config") && opt_priority && opt_key_slot == CRYPT_ANY_SLOT)
usage(popt_context, EXIT_FAILURE,
_("Keyslot specification is required.\n"),
poptGetInvocationName(popt_context));
if (opt_pbkdf && crypt_parse_pbkdf(opt_pbkdf, &opt_pbkdf))
usage(popt_context, EXIT_FAILURE,
_("PBKDF can be only pbkdf2 or argon2i/argon2id.\n"),
_("Password-based key derivation function (PBKDF) can be only pbkdf2 or argon2i/argon2id.\n"),
poptGetInvocationName(popt_context));
if (opt_pbkdf_iterations && opt_iteration_time)
@@ -1955,12 +2357,27 @@ int main(int argc, const char **argv)
_("PBKDF forced iterations cannot be combined with iteration time option.\n"),
poptGetInvocationName(popt_context));
if ((opt_sector_size != 512 && strcmp(aname, "luksFormat")) || opt_sector_size < 512
|| opt_sector_size > 4096 || (opt_sector_size & (opt_sector_size - 1)))
usage(popt_context, EXIT_FAILURE,
_("Unsupported encryption sector size.\n"),
poptGetInvocationName(popt_context));
if (opt_debug) {
opt_verbose = 1;
crypt_set_debug_level(-1);
dbg_version_and_cmd(argc, argv);
}
if (opt_disable_locks && crypt_metadata_locking(NULL, 0)) {
log_std(_("Cannot disable metadata locking.\n"));
poptFreeContext(popt_context);
exit(EXIT_FAILURE);
}
if (opt_disable_keyring)
(void) crypt_volume_key_keyring(NULL, 0);
r = run_action(action);
poptFreeContext(popt_context);
return r;