/* Try to open a particular key slot */ static int LUKS_open_key(unsigned int keyIndex, const char *password, size_t passwordLen, struct luks_phdr *hdr, struct volume_key *vk, struct crypt_device *ctx) { crypt_keyslot_info ki = LUKS_keyslot_info(hdr, keyIndex); struct volume_key *derived_key; char *AfKey; size_t AFEKSize; int r; log_dbg("Trying to open key slot %d [%s].", keyIndex, dbg_slot_state(ki)); if (ki < CRYPT_SLOT_ACTIVE) return -ENOENT; derived_key = crypt_alloc_volume_key(hdr->keyBytes, NULL); if (!derived_key) return -ENOMEM; assert(vk->keylength == hdr->keyBytes); AFEKSize = AF_split_sectors(vk->keylength, hdr->keyblock[keyIndex].stripes) * SECTOR_SIZE; AfKey = crypt_safe_alloc(AFEKSize); if (!AfKey) { r = -ENOMEM; goto out; } r = crypt_pbkdf("pbkdf2", hdr->hashSpec, password, passwordLen, hdr->keyblock[keyIndex].passwordSalt, LUKS_SALTSIZE, derived_key->key, hdr->keyBytes, hdr->keyblock[keyIndex].passwordIterations); if (r < 0) goto out; log_dbg("Reading key slot %d area.", keyIndex); r = LUKS_decrypt_from_storage(AfKey, AFEKSize, hdr->cipherName, hdr->cipherMode, derived_key, hdr->keyblock[keyIndex].keyMaterialOffset, ctx); if (r < 0) goto out; r = AF_merge(AfKey,vk->key,vk->keylength,hdr->keyblock[keyIndex].stripes,hdr->hashSpec); if (r < 0) goto out; r = LUKS_verify_volume_key(hdr, vk); if (!r) log_verbose(ctx, _("Key slot %d unlocked.\n"), keyIndex); out: crypt_safe_free(AfKey); crypt_free_volume_key(derived_key); return r; }
size_t LUKS_device_sectors(const struct luks_phdr *hdr) { int sorted_areas[LUKS_NUMKEYS] = { 0, 1, 2, 3, 4, 5, 6, 7 }; LUKS_sort_keyslots(hdr, sorted_areas); return hdr->keyblock[sorted_areas[LUKS_NUMKEYS-1]].keyMaterialOffset + AF_split_sectors(hdr->keyBytes, LUKS_STRIPES); }
int LUKS_keyslot_area(struct luks_phdr *hdr, int keyslot, uint64_t *offset, uint64_t *length) { if(keyslot >= LUKS_NUMKEYS || keyslot < 0) return -EINVAL; *offset = hdr->keyblock[keyslot].keyMaterialOffset * SECTOR_SIZE; *length = AF_split_sectors(hdr->keyBytes, LUKS_STRIPES) * SECTOR_SIZE; return 0; }
/* Get size of struct luks_phdr with all keyslots material space */ static size_t LUKS_device_sectors(size_t keyLen) { size_t keyslot_sectors, sector; int i; keyslot_sectors = AF_split_sectors(keyLen, LUKS_STRIPES); sector = LUKS_ALIGN_KEYSLOTS / SECTOR_SIZE; for (i = 0; i < LUKS_NUMKEYS; i++) { sector = size_round_up(sector, LUKS_ALIGN_KEYSLOTS / SECTOR_SIZE); sector += keyslot_sectors; } return sector; }
int LUKS_del_key(unsigned int keyIndex, struct luks_phdr *hdr, struct crypt_device *ctx) { struct device *device = crypt_metadata_device(ctx); unsigned int startOffset, endOffset; int r; r = LUKS_read_phdr(hdr, 1, 0, ctx); if (r) return r; r = LUKS_keyslot_set(hdr, keyIndex, 0); if (r) { log_err(ctx, _("Key slot %d is invalid, please select keyslot between 0 and %d.\n"), keyIndex, LUKS_NUMKEYS - 1); return r; } /* secure deletion of key material */ startOffset = hdr->keyblock[keyIndex].keyMaterialOffset; endOffset = startOffset + AF_split_sectors(hdr->keyBytes, hdr->keyblock[keyIndex].stripes); r = crypt_wipe(device, startOffset * SECTOR_SIZE, (endOffset - startOffset) * SECTOR_SIZE, CRYPT_WIPE_DISK, 0); if (r) { if (r == -EACCES) { log_err(ctx, _("Cannot write to device %s, permission denied.\n"), device_path(device)); r = -EINVAL; } else log_err(ctx, _("Cannot wipe device %s.\n"), device_path(device)); return r; } /* Wipe keyslot info */ memset(&hdr->keyblock[keyIndex].passwordSalt, 0, LUKS_SALTSIZE); hdr->keyblock[keyIndex].passwordIterations = 0; r = LUKS_write_phdr(hdr, ctx); return r; }
/* Check keyslot to prevent access outside of header and keyslot area */ static int LUKS_check_keyslot_size(const struct luks_phdr *phdr, unsigned int keyIndex) { uint32_t secs_per_stripes; /* First sectors is the header itself */ if (phdr->keyblock[keyIndex].keyMaterialOffset * SECTOR_SIZE < sizeof(*phdr)) { log_dbg("Invalid offset %u in keyslot %u.", phdr->keyblock[keyIndex].keyMaterialOffset, keyIndex); return 1; } /* Ignore following check for detached header where offset can be zero. */ if (phdr->payloadOffset == 0) return 0; if (phdr->payloadOffset <= phdr->keyblock[keyIndex].keyMaterialOffset) { log_dbg("Invalid offset %u in keyslot %u (beyond data area offset %u).", phdr->keyblock[keyIndex].keyMaterialOffset, keyIndex, phdr->payloadOffset); return 1; } secs_per_stripes = AF_split_sectors(phdr->keyBytes, phdr->keyblock[keyIndex].stripes); if (phdr->payloadOffset < (phdr->keyblock[keyIndex].keyMaterialOffset + secs_per_stripes)) { log_dbg("Invalid keyslot size %u (offset %u, stripes %u) in " "keyslot %u (beyond data area offset %u).", secs_per_stripes, phdr->keyblock[keyIndex].keyMaterialOffset, phdr->keyblock[keyIndex].stripes, keyIndex, phdr->payloadOffset); return 1; } return 0; }
int LUKS_set_key(unsigned int keyIndex, const char *password, size_t passwordLen, struct luks_phdr *hdr, struct volume_key *vk, uint32_t iteration_time_ms, uint64_t *PBKDF2_per_sec, struct crypt_device *ctx) { struct volume_key *derived_key; char *AfKey = NULL; size_t AFEKSize; uint64_t PBKDF2_temp; int r; if(hdr->keyblock[keyIndex].active != LUKS_KEY_DISABLED) { log_err(ctx, _("Key slot %d active, purge first.\n"), keyIndex); return -EINVAL; } /* LUKS keyslot has always at least 4000 stripes accoding to specification */ if(hdr->keyblock[keyIndex].stripes < 4000) { log_err(ctx, _("Key slot %d material includes too few stripes. Header manipulation?\n"), keyIndex); return -EINVAL; } log_dbg("Calculating data for key slot %d", keyIndex); r = crypt_benchmark_kdf(ctx, "pbkdf2", hdr->hashSpec, "foo", 3, "bar", 3, PBKDF2_per_sec); if (r < 0) { log_err(ctx, _("Not compatible PBKDF2 options (using hash algorithm %s).\n"), hdr->hashSpec); return r; } /* * Avoid floating point operation * Final iteration count is at least LUKS_SLOT_ITERATIONS_MIN */ PBKDF2_temp = (*PBKDF2_per_sec / 2) * (uint64_t)iteration_time_ms; PBKDF2_temp /= 1024; if (PBKDF2_temp > UINT32_MAX) PBKDF2_temp = UINT32_MAX; hdr->keyblock[keyIndex].passwordIterations = at_least((uint32_t)PBKDF2_temp, LUKS_SLOT_ITERATIONS_MIN); log_dbg("Key slot %d use %" PRIu32 " password iterations.", keyIndex, hdr->keyblock[keyIndex].passwordIterations); derived_key = crypt_alloc_volume_key(hdr->keyBytes, NULL); if (!derived_key) return -ENOMEM; r = crypt_random_get(ctx, hdr->keyblock[keyIndex].passwordSalt, LUKS_SALTSIZE, CRYPT_RND_SALT); if (r < 0) goto out; r = crypt_pbkdf("pbkdf2", hdr->hashSpec, password, passwordLen, hdr->keyblock[keyIndex].passwordSalt, LUKS_SALTSIZE, derived_key->key, hdr->keyBytes, hdr->keyblock[keyIndex].passwordIterations); if (r < 0) goto out; /* * AF splitting, the masterkey stored in vk->key is split to AfKey */ assert(vk->keylength == hdr->keyBytes); AFEKSize = AF_split_sectors(vk->keylength, hdr->keyblock[keyIndex].stripes) * SECTOR_SIZE; AfKey = crypt_safe_alloc(AFEKSize); if (!AfKey) { r = -ENOMEM; goto out; } log_dbg("Using hash %s for AF in key slot %d, %d stripes", hdr->hashSpec, keyIndex, hdr->keyblock[keyIndex].stripes); r = AF_split(vk->key,AfKey,vk->keylength,hdr->keyblock[keyIndex].stripes,hdr->hashSpec); if (r < 0) goto out; log_dbg("Updating key slot %d [0x%04x] area.", keyIndex, hdr->keyblock[keyIndex].keyMaterialOffset << 9); /* Encryption via dm */ r = LUKS_encrypt_to_storage(AfKey, AFEKSize, hdr->cipherName, hdr->cipherMode, derived_key, hdr->keyblock[keyIndex].keyMaterialOffset, ctx); if (r < 0) goto out; /* Mark the key as active in phdr */ r = LUKS_keyslot_set(hdr, (int)keyIndex, 1); if (r < 0) goto out; r = LUKS_write_phdr(hdr, ctx); if (r < 0) goto out; r = 0; out: crypt_safe_free(AfKey); crypt_free_volume_key(derived_key); return r; }
int LUKS_generate_phdr(struct luks_phdr *header, const struct volume_key *vk, const char *cipherName, const char *cipherMode, const char *hashSpec, const char *uuid, unsigned int stripes, unsigned int alignPayload, unsigned int alignOffset, uint32_t iteration_time_ms, uint64_t *PBKDF2_per_sec, int detached_metadata_device, struct crypt_device *ctx) { unsigned int i = 0, hdr_sectors = LUKS_device_sectors(vk->keylength); size_t blocksPerStripeSet, currentSector; int r; uuid_t partitionUuid; char luksMagic[] = LUKS_MAGIC; /* For separate metadata device allow zero alignment */ if (alignPayload == 0 && !detached_metadata_device) alignPayload = DEFAULT_DISK_ALIGNMENT / SECTOR_SIZE; if (alignPayload && detached_metadata_device && alignPayload < hdr_sectors) { log_err(ctx, _("Data offset for detached LUKS header must be " "either 0 or higher than header size (%d sectors).\n"), hdr_sectors); return -EINVAL; } if (crypt_hmac_size(hashSpec) < LUKS_DIGESTSIZE) { log_err(ctx, _("Requested LUKS hash %s is not supported.\n"), hashSpec); return -EINVAL; } if (uuid && uuid_parse(uuid, partitionUuid) == -1) { log_err(ctx, _("Wrong LUKS UUID format provided.\n")); return -EINVAL; } if (!uuid) uuid_generate(partitionUuid); memset(header,0,sizeof(struct luks_phdr)); /* Set Magic */ memcpy(header->magic,luksMagic,LUKS_MAGIC_L); header->version=1; strncpy(header->cipherName,cipherName,LUKS_CIPHERNAME_L); strncpy(header->cipherMode,cipherMode,LUKS_CIPHERMODE_L); strncpy(header->hashSpec,hashSpec,LUKS_HASHSPEC_L); header->keyBytes=vk->keylength; LUKS_fix_header_compatible(header); r = LUKS_check_cipher(header, ctx); if (r < 0) return r; log_dbg("Generating LUKS header version %d using hash %s, %s, %s, MK %d bytes", header->version, header->hashSpec ,header->cipherName, header->cipherMode, header->keyBytes); r = crypt_random_get(ctx, header->mkDigestSalt, LUKS_SALTSIZE, CRYPT_RND_SALT); if(r < 0) { log_err(ctx, _("Cannot create LUKS header: reading random salt failed.\n")); return r; } r = crypt_benchmark_kdf(ctx, "pbkdf2", header->hashSpec, "foo", 3, "bar", 3, PBKDF2_per_sec); if (r < 0) { log_err(ctx, _("Not compatible PBKDF2 options (using hash algorithm %s).\n"), header->hashSpec); return r; } /* Compute master key digest */ iteration_time_ms /= 8; header->mkDigestIterations = at_least((uint32_t)(*PBKDF2_per_sec/1024) * iteration_time_ms, LUKS_MKD_ITERATIONS_MIN); r = crypt_pbkdf("pbkdf2", header->hashSpec, vk->key,vk->keylength, header->mkDigestSalt, LUKS_SALTSIZE, header->mkDigest,LUKS_DIGESTSIZE, header->mkDigestIterations); if(r < 0) { log_err(ctx, _("Cannot create LUKS header: header digest failed (using hash %s).\n"), header->hashSpec); return r; } currentSector = LUKS_ALIGN_KEYSLOTS / SECTOR_SIZE; blocksPerStripeSet = AF_split_sectors(vk->keylength, stripes); for(i = 0; i < LUKS_NUMKEYS; ++i) { header->keyblock[i].active = LUKS_KEY_DISABLED; header->keyblock[i].keyMaterialOffset = currentSector; header->keyblock[i].stripes = stripes; currentSector = size_round_up(currentSector + blocksPerStripeSet, LUKS_ALIGN_KEYSLOTS / SECTOR_SIZE); } if (detached_metadata_device) { /* for separate metadata device use alignPayload directly */ header->payloadOffset = alignPayload; } else { /* alignOffset - offset from natural device alignment provided by topology info */ currentSector = size_round_up(currentSector, alignPayload); header->payloadOffset = currentSector + alignOffset; } uuid_unparse(partitionUuid, header->uuid); log_dbg("Data offset %d, UUID %s, digest iterations %" PRIu32, header->payloadOffset, header->uuid, header->mkDigestIterations); return 0; }
int LUKS_set_key(unsigned int keyIndex, const char *password, size_t passwordLen, struct luks_phdr *hdr, struct volume_key *vk, struct crypt_device *ctx) { struct volume_key *derived_key; char *AfKey = NULL; size_t AFEKSize; struct crypt_pbkdf_type *pbkdf; int r; if(hdr->keyblock[keyIndex].active != LUKS_KEY_DISABLED) { log_err(ctx, _("Key slot %d active, purge first."), keyIndex); return -EINVAL; } /* LUKS keyslot has always at least 4000 stripes according to specification */ if(hdr->keyblock[keyIndex].stripes < 4000) { log_err(ctx, _("Key slot %d material includes too few stripes. Header manipulation?"), keyIndex); return -EINVAL; } log_dbg("Calculating data for key slot %d", keyIndex); pbkdf = crypt_get_pbkdf(ctx); r = crypt_benchmark_pbkdf_internal(ctx, pbkdf, vk->keylength); if (r < 0) return r; assert(pbkdf->iterations); /* * Final iteration count is at least LUKS_SLOT_ITERATIONS_MIN */ hdr->keyblock[keyIndex].passwordIterations = at_least(pbkdf->iterations, LUKS_SLOT_ITERATIONS_MIN); log_dbg("Key slot %d use %" PRIu32 " password iterations.", keyIndex, hdr->keyblock[keyIndex].passwordIterations); derived_key = crypt_alloc_volume_key(hdr->keyBytes, NULL); if (!derived_key) return -ENOMEM; r = crypt_random_get(ctx, hdr->keyblock[keyIndex].passwordSalt, LUKS_SALTSIZE, CRYPT_RND_SALT); if (r < 0) goto out; r = crypt_pbkdf(CRYPT_KDF_PBKDF2, hdr->hashSpec, password, passwordLen, hdr->keyblock[keyIndex].passwordSalt, LUKS_SALTSIZE, derived_key->key, hdr->keyBytes, hdr->keyblock[keyIndex].passwordIterations, 0, 0); if (r < 0) goto out; /* * AF splitting, the masterkey stored in vk->key is split to AfKey */ assert(vk->keylength == hdr->keyBytes); AFEKSize = AF_split_sectors(vk->keylength, hdr->keyblock[keyIndex].stripes) * SECTOR_SIZE; AfKey = crypt_safe_alloc(AFEKSize); if (!AfKey) { r = -ENOMEM; goto out; } log_dbg("Using hash %s for AF in key slot %d, %d stripes", hdr->hashSpec, keyIndex, hdr->keyblock[keyIndex].stripes); r = AF_split(vk->key,AfKey,vk->keylength,hdr->keyblock[keyIndex].stripes,hdr->hashSpec); if (r < 0) goto out; log_dbg("Updating key slot %d [0x%04x] area.", keyIndex, hdr->keyblock[keyIndex].keyMaterialOffset << 9); /* Encryption via dm */ r = LUKS_encrypt_to_storage(AfKey, AFEKSize, hdr->cipherName, hdr->cipherMode, derived_key, hdr->keyblock[keyIndex].keyMaterialOffset, ctx); if (r < 0) goto out; /* Mark the key as active in phdr */ r = LUKS_keyslot_set(hdr, (int)keyIndex, 1); if (r < 0) goto out; r = LUKS_write_phdr(hdr, ctx); if (r < 0) goto out; r = 0; out: crypt_safe_free(AfKey); crypt_free_volume_key(derived_key); return r; }
static int LUKS_check_keyslots(struct crypt_device *ctx, const struct luks_phdr *phdr) { int i, prev, next, sorted_areas[LUKS_NUMKEYS] = { 0, 1, 2, 3, 4, 5, 6, 7 }; uint32_t secs_per_stripes = AF_split_sectors(phdr->keyBytes, LUKS_STRIPES); LUKS_sort_keyslots(phdr, sorted_areas); /* Check keyslot to prevent access outside of header and keyslot area */ for (i = 0; i < LUKS_NUMKEYS; i++) { /* enforce stripes == 4000 */ if (phdr->keyblock[i].stripes != LUKS_STRIPES) { log_dbg("Invalid stripes count %u in keyslot %u.", phdr->keyblock[i].stripes, i); log_err(ctx, _("LUKS keyslot %u is invalid."), i); return -1; } /* First sectors is the header itself */ if (phdr->keyblock[i].keyMaterialOffset * SECTOR_SIZE < sizeof(*phdr)) { log_dbg("Invalid offset %u in keyslot %u.", phdr->keyblock[i].keyMaterialOffset, i); log_err(ctx, _("LUKS keyslot %u is invalid."), i); return -1; } /* Ignore following check for detached header where offset can be zero. */ if (phdr->payloadOffset == 0) continue; if (phdr->payloadOffset <= phdr->keyblock[i].keyMaterialOffset) { log_dbg("Invalid offset %u in keyslot %u (beyond data area offset %u).", phdr->keyblock[i].keyMaterialOffset, i, phdr->payloadOffset); log_err(ctx, _("LUKS keyslot %u is invalid."), i); return -1; } if (phdr->payloadOffset < (phdr->keyblock[i].keyMaterialOffset + secs_per_stripes)) { log_dbg("Invalid keyslot size %u (offset %u, stripes %u) in " "keyslot %u (beyond data area offset %u).", secs_per_stripes, phdr->keyblock[i].keyMaterialOffset, phdr->keyblock[i].stripes, i, phdr->payloadOffset); log_err(ctx, _("LUKS keyslot %u is invalid."), i); return -1; } } /* check no keyslot overlaps with each other */ for (i = 1; i < LUKS_NUMKEYS; i++) { prev = sorted_areas[i-1]; next = sorted_areas[i]; if (phdr->keyblock[next].keyMaterialOffset < (phdr->keyblock[prev].keyMaterialOffset + secs_per_stripes)) { log_dbg("Not enough space in LUKS keyslot %d.", prev); log_err(ctx, _("LUKS keyslot %u is invalid."), prev); return -1; } } /* do not check last keyslot on purpose, it must be tested in device size check */ return 0; }
int LUKS_generate_phdr(struct luks_phdr *header, const struct volume_key *vk, const char *cipherName, const char *cipherMode, const char *hashSpec, const char *uuid, uint64_t data_offset, /* in bytes */ uint64_t align_offset, /* in bytes */ uint64_t required_alignment, /* in bytes */ struct crypt_device *ctx) { int i, r; size_t keyslot_sectors, header_sectors; uuid_t partitionUuid; struct crypt_pbkdf_type *pbkdf; double PBKDF2_temp; char luksMagic[] = LUKS_MAGIC; if (data_offset % SECTOR_SIZE || align_offset % SECTOR_SIZE || required_alignment % SECTOR_SIZE) return -EINVAL; memset(header, 0, sizeof(struct luks_phdr)); keyslot_sectors = AF_split_sectors(vk->keylength, LUKS_STRIPES); header_sectors = LUKS_ALIGN_KEYSLOTS / SECTOR_SIZE; for (i = 0; i < LUKS_NUMKEYS; i++) { header->keyblock[i].active = LUKS_KEY_DISABLED; header->keyblock[i].keyMaterialOffset = header_sectors; header->keyblock[i].stripes = LUKS_STRIPES; header_sectors = size_round_up(header_sectors + keyslot_sectors, LUKS_ALIGN_KEYSLOTS / SECTOR_SIZE); } /* In sector is now size of all keyslot material space */ /* Data offset has priority */ if (data_offset) header->payloadOffset = data_offset / SECTOR_SIZE; else if (required_alignment) { header->payloadOffset = size_round_up(header_sectors, (required_alignment / SECTOR_SIZE)); header->payloadOffset += (align_offset / SECTOR_SIZE); } else header->payloadOffset = 0; if (header->payloadOffset && header->payloadOffset < header_sectors) { log_err(ctx, _("Data offset for LUKS header must be " "either 0 or higher than header size.")); return -EINVAL; } if (crypt_hmac_size(hashSpec) < LUKS_DIGESTSIZE) { log_err(ctx, _("Requested LUKS hash %s is not supported."), hashSpec); return -EINVAL; } if (uuid && uuid_parse(uuid, partitionUuid) == -1) { log_err(ctx, _("Wrong LUKS UUID format provided.")); return -EINVAL; } if (!uuid) uuid_generate(partitionUuid); /* Set Magic */ memcpy(header->magic,luksMagic,LUKS_MAGIC_L); header->version=1; strncpy(header->cipherName,cipherName,LUKS_CIPHERNAME_L-1); strncpy(header->cipherMode,cipherMode,LUKS_CIPHERMODE_L-1); strncpy(header->hashSpec,hashSpec,LUKS_HASHSPEC_L-1); header->keyBytes=vk->keylength; LUKS_fix_header_compatible(header); log_dbg(ctx, "Generating LUKS header version %d using hash %s, %s, %s, MK %d bytes", header->version, header->hashSpec ,header->cipherName, header->cipherMode, header->keyBytes); r = crypt_random_get(ctx, header->mkDigestSalt, LUKS_SALTSIZE, CRYPT_RND_SALT); if(r < 0) { log_err(ctx, _("Cannot create LUKS header: reading random salt failed.")); return r; } /* Compute master key digest */ pbkdf = crypt_get_pbkdf(ctx); r = crypt_benchmark_pbkdf_internal(ctx, pbkdf, vk->keylength); if (r < 0) return r; assert(pbkdf->iterations); PBKDF2_temp = (double)pbkdf->iterations * LUKS_MKD_ITERATIONS_MS / pbkdf->time_ms; if (PBKDF2_temp > (double)UINT32_MAX) return -EINVAL; header->mkDigestIterations = at_least((uint32_t)PBKDF2_temp, LUKS_MKD_ITERATIONS_MIN); r = crypt_pbkdf(CRYPT_KDF_PBKDF2, header->hashSpec, vk->key,vk->keylength, header->mkDigestSalt, LUKS_SALTSIZE, header->mkDigest,LUKS_DIGESTSIZE, header->mkDigestIterations, 0, 0); if (r < 0) { log_err(ctx, _("Cannot create LUKS header: header digest failed (using hash %s)."), header->hashSpec); return r; } uuid_unparse(partitionUuid, header->uuid); log_dbg(ctx, "Data offset %d, UUID %s, digest iterations %" PRIu32, header->payloadOffset, header->uuid, header->mkDigestIterations); return 0; }