/*
* VAS_EBOOT -- GRand Unified Bootloader
* Copyright (C) 2019 Free Software Foundation, Inc.
*
* VAS_EBOOT 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 3 of the License, or
* (at your option) any later version.
*
* VAS_EBOOT 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 VAS_EBOOT. If not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
VAS_EBOOT_MOD_LICENSE ("GPLv3+");
#define LUKS_MAGIC_1ST "LUKS\xBA\xBE"
#define LUKS_MAGIC_2ND "SKUL\xBA\xBE"
enum VasEBoot_luks2_kdf_type
{
LUKS2_KDF_TYPE_ARGON2I,
LUKS2_KDF_TYPE_ARGON2ID,
LUKS2_KDF_TYPE_PBKDF2
};
typedef enum VasEBoot_luks2_kdf_type VasEBoot_luks2_kdf_type_t;
/* On disk LUKS header */
struct VasEBoot_luks2_header
{
char magic[6];
VasEBoot_uint16_t version;
VasEBoot_uint64_t hdr_size;
VasEBoot_uint64_t seqid;
char label[48];
char csum_alg[32];
VasEBoot_uint8_t salt[64];
char uuid[40];
char subsystem[48];
VasEBoot_uint64_t hdr_offset;
char _padding[184];
VasEBoot_uint8_t csum[64];
char _padding4096[7*512];
} VAS_EBOOT_PACKED;
typedef struct VasEBoot_luks2_header VasEBoot_luks2_header_t;
struct VasEBoot_luks2_keyslot
{
/* The integer key to the associative array of keyslots. */
VasEBoot_uint64_t idx;
VasEBoot_int64_t key_size;
VasEBoot_int64_t priority;
struct
{
const char *encryption;
VasEBoot_uint64_t offset;
VasEBoot_uint64_t size;
VasEBoot_int64_t key_size;
} area;
struct
{
const char *hash;
VasEBoot_int64_t stripes;
} af;
struct
{
VasEBoot_luks2_kdf_type_t type;
const char *salt;
union
{
struct
{
VasEBoot_int64_t time;
VasEBoot_int64_t memory;
VasEBoot_int64_t cpus;
} argon2i;
struct
{
const char *hash;
VasEBoot_int64_t iterations;
} pbkdf2;
} u;
} kdf;
};
typedef struct VasEBoot_luks2_keyslot VasEBoot_luks2_keyslot_t;
struct VasEBoot_luks2_segment
{
VasEBoot_uint64_t idx;
VasEBoot_uint64_t offset;
const char *size;
const char *encryption;
VasEBoot_int64_t sector_size;
};
typedef struct VasEBoot_luks2_segment VasEBoot_luks2_segment_t;
struct VasEBoot_luks2_digest
{
VasEBoot_uint64_t idx;
/* Both keyslots and segments are interpreted as bitfields here */
VasEBoot_uint64_t keyslots;
VasEBoot_uint64_t segments;
const char *salt;
const char *digest;
const char *hash;
VasEBoot_int64_t iterations;
};
typedef struct VasEBoot_luks2_digest VasEBoot_luks2_digest_t;
gcry_err_code_t AF_merge (const gcry_md_spec_t * hash, VasEBoot_uint8_t * src,
VasEBoot_uint8_t * dst, VasEBoot_size_t blocksize,
VasEBoot_size_t blocknumbers);
static VasEBoot_err_t
luks2_parse_keyslot (VasEBoot_luks2_keyslot_t *out, const VasEBoot_json_t *keyslot)
{
VasEBoot_json_t area, af, kdf;
const char *type;
if (VasEBoot_json_getstring (&type, keyslot, "type"))
return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, "Missing or invalid keyslot");
else if (VasEBoot_strcmp (type, "luks2"))
return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, "Unsupported keyslot type %s", type);
else if (VasEBoot_json_getint64 (&out->key_size, keyslot, "key_size"))
return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, "Missing keyslot information");
if (VasEBoot_json_getint64 (&out->priority, keyslot, "priority"))
out->priority = 1;
if (VasEBoot_json_getvalue (&area, keyslot, "area") ||
VasEBoot_json_getstring (&type, &area, "type"))
return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, "Missing or invalid key area");
else if (VasEBoot_strcmp (type, "raw"))
return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, "Unsupported key area type: %s", type);
else if (VasEBoot_json_getuint64 (&out->area.offset, &area, "offset") ||
VasEBoot_json_getuint64 (&out->area.size, &area, "size") ||
VasEBoot_json_getstring (&out->area.encryption, &area, "encryption") ||
VasEBoot_json_getint64 (&out->area.key_size, &area, "key_size"))
return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, "Missing key area information");
if (VasEBoot_json_getvalue (&kdf, keyslot, "kdf") ||
VasEBoot_json_getstring (&type, &kdf, "type") ||
VasEBoot_json_getstring (&out->kdf.salt, &kdf, "salt"))
return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, "Missing or invalid KDF");
else if (!VasEBoot_strcmp (type, "argon2i"))
{
out->kdf.type = LUKS2_KDF_TYPE_ARGON2I;
if (VasEBoot_json_getint64 (&out->kdf.u.argon2i.time, &kdf, "time") ||
VasEBoot_json_getint64 (&out->kdf.u.argon2i.memory, &kdf, "memory") ||
VasEBoot_json_getint64 (&out->kdf.u.argon2i.cpus, &kdf, "cpus"))
return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, "missing Argon2i parameters");
}
else if (!VasEBoot_strcmp (type, "argon2id"))
{
out->kdf.type = LUKS2_KDF_TYPE_ARGON2ID;
if (VasEBoot_json_getint64 (&out->kdf.u.argon2i.time, &kdf, "time") ||
VasEBoot_json_getint64 (&out->kdf.u.argon2i.memory, &kdf, "memory") ||
VasEBoot_json_getint64 (&out->kdf.u.argon2i.cpus, &kdf, "cpus"))
return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, "missing Argon2id parameters");
}
else if (!VasEBoot_strcmp (type, "pbkdf2"))
{
out->kdf.type = LUKS2_KDF_TYPE_PBKDF2;
if (VasEBoot_json_getstring (&out->kdf.u.pbkdf2.hash, &kdf, "hash") ||
VasEBoot_json_getint64 (&out->kdf.u.pbkdf2.iterations, &kdf, "iterations"))
return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, "Missing PBKDF2 parameters");
}
else
return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, "Unsupported KDF type %s", type);
if (VasEBoot_json_getvalue (&af, keyslot, "af") ||
VasEBoot_json_getstring (&type, &af, "type"))
return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, "missing or invalid area");
if (VasEBoot_strcmp (type, "luks1"))
return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, "Unsupported AF type %s", type);
if (VasEBoot_json_getint64 (&out->af.stripes, &af, "stripes") ||
VasEBoot_json_getstring (&out->af.hash, &af, "hash"))
return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, "Missing AF parameters");
return VAS_EBOOT_ERR_NONE;
}
static VasEBoot_err_t
luks2_parse_segment (VasEBoot_luks2_segment_t *out, const VasEBoot_json_t *segment)
{
const char *type;
if (VasEBoot_json_getstring (&type, segment, "type"))
return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, "Invalid segment type");
else if (VasEBoot_strcmp (type, "crypt"))
return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, "Unsupported segment type %s", type);
if (VasEBoot_json_getuint64 (&out->offset, segment, "offset") ||
VasEBoot_json_getstring (&out->size, segment, "size") ||
VasEBoot_json_getstring (&out->encryption, segment, "encryption") ||
VasEBoot_json_getint64 (&out->sector_size, segment, "sector_size"))
return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, "Missing segment parameters");
return VAS_EBOOT_ERR_NONE;
}
static VasEBoot_err_t
luks2_parse_digest (VasEBoot_luks2_digest_t *out, const VasEBoot_json_t *digest)
{
VasEBoot_json_t segments, keyslots, o;
VasEBoot_size_t i, size;
VasEBoot_uint64_t bit;
const char *type;
if (VasEBoot_json_getstring (&type, digest, "type"))
return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, "Invalid digest type");
else if (VasEBoot_strcmp (type, "pbkdf2"))
return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, "Unsupported digest type %s", type);
if (VasEBoot_json_getvalue (&segments, digest, "segments") ||
VasEBoot_json_getvalue (&keyslots, digest, "keyslots") ||
VasEBoot_json_getstring (&out->salt, digest, "salt") ||
VasEBoot_json_getstring (&out->digest, digest, "digest") ||
VasEBoot_json_getstring (&out->hash, digest, "hash") ||
VasEBoot_json_getint64 (&out->iterations, digest, "iterations"))
return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, "Missing digest parameters");
if (VasEBoot_json_getsize (&size, &segments))
return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT,
"Digest references no segments");
out->segments = 0;
for (i = 0; i < size; i++)
{
if (VasEBoot_json_getchild (&o, &segments, i) ||
VasEBoot_json_getuint64 (&bit, &o, NULL))
return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, "Invalid segment");
out->segments |= (1 << bit);
}
if (VasEBoot_json_getsize (&size, &keyslots))
return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT,
"Digest references no keyslots");
out->keyslots = 0;
for (i = 0; i < size; i++)
{
if (VasEBoot_json_getchild (&o, &keyslots, i) ||
VasEBoot_json_getuint64 (&bit, &o, NULL))
return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, "Invalid keyslot");
out->keyslots |= (1 << bit);
}
return VAS_EBOOT_ERR_NONE;
}
static VasEBoot_err_t
luks2_get_keyslot (VasEBoot_luks2_keyslot_t *k, VasEBoot_luks2_digest_t *d, VasEBoot_luks2_segment_t *s,
const VasEBoot_json_t *root, VasEBoot_size_t keyslot_json_idx)
{
VasEBoot_json_t keyslots, keyslot, digests, digest, segments, segment;
VasEBoot_size_t json_idx, size;
/* Get nth keyslot */
if (VasEBoot_json_getvalue (&keyslots, root, "keyslots") ||
VasEBoot_json_getchild (&keyslot, &keyslots, keyslot_json_idx) ||
VasEBoot_json_getuint64 (&k->idx, &keyslot, NULL) ||
VasEBoot_json_getchild (&keyslot, &keyslot, 0) ||
luks2_parse_keyslot (k, &keyslot))
return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, "Could not parse keyslot index %" PRIuVAS_EBOOT_SIZE, keyslot_json_idx);
/* Get digest that matches the keyslot. */
if (VasEBoot_json_getvalue (&digests, root, "digests") ||
VasEBoot_json_getsize (&size, &digests))
return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, "Could not get digests");
for (json_idx = 0; json_idx < size; json_idx++)
{
if (VasEBoot_json_getchild (&digest, &digests, json_idx) ||
VasEBoot_json_getuint64 (&d->idx, &digest, NULL) ||
VasEBoot_json_getchild (&digest, &digest, 0) ||
luks2_parse_digest (d, &digest))
return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, "Could not parse digest index %" PRIuVAS_EBOOT_SIZE, json_idx);
if ((d->keyslots & (1 << k->idx)))
break;
}
if (json_idx == size)
return VasEBoot_error (VAS_EBOOT_ERR_FILE_NOT_FOUND, "No digest for keyslot \"%" PRIuVAS_EBOOT_UINT64_T "\"", k->idx);
/* Get segment that matches the digest. */
if (VasEBoot_json_getvalue (&segments, root, "segments") ||
VasEBoot_json_getsize (&size, &segments))
return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, "Could not get segments");
for (json_idx = 0; json_idx < size; json_idx++)
{
if (VasEBoot_json_getchild (&segment, &segments, json_idx) ||
VasEBoot_json_getuint64 (&s->idx, &segment, NULL) ||
VasEBoot_json_getchild (&segment, &segment, 0) ||
luks2_parse_segment (s, &segment))
return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, "Could not parse segment index %" PRIuVAS_EBOOT_SIZE, json_idx);
if ((d->segments & (1 << s->idx)))
break;
}
if (json_idx == size)
return VasEBoot_error (VAS_EBOOT_ERR_FILE_NOT_FOUND, "No segment for digest \"%" PRIuVAS_EBOOT_UINT64_T "\"", d->idx);
return VAS_EBOOT_ERR_NONE;
}
/* Determine whether to use primary or secondary header */
static VasEBoot_err_t
luks2_read_header (VasEBoot_disk_t disk, VasEBoot_luks2_header_t *outhdr)
{
VasEBoot_luks2_header_t primary, secondary, *header = &primary;
VasEBoot_err_t ret;
/* Read the primary LUKS header. */
ret = VasEBoot_disk_read (disk, 0, 0, sizeof (primary), &primary);
if (ret)
return ret;
/* Look for LUKS magic sequence. */
if (VasEBoot_memcmp (primary.magic, LUKS_MAGIC_1ST, sizeof (primary.magic)) ||
VasEBoot_be_to_cpu16 (primary.version) != 2)
return VAS_EBOOT_ERR_BAD_SIGNATURE;
/* Read the secondary header. */
ret = VasEBoot_disk_read (disk, 0, VasEBoot_be_to_cpu64 (primary.hdr_size), sizeof (secondary), &secondary);
if (ret)
return ret;
/* Look for LUKS magic sequence. */
if (VasEBoot_memcmp (secondary.magic, LUKS_MAGIC_2ND, sizeof (secondary.magic)) ||
VasEBoot_be_to_cpu16 (secondary.version) != 2)
return VAS_EBOOT_ERR_BAD_SIGNATURE;
if (VasEBoot_be_to_cpu64 (primary.seqid) < VasEBoot_be_to_cpu64 (secondary.seqid))
header = &secondary;
VasEBoot_memcpy (outhdr, header, sizeof (*header));
return VAS_EBOOT_ERR_NONE;
}
static VasEBoot_cryptodisk_t
luks2_scan (VasEBoot_disk_t disk, VasEBoot_cryptomount_args_t cargs)
{
VasEBoot_cryptodisk_t cryptodisk;
VasEBoot_luks2_header_t header;
if (cargs->check_boot)
return NULL;
if (luks2_read_header (disk, &header))
{
VasEBoot_errno = VAS_EBOOT_ERR_NONE;
return NULL;
}
if (cargs->search_uuid != NULL && VasEBoot_uuidcasecmp (cargs->search_uuid, header.uuid, sizeof (header.uuid)) != 0)
{
VasEBoot_dprintf ("luks2", "%s != %s\n", header.uuid, cargs->search_uuid);
return NULL;
}
cryptodisk = VasEBoot_zalloc (sizeof (*cryptodisk));
if (!cryptodisk)
return NULL;
COMPILE_TIME_ASSERT (sizeof (cryptodisk->uuid) >= sizeof (header.uuid));
VasEBoot_memcpy (cryptodisk->uuid, header.uuid, sizeof (header.uuid));
cryptodisk->modname = "luks2";
return cryptodisk;
}
static VasEBoot_err_t
luks2_base64_decode (const char *in, VasEBoot_size_t inlen, VasEBoot_uint8_t *decoded, idx_t *decodedlen)
{
VasEBoot_size_t unescaped_len = 0;
char *unescaped = NULL;
bool successful;
if (VasEBoot_json_unescape (&unescaped, &unescaped_len, in, inlen) != VAS_EBOOT_ERR_NONE)
return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, N_("could not unescape Base64 string"));
successful = base64_decode (unescaped, unescaped_len, (char *) decoded, decodedlen);
VasEBoot_free (unescaped);
if (!successful)
return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, N_("could not decode Base64 string"));
return VAS_EBOOT_ERR_NONE;
}
static VasEBoot_err_t
luks2_verify_key (VasEBoot_luks2_digest_t *d, VasEBoot_uint8_t *candidate_key,
VasEBoot_size_t candidate_key_len)
{
VasEBoot_uint8_t candidate_digest[VAS_EBOOT_CRYPTODISK_MAX_KEYLEN];
VasEBoot_uint8_t digest[VAS_EBOOT_CRYPTODISK_MAX_KEYLEN], salt[VAS_EBOOT_CRYPTODISK_MAX_KEYLEN];
idx_t saltlen = sizeof (salt), digestlen = sizeof (digest);
const gcry_md_spec_t *hash;
gcry_err_code_t gcry_ret;
/* Decode both digest and salt */
if (luks2_base64_decode (d->digest, VasEBoot_strlen (d->digest),
digest, &digestlen) != VAS_EBOOT_ERR_NONE)
return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, "Invalid digest");
if (luks2_base64_decode (d->salt, VasEBoot_strlen (d->salt),
salt, &saltlen) != VAS_EBOOT_ERR_NONE)
return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, "Invalid digest salt");
/* Configure the hash used for the digest. */
hash = VasEBoot_crypto_lookup_md_by_name (d->hash);
if (!hash)
return VasEBoot_error (VAS_EBOOT_ERR_FILE_NOT_FOUND, "Couldn't load %s hash", d->hash);
/* Calculate the candidate key's digest */
gcry_ret = VasEBoot_crypto_pbkdf2 (hash,
candidate_key, candidate_key_len,
salt, saltlen,
d->iterations,
candidate_digest, digestlen);
if (gcry_ret)
return VasEBoot_crypto_gcry_error (gcry_ret);
if (VasEBoot_memcmp (candidate_digest, digest, digestlen) != 0)
return VasEBoot_error (VAS_EBOOT_ERR_ACCESS_DENIED, "Mismatching digests");
return VAS_EBOOT_ERR_NONE;
}
static VasEBoot_err_t
luks2_decrypt_key (VasEBoot_uint8_t *out_key,
VasEBoot_disk_t source, VasEBoot_cryptodisk_t crypt,
VasEBoot_luks2_keyslot_t *k,
const VasEBoot_uint8_t *passphrase, VasEBoot_size_t passphraselen)
{
VasEBoot_uint8_t area_key[VAS_EBOOT_CRYPTODISK_MAX_KEYLEN];
VasEBoot_uint8_t salt[VAS_EBOOT_CRYPTODISK_MAX_KEYLEN];
VasEBoot_uint8_t *split_key = NULL;
idx_t saltlen = sizeof (salt);
int subalgo;
unsigned long param[4];
char cipher[32], *p;
const gcry_md_spec_t *hash;
gcry_err_code_t gcry_ret;
VasEBoot_err_t ret;
if (luks2_base64_decode (k->kdf.salt, VasEBoot_strlen (k->kdf.salt),
salt, &saltlen) != VAS_EBOOT_ERR_NONE)
{
ret = VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, "Invalid keyslot salt");
goto err;
}
/* Calculate the binary area key of the user supplied passphrase. */
switch (k->kdf.type)
{
case LUKS2_KDF_TYPE_ARGON2I:
case LUKS2_KDF_TYPE_ARGON2ID:
if (k->kdf.type == LUKS2_KDF_TYPE_ARGON2I)
subalgo = VAS_EBOOT_GCRY_KDF_ARGON2I;
else
subalgo = VAS_EBOOT_GCRY_KDF_ARGON2ID;
param[0] = k->area.key_size;
param[1] = k->kdf.u.argon2i.time;
param[2] = k->kdf.u.argon2i.memory;
param[3] = k->kdf.u.argon2i.cpus;
gcry_ret = VasEBoot_crypto_argon2 (subalgo, param, 4,
passphrase, passphraselen,
salt, saltlen,
NULL, 0, NULL, 0,
k->area.key_size, area_key);
if (gcry_ret)
{
ret = VasEBoot_crypto_gcry_error (gcry_ret);
goto err;
}
break;
case LUKS2_KDF_TYPE_PBKDF2:
hash = VasEBoot_crypto_lookup_md_by_name (k->kdf.u.pbkdf2.hash);
if (!hash)
{
ret = VasEBoot_error (VAS_EBOOT_ERR_FILE_NOT_FOUND, "Couldn't load %s hash",
k->kdf.u.pbkdf2.hash);
goto err;
}
gcry_ret = VasEBoot_crypto_pbkdf2 (hash, (VasEBoot_uint8_t *) passphrase,
passphraselen,
salt, saltlen,
k->kdf.u.pbkdf2.iterations,
area_key, k->area.key_size);
if (gcry_ret)
{
ret = VasEBoot_crypto_gcry_error (gcry_ret);
goto err;
}
break;
}
/* Set up disk encryption parameters for the key area */
VasEBoot_strncpy (cipher, k->area.encryption, sizeof (cipher));
p = VasEBoot_memchr (cipher, '-', VasEBoot_strlen (cipher));
if (!p)
return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, "Invalid encryption");
*p = '\0';
ret = VasEBoot_cryptodisk_setcipher (crypt, cipher, p + 1);
if (ret)
return ret;
gcry_ret = VasEBoot_cryptodisk_setkey (crypt, area_key, k->area.key_size);
if (gcry_ret)
{
ret = VasEBoot_crypto_gcry_error (gcry_ret);
goto err;
}
/* Read and decrypt the binary key area with the area key. */
split_key = VasEBoot_malloc (k->area.size);
if (!split_key)
{
ret = VasEBoot_errno;
goto err;
}
VasEBoot_errno = VAS_EBOOT_ERR_NONE;
ret = VasEBoot_disk_read (source, 0, k->area.offset, k->area.size, split_key);
if (ret)
{
VasEBoot_error (VAS_EBOOT_ERR_IO, "Read error: %s\n", VasEBoot_errmsg);
goto err;
}
/*
* The key slots area is always encrypted in 512-byte sectors,
* regardless of encrypted data sector size.
*/
gcry_ret = VasEBoot_cryptodisk_decrypt (crypt, split_key, k->area.size, 0,
VAS_EBOOT_LUKS1_LOG_SECTOR_SIZE);
if (gcry_ret)
{
ret = VasEBoot_crypto_gcry_error (gcry_ret);
goto err;
}
/* Configure the hash used for anti-forensic merging. */
hash = VasEBoot_crypto_lookup_md_by_name (k->af.hash);
if (!hash)
{
ret = VasEBoot_error (VAS_EBOOT_ERR_FILE_NOT_FOUND, "Couldn't load %s hash",
k->af.hash);
goto err;
}
/* Merge the decrypted key material to get the candidate master key. */
gcry_ret = AF_merge (hash, split_key, out_key, k->key_size, k->af.stripes);
if (gcry_ret)
{
ret = VasEBoot_crypto_gcry_error (gcry_ret);
goto err;
}
VasEBoot_dprintf ("luks2", "Candidate key recovered\n");
err:
VasEBoot_free (split_key);
return ret;
}
static VasEBoot_err_t
luks2_recover_key (VasEBoot_disk_t source,
VasEBoot_cryptodisk_t crypt,
VasEBoot_cryptomount_args_t cargs)
{
VasEBoot_uint8_t candidate_key[VAS_EBOOT_CRYPTODISK_MAX_KEYLEN];
char cipher[32], *json_header = NULL, *ptr;
VasEBoot_size_t candidate_key_len = 0, json_idx, size;
VasEBoot_luks2_header_t header;
VasEBoot_luks2_keyslot_t keyslot;
VasEBoot_luks2_digest_t digest;
VasEBoot_luks2_segment_t segment;
gcry_err_code_t gcry_ret;
VasEBoot_json_t *json = NULL, keyslots;
VasEBoot_err_t ret;
VasEBoot_size_t sz;
if (cargs->key_data == NULL || cargs->key_len == 0)
return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, "no key data");
ret = luks2_read_header (source, &header);
if (ret)
return ret;
VasEBoot_puts_ (N_("Attempting to decrypt master key..."));
if (VasEBoot_sub (VasEBoot_be_to_cpu64 (header.hdr_size), sizeof (header), &sz))
return VasEBoot_error (VAS_EBOOT_ERR_OUT_OF_RANGE, "underflow detected while calculating json header size");
json_header = VasEBoot_zalloc (sz);
if (!json_header)
return VAS_EBOOT_ERR_OUT_OF_MEMORY;
/* Read the JSON area. */
ret = VasEBoot_disk_read (source, 0, VasEBoot_be_to_cpu64 (header.hdr_offset) + sizeof (header),
VasEBoot_be_to_cpu64 (header.hdr_size) - sizeof (header), json_header);
if (ret)
goto err;
ptr = VasEBoot_memchr (json_header, 0, VasEBoot_be_to_cpu64 (header.hdr_size) - sizeof (header));
if (!ptr)
goto err;
ret = VasEBoot_json_parse (&json, json_header, VasEBoot_be_to_cpu64 (header.hdr_size));
if (ret)
{
ret = VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, "Invalid LUKS2 JSON header");
goto err;
}
if (VasEBoot_json_getvalue (&keyslots, json, "keyslots") ||
VasEBoot_json_getsize (&size, &keyslots))
{
ret = VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, "Could not get keyslots");
goto err;
}
if (VasEBoot_disk_native_sectors (source) == VAS_EBOOT_DISK_SIZE_UNKNOWN)
{
/* FIXME: Allow use of source disk, and maybe cause errors in read. */
VasEBoot_dprintf ("luks2", "Source disk %s has an unknown size, "
"conservatively returning error\n", source->name);
ret = VasEBoot_error (VAS_EBOOT_ERR_BUG, "Unknown size of luks2 source device");
goto err;
}
/* Try all keyslot */
for (json_idx = 0; json_idx < size; json_idx++)
{
char indexstr[21]; /* log10(2^64) ~ 20, plus NUL character. */
typeof (source->total_sectors) max_crypt_sectors = 0;
VasEBoot_errno = VAS_EBOOT_ERR_NONE;
ret = luks2_get_keyslot (&keyslot, &digest, &segment, json, json_idx);
if (ret)
{
/*
* luks2_get_keyslot() can fail for a variety of reasons that do not
* necessarily mean the next keyslot should not be tried (e.g. a new
* kdf type). So always try the next slot.
*/
VasEBoot_dprintf ("luks2", "Failed to get keyslot %" PRIuVAS_EBOOT_UINT64_T "\n", keyslot.idx);
continue;
}
if (VasEBoot_errno != VAS_EBOOT_ERR_NONE)
VasEBoot_dprintf ("luks2", "Ignoring unhandled error %d from luks2_get_keyslot\n", VasEBoot_errno);
if (keyslot.priority == 0)
{
VasEBoot_dprintf ("luks2", "Ignoring keyslot \"%" PRIuVAS_EBOOT_UINT64_T "\" due to priority\n", keyslot.idx);
continue;
}
VasEBoot_dprintf ("luks2", "Trying keyslot \"%" PRIuVAS_EBOOT_UINT64_T "\"\n", keyslot.idx);
/* Sector size should be one of 512, 1024, 2048, or 4096. */
if (!(segment.sector_size == 512 || segment.sector_size == 1024 ||
segment.sector_size == 2048 || segment.sector_size == 4096))
{
VasEBoot_dprintf ("luks2", "Segment \"%" PRIuVAS_EBOOT_UINT64_T "\" sector"
" size %" PRIuVAS_EBOOT_UINT64_T " is not one of"
" 512, 1024, 2048, or 4096\n",
segment.idx, segment.sector_size);
continue;
}
/* Set up disk according to keyslot's segment. */
crypt->offset_sectors = VasEBoot_divmod64 (segment.offset, segment.sector_size, NULL);
crypt->log_sector_size = VasEBoot_log2ull (segment.sector_size);
/* Set to the source disk/partition size, which is the maximum we allow. */
max_crypt_sectors = VasEBoot_disk_native_sectors (source);
max_crypt_sectors = VasEBoot_convert_sector (max_crypt_sectors, VAS_EBOOT_DISK_SECTOR_BITS,
crypt->log_sector_size);
if (max_crypt_sectors < crypt->offset_sectors)
{
VasEBoot_dprintf ("luks2", "Segment \"%" PRIuVAS_EBOOT_UINT64_T "\" has offset"
" %" PRIuVAS_EBOOT_UINT64_T " which is greater than"
" source disk size %" PRIuVAS_EBOOT_UINT64_T ","
" skipping\n", segment.idx, crypt->offset_sectors,
max_crypt_sectors);
continue;
}
if (VasEBoot_strcmp (segment.size, "dynamic") == 0)
crypt->total_sectors = max_crypt_sectors - crypt->offset_sectors;
else
{
VasEBoot_errno = VAS_EBOOT_ERR_NONE;
/* Convert segment.size to sectors, rounding up to nearest sector */
crypt->total_sectors = VasEBoot_strtoull (segment.size, NULL, 10);
if (VasEBoot_errno == VAS_EBOOT_ERR_NONE)
{
crypt->total_sectors = ALIGN_UP (crypt->total_sectors,
1 << crypt->log_sector_size);
crypt->total_sectors >>= crypt->log_sector_size;
}
else if (VasEBoot_errno == VAS_EBOOT_ERR_BAD_NUMBER)
{
VasEBoot_dprintf ("luks2", "Segment \"%" PRIuVAS_EBOOT_UINT64_T "\" size"
" \"%s\" is not a parsable number,"
" skipping keyslot\n",
segment.idx, segment.size);
continue;
}
else if (VasEBoot_errno == VAS_EBOOT_ERR_OUT_OF_RANGE)
{
/*
* There was an overflow in parsing segment.size, so disk must
* be very large or the string is incorrect.
*
* TODO: Allow reading of at least up max_crypt_sectors. Really,
* its very unlikely one would be booting from such a large drive
* anyway. Use another smaller LUKS2 boot device.
*/
VasEBoot_dprintf ("luks2", "Segment \"%" PRIuVAS_EBOOT_UINT64_T "\" size"
" %s overflowed 64-bit unsigned integer,"
" skipping keyslot\n", segment.idx, segment.size);
continue;
}
}
if (crypt->total_sectors == 0)
{
VasEBoot_dprintf ("luks2", "Segment \"%" PRIuVAS_EBOOT_UINT64_T "\" has zero"
" sectors, skipping\n", segment.idx);
continue;
}
else if (max_crypt_sectors < (crypt->offset_sectors + crypt->total_sectors))
{
VasEBoot_dprintf ("luks2", "Segment \"%" PRIuVAS_EBOOT_UINT64_T "\" has last"
" data position greater than source disk size,"
" the end of the crypto device will be"
" inaccessible\n", segment.idx);
/* Allow decryption up to the end of the source disk. */
crypt->total_sectors = max_crypt_sectors - crypt->offset_sectors;
}
ret = luks2_decrypt_key (candidate_key, source, crypt, &keyslot,
cargs->key_data, cargs->key_len);
if (ret)
{
VasEBoot_dprintf ("luks2", "Decryption with keyslot \"%" PRIuVAS_EBOOT_UINT64_T "\" failed: %s\n",
keyslot.idx, VasEBoot_errmsg);
continue;
}
ret = luks2_verify_key (&digest, candidate_key, keyslot.key_size);
if (ret)
{
VasEBoot_dprintf ("luks2", "Could not open keyslot \"%" PRIuVAS_EBOOT_UINT64_T "\": %s\n",
keyslot.idx, VasEBoot_errmsg);
continue;
}
VasEBoot_snprintf (indexstr, sizeof (indexstr) - 1, "%" PRIuVAS_EBOOT_UINT64_T, keyslot.idx);
/*
* TRANSLATORS: It's a cryptographic key slot: one element of an array
* where each element is either empty or holds a key.
*/
VasEBoot_printf_ (N_("Slot \"%s\" opened\n"), indexstr);
candidate_key_len = keyslot.key_size;
break;
}
if (candidate_key_len == 0)
{
ret = VasEBoot_error (VAS_EBOOT_ERR_ACCESS_DENIED, "Invalid passphrase");
goto err;
}
/* Set up disk cipher. */
VasEBoot_strncpy (cipher, segment.encryption, sizeof (cipher));
ptr = VasEBoot_memchr (cipher, '-', VasEBoot_strlen (cipher));
if (!ptr)
return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, "Invalid encryption");
*ptr = '\0';
ret = VasEBoot_cryptodisk_setcipher (crypt, cipher, ptr + 1);
if (ret)
goto err;
/* Set the master key. */
gcry_ret = VasEBoot_cryptodisk_setkey (crypt, candidate_key, candidate_key_len);
if (gcry_ret)
{
ret = VasEBoot_crypto_gcry_error (gcry_ret);
goto err;
}
err:
VasEBoot_free (json_header);
VasEBoot_json_free (json);
return ret;
}
static struct VasEBoot_cryptodisk_dev luks2_crypto = {
.scan = luks2_scan,
.recover_key = luks2_recover_key
};
VAS_EBOOT_MOD_INIT (luks2)
{
VasEBoot_cryptodisk_dev_register (&luks2_crypto);
}
VAS_EBOOT_MOD_FINI (luks2)
{
VasEBoot_cryptodisk_dev_unregister (&luks2_crypto);
}