|
Packit |
94f725 |
/*
|
|
Packit |
94f725 |
* Generic wrapper for storage encryption modes and Initial Vectors
|
|
Packit |
94f725 |
* (reimplementation of some functions from Linux dm-crypt kernel)
|
|
Packit |
94f725 |
*
|
|
Packit |
94f725 |
* Copyright (C) 2014-2020 Milan Broz
|
|
Packit |
94f725 |
*
|
|
Packit |
94f725 |
* This file is free software; you can redistribute it and/or
|
|
Packit |
94f725 |
* modify it under the terms of the GNU Lesser General Public
|
|
Packit |
94f725 |
* License as published by the Free Software Foundation; either
|
|
Packit |
94f725 |
* version 2.1 of the License, or (at your option) any later version.
|
|
Packit |
94f725 |
*
|
|
Packit |
94f725 |
* This file is distributed in the hope that it will be useful,
|
|
Packit |
94f725 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
94f725 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
94f725 |
* Lesser General Public License for more details.
|
|
Packit |
94f725 |
*
|
|
Packit |
94f725 |
* You should have received a copy of the GNU Lesser General Public
|
|
Packit |
94f725 |
* License along with this file; if not, write to the Free Software
|
|
Packit |
94f725 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
Packit |
94f725 |
*/
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
#include <stdlib.h>
|
|
Packit |
94f725 |
#include <errno.h>
|
|
Packit |
94f725 |
#include "bitops.h"
|
|
Packit |
94f725 |
#include "crypto_backend.h"
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
#define SECTOR_SHIFT 9
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
/*
|
|
Packit |
94f725 |
* Internal IV helper
|
|
Packit |
94f725 |
* IV documentation: https://gitlab.com/cryptsetup/cryptsetup/wikis/DMCrypt
|
|
Packit |
94f725 |
*/
|
|
Packit |
94f725 |
struct crypt_sector_iv {
|
|
Packit |
94f725 |
enum { IV_NONE, IV_NULL, IV_PLAIN, IV_PLAIN64, IV_ESSIV, IV_BENBI, IV_PLAIN64BE, IV_EBOIV } type;
|
|
Packit |
94f725 |
int iv_size;
|
|
Packit |
94f725 |
char *iv;
|
|
Packit |
94f725 |
struct crypt_cipher *cipher;
|
|
Packit |
94f725 |
int shift;
|
|
Packit |
94f725 |
};
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
/* Block encryption storage context */
|
|
Packit |
94f725 |
struct crypt_storage {
|
|
Packit |
94f725 |
unsigned sector_shift;
|
|
Packit |
94f725 |
unsigned iv_shift;
|
|
Packit |
94f725 |
struct crypt_cipher *cipher;
|
|
Packit |
94f725 |
struct crypt_sector_iv cipher_iv;
|
|
Packit |
94f725 |
};
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
static int int_log2(unsigned int x)
|
|
Packit |
94f725 |
{
|
|
Packit |
94f725 |
int r = 0;
|
|
Packit |
94f725 |
for (x >>= 1; x > 0; x >>= 1)
|
|
Packit |
94f725 |
r++;
|
|
Packit |
94f725 |
return r;
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
static int crypt_sector_iv_init(struct crypt_sector_iv *ctx,
|
|
Packit |
94f725 |
const char *cipher_name, const char *mode_name,
|
|
Packit |
94f725 |
const char *iv_name, const void *key, size_t key_length, size_t sector_size)
|
|
Packit |
94f725 |
{
|
|
Packit |
94f725 |
int r;
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
memset(ctx, 0, sizeof(*ctx));
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
ctx->iv_size = crypt_cipher_ivsize(cipher_name, mode_name);
|
|
Packit Service |
047db2 |
if (ctx->iv_size < 0 || (strcmp(mode_name, "ecb") && ctx->iv_size < 8))
|
|
Packit |
94f725 |
return -ENOENT;
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
if (!strcmp(cipher_name, "cipher_null") ||
|
|
Packit |
94f725 |
!strcmp(mode_name, "ecb")) {
|
|
Packit |
94f725 |
if (iv_name)
|
|
Packit |
94f725 |
return -EINVAL;
|
|
Packit |
94f725 |
ctx->type = IV_NONE;
|
|
Packit |
94f725 |
ctx->iv_size = 0;
|
|
Packit |
94f725 |
return 0;
|
|
Packit |
94f725 |
} else if (!iv_name) {
|
|
Packit |
94f725 |
return -EINVAL;
|
|
Packit |
94f725 |
} else if (!strcasecmp(iv_name, "null")) {
|
|
Packit |
94f725 |
ctx->type = IV_NULL;
|
|
Packit |
94f725 |
} else if (!strcasecmp(iv_name, "plain64")) {
|
|
Packit |
94f725 |
ctx->type = IV_PLAIN64;
|
|
Packit |
94f725 |
} else if (!strcasecmp(iv_name, "plain64be")) {
|
|
Packit |
94f725 |
ctx->type = IV_PLAIN64BE;
|
|
Packit |
94f725 |
} else if (!strcasecmp(iv_name, "plain")) {
|
|
Packit |
94f725 |
ctx->type = IV_PLAIN;
|
|
Packit |
94f725 |
} else if (!strncasecmp(iv_name, "essiv:", 6)) {
|
|
Packit |
94f725 |
struct crypt_hash *h = NULL;
|
|
Packit |
94f725 |
char *hash_name = strchr(iv_name, ':');
|
|
Packit |
94f725 |
int hash_size;
|
|
Packit |
94f725 |
char tmp[256];
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
if (!hash_name)
|
|
Packit |
94f725 |
return -EINVAL;
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
hash_size = crypt_hash_size(++hash_name);
|
|
Packit |
94f725 |
if (hash_size < 0)
|
|
Packit |
94f725 |
return -ENOENT;
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
if ((unsigned)hash_size > sizeof(tmp))
|
|
Packit |
94f725 |
return -EINVAL;
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
if (crypt_hash_init(&h, hash_name))
|
|
Packit |
94f725 |
return -EINVAL;
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
r = crypt_hash_write(h, key, key_length);
|
|
Packit |
94f725 |
if (r) {
|
|
Packit |
94f725 |
crypt_hash_destroy(h);
|
|
Packit |
94f725 |
return r;
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
r = crypt_hash_final(h, tmp, hash_size);
|
|
Packit |
94f725 |
crypt_hash_destroy(h);
|
|
Packit |
94f725 |
if (r) {
|
|
Packit |
94f725 |
crypt_backend_memzero(tmp, sizeof(tmp));
|
|
Packit |
94f725 |
return r;
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
r = crypt_cipher_init(&ctx->cipher, cipher_name, "ecb",
|
|
Packit |
94f725 |
tmp, hash_size);
|
|
Packit |
94f725 |
crypt_backend_memzero(tmp, sizeof(tmp));
|
|
Packit |
94f725 |
if (r)
|
|
Packit |
94f725 |
return r;
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
ctx->type = IV_ESSIV;
|
|
Packit |
94f725 |
} else if (!strncasecmp(iv_name, "benbi", 5)) {
|
|
Packit |
94f725 |
int log = int_log2(ctx->iv_size);
|
|
Packit |
94f725 |
if (log > SECTOR_SHIFT)
|
|
Packit |
94f725 |
return -EINVAL;
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
ctx->type = IV_BENBI;
|
|
Packit |
94f725 |
ctx->shift = SECTOR_SHIFT - log;
|
|
Packit |
94f725 |
} else if (!strncasecmp(iv_name, "eboiv", 5)) {
|
|
Packit |
94f725 |
r = crypt_cipher_init(&ctx->cipher, cipher_name, "ecb",
|
|
Packit |
94f725 |
key, key_length);
|
|
Packit |
94f725 |
if (r)
|
|
Packit |
94f725 |
return r;
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
ctx->type = IV_EBOIV;
|
|
Packit |
94f725 |
ctx->shift = int_log2(sector_size);
|
|
Packit |
94f725 |
} else
|
|
Packit |
94f725 |
return -ENOENT;
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
ctx->iv = malloc(ctx->iv_size);
|
|
Packit |
94f725 |
if (!ctx->iv)
|
|
Packit |
94f725 |
return -ENOMEM;
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
return 0;
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
static int crypt_sector_iv_generate(struct crypt_sector_iv *ctx, uint64_t sector)
|
|
Packit |
94f725 |
{
|
|
Packit |
94f725 |
uint64_t val;
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
switch (ctx->type) {
|
|
Packit |
94f725 |
case IV_NONE:
|
|
Packit |
94f725 |
break;
|
|
Packit |
94f725 |
case IV_NULL:
|
|
Packit |
94f725 |
memset(ctx->iv, 0, ctx->iv_size);
|
|
Packit |
94f725 |
break;
|
|
Packit |
94f725 |
case IV_PLAIN:
|
|
Packit |
94f725 |
memset(ctx->iv, 0, ctx->iv_size);
|
|
Packit |
94f725 |
*(uint32_t *)ctx->iv = cpu_to_le32(sector & 0xffffffff);
|
|
Packit |
94f725 |
break;
|
|
Packit |
94f725 |
case IV_PLAIN64:
|
|
Packit |
94f725 |
memset(ctx->iv, 0, ctx->iv_size);
|
|
Packit |
94f725 |
*(uint64_t *)ctx->iv = cpu_to_le64(sector);
|
|
Packit |
94f725 |
break;
|
|
Packit |
94f725 |
case IV_PLAIN64BE:
|
|
Packit |
94f725 |
memset(ctx->iv, 0, ctx->iv_size);
|
|
Packit |
94f725 |
*(uint64_t *)&ctx->iv[ctx->iv_size - sizeof(uint64_t)] = cpu_to_be64(sector);
|
|
Packit |
94f725 |
break;
|
|
Packit |
94f725 |
case IV_ESSIV:
|
|
Packit |
94f725 |
memset(ctx->iv, 0, ctx->iv_size);
|
|
Packit |
94f725 |
*(uint64_t *)ctx->iv = cpu_to_le64(sector);
|
|
Packit |
94f725 |
return crypt_cipher_encrypt(ctx->cipher,
|
|
Packit |
94f725 |
ctx->iv, ctx->iv, ctx->iv_size, NULL, 0);
|
|
Packit |
94f725 |
break;
|
|
Packit |
94f725 |
case IV_BENBI:
|
|
Packit |
94f725 |
memset(ctx->iv, 0, ctx->iv_size);
|
|
Packit |
94f725 |
val = cpu_to_be64((sector << ctx->shift) + 1);
|
|
Packit |
94f725 |
memcpy(ctx->iv + ctx->iv_size - sizeof(val), &val, sizeof(val));
|
|
Packit |
94f725 |
break;
|
|
Packit |
94f725 |
case IV_EBOIV:
|
|
Packit |
94f725 |
memset(ctx->iv, 0, ctx->iv_size);
|
|
Packit |
94f725 |
*(uint64_t *)ctx->iv = cpu_to_le64(sector << ctx->shift);
|
|
Packit |
94f725 |
return crypt_cipher_encrypt(ctx->cipher,
|
|
Packit |
94f725 |
ctx->iv, ctx->iv, ctx->iv_size, NULL, 0);
|
|
Packit |
94f725 |
break;
|
|
Packit |
94f725 |
default:
|
|
Packit |
94f725 |
return -EINVAL;
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
return 0;
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
static void crypt_sector_iv_destroy(struct crypt_sector_iv *ctx)
|
|
Packit |
94f725 |
{
|
|
Packit |
94f725 |
if (ctx->type == IV_ESSIV || ctx->type == IV_EBOIV)
|
|
Packit |
94f725 |
crypt_cipher_destroy(ctx->cipher);
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
if (ctx->iv) {
|
|
Packit |
94f725 |
memset(ctx->iv, 0, ctx->iv_size);
|
|
Packit |
94f725 |
free(ctx->iv);
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
memset(ctx, 0, sizeof(*ctx));
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
/* Block encryption storage wrappers */
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
int crypt_storage_init(struct crypt_storage **ctx,
|
|
Packit |
94f725 |
size_t sector_size,
|
|
Packit |
94f725 |
const char *cipher,
|
|
Packit |
94f725 |
const char *cipher_mode,
|
|
Packit |
94f725 |
const void *key, size_t key_length)
|
|
Packit |
94f725 |
{
|
|
Packit |
94f725 |
struct crypt_storage *s;
|
|
Packit |
94f725 |
char mode_name[64];
|
|
Packit |
94f725 |
char *cipher_iv = NULL;
|
|
Packit |
94f725 |
int r = -EIO;
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
if (sector_size < (1 << SECTOR_SHIFT) ||
|
|
Packit |
94f725 |
sector_size > (1 << (SECTOR_SHIFT + 3)) ||
|
|
Packit |
94f725 |
sector_size & (sector_size - 1))
|
|
Packit |
94f725 |
return -EINVAL;
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
s = malloc(sizeof(*s));
|
|
Packit |
94f725 |
if (!s)
|
|
Packit |
94f725 |
return -ENOMEM;
|
|
Packit |
94f725 |
memset(s, 0, sizeof(*s));
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
/* Remove IV if present */
|
|
Packit |
94f725 |
strncpy(mode_name, cipher_mode, sizeof(mode_name));
|
|
Packit |
94f725 |
mode_name[sizeof(mode_name) - 1] = 0;
|
|
Packit |
94f725 |
cipher_iv = strchr(mode_name, '-');
|
|
Packit |
94f725 |
if (cipher_iv) {
|
|
Packit |
94f725 |
*cipher_iv = '\0';
|
|
Packit |
94f725 |
cipher_iv++;
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
r = crypt_cipher_init(&s->cipher, cipher, mode_name, key, key_length);
|
|
Packit |
94f725 |
if (r) {
|
|
Packit |
94f725 |
crypt_storage_destroy(s);
|
|
Packit |
94f725 |
return r;
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
r = crypt_sector_iv_init(&s->cipher_iv, cipher, mode_name, cipher_iv, key, key_length, sector_size);
|
|
Packit |
94f725 |
if (r) {
|
|
Packit |
94f725 |
crypt_storage_destroy(s);
|
|
Packit |
94f725 |
return r;
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
s->sector_shift = int_log2(sector_size);
|
|
Packit |
94f725 |
s->iv_shift = s->sector_shift - SECTOR_SHIFT;
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
*ctx = s;
|
|
Packit |
94f725 |
return 0;
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
int crypt_storage_decrypt(struct crypt_storage *ctx,
|
|
Packit |
94f725 |
uint64_t iv_offset,
|
|
Packit |
94f725 |
uint64_t length, char *buffer)
|
|
Packit |
94f725 |
{
|
|
Packit |
94f725 |
uint64_t i;
|
|
Packit |
94f725 |
int r = 0;
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
if (length & ((1 << ctx->sector_shift) - 1))
|
|
Packit |
94f725 |
return -EINVAL;
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
length >>= ctx->sector_shift;
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
for (i = 0; i < length; i++) {
|
|
Packit |
94f725 |
r = crypt_sector_iv_generate(&ctx->cipher_iv, iv_offset + (uint64_t)(i << ctx->iv_shift));
|
|
Packit |
94f725 |
if (r)
|
|
Packit |
94f725 |
break;
|
|
Packit |
94f725 |
r = crypt_cipher_decrypt(ctx->cipher,
|
|
Packit |
94f725 |
&buffer[i << ctx->sector_shift],
|
|
Packit |
94f725 |
&buffer[i << ctx->sector_shift],
|
|
Packit |
94f725 |
1 << ctx->sector_shift,
|
|
Packit |
94f725 |
ctx->cipher_iv.iv,
|
|
Packit |
94f725 |
ctx->cipher_iv.iv_size);
|
|
Packit |
94f725 |
if (r)
|
|
Packit |
94f725 |
break;
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
return r;
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
int crypt_storage_encrypt(struct crypt_storage *ctx,
|
|
Packit |
94f725 |
uint64_t iv_offset,
|
|
Packit |
94f725 |
uint64_t length, char *buffer)
|
|
Packit |
94f725 |
{
|
|
Packit |
94f725 |
uint64_t i;
|
|
Packit |
94f725 |
int r = 0;
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
if (length & ((1 << ctx->sector_shift) - 1))
|
|
Packit |
94f725 |
return -EINVAL;
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
length >>= ctx->sector_shift;
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
for (i = 0; i < length; i++) {
|
|
Packit |
94f725 |
r = crypt_sector_iv_generate(&ctx->cipher_iv, iv_offset + (i << ctx->iv_shift));
|
|
Packit |
94f725 |
if (r)
|
|
Packit |
94f725 |
break;
|
|
Packit |
94f725 |
r = crypt_cipher_encrypt(ctx->cipher,
|
|
Packit |
94f725 |
&buffer[i << ctx->sector_shift],
|
|
Packit |
94f725 |
&buffer[i << ctx->sector_shift],
|
|
Packit |
94f725 |
1 << ctx->sector_shift,
|
|
Packit |
94f725 |
ctx->cipher_iv.iv,
|
|
Packit |
94f725 |
ctx->cipher_iv.iv_size);
|
|
Packit |
94f725 |
if (r)
|
|
Packit |
94f725 |
break;
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
return r;
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
void crypt_storage_destroy(struct crypt_storage *ctx)
|
|
Packit |
94f725 |
{
|
|
Packit |
94f725 |
if (!ctx)
|
|
Packit |
94f725 |
return;
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
crypt_sector_iv_destroy(&ctx->cipher_iv);
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
if (ctx->cipher)
|
|
Packit |
94f725 |
crypt_cipher_destroy(ctx->cipher);
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
memset(ctx, 0, sizeof(*ctx));
|
|
Packit |
94f725 |
free(ctx);
|
|
Packit |
94f725 |
}
|
|
Packit |
94f725 |
|
|
Packit |
94f725 |
bool crypt_storage_kernel_only(struct crypt_storage *ctx)
|
|
Packit |
94f725 |
{
|
|
Packit |
94f725 |
return crypt_cipher_kernel_only(ctx->cipher);
|
|
Packit |
94f725 |
}
|