Files
cryptsetup/lib/luks2/luks2_json_format.c
2017-09-27 10:18:38 +02:00

234 lines
7.6 KiB
C

/*
* 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 = (uint64_t)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;
}