Commit fd02dca6 authored by Milan Broz's avatar Milan Broz

Add crypt_set_metadata_size / crypt_get_metadata_size API.

parent 2a1d58ed
......@@ -345,6 +345,39 @@ int crypt_memory_lock(struct crypt_device *cd, int lock);
* In current version locking can be only switched off and cannot be switched on later.
*/
int crypt_metadata_locking(struct crypt_device *cd, int enable);
/**
* Set metadata header area sizes. This applies only to LUKS2.
* These values limit amount of metadata anf number of supportable keyslots.
*
* @param cd crypt device handle, can be @e NULL
* @param metadata_size size in bytes of JSON area + 4k binary header
* @param keyslots_size size in bytes of binary keyslots area
*
* @returns @e 0 on success or negative errno value otherwise.
*
* @note The metadata area is stored twice and both copies contain 4k binary header.
* Only 16,32,64,128,256,512,1024,2048 and 4096 kB value is allowed (see LUKS2 specification).
* @note Keyslots area size must be multiple of 4k with maximum 128MB.
*/
int crypt_set_metadata_size(struct crypt_device *cd,
uint64_t metadata_size,
uint64_t keyslots_size);
/**
* Get metadata header area sizes. This applies only to LUKS2.
* These values limit amount of metadata anf number of supportable keyslots.
*
* @param cd crypt device handle
* @param metadata_size size in bytes of JSON area + 4k binary header
* @param keyslots_size size in bytes of binary keyslots area
*
* @returns @e 0 on success or negative errno value otherwise.
*/
int crypt_get_metadata_size(struct crypt_device *cd,
uint64_t *metadata_size,
uint64_t *keyslots_size);
/** @} */
/**
......
......@@ -75,6 +75,8 @@ CRYPTSETUP_2.0 {
crypt_get_volume_key_size;
crypt_get_device_name;
crypt_get_metadata_device_name;
crypt_get_metadata_size;
crypt_set_metadata_size;
crypt_get_verity_info;
crypt_get_sector_size;
......
......@@ -37,23 +37,6 @@
#include "af.h"
#include "internal.h"
/* Get size of struct luks_phdr with all keyslots material space */
static size_t LUKS_calculate_device_sectors(size_t keyLen)
{
size_t keyslot_sectors, sector;
int i;
keyslot_sectors = AF_split_sectors(keyLen, LUKS_STRIPES);
sector = LUKS_ALIGN_KEYSLOTS / SECTOR_SIZE;
for (i = 0; i < LUKS_NUMKEYS; i++) {
sector = size_round_up(sector, LUKS_ALIGN_KEYSLOTS / SECTOR_SIZE);
sector += keyslot_sectors;
}
return sector;
}
int LUKS_keyslot_area(const struct luks_phdr *hdr,
int keyslot,
uint64_t *offset,
......@@ -416,8 +399,8 @@ static int _keyslot_repair(struct luks_phdr *phdr, struct crypt_device *ctx)
/* cipherName, cipherMode, hashSpec, uuid are already null terminated */
/* payloadOffset - cannot check */
r = LUKS_generate_phdr(&temp_phdr, vk, phdr->cipherName, phdr->cipherMode,
phdr->hashSpec,phdr->uuid, LUKS_STRIPES,
phdr->payloadOffset * SECTOR_SIZE, 0, true, ctx);
phdr->hashSpec, phdr->uuid,
phdr->payloadOffset * SECTOR_SIZE, 0, 0, ctx);
if (r < 0)
goto out;
......@@ -721,29 +704,52 @@ int LUKS_check_cipher(struct crypt_device *ctx, size_t keylength, const char *ci
}
int LUKS_generate_phdr(struct luks_phdr *header,
const struct volume_key *vk,
const char *cipherName, const char *cipherMode, const char *hashSpec,
const char *uuid, unsigned int stripes,
uint64_t data_offset,
uint64_t align_offset,
bool fixed_data_offset,
struct crypt_device *ctx)
const struct volume_key *vk,
const char *cipherName,
const char *cipherMode,
const char *hashSpec,
const char *uuid,
uint64_t data_offset, /* in bytes */
uint64_t align_offset, /* in bytes */
uint64_t required_alignment, /* in bytes */
struct crypt_device *ctx)
{
unsigned int i = 0, alignPayload, alignOffset, hdr_sectors = LUKS_calculate_device_sectors(vk->keylength);
size_t blocksPerStripeSet, currentSector;
int r;
int i, r;
size_t keyslot_sectors, header_sectors;
uuid_t partitionUuid;
struct crypt_pbkdf_type *pbkdf;
double PBKDF2_temp;
char luksMagic[] = LUKS_MAGIC;
if (data_offset % SECTOR_SIZE || align_offset % SECTOR_SIZE)
if (data_offset % SECTOR_SIZE || align_offset % SECTOR_SIZE ||
required_alignment % SECTOR_SIZE)
return -EINVAL;
alignPayload = data_offset / SECTOR_SIZE;
alignOffset = align_offset / SECTOR_SIZE;
if (alignPayload && fixed_data_offset && alignPayload < hdr_sectors) {
log_err(ctx, _("Data offset for detached LUKS header must be "
memset(header, 0, sizeof(struct luks_phdr));
keyslot_sectors = AF_split_sectors(vk->keylength, LUKS_STRIPES);
header_sectors = LUKS_ALIGN_KEYSLOTS / SECTOR_SIZE;
for (i = 0; i < LUKS_NUMKEYS; i++) {
header->keyblock[i].active = LUKS_KEY_DISABLED;
header->keyblock[i].keyMaterialOffset = header_sectors;
header->keyblock[i].stripes = LUKS_STRIPES;
header_sectors = size_round_up(header_sectors + keyslot_sectors,
LUKS_ALIGN_KEYSLOTS / SECTOR_SIZE);
}
/* In sector is now size of all keyslot material space */
/* Data offset has priority */
if (data_offset)
header->payloadOffset = data_offset / SECTOR_SIZE;
else if (required_alignment) {
header->payloadOffset = size_round_up(header_sectors, (required_alignment / SECTOR_SIZE));
header->payloadOffset += (align_offset / SECTOR_SIZE);
} else
header->payloadOffset = 0;
if (header->payloadOffset && header->payloadOffset < header_sectors) {
log_err(ctx, _("Data offset for LUKS header must be "
"either 0 or higher than header size."));
return -EINVAL;
}
......@@ -760,8 +766,6 @@ int LUKS_generate_phdr(struct luks_phdr *header,
if (!uuid)
uuid_generate(partitionUuid);
memset(header,0,sizeof(struct luks_phdr));
/* Set Magic */
memcpy(header->magic,luksMagic,LUKS_MAGIC_L);
header->version=1;
......@@ -799,31 +803,12 @@ int LUKS_generate_phdr(struct luks_phdr *header,
header->mkDigestSalt, LUKS_SALTSIZE,
header->mkDigest,LUKS_DIGESTSIZE,
header->mkDigestIterations, 0, 0);
if(r < 0) {
if (r < 0) {
log_err(ctx, _("Cannot create LUKS header: header digest failed (using hash %s)."),
header->hashSpec);
return r;
}
currentSector = LUKS_ALIGN_KEYSLOTS / SECTOR_SIZE;
blocksPerStripeSet = AF_split_sectors(vk->keylength, stripes);
for(i = 0; i < LUKS_NUMKEYS; ++i) {
header->keyblock[i].active = LUKS_KEY_DISABLED;
header->keyblock[i].keyMaterialOffset = currentSector;
header->keyblock[i].stripes = stripes;
currentSector = size_round_up(currentSector + blocksPerStripeSet,
LUKS_ALIGN_KEYSLOTS / SECTOR_SIZE);
}
if (fixed_data_offset) {
/* for separate metadata device use alignPayload directly */
header->payloadOffset = alignPayload;
} else {
/* alignOffset - offset from natural device alignment provided by topology info */
currentSector = size_round_up(currentSector, alignPayload);
header->payloadOffset = currentSector + alignOffset;
}
uuid_unparse(partitionUuid, header->uuid);
log_dbg(ctx, "Data offset %d, UUID %s, digest iterations %" PRIu32,
......
......@@ -26,7 +26,6 @@
* LUKS partition header
*/
#include <stdbool.h>
#include "libcryptsetup.h"
#define LUKS_CIPHERNAME_L 32
......@@ -108,17 +107,15 @@ int LUKS_check_cipher(struct crypt_device *ctx,
const char *cipher,
const char *cipher_mode);
int LUKS_generate_phdr(
struct luks_phdr *header,
int LUKS_generate_phdr(struct luks_phdr *header,
const struct volume_key *vk,
const char *cipherName,
const char *cipherMode,
const char *hashSpec,
const char *uuid,
unsigned int stripes,
uint64_t data_offset,
uint64_t align_offset,
bool fixed_data_offset,
uint64_t required_alignment,
struct crypt_device *ctx);
int LUKS_read_phdr(
......
......@@ -22,7 +22,6 @@
#ifndef _CRYPTSETUP_LUKS2_ONDISK_H
#define _CRYPTSETUP_LUKS2_ONDISK_H
#include <stdbool.h>
#include "libcryptsetup.h"
#define LUKS2_MAGIC_1ST "LUKS\xba\xbe"
......@@ -126,7 +125,7 @@ struct luks2_keyslot_params {
#define LUKS2_HDR_BIN_LEN sizeof(struct luks2_hdr_disk)
#define LUKS2_HDR_DEFAULT_LEN 0x400000 /* 4 MiB */
#define LUKS2_DEFAULT_KEYSLOTS_SIZE (0x400000 - 2*LUKS2_HDR_16K_LEN) /* 4 MiB */
#define LUKS2_MAX_KEYSLOTS_SIZE 0x8000000 /* 128 MiB */
......@@ -162,6 +161,7 @@ int LUKS2_hdr_restore(struct crypt_device *cd,
uint64_t LUKS2_hdr_and_areas_size(json_object *jobj);
uint64_t LUKS2_keyslots_size(json_object *jobj);
uint64_t LUKS2_metadata_size(json_object *jobj);
int LUKS2_keyslot_cipher_incompatible(struct crypt_device *cd);
......@@ -326,7 +326,9 @@ int LUKS2_generate_hdr(
unsigned int sector_size,
uint64_t data_offset,
uint64_t align_offset,
bool fixed_data_offset);
uint64_t required_alignment,
uint64_t metadata_size,
uint64_t keyslots_size);
int LUKS2_check_metadata_area_size(uint64_t metadata_size);
int LUKS2_check_keyslots_area_size(uint64_t keyslots_size);
......
......@@ -141,15 +141,32 @@ int LUKS2_generate_hdr(
unsigned int sector_size, /* in bytes */
uint64_t data_offset, /* in bytes */
uint64_t align_offset, /* in bytes */
bool fixed_data_offset)
uint64_t required_alignment,
uint64_t metadata_size,
uint64_t keyslots_size)
{
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;
if (!metadata_size)
metadata_size = LUKS2_HDR_16K_LEN;
hdr->hdr_size = metadata_size;
if (!keyslots_size)
keyslots_size = LUKS2_DEFAULT_KEYSLOTS_SIZE;
/* Decrease keyslots_size if we have smaller data_offset */
if (data_offset && (keyslots_size + get_min_offset(hdr)) > data_offset)
keyslots_size = data_offset - get_min_offset(hdr);
/* Data offset has priority */
if (!data_offset && required_alignment) {
data_offset = size_round_up(get_min_offset(hdr) + keyslots_size, (size_t)required_alignment);
data_offset += align_offset;
}
hdr->seqid = 1;
hdr->version = 2;
memset(hdr->label, 0, LUKS2_LABEL_L);
......@@ -197,16 +214,7 @@ int LUKS2_generate_hdr(
jobj_segment = json_object_new_object();
json_object_object_add(jobj_segment, "type", json_object_new_string("crypt"));
if (fixed_data_offset)
offset = data_offset;
else {
//FIXME
//offset = size_round_up(areas[7].offset + areas[7].length, alignPayload * SECTOR_SIZE);
offset = size_round_up(LUKS2_HDR_DEFAULT_LEN, (size_t)data_offset);
offset += align_offset;
}
json_object_object_add(jobj_segment, "offset", json_object_new_uint64(offset));
json_object_object_add(jobj_segment, "offset", json_object_new_uint64(data_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));
......@@ -223,23 +231,7 @@ int LUKS2_generate_hdr(
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_uint64(json_size));
/* for detached metadata device compute reasonable keyslot areas size */
// FIXME: this is coupled with FIXME above
if (fixed_data_offset && !offset)
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, "json_size", json_object_new_uint64(metadata_size - LUKS2_HDR_BIN_LEN));
json_object_object_add(jobj_config, "keyslots_size", json_object_new_uint64(keyslots_size));
JSON_DBG(cd, hdr->jobj, "Header JSON");
......
......@@ -633,7 +633,7 @@ static int hdr_validate_segments(struct crypt_device *cd, json_object *hdr_jobj)
return 0;
}
static uint64_t LUKS2_metadata_size(json_object *jobj)
uint64_t LUKS2_metadata_size(json_object *jobj)
{
json_object *jobj1, *jobj2;
uint64_t json_size;
......
......@@ -55,6 +55,8 @@ struct crypt_device {
unsigned key_in_keyring:1;
uint64_t data_offset;
uint64_t metadata_size; /* Used in LUKS2 format */
uint64_t keyslots_size; /* Used in LUKS2 format */
// FIXME: private binary headers and access it properly
// through sub-library (LUKS1, TCRYPT)
......@@ -102,7 +104,6 @@ struct crypt_device {
char cipher[MAX_CIPHER_LEN];
char cipher_mode[MAX_CIPHER_LEN];
unsigned int key_size;
unsigned int veracrypt_pim;
} none;
} u;
......@@ -362,6 +363,8 @@ static void crypt_set_null_type(struct crypt_device *cd)
cd->type = NULL;
cd->u.none.active_name = NULL;
cd->data_offset = 0;
cd->metadata_size = 0;
cd->keyslots_size = 0;
}
static void crypt_reset_null_type(struct crypt_device *cd)
......@@ -949,6 +952,8 @@ int crypt_load(struct crypt_device *cd,
crypt_reset_null_type(cd);
cd->data_offset = 0;
cd->metadata_size = 0;
cd->keyslots_size = 0;
if (!requested_type || isLUKS1(requested_type) || isLUKS2(requested_type)) {
if (cd->type && !isLUKS1(cd->type) && !isLUKS2(cd->type)) {
......@@ -1500,18 +1505,10 @@ static int _crypt_format_luks1(struct crypt_device *cd,
if (r < 0)
return r;
/* New data_offset has priority */
if (cd->data_offset) {
required_alignment = cd->data_offset * SECTOR_SIZE;
/* Check overflow */
if (required_alignment != (cd->data_offset * (uint64_t)SECTOR_SIZE))
return -EINVAL;
}
r = LUKS_generate_phdr(&cd->u.luks1.hdr, cd->volume_key, cipher, cipher_mode,
cd->pbkdf.hash, uuid, LUKS_STRIPES,
required_alignment, alignment_offset,
(cd->data_offset || cd->metadata_device), cd);
cd->pbkdf.hash, uuid,
cd->data_offset * SECTOR_SIZE,
alignment_offset, required_alignment, cd);
if (r < 0)
return r;
......@@ -1660,23 +1657,14 @@ static int _crypt_format_luks2(struct crypt_device *cd,
goto out;
}
/* New data_offset has priority */
if (cd->data_offset) {
required_alignment = cd->data_offset * SECTOR_SIZE;
/* Check overflow */
if (required_alignment != (cd->data_offset * (uint64_t)SECTOR_SIZE)) {
r = -EINVAL;
goto out;
}
}
r = LUKS2_generate_hdr(cd, &cd->u.luks2.hdr, cd->volume_key,
cipher, cipher_mode,
integrity, uuid,
sector_size,
required_alignment,
cd->data_offset * SECTOR_SIZE,
alignment_offset,
(cd->data_offset || cd->metadata_device));
required_alignment,
cd->metadata_size, cd->keyslots_size);
if (r < 0)
goto out;
......@@ -3970,6 +3958,57 @@ int crypt_set_data_offset(struct crypt_device *cd, uint64_t data_offset)
return 0;
}
int crypt_set_metadata_size(struct crypt_device *cd,
uint64_t metadata_size,
uint64_t keyslots_size)
{
if (!cd)
return -EINVAL;
if (cd->type && !isLUKS2(cd->type))
return -EINVAL;
if (metadata_size && LUKS2_check_metadata_area_size(metadata_size))
return -EINVAL;
if (keyslots_size && LUKS2_check_keyslots_area_size(keyslots_size))
return -EINVAL;
cd->metadata_size = metadata_size;
cd->keyslots_size = keyslots_size;
return 0;
}
int crypt_get_metadata_size(struct crypt_device *cd,
uint64_t *metadata_size,
uint64_t *keyslots_size)
{
uint64_t msize, ksize;
if (!cd)
return -EINVAL;
if (!cd->type) {
msize = cd->metadata_size;
ksize = cd->keyslots_size;
} else if (isLUKS1(cd->type)) {
msize = LUKS_ALIGN_KEYSLOTS;
ksize = LUKS_device_sectors(&cd->u.luks1.hdr) * SECTOR_SIZE - msize;
} else if (isLUKS2(cd->type)) {
msize = LUKS2_metadata_size(cd->u.luks2.hdr.jobj);
ksize = LUKS2_keyslots_size(cd->u.luks2.hdr.jobj);
} else
return -EINVAL;
if (metadata_size)
*metadata_size = msize;
if (keyslots_size)
*keyslots_size = ksize;
return 0;
}
uint64_t crypt_get_data_offset(struct crypt_device *cd)
{
if (!cd)
......
......@@ -579,6 +579,7 @@ static void AddDeviceLuks2(void)
const char *cipher = "aes";
const char *cipher_mode = "cbc-essiv:sha256";
uint64_t r_payload_offset, r_header_size, r_size_1;
uint64_t mdata_size, keyslots_size;
crypt_decode_key(key, mk_hex, key_size);
crypt_decode_key(key3, mk_hex2, key_size);
......@@ -588,6 +589,47 @@ static void AddDeviceLuks2(void)
OK_(create_dmdevice_over_loop(H_DEVICE, r_header_size));
OK_(create_dmdevice_over_loop(H_DEVICE_WRONG, r_header_size - 1));
//default metadata sizes
OK_(crypt_init(&cd, DMDIR H_DEVICE));
OK_(crypt_get_metadata_size(cd, &mdata_size, &keyslots_size));
EQ_(mdata_size, 0);
EQ_(keyslots_size, 0);
OK_(crypt_set_metadata_size(cd, 0, 0));
OK_(crypt_get_metadata_size(cd, &mdata_size, &keyslots_size));
EQ_(mdata_size, 0);
EQ_(keyslots_size, 0);
OK_(crypt_set_metadata_size(cd, 0x004000, 0x004000));
OK_(crypt_get_metadata_size(cd, &mdata_size, &keyslots_size));
EQ_(mdata_size, 0x004000);
EQ_(keyslots_size, 0x004000);
OK_(crypt_set_metadata_size(cd, 0x008000, 0x008000));
OK_(crypt_get_metadata_size(cd, &mdata_size, &keyslots_size));
EQ_(mdata_size, 0x008000);
EQ_(keyslots_size, 0x008000);
FAIL_(crypt_set_metadata_size(cd, 0x008001, 0x008000), "Wrong size");
FAIL_(crypt_set_metadata_size(cd, 0x008000, 0x008001), "Wrong size");
crypt_free(cd);
// metadata settings
OK_(crypt_init(&cd, DMDIR H_DEVICE));
OK_(crypt_set_metadata_size(cd, 0x080000, 0x080000));
OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params));
EQ_(crypt_keyslot_add_by_volume_key(cd, 7, key, key_size, passphrase, strlen(passphrase)), 7);
crypt_free(cd);
OK_(crypt_init(&cd, DMDIR H_DEVICE));
OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
OK_(crypt_get_metadata_size(cd, &mdata_size, &keyslots_size));
EQ_(mdata_size, 0x080000);
EQ_(keyslots_size, 0x080000);
crypt_free(cd);
// default
OK_(crypt_init(&cd, DMDIR H_DEVICE));
OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params));
OK_(crypt_get_metadata_size(cd, &mdata_size, &keyslots_size));
EQ_(mdata_size, 0x04000);
EQ_(keyslots_size, 0x3f8000);
crypt_free(cd);
// format
OK_(crypt_init(&cd, DMDIR H_DEVICE_WRONG));
params.data_alignment = 0;
......
......@@ -1144,6 +1144,7 @@ static void LuksHeaderLoad(void)
const char *cipher = "aes";
const char *cipher_mode = "cbc-essiv:sha256";
uint64_t r_payload_offset, r_header_size;
uint64_t mdata_size, keyslots_size;
crypt_decode_key(key, mk_hex, key_size);
......@@ -1201,6 +1202,10 @@ static void LuksHeaderLoad(void)
params.data_device = NULL;
OK_(crypt_init(&cd, DMDIR L_DEVICE_0S));
OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params));
FAIL_(crypt_set_metadata_size(cd, 0x004000, 0x004000), "Wrong context type");
OK_(crypt_get_metadata_size(cd, &mdata_size, &keyslots_size));
EQ_(mdata_size, LUKS_ALIGN_KEYSLOTS);
EQ_(keyslots_size, r_header_size * SECTOR_SIZE - mdata_size);
crypt_free(cd);
// load should be ok
OK_(crypt_init(&cd, DMDIR L_DEVICE_0S));
......@@ -1222,6 +1227,8 @@ static void LuksHeaderLoad(void)
OK_(crypt_init(&cd, DMDIR H_DEVICE));
OK_(crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, key, key_size, &pl_params));
FAIL_(crypt_load(cd, CRYPT_LUKS1, NULL), "Can't load over nonLUKS device type");
FAIL_(crypt_set_metadata_size(cd, 0x004000, 0x004000), "Wrong context type");
FAIL_(crypt_get_metadata_size(cd, &mdata_size, &keyslots_size), "Wrong context type");
crypt_free(cd);
/* check load sets proper device type */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment