Autodetect optimal encryption sector size on LUKS2 format.

This commit is contained in:
Ondrej Kozina
2021-02-05 18:13:26 +01:00
parent 1aeb0a1f6e
commit 8d0e90b90a
10 changed files with 138 additions and 20 deletions

View File

@@ -120,6 +120,7 @@ int device_check_size(struct crypt_device *cd,
struct device *device,
uint64_t req_offset, int falloc);
void device_set_block_size(struct device *device, size_t size);
size_t device_optimal_encryption_sector_size(struct crypt_device *cd, struct device *device);
int device_open_locked(struct crypt_device *cd, struct device *device, int flags);
int device_read_lock(struct crypt_device *cd, struct device *device);

View File

@@ -599,7 +599,7 @@ struct crypt_params_luks2 {
const struct crypt_params_integrity *integrity_params; /**< Data integrity parameters or @e NULL*/
size_t data_alignment; /**< data area alignment in 512B 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 */
uint32_t sector_size; /**< encryption sector size, 0 triggers auto-detection for optimal encryption sector size */
const char *label; /**< header label or @e NULL*/
const char *subsystem; /**< header subsystem label or @e NULL*/
};

View File

@@ -7,6 +7,7 @@ CRYPTSETUP_2.4 {
crypt_activate_by_token_type;
crypt_activate_by_token_pin;
crypt_dump_json;
crypt_format;
};
CRYPTSETUP_2.0 {

View File

@@ -1706,12 +1706,13 @@ static int _crypt_format_luks2(struct crypt_device *cd,
const char *uuid,
const char *volume_key,
size_t volume_key_size,
struct crypt_params_luks2 *params)
struct crypt_params_luks2 *params,
bool sector_size_autodetect)
{
int r, integrity_key_size = 0;
unsigned long required_alignment = DEFAULT_DISK_ALIGNMENT;
unsigned long alignment_offset = 0;
unsigned int sector_size = params ? params->sector_size : SECTOR_SIZE;
unsigned int sector_size;
const char *integrity = params ? params->integrity : NULL;
uint64_t dev_size;
uint32_t dmc_flags;
@@ -1733,15 +1734,30 @@ static int _crypt_format_luks2(struct crypt_device *cd,
return -EINVAL;
}
if (params && params->sector_size)
sector_size_autodetect = false;
if (sector_size_autodetect) {
sector_size = device_optimal_encryption_sector_size(cd, crypt_data_device(cd));
log_dbg(cd, "Auto-detected optimal encryption sector size for device %s is %d bytes.",
device_path(crypt_data_device(cd)), sector_size);
} else
sector_size = params ? params->sector_size : SECTOR_SIZE;
if (sector_size < SECTOR_SIZE || sector_size > MAX_SECTOR_SIZE ||
NOTPOW2(sector_size)) {
log_err(cd, _("Unsupported encryption sector size."));
return -EINVAL;
}
if (sector_size != SECTOR_SIZE && !dm_flags(cd, DM_CRYPT, &dmc_flags) &&
!(dmc_flags & DM_SECTOR_SIZE_SUPPORTED))
!(dmc_flags & DM_SECTOR_SIZE_SUPPORTED)) {
if (sector_size_autodetect) {
log_dbg(cd, "dm-crypt does not support encryption sector size option. Reverting to 512 bytes.");
sector_size = SECTOR_SIZE;
} else
log_std(cd, _("WARNING: The device activation will fail, dm-crypt is missing "
"support for requested encryption sector size.\n"));
}
if (integrity) {
if (params->integrity_params) {
@@ -1813,6 +1829,21 @@ static int _crypt_format_luks2(struct crypt_device *cd,
&required_alignment,
&alignment_offset, DEFAULT_DISK_ALIGNMENT);
r = device_size(crypt_data_device(cd), &dev_size);
if (r < 0)
goto out;
if (sector_size_autodetect) {
if (cd->data_offset && MISALIGNED(cd->data_offset, sector_size)) {
log_dbg(cd, "Data offset not alligned to sector size. Reverting to 512 bytes.");
sector_size = SECTOR_SIZE;
} else if (MISALIGNED(dev_size - (uint64_t)required_alignment - (uint64_t)alignment_offset, sector_size)) {
/* underflow does not affect misalignment checks */
log_err(cd, "Device size is not aligned to sector size. Reverting to 512 bytes.");
sector_size = SECTOR_SIZE;
}
}
/* FIXME: allow this later also for normal ciphers (check AF_ALG availability. */
if (integrity && !integrity_key_size) {
r = crypt_cipher_check_kernel(cipher, cipher_mode, integrity, volume_key_size);
@@ -1842,10 +1873,6 @@ static int _crypt_format_luks2(struct crypt_device *cd,
if (r < 0)
goto out;
r = device_size(crypt_data_device(cd), &dev_size);
if (r < 0)
goto out;
if (dev_size < (crypt_get_data_offset(cd) * SECTOR_SIZE))
log_std(cd, _("WARNING: Data offset is outside of currently available data device.\n"));
@@ -2241,14 +2268,15 @@ out:
return r;
}
int crypt_format(struct crypt_device *cd,
static int _crypt_format(struct crypt_device *cd,
const char *type,
const char *cipher,
const char *cipher_mode,
const char *uuid,
const char *volume_key,
size_t volume_key_size,
void *params)
void *params,
bool sector_size_autodetect)
{
int r;
@@ -2276,7 +2304,7 @@ int crypt_format(struct crypt_device *cd,
uuid, volume_key, volume_key_size, params);
else if (isLUKS2(type))
r = _crypt_format_luks2(cd, cipher, cipher_mode,
uuid, volume_key, volume_key_size, params);
uuid, volume_key, volume_key_size, params, sector_size_autodetect);
else if (isLOOPAES(type))
r = _crypt_format_loopaes(cd, cipher, uuid, volume_key_size, params);
else if (isVERITY(type))
@@ -2297,6 +2325,34 @@ int crypt_format(struct crypt_device *cd,
return r;
}
int crypt_format(struct crypt_device *cd,
const char *type,
const char *cipher,
const char *cipher_mode,
const char *uuid,
const char *volume_key,
size_t volume_key_size,
void *params)
{
return _crypt_format(cd, type, cipher, cipher_mode, uuid, volume_key, volume_key_size, params, true);
}
#if defined(__GNUC__)
#define CRYPT_EXPORT_SYMBOL(func, maj, min) \
__asm__(".symver " #func "_v" #maj "_" #min ", " #func "@CRYPTSETUP_" #maj "." #min)
int crypt_format_v2_0(struct crypt_device *cd, const char *type, const char *cipher,
const char *cipher_mode,const char *uuid, const char *volume_key,
size_t volume_key_size, void *params);
int crypt_format_v2_0(struct crypt_device *cd, const char *type, const char *cipher,
const char *cipher_mode,const char *uuid, const char *volume_key,
size_t volume_key_size, void *params)
{
return _crypt_format(cd, type, cipher, cipher_mode, uuid, volume_key, volume_key_size, params, false);
}
CRYPT_EXPORT_SYMBOL(crypt_format, 2, 0);
#endif
int crypt_repair(struct crypt_device *cd,
const char *requested_type,
void *params __attribute__((unused)))

View File

@@ -112,6 +112,23 @@ static size_t device_block_size_fd(int fd, size_t *min_size)
return bsize;
}
static size_t device_block_phys_size_fd(int fd)
{
struct stat st;
int arg;
size_t bsize = SECTOR_SIZE;
if (fstat(fd, &st) < 0)
return bsize;
if (S_ISREG(st.st_mode))
bsize = MAX_SECTOR_SIZE;
else if (ioctl(fd, BLKPBSZGET, &arg) >= 0)
bsize = (size_t)arg;
return bsize;
}
static size_t device_alignment_fd(int devfd)
{
long alignment = DEFAULT_MEM_ALIGNMENT;
@@ -570,6 +587,42 @@ size_t device_block_size(struct crypt_device *cd, struct device *device)
return device->block_size;
}
size_t device_optimal_encryption_sector_size(struct crypt_device *cd, struct device *device)
{
int fd;
size_t phys_block_size;
if (!device)
return SECTOR_SIZE;
fd = open(device->file_path ?: device->path, O_RDONLY);
if (fd < 0) {
log_dbg(cd, "Cannot get optimal encryption sector size for device %s.", device_path(device));
return SECTOR_SIZE;
}
/* cache device block size */
device->block_size = device_block_size_fd(fd, NULL);
if (!device->block_size) {
close(fd);
log_dbg(cd, "Cannot get block size for device %s.", device_path(device));
return SECTOR_SIZE;
}
if (device->block_size >= MAX_SECTOR_SIZE) {
close(fd);
return MISALIGNED(device->block_size, MAX_SECTOR_SIZE) ? SECTOR_SIZE : MAX_SECTOR_SIZE;
}
phys_block_size = device_block_phys_size_fd(fd);
close(fd);
if (device->block_size >= phys_block_size || phys_block_size <= SECTOR_SIZE || phys_block_size > MAX_SECTOR_SIZE || MISALIGNED(phys_block_size, device->block_size))
return device->block_size;
return phys_block_size;
}
int device_read_ahead(struct device *device, uint32_t *read_ahead)
{
int fd, r = 0;

View File

@@ -1328,8 +1328,15 @@ 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.
and in range 512 - 4096 bytes. This option is available only in the LUKS2
or plain modes.
The default for plain mode is 512 bytes. For LUKS2 devices it's established
during luksFormat operation based on parameters provided by underlying data device.
For native 4K block devices it's 4096 bytes. For 4K/512e (4K physical sector size
with 512 bytes emulation) it's 4096 bytes. For drives reporting only 512 bytes
block size it remains 512 bytes. If data device is regular file put in filesystem
it's 4096 bytes.
Note that if sector size is higher than underlying device hardware sector
and there is not integrity protection that uses data journal, using

View File

@@ -1288,7 +1288,7 @@ static int _luksFormat(struct crypt_device **r_cd, char **r_password, size_t *r_
struct crypt_params_luks2 params2 = {
.data_alignment = params1.data_alignment,
.data_device = params1.data_device,
.sector_size = ARG_UINT32(OPT_SECTOR_SIZE_ID) ?: SECTOR_SIZE,
.sector_size = ARG_UINT32(OPT_SECTOR_SIZE_ID),
.label = ARG_STR(OPT_LABEL_ID),
.subsystem = ARG_STR(OPT_SUBSYSTEM_ID)
};

View File

@@ -711,7 +711,7 @@ echo $PWD1 | $CRYPTSETUP luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV --head
echo $PWD1 | $CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV --header $HEADER_IMG --align-payload 8192 || fail
echo $PWD1 | $CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV --header $HEADER_IMG --align-payload 4096 >/dev/null || fail
$CRYPTSETUP luksDump $HEADER_IMG | grep -e "0: crypt" -A1 | grep -qe $((4096*512)) || fail
echo $PWD1 | $CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV --header $HEADER_IMG --align-payload 0 || fail
echo $PWD1 | $CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV --header $HEADER_IMG --align-payload 0 --sector-size 512 || fail
echo $PWD1 | $CRYPTSETUP luksOpen $LOOPDEV-missing --header $HEADER_IMG $DEV_NAME 2>/dev/null && fail
echo $PWD1 | $CRYPTSETUP luksOpen $LOOPDEV --header $HEADER_IMG $DEV_NAME || fail
echo $PWD1 | $CRYPTSETUP -q resize $DEV_NAME --size 100 --header $HEADER_IMG || fail

View File

@@ -917,7 +917,7 @@ $CRYPTSETUP close $DEV_NAME || fail
$CRYPTSETUP close $DEV_NAME2 || fail
echo "[5] Decryption with detached header"
echo $PWD1 | $CRYPTSETUP luksFormat --type luks2 -c aes-cbc-essiv:sha256 -s 128 --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV || fail
echo $PWD1 | $CRYPTSETUP luksFormat --type luks2 --sector-size 512 -c aes-cbc-essiv:sha256 -s 128 --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV || fail
wipe $PWD1 $IMG_HDR
echo $PWD1 | $CRYPTSETUP reencrypt -q --decrypt --header $IMG_HDR $DEV || fail
check_hash_dev $DEV $HASH3
@@ -935,7 +935,7 @@ echo $PWD1 | $CRYPTSETUP reencrypt -q --decrypt --resilience checksum --header $
check_hash_dev $DEV $HASH3
# check deferred remove works as expected after decryption
echo $PWD1 | $CRYPTSETUP luksFormat --type luks2 -c serpent-xts-plain --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV || fail
echo $PWD1 | $CRYPTSETUP luksFormat --type luks2 --sector-size 512 -c serpent-xts-plain --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV || fail
open_crypt $PWD1 $IMG_HDR
dmsetup create $DEV_NAME2 --table "0 1 linear /dev/mapper/$DEV_NAME 0" || fail
echo $PWD1 | $CRYPTSETUP reencrypt -q --decrypt --resilience checksum --header $IMG_HDR --active-name $DEV_NAME || fail

View File

@@ -462,7 +462,7 @@ check_hash $PWD1 $HASH1
$CRYPTSETUP -q convert --type luks2 $IMG || fail
echo $PWD1 | $REENC $IMG -q $FAST_PBKDF_PBKDF2 || fail
check_hash $PWD1 $HASH1
echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 $FAST_PBKDF_PBKDF2 $IMG --offset 8192 || fail
echo $PWD1 | $CRYPTSETUP -q luksFormat --sector-size 512 --type luks2 $FAST_PBKDF_PBKDF2 $IMG --offset 8192 || fail
wipe $PWD1
check_hash $PWD1 $HASH5
$CRYPTSETUP -q convert --type luks1 $IMG || fail