static int diffuse(char *src, char *dst, size_t size, const char *hash_name) { int hash_size = crypt_hash_size(hash_name); unsigned int digest_size; unsigned int i, blocks, padding; if (hash_size <= 0) return 1; digest_size = hash_size; blocks = size / digest_size; padding = size % digest_size; for (i = 0; i < blocks; i++) if(hash_buf(src + digest_size * i, dst + digest_size * i, i, (size_t)digest_size, hash_name)) return 1; if(padding) if(hash_buf(src + digest_size * i, dst + digest_size * i, i, (size_t)padding, hash_name)) return 1; return 0; }
static int hash(const char *hash_name, size_t key_size, char *key, size_t passphrase_size, const char *passphrase) { struct crypt_hash *md = NULL; size_t len; int round, i, r = 0; if (crypt_hash_init(&md, hash_name)) return -ENOENT; len = crypt_hash_size(hash_name); for(round = 0; key_size && !r; round++) { /* hack from hashalot to avoid null bytes in key */ for(i = 0; i < round; i++) if (crypt_hash_write(md, "A", 1)) r = 1; if (crypt_hash_write(md, passphrase, passphrase_size)) r = 1; if (len > key_size) len = key_size; if (crypt_hash_final(md, key, len)) r = 1; key += len; key_size -= len; } crypt_hash_destroy(md); return r; }
/* * Compare hash (checksum) of on-disk and in-memory header. */ static int hdr_checksum_check(const char *alg, struct luks2_hdr_disk *hdr_disk, const char *json_area, size_t json_len) { struct luks2_hdr_disk hdr_tmp; int hash_size, r; hash_size = crypt_hash_size(alg); if (hash_size <= 0) return -EINVAL; /* Copy header and zero checksum. */ memcpy(&hdr_tmp, hdr_disk, LUKS2_HDR_BIN_LEN); memset(&hdr_tmp.csum, 0, sizeof(hdr_tmp.csum)); r = hdr_checksum_calculate(alg, &hdr_tmp, json_area, json_len); if (r < 0) return r; log_dbg_checksum(hdr_disk->csum, alg, "on-disk"); log_dbg_checksum(hdr_tmp.csum, alg, "in-memory"); if (memcmp(hdr_tmp.csum, hdr_disk->csum, (size_t)hash_size)) return -EINVAL; return 0; }
static void log_dbg_checksum(const uint8_t *csum, const char *csum_alg, const char *info) { char csum_txt[2*LUKS2_CHECKSUM_L+1]; int i; for (i = 0; i < crypt_hash_size(csum_alg); i++) snprintf(&csum_txt[i*2], 3, "%02hhx", (const char)csum[i]); csum_txt[i*2+1] = '\0'; /* Just to be safe, sprintf should write \0 there. */ log_dbg("Checksum:%s (%s)", &csum_txt[0], info); }
/* * Calculate hash (checksum) of |LUKS2_bin|LUKS2_JSON_area| from in-memory structs. * LUKS2 on-disk header contains uniques salt both for primary and secondary header. * Checksum is always calculated with zeroed checksum field in binary header. */ static int hdr_checksum_calculate(const char *alg, struct luks2_hdr_disk *hdr_disk, const char *json_area, size_t json_len) { struct crypt_hash *hd = NULL; int hash_size, r; hash_size = crypt_hash_size(alg); if (hash_size <= 0 || crypt_hash_init(&hd, alg)) return -EINVAL; /* Binary header, csum zeroed. */ r = crypt_hash_write(hd, (char*)hdr_disk, LUKS2_HDR_BIN_LEN); /* JSON area (including unused space) */ if (!r) r = crypt_hash_write(hd, json_area, json_len); if (!r) r = crypt_hash_final(hd, (char*)hdr_disk->csum, (size_t)hash_size); crypt_hash_destroy(hd); return r; }
/* HMAC */ int crypt_hmac_size(const char *name) { return crypt_hash_size(name); }
static int TCRYPT_init_hdr(struct crypt_device *cd, struct tcrypt_phdr *hdr, struct crypt_params_tcrypt *params) { unsigned char pwd[TCRYPT_KEY_POOL_LEN] = {}; size_t passphrase_size; char *key; unsigned int i, skipped = 0; int r = -EPERM; if (posix_memalign((void*)&key, crypt_getpagesize(), TCRYPT_HDR_KEY_LEN)) return -ENOMEM; if (params->keyfiles_count) passphrase_size = TCRYPT_KEY_POOL_LEN; else passphrase_size = params->passphrase_size; if (params->passphrase_size > TCRYPT_KEY_POOL_LEN) { log_err(cd, _("Maximum TCRYPT passphrase length (%d) exceeded.\n"), TCRYPT_KEY_POOL_LEN); goto out; } /* Calculate pool content from keyfiles */ for (i = 0; i < params->keyfiles_count; i++) { r = TCRYPT_pool_keyfile(cd, pwd, params->keyfiles[i]); if (r < 0) goto out; } /* If provided password, combine it with pool */ for (i = 0; i < params->passphrase_size; i++) pwd[i] += params->passphrase[i]; for (i = 0; tcrypt_kdf[i].name; i++) { if (!(params->flags & CRYPT_TCRYPT_LEGACY_MODES) && tcrypt_kdf[i].legacy) continue; if (!(params->flags & CRYPT_TCRYPT_VERA_MODES) && tcrypt_kdf[i].veracrypt) continue; /* Derive header key */ log_dbg("TCRYPT: trying KDF: %s-%s-%d.", tcrypt_kdf[i].name, tcrypt_kdf[i].hash, tcrypt_kdf[i].iterations); r = crypt_pbkdf(tcrypt_kdf[i].name, tcrypt_kdf[i].hash, (char*)pwd, passphrase_size, hdr->salt, TCRYPT_HDR_SALT_LEN, key, TCRYPT_HDR_KEY_LEN, tcrypt_kdf[i].iterations); if (r < 0 && crypt_hash_size(tcrypt_kdf[i].hash) < 0) { log_verbose(cd, _("PBKDF2 hash algorithm %s not available, skipping.\n"), tcrypt_kdf[i].hash); continue; } if (r < 0) break; /* Decrypt header */ r = TCRYPT_decrypt_hdr(cd, hdr, key, params->flags); if (r == -ENOENT) { skipped++; r = -EPERM; } if (r != -EPERM) break; } if ((r < 0 && r != -EPERM && skipped && skipped == i) || r == -ENOTSUP) { log_err(cd, _("Required kernel crypto interface not available.\n")); #ifdef ENABLE_AF_ALG log_err(cd, _("Ensure you have algif_skcipher kernel module loaded.\n")); #endif } if (r < 0) goto out; r = TCRYPT_hdr_from_disk(hdr, params, i, r); if (!r) { log_dbg("TCRYPT: Magic: %s, Header version: %d, req. %d, sector %d" ", mk_offset %" PRIu64 ", hidden_size %" PRIu64 ", volume size %" PRIu64, tcrypt_kdf[i].veracrypt ? VCRYPT_HDR_MAGIC : TCRYPT_HDR_MAGIC, (int)hdr->d.version, (int)hdr->d.version_tc, (int)hdr->d.sector_size, hdr->d.mk_offset, hdr->d.hidden_volume_size, hdr->d.volume_size); log_dbg("TCRYPT: Header cipher %s-%s, key size %zu", params->cipher, params->mode, params->key_size); } out: crypt_memzero(pwd, TCRYPT_KEY_POOL_LEN); if (key) crypt_memzero(key, TCRYPT_HDR_KEY_LEN); free(key); return r; }
/* Read verity superblock from disk */ int VERITY_read_sb(struct crypt_device *cd, uint64_t sb_offset, char **uuid_string, struct crypt_params_verity *params) { struct device *device = crypt_metadata_device(cd); int bsize = device_block_size(device); struct verity_sb sb = {}; ssize_t hdr_size = sizeof(struct verity_sb); int devfd = 0, sb_version; log_dbg("Reading VERITY header of size %zu on device %s, offset %" PRIu64 ".", sizeof(struct verity_sb), device_path(device), sb_offset); if (params->flags & CRYPT_VERITY_NO_HEADER) { log_err(cd, _("Verity device %s doesn't use on-disk header.\n"), device_path(device)); return -EINVAL; } if (sb_offset % 512) { log_err(cd, _("Unsupported VERITY hash offset.\n")); return -EINVAL; } devfd = device_open(device, O_RDONLY); if(devfd == -1) { log_err(cd, _("Cannot open device %s.\n"), device_path(device)); return -EINVAL; } if(lseek(devfd, sb_offset, SEEK_SET) < 0 || read_blockwise(devfd, bsize, &sb, hdr_size) < hdr_size) { close(devfd); return -EIO; } close(devfd); if (memcmp(sb.signature, VERITY_SIGNATURE, sizeof(sb.signature))) { log_err(cd, _("Device %s is not a valid VERITY device.\n"), device_path(device)); return -EINVAL; } sb_version = le32_to_cpu(sb.version); if (sb_version != 1) { log_err(cd, _("Unsupported VERITY version %d.\n"), sb_version); return -EINVAL; } params->hash_type = le32_to_cpu(sb.hash_type); if (params->hash_type > VERITY_MAX_HASH_TYPE) { log_err(cd, _("Unsupported VERITY hash type %d.\n"), params->hash_type); return -EINVAL; } params->data_block_size = le32_to_cpu(sb.data_block_size); params->hash_block_size = le32_to_cpu(sb.hash_block_size); if (VERITY_BLOCK_SIZE_OK(params->data_block_size) || VERITY_BLOCK_SIZE_OK(params->hash_block_size)) { log_err(cd, _("Unsupported VERITY block size.\n")); return -EINVAL; } params->data_size = le64_to_cpu(sb.data_blocks); params->hash_name = strndup((const char*)sb.algorithm, sizeof(sb.algorithm)); if (!params->hash_name) return -ENOMEM; if (crypt_hash_size(params->hash_name) <= 0) { log_err(cd, _("Hash algorithm %s not supported.\n"), params->hash_name); free(CONST_CAST(char*)params->hash_name); return -EINVAL; } params->salt_size = le16_to_cpu(sb.salt_size); if (params->salt_size > sizeof(sb.salt)) { log_err(cd, _("VERITY header corrupted.\n")); free(CONST_CAST(char*)params->hash_name); return -EINVAL; } params->salt = malloc(params->salt_size); if (!params->salt) { free(CONST_CAST(char*)params->hash_name); return -ENOMEM; } memcpy(CONST_CAST(char*)params->salt, sb.salt, params->salt_size); if ((*uuid_string = malloc(40))) uuid_unparse(sb.uuid, *uuid_string); params->hash_area_offset = sb_offset; return 0; }