2 * Integrity volume handling
4 * Copyright (C) 2016-2020 Milan Broz
6 * This file is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This file is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this file; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 #include <uuid/uuid.h>
27 #include "integrity.h"
30 static int INTEGRITY_read_superblock(struct crypt_device *cd,
31 struct device *device,
32 uint64_t offset, struct superblock *sb)
36 devfd = device_open(cd, device, O_RDONLY);
40 if (read_lseek_blockwise(devfd, device_block_size(cd, device),
41 device_alignment(device), sb, sizeof(*sb), offset) != sizeof(*sb) ||
42 memcmp(sb->magic, SB_MAGIC, sizeof(sb->magic)) ||
43 sb->version < SB_VERSION_1 || sb->version > SB_VERSION_4) {
44 log_std(cd, "No integrity superblock detected on %s.\n",
48 sb->integrity_tag_size = le16toh(sb->integrity_tag_size);
49 sb->journal_sections = le32toh(sb->journal_sections);
50 sb->provided_data_sectors = le64toh(sb->provided_data_sectors);
51 sb->recalc_sector = le64toh(sb->recalc_sector);
52 sb->flags = le32toh(sb->flags);
59 int INTEGRITY_read_sb(struct crypt_device *cd,
60 struct crypt_params_integrity *params,
66 r = INTEGRITY_read_superblock(cd, crypt_metadata_device(cd), 0, &sb);
70 params->sector_size = SECTOR_SIZE << sb.log2_sectors_per_block;
71 params->tag_size = sb.integrity_tag_size;
79 int INTEGRITY_dump(struct crypt_device *cd, struct device *device, uint64_t offset)
84 r = INTEGRITY_read_superblock(cd, device, offset, &sb);
88 log_std(cd, "Info for integrity device %s.\n", device_path(device));
89 log_std(cd, "superblock_version %d\n", (unsigned)sb.version);
90 log_std(cd, "log2_interleave_sectors %d\n", sb.log2_interleave_sectors);
91 log_std(cd, "integrity_tag_size %u\n", sb.integrity_tag_size);
92 log_std(cd, "journal_sections %u\n", sb.journal_sections);
93 log_std(cd, "provided_data_sectors %" PRIu64 "\n", sb.provided_data_sectors);
94 log_std(cd, "sector_size %u\n", SECTOR_SIZE << sb.log2_sectors_per_block);
95 if (sb.version == SB_VERSION_2 && (sb.flags & SB_FLAG_RECALCULATING))
96 log_std(cd, "recalc_sector %" PRIu64 "\n", sb.recalc_sector);
97 log_std(cd, "log2_blocks_per_bitmap %u\n", sb.log2_blocks_per_bitmap_bit);
98 log_std(cd, "flags %s%s%s%s\n",
99 sb.flags & SB_FLAG_HAVE_JOURNAL_MAC ? "have_journal_mac " : "",
100 sb.flags & SB_FLAG_RECALCULATING ? "recalculating " : "",
101 sb.flags & SB_FLAG_DIRTY_BITMAP ? "dirty_bitmap " : "",
102 sb.flags & SB_FLAG_FIXED_PADDING ? "fix_padding " : "");
107 int INTEGRITY_data_sectors(struct crypt_device *cd,
108 struct device *device, uint64_t offset,
109 uint64_t *data_sectors)
111 struct superblock sb;
114 r = INTEGRITY_read_superblock(cd, device, offset, &sb);
118 *data_sectors = sb.provided_data_sectors;
122 int INTEGRITY_key_size(struct crypt_device *cd, const char *integrity)
127 //FIXME: use crypto backend hash size
128 if (!strcmp(integrity, "aead"))
130 else if (!strcmp(integrity, "hmac(sha1)"))
132 else if (!strcmp(integrity, "hmac(sha256)"))
134 else if (!strcmp(integrity, "hmac(sha512)"))
136 else if (!strcmp(integrity, "poly1305"))
138 else if (!strcmp(integrity, "none"))
144 /* Return hash or hmac(hash) size, if known */
145 int INTEGRITY_hash_tag_size(const char *integrity)
147 char hash[MAX_CIPHER_LEN];
153 if (!strcmp(integrity, "crc32") || !strcmp(integrity, "crc32c"))
156 r = sscanf(integrity, "hmac(%" MAX_CIPHER_LEN_STR "[^)]s", hash);
158 r = crypt_hash_size(hash);
160 r = crypt_hash_size(integrity);
162 return r < 0 ? 0 : r;
165 int INTEGRITY_tag_size(struct crypt_device *cd,
166 const char *integrity,
168 const char *cipher_mode)
170 int iv_tag_size = 0, auth_tag_size = 0;
174 else if (!strcmp(cipher_mode, "xts-random"))
176 else if (!strcmp(cipher_mode, "gcm-random"))
178 else if (!strcmp(cipher_mode, "ccm-random"))
180 else if (!strcmp(cipher_mode, "ctr-random"))
182 else if (!strcmp(cipher, "aegis256") && !strcmp(cipher_mode, "random"))
184 else if (!strcmp(cipher_mode, "random"))
187 //FIXME: use crypto backend hash size
188 if (!integrity || !strcmp(integrity, "none"))
190 else if (!strcmp(integrity, "aead"))
191 auth_tag_size = 16; //FIXME gcm- mode only
192 else if (!strcmp(integrity, "cmac(aes)"))
194 else if (!strcmp(integrity, "hmac(sha1)"))
196 else if (!strcmp(integrity, "hmac(sha256)"))
198 else if (!strcmp(integrity, "hmac(sha512)"))
200 else if (!strcmp(integrity, "poly1305")) {
206 return iv_tag_size + auth_tag_size;
209 int INTEGRITY_create_dmd_device(struct crypt_device *cd,
210 const struct crypt_params_integrity *params,
211 struct volume_key *vk,
212 struct volume_key *journal_crypt_key,
213 struct volume_key *journal_mac_key,
214 struct crypt_dm_active_device *dmd,
215 uint32_t flags, uint32_t sb_flags)
222 *dmd = (struct crypt_dm_active_device) {
226 /* Workaround for kernel dm-integrity table bug */
227 if (sb_flags & SB_FLAG_RECALCULATING)
228 dmd->flags |= CRYPT_ACTIVATE_RECALCULATE;
230 r = INTEGRITY_data_sectors(cd, crypt_metadata_device(cd),
231 crypt_get_data_offset(cd) * SECTOR_SIZE, &dmd->size);
235 return dm_integrity_target_set(cd, &dmd->segment, 0, dmd->size,
236 crypt_metadata_device(cd), crypt_data_device(cd),
237 crypt_get_integrity_tag_size(cd), crypt_get_data_offset(cd),
238 crypt_get_sector_size(cd), vk, journal_crypt_key,
239 journal_mac_key, params);
242 int INTEGRITY_activate_dmd_device(struct crypt_device *cd,
245 struct crypt_dm_active_device *dmd,
250 struct dm_target *tgt = &dmd->segment;
252 if (!single_segment(dmd) || tgt->type != DM_INTEGRITY)
255 log_dbg(cd, "Trying to activate INTEGRITY device on top of %s, using name %s, tag size %d, provided sectors %" PRIu64".",
256 device_path(tgt->data_device), name, tgt->u.integrity.tag_size, dmd->size);
258 r = device_block_adjust(cd, tgt->data_device, DEV_EXCL,
259 tgt->u.integrity.offset, NULL, &dmd->flags);
263 if (tgt->u.integrity.meta_device) {
264 r = device_block_adjust(cd, tgt->u.integrity.meta_device, DEV_EXCL, 0, NULL, NULL);
269 r = dm_create_device(cd, name, type, dmd);
270 if (r < 0 && (dm_flags(cd, DM_INTEGRITY, &dmi_flags) || !(dmi_flags & DM_INTEGRITY_SUPPORTED))) {
271 log_err(cd, _("Kernel does not support dm-integrity mapping."));
275 if (r < 0 && (sb_flags & SB_FLAG_FIXED_PADDING) && !dm_flags(cd, DM_INTEGRITY, &dmi_flags) &&
276 !(dmi_flags & DM_INTEGRITY_FIX_PADDING_SUPPORTED)) {
277 log_err(cd, _("Kernel does not support dm-integrity fixed metadata alignment."));
284 int INTEGRITY_activate(struct crypt_device *cd,
286 const struct crypt_params_integrity *params,
287 struct volume_key *vk,
288 struct volume_key *journal_crypt_key,
289 struct volume_key *journal_mac_key,
290 uint32_t flags, uint32_t sb_flags)
292 struct crypt_dm_active_device dmd = {};
293 int r = INTEGRITY_create_dmd_device(cd, params, vk, journal_crypt_key,
294 journal_mac_key, &dmd, flags, sb_flags);
299 r = INTEGRITY_activate_dmd_device(cd, name, CRYPT_INTEGRITY, &dmd, sb_flags);
300 dm_targets_free(cd, &dmd);
304 int INTEGRITY_format(struct crypt_device *cd,
305 const struct crypt_params_integrity *params,
306 struct volume_key *journal_crypt_key,
307 struct volume_key *journal_mac_key)
310 char tmp_name[64], tmp_uuid[40];
311 struct crypt_dm_active_device dmdi = {
313 .flags = CRYPT_ACTIVATE_PRIVATE, /* We always create journal but it can be unused later */
315 struct dm_target *tgt = &dmdi.segment;
318 struct volume_key *vk = NULL;
320 uuid_generate(tmp_uuid_bin);
321 uuid_unparse(tmp_uuid_bin, tmp_uuid);
323 snprintf(tmp_name, sizeof(tmp_name), "temporary-cryptsetup-%s", tmp_uuid);
325 /* There is no data area, we can actually use fake zeroed key */
326 if (params && params->integrity_key_size)
327 vk = crypt_alloc_volume_key(params->integrity_key_size, NULL);
329 r = dm_integrity_target_set(cd, tgt, 0, dmdi.size, crypt_metadata_device(cd),
330 crypt_data_device(cd), crypt_get_integrity_tag_size(cd),
331 crypt_get_data_offset(cd), crypt_get_sector_size(cd), vk,
332 journal_crypt_key, journal_mac_key, params);
334 crypt_free_volume_key(vk);
338 log_dbg(cd, "Trying to format INTEGRITY device on top of %s, tmp name %s, tag size %d.",
339 device_path(tgt->data_device), tmp_name, tgt->u.integrity.tag_size);
341 r = device_block_adjust(cd, tgt->data_device, DEV_EXCL, tgt->u.integrity.offset, NULL, NULL);
342 if (r < 0 && (dm_flags(cd, DM_INTEGRITY, &dmi_flags) || !(dmi_flags & DM_INTEGRITY_SUPPORTED))) {
343 log_err(cd, _("Kernel does not support dm-integrity mapping."));
347 dm_targets_free(cd, &dmdi);
351 if (tgt->u.integrity.meta_device) {
352 r = device_block_adjust(cd, tgt->u.integrity.meta_device, DEV_EXCL, 0, NULL, NULL);
354 dm_targets_free(cd, &dmdi);
359 r = dm_create_device(cd, tmp_name, CRYPT_INTEGRITY, &dmdi);
360 crypt_free_volume_key(vk);
361 dm_targets_free(cd, &dmdi);
365 return dm_remove_device(cd, tmp_name, CRYPT_DEACTIVATE_FORCE);