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, int detached_metadata_device, struct crypt_device *ctx) { unsigned int i = 0, hdr_sectors = LUKS_calculate_device_sectors(vk->keylength); size_t blocksPerStripeSet, currentSector; int r; uuid_t partitionUuid; struct crypt_pbkdf_type *pbkdf; double PBKDF2_temp; 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)."), hdr_sectors); 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); 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-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("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; } 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; }
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; }