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