NetBSD/sys/dev/cgd_crypto.c

616 lines
16 KiB
C
Raw Normal View History

/* $NetBSD: cgd_crypto.c,v 1.15 2017/01/02 14:28:29 alnsn Exp $ */
2002-10-04 22:22:35 +04:00
/*-
* Copyright (c) 2002 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Roland C. Dowdeswell.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Crypto Framework For cgd.c
*
* This framework is temporary and awaits a more complete
* kernel wide crypto implementation.
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: cgd_crypto.c,v 1.15 2017/01/02 14:28:29 alnsn Exp $");
2002-10-04 22:22:35 +04:00
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <dev/cgd_crypto.h>
2012-12-05 06:23:20 +04:00
#include <crypto/rijndael/rijndael-api-fst.h>
#include <crypto/des/des.h>
#include <crypto/blowfish/blowfish.h>
2002-10-04 22:22:35 +04:00
#ifdef DIAGNOSTIC
#define DIAGPANIC(x) panic x
2002-10-04 22:22:35 +04:00
#else
#define DIAGPANIC(x)
#endif
/*
* The general framework provides only one generic function.
* It takes the name of an algorith and returns a struct cryptfuncs *
* for it. It is up to the initialisation routines of the algorithm
* to check key size and block size.
*/
2016-12-11 03:20:49 +03:00
static cfunc_init cgd_cipher_aes_cbc_init;
static cfunc_destroy cgd_cipher_aes_cbc_destroy;
static cfunc_cipher cgd_cipher_aes_cbc;
static cfunc_cipher_prep cgd_cipher_aes_cbc_prep;
2012-12-05 06:23:20 +04:00
2016-12-11 03:20:49 +03:00
static cfunc_init cgd_cipher_aes_xts_init;
static cfunc_destroy cgd_cipher_aes_xts_destroy;
static cfunc_cipher cgd_cipher_aes_xts;
static cfunc_cipher_prep cgd_cipher_aes_xts_prep;
2012-12-05 06:23:20 +04:00
2016-12-11 03:20:49 +03:00
static cfunc_init cgd_cipher_3des_init;
static cfunc_destroy cgd_cipher_3des_destroy;
static cfunc_cipher cgd_cipher_3des_cbc;
static cfunc_cipher_prep cgd_cipher_3des_cbc_prep;
static cfunc_init cgd_cipher_bf_init;
static cfunc_destroy cgd_cipher_bf_destroy;
static cfunc_cipher cgd_cipher_bf_cbc;
static cfunc_cipher_prep cgd_cipher_bf_cbc_prep;
2012-12-05 06:23:20 +04:00
static const struct cryptfuncs cf[] = {
2016-12-11 03:20:49 +03:00
{
.cf_name = "aes-xts",
.cf_init = cgd_cipher_aes_xts_init,
.cf_destroy = cgd_cipher_aes_xts_destroy,
.cf_cipher = cgd_cipher_aes_xts,
.cf_cipher_prep = cgd_cipher_aes_xts_prep,
},
2012-12-05 06:23:20 +04:00
{
.cf_name = "aes-cbc",
2016-12-11 03:20:49 +03:00
.cf_init = cgd_cipher_aes_cbc_init,
.cf_destroy = cgd_cipher_aes_cbc_destroy,
2012-12-05 06:23:20 +04:00
.cf_cipher = cgd_cipher_aes_cbc,
2016-12-11 03:20:49 +03:00
.cf_cipher_prep = cgd_cipher_aes_cbc_prep,
2012-12-05 06:23:20 +04:00
},
{
.cf_name = "3des-cbc",
.cf_init = cgd_cipher_3des_init,
.cf_destroy = cgd_cipher_3des_destroy,
.cf_cipher = cgd_cipher_3des_cbc,
2016-12-11 03:20:49 +03:00
.cf_cipher_prep = cgd_cipher_3des_cbc_prep,
2012-12-05 06:23:20 +04:00
},
{
.cf_name = "blowfish-cbc",
.cf_init = cgd_cipher_bf_init,
.cf_destroy = cgd_cipher_bf_destroy,
.cf_cipher = cgd_cipher_bf_cbc,
2016-12-11 03:20:49 +03:00
.cf_cipher_prep = cgd_cipher_bf_cbc_prep,
2012-12-05 06:23:20 +04:00
},
};
const struct cryptfuncs *
cryptfuncs_find(const char *alg)
2002-10-04 22:22:35 +04:00
{
2012-12-05 06:23:20 +04:00
for (size_t i = 0; i < __arraycount(cf); i++)
if (strcmp(cf[i].cf_name, alg) == 0)
return &cf[i];
2002-10-04 22:22:35 +04:00
return NULL;
}
2007-01-22 02:00:08 +03:00
typedef void (*cipher_func)(void *, void *, const void *, size_t);
2002-10-04 22:22:35 +04:00
2012-12-05 06:23:20 +04:00
static void
2016-12-11 03:20:49 +03:00
cgd_cipher_uio(void *privdata, cipher_func cipher,
2002-10-04 22:22:35 +04:00
struct uio *dstuio, struct uio *srcuio);
/*
2016-12-11 03:20:49 +03:00
* cgd_cipher_uio takes a simple cbc or xts cipher and iterates
2002-10-04 22:22:35 +04:00
* it over two struct uio's. It presumes that the cipher function
* that is passed to it keeps the IV state between calls.
*
* We assume that the caller has ensured that each segment is evenly
* divisible by the block size, which for the cgd is a valid assumption.
* If we were to make this code more generic, we might need to take care
* of this case, either by issuing an error or copying the data.
*/
2012-12-05 06:23:20 +04:00
static void
2016-12-11 03:20:49 +03:00
cgd_cipher_uio(void *privdata, cipher_func cipher,
struct uio *dstuio, struct uio *srcuio)
2002-10-04 22:22:35 +04:00
{
2015-04-25 15:55:04 +03:00
const struct iovec *dst;
const struct iovec *src;
2002-10-04 22:22:35 +04:00
int dstnum;
int dstoff = 0;
int srcnum;
int srcoff = 0;
dst = dstuio->uio_iov;
dstnum = dstuio->uio_iovcnt;
src = srcuio->uio_iov;
srcnum = srcuio->uio_iovcnt;
for (;;) {
int l = MIN(dst->iov_len - dstoff, src->iov_len - srcoff);
u_int8_t *d = (u_int8_t *)dst->iov_base + dstoff;
2015-04-25 15:55:04 +03:00
const u_int8_t *s = (const u_int8_t *)src->iov_base + srcoff;
2002-10-04 22:22:35 +04:00
cipher(privdata, d, s, l);
dstoff += l;
srcoff += l;
/*
* We assume that {dst,src} == {dst,src}->iov_len,
* because it should not be possible for it not to be.
*/
if (dstoff == dst->iov_len) {
dstoff = 0;
dstnum--;
dst++;
}
if (srcoff == src->iov_len) {
srcoff = 0;
srcnum--;
src++;
}
if (!srcnum || !dstnum)
break;
}
}
/*
* AES Framework
*/
/*
* NOTE: we do not store the blocksize in here, because it is not
* variable [yet], we hardcode the blocksize to 16 (128 bits).
*/
struct aes_privdata {
keyInstance ap_enckey;
keyInstance ap_deckey;
};
struct aes_encdata {
keyInstance *ae_key; /* key for this direction */
u_int8_t ae_iv[CGD_AES_BLOCK_SIZE]; /* Initialization Vector */
2002-10-04 22:22:35 +04:00
};
2012-12-05 06:23:20 +04:00
static void *
2016-12-11 03:20:49 +03:00
cgd_cipher_aes_cbc_init(size_t keylen, const void *key, size_t *blocksize)
2002-10-04 22:22:35 +04:00
{
struct aes_privdata *ap;
if (!blocksize)
return NULL;
if (keylen != 128 && keylen != 192 && keylen != 256)
return NULL;
if (*blocksize == (size_t)-1)
2002-10-04 22:22:35 +04:00
*blocksize = 128;
if (*blocksize != 128)
return NULL;
ap = malloc(sizeof(*ap), M_DEVBUF, 0);
if (!ap)
return NULL;
rijndael_makeKey(&ap->ap_enckey, DIR_ENCRYPT, keylen, key);
rijndael_makeKey(&ap->ap_deckey, DIR_DECRYPT, keylen, key);
return ap;
2002-10-04 22:22:35 +04:00
}
2012-12-05 06:23:20 +04:00
static void
2016-12-11 03:20:49 +03:00
cgd_cipher_aes_cbc_destroy(void *data)
2002-10-04 22:22:35 +04:00
{
struct aes_privdata *apd = data;
2002-10-04 22:22:35 +04:00
explicit_memset(apd, 0, sizeof(*apd));
2002-10-04 22:22:35 +04:00
free(apd, M_DEVBUF);
}
2016-12-11 03:20:49 +03:00
static void
cgd_cipher_aes_cbc_prep(void *privdata, char *iv,
const char *blkno_buf, size_t blocksize, int dir)
{
struct aes_privdata *apd = privdata;
cipherInstance cipher;
int cipher_ok __diagused;
cipher_ok = rijndael_cipherInit(&cipher, MODE_CBC, NULL);
KASSERT(cipher_ok > 0);
rijndael_blockEncrypt(&cipher, &apd->ap_enckey,
blkno_buf, blocksize * 8, iv);
if (blocksize > CGD_AES_BLOCK_SIZE) {
(void)memmove(iv, iv + blocksize - CGD_AES_BLOCK_SIZE,
CGD_AES_BLOCK_SIZE);
}
2016-12-11 03:20:49 +03:00
}
2012-12-05 06:23:20 +04:00
static void
2007-01-22 02:00:08 +03:00
aes_cbc_enc_int(void *privdata, void *dst, const void *src, size_t len)
2002-10-04 22:22:35 +04:00
{
struct aes_encdata *ae = privdata;
2002-10-04 22:22:35 +04:00
cipherInstance cipher;
2016-12-11 03:20:49 +03:00
int cipher_ok __diagused;
2002-10-04 22:22:35 +04:00
2016-12-11 03:20:49 +03:00
cipher_ok = rijndael_cipherInit(&cipher, MODE_CBC, ae->ae_iv);
KASSERT(cipher_ok > 0);
2002-10-04 22:22:35 +04:00
rijndael_blockEncrypt(&cipher, ae->ae_key, src, len * 8, dst);
(void)memcpy(ae->ae_iv, (u_int8_t *)dst +
(len - CGD_AES_BLOCK_SIZE), CGD_AES_BLOCK_SIZE);
2002-10-04 22:22:35 +04:00
}
2012-12-05 06:23:20 +04:00
static void
2007-01-22 02:00:08 +03:00
aes_cbc_dec_int(void *privdata, void *dst, const void *src, size_t len)
2002-10-04 22:22:35 +04:00
{
struct aes_encdata *ae = privdata;
2002-10-04 22:22:35 +04:00
cipherInstance cipher;
2016-12-11 03:20:49 +03:00
int cipher_ok __diagused;
2002-10-04 22:22:35 +04:00
2016-12-11 03:20:49 +03:00
cipher_ok = rijndael_cipherInit(&cipher, MODE_CBC, ae->ae_iv);
KASSERT(cipher_ok > 0);
2002-10-04 22:22:35 +04:00
rijndael_blockDecrypt(&cipher, ae->ae_key, src, len * 8, dst);
(void)memcpy(ae->ae_iv, (const u_int8_t *)src +
(len - CGD_AES_BLOCK_SIZE), CGD_AES_BLOCK_SIZE);
2002-10-04 22:22:35 +04:00
}
2012-12-05 06:23:20 +04:00
static void
cgd_cipher_aes_cbc(void *privdata, struct uio *dstuio,
2015-04-25 15:55:04 +03:00
struct uio *srcuio, const void *iv, int dir)
2002-10-04 22:22:35 +04:00
{
struct aes_privdata *apd = privdata;
2002-10-04 22:22:35 +04:00
struct aes_encdata encd;
(void)memcpy(encd.ae_iv, iv, CGD_AES_BLOCK_SIZE);
2002-10-04 22:22:35 +04:00
switch (dir) {
case CGD_CIPHER_ENCRYPT:
encd.ae_key = &apd->ap_enckey;
2016-12-11 03:20:49 +03:00
cgd_cipher_uio(&encd, aes_cbc_enc_int, dstuio, srcuio);
break;
case CGD_CIPHER_DECRYPT:
encd.ae_key = &apd->ap_deckey;
cgd_cipher_uio(&encd, aes_cbc_dec_int, dstuio, srcuio);
break;
default:
DIAGPANIC(("%s: unrecognised direction %d", __func__, dir));
}
}
static void *
cgd_cipher_aes_xts_init(size_t keylen, const void *xtskey, size_t *blocksize)
{
struct aes_privdata *ap;
const char *key, *key2; /* XTS key is made of two AES keys. */
if (!blocksize)
return NULL;
if (keylen != 256 && keylen != 512)
return NULL;
if (*blocksize == (size_t)-1)
*blocksize = 128;
if (*blocksize != 128)
return NULL;
ap = malloc(2 * sizeof(*ap), M_DEVBUF, 0);
if (!ap)
return NULL;
keylen /= 2;
key = xtskey;
key2 = key + keylen / CHAR_BIT;
rijndael_makeKey(&ap[0].ap_enckey, DIR_ENCRYPT, keylen, key);
rijndael_makeKey(&ap[0].ap_deckey, DIR_DECRYPT, keylen, key);
rijndael_makeKey(&ap[1].ap_enckey, DIR_ENCRYPT, keylen, key2);
return ap;
}
static void
cgd_cipher_aes_xts_destroy(void *data)
{
struct aes_privdata *apd = data;
explicit_memset(apd, 0, 2 * sizeof(*apd));
free(apd, M_DEVBUF);
}
static void
cgd_cipher_aes_xts_prep(void *privdata, char *iv,
const char *blkno_buf, size_t blocksize, int dir)
{
struct aes_privdata *apd = privdata;
cipherInstance cipher;
int cipher_ok __diagused;
cipher_ok = rijndael_cipherInit(&cipher, MODE_ECB, NULL);
KASSERT(cipher_ok > 0);
rijndael_blockEncrypt(&cipher, &apd[1].ap_enckey,
blkno_buf, blocksize * 8, iv);
}
static void
aes_xts_enc_int(void *privdata, void *dst, const void *src, size_t len)
{
struct aes_encdata *ae = privdata;
cipherInstance cipher;
int cipher_ok __diagused;
cipher_ok = rijndael_cipherInit(&cipher, MODE_XTS, ae->ae_iv);
KASSERT(cipher_ok > 0);
rijndael_blockEncrypt(&cipher, ae->ae_key, src, len * 8, dst);
(void)memcpy(ae->ae_iv, cipher.IV, CGD_AES_BLOCK_SIZE);
2016-12-11 03:20:49 +03:00
}
static void
aes_xts_dec_int(void *privdata, void *dst, const void *src, size_t len)
{
struct aes_encdata *ae = privdata;
cipherInstance cipher;
int cipher_ok __diagused;
cipher_ok = rijndael_cipherInit(&cipher, MODE_XTS, ae->ae_iv);
KASSERT(cipher_ok > 0);
rijndael_blockDecrypt(&cipher, ae->ae_key, src, len * 8, dst);
(void)memcpy(ae->ae_iv, cipher.IV, CGD_AES_BLOCK_SIZE);
2016-12-11 03:20:49 +03:00
}
static void
cgd_cipher_aes_xts(void *privdata, struct uio *dstuio,
struct uio *srcuio, const void *iv, int dir)
{
struct aes_privdata *apd = privdata;
struct aes_encdata encd;
(void)memcpy(encd.ae_iv, iv, CGD_AES_BLOCK_SIZE);
2016-12-11 03:20:49 +03:00
switch (dir) {
case CGD_CIPHER_ENCRYPT:
encd.ae_key = &apd->ap_enckey;
cgd_cipher_uio(&encd, aes_xts_enc_int, dstuio, srcuio);
2002-10-04 22:22:35 +04:00
break;
case CGD_CIPHER_DECRYPT:
encd.ae_key = &apd->ap_deckey;
2016-12-11 03:20:49 +03:00
cgd_cipher_uio(&encd, aes_xts_dec_int, dstuio, srcuio);
2002-10-04 22:22:35 +04:00
break;
default:
2007-12-15 03:39:14 +03:00
DIAGPANIC(("%s: unrecognised direction %d", __func__, dir));
2002-10-04 22:22:35 +04:00
}
}
/*
* 3DES Framework
*/
struct c3des_privdata {
des_key_schedule cp_key1;
des_key_schedule cp_key2;
des_key_schedule cp_key3;
};
struct c3des_encdata {
des_key_schedule *ce_key1;
des_key_schedule *ce_key2;
des_key_schedule *ce_key3;
u_int8_t ce_iv[CGD_3DES_BLOCK_SIZE];
2002-10-04 22:22:35 +04:00
};
2012-12-05 06:23:20 +04:00
static void *
2007-01-22 02:00:08 +03:00
cgd_cipher_3des_init(size_t keylen, const void *key, size_t *blocksize)
2002-10-04 22:22:35 +04:00
{
struct c3des_privdata *cp;
int error = 0;
2007-01-22 02:00:08 +03:00
des_cblock *block;
2002-10-04 22:22:35 +04:00
if (!blocksize)
return NULL;
if (*blocksize == (size_t)-1)
2002-10-04 22:22:35 +04:00
*blocksize = 64;
if (keylen != (DES_KEY_SZ * 3 * 8) || *blocksize != 64)
return NULL;
cp = malloc(sizeof(*cp), M_DEVBUF, 0);
if (!cp)
return NULL;
2007-01-22 02:00:08 +03:00
block = __UNCONST(key);
error = des_key_sched(block, cp->cp_key1);
error |= des_key_sched(block + 1, cp->cp_key2);
error |= des_key_sched(block + 2, cp->cp_key3);
2002-10-04 22:22:35 +04:00
if (error) {
explicit_memset(cp, 0, sizeof(*cp));
2002-10-04 22:22:35 +04:00
free(cp, M_DEVBUF);
return NULL;
}
return cp;
2002-10-04 22:22:35 +04:00
}
2012-12-05 06:23:20 +04:00
static void
cgd_cipher_3des_destroy(void *data)
2002-10-04 22:22:35 +04:00
{
struct c3des_privdata *cp = data;
2002-10-04 22:22:35 +04:00
explicit_memset(cp, 0, sizeof(*cp));
2002-10-04 22:22:35 +04:00
free(cp, M_DEVBUF);
}
2016-12-11 03:20:49 +03:00
static void
cgd_cipher_3des_cbc_prep(void *privdata, char *iv,
const char *blkno_buf, size_t blocksize, int dir)
{
struct c3des_privdata *cp = privdata;
char zero_iv[CGD_3DES_BLOCK_SIZE];
2016-12-11 03:20:49 +03:00
memset(zero_iv, 0, sizeof(zero_iv));
des_ede3_cbc_encrypt(blkno_buf, iv, blocksize,
cp->cp_key1, cp->cp_key2, cp->cp_key3, (des_cblock *)zero_iv, 1);
if (blocksize > CGD_3DES_BLOCK_SIZE) {
(void)memmove(iv, iv + blocksize - CGD_3DES_BLOCK_SIZE,
CGD_3DES_BLOCK_SIZE);
}
2016-12-11 03:20:49 +03:00
}
2002-10-04 22:22:35 +04:00
static void
2007-01-22 02:00:08 +03:00
c3des_cbc_enc_int(void *privdata, void *dst, const void *src, size_t len)
2002-10-04 22:22:35 +04:00
{
struct c3des_encdata *ce = privdata;
2002-10-04 22:22:35 +04:00
des_ede3_cbc_encrypt(src, dst, len, *ce->ce_key1, *ce->ce_key2,
*ce->ce_key3, (des_cblock *)ce->ce_iv, 1);
(void)memcpy(ce->ce_iv, (const u_int8_t *)dst +
(len - CGD_3DES_BLOCK_SIZE), CGD_3DES_BLOCK_SIZE);
2002-10-04 22:22:35 +04:00
}
static void
2007-01-22 02:00:08 +03:00
c3des_cbc_dec_int(void *privdata, void *dst, const void *src, size_t len)
2002-10-04 22:22:35 +04:00
{
struct c3des_encdata *ce = privdata;
2002-10-04 22:22:35 +04:00
des_ede3_cbc_encrypt(src, dst, len, *ce->ce_key1, *ce->ce_key2,
*ce->ce_key3, (des_cblock *)ce->ce_iv, 0);
(void)memcpy(ce->ce_iv, (const u_int8_t *)src +
(len - CGD_3DES_BLOCK_SIZE), CGD_3DES_BLOCK_SIZE);
2002-10-04 22:22:35 +04:00
}
2012-12-05 06:23:20 +04:00
static void
cgd_cipher_3des_cbc(void *privdata, struct uio *dstuio,
2015-04-25 15:55:04 +03:00
struct uio *srcuio, const void *iv, int dir)
2002-10-04 22:22:35 +04:00
{
struct c3des_privdata *cp = privdata;
2002-10-04 22:22:35 +04:00
struct c3des_encdata ce;
(void)memcpy(ce.ce_iv, iv, CGD_3DES_BLOCK_SIZE);
2002-10-04 22:22:35 +04:00
ce.ce_key1 = &cp->cp_key1;
ce.ce_key2 = &cp->cp_key2;
ce.ce_key3 = &cp->cp_key3;
switch (dir) {
case CGD_CIPHER_ENCRYPT:
2016-12-11 03:20:49 +03:00
cgd_cipher_uio(&ce, c3des_cbc_enc_int, dstuio, srcuio);
2002-10-04 22:22:35 +04:00
break;
case CGD_CIPHER_DECRYPT:
2016-12-11 03:20:49 +03:00
cgd_cipher_uio(&ce, c3des_cbc_dec_int, dstuio, srcuio);
2002-10-04 22:22:35 +04:00
break;
default:
2007-12-15 03:39:14 +03:00
DIAGPANIC(("%s: unrecognised direction %d", __func__, dir));
2002-10-04 22:22:35 +04:00
}
}
/*
* Blowfish Framework
*/
struct bf_privdata {
BF_KEY bp_key;
};
struct bf_encdata {
BF_KEY *be_key;
u_int8_t be_iv[CGD_BF_BLOCK_SIZE];
2002-10-04 22:22:35 +04:00
};
2012-12-05 06:23:20 +04:00
static void *
2007-01-22 02:00:08 +03:00
cgd_cipher_bf_init(size_t keylen, const void *key, size_t *blocksize)
2002-10-04 22:22:35 +04:00
{
struct bf_privdata *bp;
if (!blocksize)
return NULL;
Fix a longstanding bug in key-handling for the blowfish cipher. This is an incompatible change, and will break all existing cgd images encrypted with blowfish. Users will need to dump their data before booting a kernel with this change, and recreate cgd's and restore data afterwards. I believe this affects a very small number of users other than myself; indeed after several alert mails in an attempt to find them, only 2 such users have come forward. They have both agreed the requirement for backwards compatibility does not warrant the effort nor the mess in the code. This code does exist, if it should later prove to be needed, but will not be in the tree. Further, by the nature of the issue, I have strong reasons to believe that, even if they missed these mails, there would be few other users of blowfish who update their systems with any regularity; any such users would have tripped over the problem in the same way I did when it was first found over a year ago. The problem stems from two issues with the underlying blowfish encryption routines used by cgd: - they take key length arguments counted in bytes, rather than bits like all the opther ciphers. - they silently truncate any keys longer than an internal limit, rather than returning an error (which would have exposed the previous discrepancy immediately). As a result, the kernel reads too much data as the key from cgdconfig, and then truncates most of it. This can easily be demonstrated/tested. Currently, Blowfish users will find that if they mis-enter the cgd passphrase on the first attempt, when validation fails and cgdconfig prompts for the passphrase again, the cgd will not correctly configure even when given a correct passphrase.
2004-03-18 13:42:08 +03:00
if (keylen < 40 || keylen > 448 || (keylen % 8 != 0))
2002-10-04 22:22:35 +04:00
return NULL;
if (*blocksize == (size_t)-1)
2002-10-04 22:22:35 +04:00
*blocksize = 64;
if (*blocksize != 64)
return NULL;
bp = malloc(sizeof(*bp), M_DEVBUF, 0);
if (!bp)
return NULL;
Fix a longstanding bug in key-handling for the blowfish cipher. This is an incompatible change, and will break all existing cgd images encrypted with blowfish. Users will need to dump their data before booting a kernel with this change, and recreate cgd's and restore data afterwards. I believe this affects a very small number of users other than myself; indeed after several alert mails in an attempt to find them, only 2 such users have come forward. They have both agreed the requirement for backwards compatibility does not warrant the effort nor the mess in the code. This code does exist, if it should later prove to be needed, but will not be in the tree. Further, by the nature of the issue, I have strong reasons to believe that, even if they missed these mails, there would be few other users of blowfish who update their systems with any regularity; any such users would have tripped over the problem in the same way I did when it was first found over a year ago. The problem stems from two issues with the underlying blowfish encryption routines used by cgd: - they take key length arguments counted in bytes, rather than bits like all the opther ciphers. - they silently truncate any keys longer than an internal limit, rather than returning an error (which would have exposed the previous discrepancy immediately). As a result, the kernel reads too much data as the key from cgdconfig, and then truncates most of it. This can easily be demonstrated/tested. Currently, Blowfish users will find that if they mis-enter the cgd passphrase on the first attempt, when validation fails and cgdconfig prompts for the passphrase again, the cgd will not correctly configure even when given a correct passphrase.
2004-03-18 13:42:08 +03:00
BF_set_key(&bp->bp_key, keylen / 8, key);
return bp;
2002-10-04 22:22:35 +04:00
}
2012-12-05 06:23:20 +04:00
static void
cgd_cipher_bf_destroy(void *data)
2002-10-04 22:22:35 +04:00
{
struct bf_privdata *bp = data;
2002-10-04 22:22:35 +04:00
explicit_memset(bp, 0, sizeof(*bp));
2002-10-04 22:22:35 +04:00
free(bp, M_DEVBUF);
}
2016-12-11 03:20:49 +03:00
static void
cgd_cipher_bf_cbc_prep(void *privdata, char *iv,
const char *blkno_buf, size_t blocksize, int dir)
{
struct bf_privdata *bp = privdata;
char zero_iv[CGD_BF_BLOCK_SIZE];
2016-12-11 03:20:49 +03:00
memset(zero_iv, 0, sizeof(zero_iv));
BF_cbc_encrypt(blkno_buf, iv, blocksize, &bp->bp_key, zero_iv, 1);
if (blocksize > CGD_BF_BLOCK_SIZE) {
(void)memmove(iv, iv + blocksize - CGD_BF_BLOCK_SIZE,
CGD_BF_BLOCK_SIZE);
}
2016-12-11 03:20:49 +03:00
}
2012-12-05 06:23:20 +04:00
static void
2007-01-22 02:00:08 +03:00
bf_cbc_enc_int(void *privdata, void *dst, const void *src, size_t len)
2002-10-04 22:22:35 +04:00
{
struct bf_encdata *be = privdata;
2002-10-04 22:22:35 +04:00
BF_cbc_encrypt(src, dst, len, be->be_key, be->be_iv, 1);
(void)memcpy(be->be_iv, (u_int8_t *)dst +
(len - CGD_BF_BLOCK_SIZE), CGD_BF_BLOCK_SIZE);
2002-10-04 22:22:35 +04:00
}
2012-12-05 06:23:20 +04:00
static void
2007-01-22 02:00:08 +03:00
bf_cbc_dec_int(void *privdata, void *dst, const void *src, size_t len)
2002-10-04 22:22:35 +04:00
{
struct bf_encdata *be = privdata;
2002-10-04 22:22:35 +04:00
BF_cbc_encrypt(src, dst, len, be->be_key, be->be_iv, 0);
(void)memcpy(be->be_iv, (const u_int8_t *)src +
(len - CGD_BF_BLOCK_SIZE), CGD_BF_BLOCK_SIZE);
2002-10-04 22:22:35 +04:00
}
2012-12-05 06:23:20 +04:00
static void
cgd_cipher_bf_cbc(void *privdata, struct uio *dstuio,
2015-04-25 15:55:04 +03:00
struct uio *srcuio, const void *iv, int dir)
2002-10-04 22:22:35 +04:00
{
struct bf_privdata *bp = privdata;
2002-10-04 22:22:35 +04:00
struct bf_encdata be;
(void)memcpy(be.be_iv, iv, CGD_BF_BLOCK_SIZE);
2002-10-04 22:22:35 +04:00
be.be_key = &bp->bp_key;
switch (dir) {
case CGD_CIPHER_ENCRYPT:
2016-12-11 03:20:49 +03:00
cgd_cipher_uio(&be, bf_cbc_enc_int, dstuio, srcuio);
2002-10-04 22:22:35 +04:00
break;
case CGD_CIPHER_DECRYPT:
2016-12-11 03:20:49 +03:00
cgd_cipher_uio(&be, bf_cbc_dec_int, dstuio, srcuio);
2002-10-04 22:22:35 +04:00
break;
default:
2007-12-15 03:39:14 +03:00
DIAGPANIC(("%s: unrecognised direction %d", __func__, dir));
2002-10-04 22:22:35 +04:00
}
}