tech-crypto archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: AES XTS in cgd



If there are no objections, I'm going to commit attached patches in the
next few days. The unit tests are in the tree already. They cover all
three encryption algorithms and both ivmethods.

See below for what changed in this patchset.

Alexander Nasonov wrote:
> Because the existing code is designed for CBC mode, I had to
> introduce a new cipher_prep function that converts blkno_buf to
> XTS tweak or CBC IV. It's optional because I didn't want to touch
> Blowfish and 3DES but I'd like to switch them to cipher_prep too.

I switched Blowfish and 3DES to the cipher_prep and simplified
cgd_cipher(). The new code doesn't need to start with zero IV and
it never passes two buffers (sink and data) to the underlying
cipher. Also, blkno_buf can be converted to IV inplace. It saves
256 bytes on the stack.

Alex
Index: share/man/man4/cgd.4
===================================================================
RCS file: /cvsroot/src/share/man/man4/cgd.4,v
retrieving revision 1.19
diff -p -u -u -r1.19 cgd.4
--- share/man/man4/cgd.4	1 Jun 2016 01:34:43 -0000	1.19
+++ share/man/man4/cgd.4	12 Nov 2016 17:09:35 -0000
@@ -64,6 +64,21 @@ mode.
 .Tn AES
 uses a 128 bit blocksize and can accept keys of length 128, 192, or 256.
 The default key length is 128.
+.It Ic aes-xts
+.Tn AES
+in
+.Tn XTS
+mode.
+.Tn AES-XTS
+uses a 128 bit blocksize and can accept keys of length 256 or 512.
+Note that
+.Tn AES-XTS
+key is made of two
+.Tn AES
+keys of equal size.
+The second key is used solely to encrypt the block number of the physical
+disk block.
+The default key length is 256.
 .It Ic 3des-cbc
 Triple
 .Tn DES
@@ -159,11 +174,25 @@ and their associated data structures are
 .In dev/cgdvar.h
 header.
 .Sh WARNINGS
+.Pp
 It goes without saying that if you forget the passphrase that you used
 to configure a
 .Nm ,
 then you have irrevocably lost all of the data on the disk.
 Please ensure that you are using an appropriate backup strategy.
+.Pp
+A
+.Nm
+device doesn't authenticate data and thus it can't guarantee integrity
+of the encrypted data.
+In particular, if the plaintext is known to an adversary, it is
+possible to change every second block on a disk encrypted in the
+.Tn CBC
+mode to plaintext blocks of their choice.
+The
+.Tn
+XTS mode isn't vulnerable to this particular attack but a lack of
+integrity should be taken into accout when evaluating security risks.
 .Sh FILES
 .Bl -tag -width indentxxxxxxxxxxx
 .It /dev/{,r}cgd*
Index: sys/dev/cgd_crypto.h
===================================================================
RCS file: /cvsroot/src/sys/dev/cgd_crypto.h,v
retrieving revision 1.8
diff -p -u -u -r1.8 cgd_crypto.h
--- sys/dev/cgd_crypto.h	25 Apr 2015 12:55:04 -0000	1.8
+++ sys/dev/cgd_crypto.h	12 Nov 2016 17:33:05 -0000
@@ -40,12 +40,14 @@ typedef void *(cfunc_init)(size_t, const
 typedef void  (cfunc_destroy)(void *);
 typedef void  (cfunc_cipher)(void *, struct uio *, struct uio *, const void *,
 				int);
+typedef void  (cfunc_cipher_prep)(void *, char *, const char *, size_t, int);
 
 struct cryptfuncs {
-	const char	 *cf_name;	/* cipher name */
-	cfunc_init	 *cf_init;	/* Initialisation function */
-	cfunc_destroy	 *cf_destroy;	/* destruction function */
-	cfunc_cipher	 *cf_cipher;	/* the cipher itself */
+	const char		 *cf_name;	/* cipher name */
+	cfunc_init		 *cf_init;	/* Initialisation function */
+	cfunc_destroy		 *cf_destroy;	/* destruction function */
+	cfunc_cipher		 *cf_cipher;	/* the cipher itself */
+	cfunc_cipher_prep	 *cf_cipher_prep; /* prepare to cipher */
 };
 
 const struct cryptfuncs	*cryptfuncs_find(const char *);
Index: sys/dev/cgd_crypto.c
===================================================================
RCS file: /cvsroot/src/sys/dev/cgd_crypto.c,v
retrieving revision 1.13
diff -p -u -u -r1.13 cgd_crypto.c
--- sys/dev/cgd_crypto.c	25 Apr 2015 12:55:04 -0000	1.13
+++ sys/dev/cgd_crypto.c	12 Nov 2016 17:33:05 -0000
@@ -62,36 +62,54 @@ __KERNEL_RCSID(0, "$NetBSD: cgd_crypto.c
  * to check key size and block size.
  */
 
-static cfunc_init	cgd_cipher_aes_init;
-static cfunc_destroy	cgd_cipher_aes_destroy;
-static cfunc_cipher	cgd_cipher_aes_cbc;
-
-static cfunc_init	cgd_cipher_3des_init;
-static cfunc_destroy	cgd_cipher_3des_destroy;
-static cfunc_cipher	cgd_cipher_3des_cbc;
-
-static cfunc_init	cgd_cipher_bf_init;
-static cfunc_destroy	cgd_cipher_bf_destroy;
-static cfunc_cipher	cgd_cipher_bf_cbc;
+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;
+
+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;
+
+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;
 
 static const struct cryptfuncs cf[] = {
 	{
+		.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,
+	},
+	{
 		.cf_name	= "aes-cbc",
-		.cf_init	= cgd_cipher_aes_init,
-		.cf_destroy	= cgd_cipher_aes_destroy,
+		.cf_init	= cgd_cipher_aes_cbc_init,
+		.cf_destroy	= cgd_cipher_aes_cbc_destroy,
 		.cf_cipher	= cgd_cipher_aes_cbc,
+		.cf_cipher_prep	= cgd_cipher_aes_cbc_prep,
 	},
 	{
 		.cf_name	= "3des-cbc",
 		.cf_init	= cgd_cipher_3des_init,
 		.cf_destroy	= cgd_cipher_3des_destroy,
 		.cf_cipher	= cgd_cipher_3des_cbc,
+		.cf_cipher_prep	= cgd_cipher_3des_cbc_prep,
 	},
 	{
 		.cf_name	= "blowfish-cbc",
 		.cf_init	= cgd_cipher_bf_init,
 		.cf_destroy	= cgd_cipher_bf_destroy,
 		.cf_cipher	= cgd_cipher_bf_cbc,
+		.cf_cipher_prep	= cgd_cipher_bf_cbc_prep,
 	},
 };
 const struct cryptfuncs *
@@ -108,11 +126,11 @@ cryptfuncs_find(const char *alg)
 typedef void	(*cipher_func)(void *, void *, const void *, size_t);
 
 static void
-cgd_cipher_uio_cbc(void *privdata, cipher_func cipher,
+cgd_cipher_uio(void *privdata, cipher_func cipher,
 	struct uio *dstuio, struct uio *srcuio);
 
 /*
- * cgd_cipher_uio_cbc takes a simple cbc cipher and iterates
+ * cgd_cipher_uio takes a simple cbc or xts cipher and iterates
  * it over two struct uio's.  It presumes that the cipher function
  * that is passed to it keeps the IV state between calls.
  *
@@ -123,7 +141,7 @@ cgd_cipher_uio_cbc(void *privdata, ciphe
  */
 
 static void
-cgd_cipher_uio_cbc(void *privdata, cipher_func cipher,
+cgd_cipher_uio(void *privdata, cipher_func cipher,
     struct uio *dstuio, struct uio *srcuio)
 {
 	const struct iovec	*dst;
@@ -185,7 +203,7 @@ struct aes_encdata {
 };
 
 static void *
-cgd_cipher_aes_init(size_t keylen, const void *key, size_t *blocksize)
+cgd_cipher_aes_cbc_init(size_t keylen, const void *key, size_t *blocksize)
 {
 	struct	aes_privdata *ap;
 
@@ -206,7 +224,7 @@ cgd_cipher_aes_init(size_t keylen, const
 }
 
 static void
-cgd_cipher_aes_destroy(void *data)
+cgd_cipher_aes_cbc_destroy(void *data)
 {
 	struct aes_privdata *apd = data;
 
@@ -215,12 +233,30 @@ cgd_cipher_aes_destroy(void *data)
 }
 
 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 > 16)
+		(void)memmove(iv, iv + blocksize - 16, 16);
+}
+
+static void
 aes_cbc_enc_int(void *privdata, void *dst, const void *src, size_t len)
 {
 	struct aes_encdata	*ae = privdata;
 	cipherInstance		 cipher;
+	int			 cipher_ok __diagused;
 
-	rijndael_cipherInit(&cipher, MODE_CBC, ae->ae_iv);
+	cipher_ok = rijndael_cipherInit(&cipher, MODE_CBC, ae->ae_iv);
+	KASSERT(cipher_ok > 0);
 	rijndael_blockEncrypt(&cipher, ae->ae_key, src, len * 8, dst);
 	(void)memcpy(ae->ae_iv, (u_int8_t *)dst + (len - 16), 16);
 }
@@ -230,8 +266,10 @@ aes_cbc_dec_int(void *privdata, void *ds
 {
 	struct aes_encdata	*ae = privdata;
 	cipherInstance		 cipher;
+	int			 cipher_ok __diagused;
 
-	rijndael_cipherInit(&cipher, MODE_CBC, ae->ae_iv);
+	cipher_ok = rijndael_cipherInit(&cipher, MODE_CBC, ae->ae_iv);
+	KASSERT(cipher_ok > 0);
 	rijndael_blockDecrypt(&cipher, ae->ae_key, src, len * 8, dst);
 	(void)memcpy(ae->ae_iv, (const u_int8_t *)src + (len - 16), 16);
 }
@@ -247,11 +285,111 @@ cgd_cipher_aes_cbc(void *privdata, struc
 	switch (dir) {
 	case CGD_CIPHER_ENCRYPT:
 		encd.ae_key = &apd->ap_enckey;
-		cgd_cipher_uio_cbc(&encd, aes_cbc_enc_int, dstuio, srcuio);
+		cgd_cipher_uio(&encd, aes_cbc_enc_int, dstuio, srcuio);
 		break;
 	case CGD_CIPHER_DECRYPT:
 		encd.ae_key = &apd->ap_deckey;
-		cgd_cipher_uio_cbc(&encd, aes_cbc_dec_int, dstuio, srcuio);
+		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, 16);
+}
+
+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, 16);
+}
+
+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, 16);
+	switch (dir) {
+	case CGD_CIPHER_ENCRYPT:
+		encd.ae_key = &apd->ap_enckey;
+		cgd_cipher_uio(&encd, aes_xts_enc_int, dstuio, srcuio);
+		break;
+	case CGD_CIPHER_DECRYPT:
+		encd.ae_key = &apd->ap_deckey;
+		cgd_cipher_uio(&encd, aes_xts_dec_int, dstuio, srcuio);
 		break;
 	default:
 		DIAGPANIC(("%s: unrecognised direction %d", __func__, dir));
@@ -313,6 +451,20 @@ cgd_cipher_3des_destroy(void *data)
 }
 
 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[8];
+
+	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 > 8)
+		(void)memmove(iv, iv + blocksize - 8, 8);
+}
+
+static void
 c3des_cbc_enc_int(void *privdata, void *dst, const void *src, size_t len)
 {
 	struct	c3des_encdata *ce = privdata;
@@ -345,10 +497,10 @@ cgd_cipher_3des_cbc(void *privdata, stru
 	ce.ce_key3 = &cp->cp_key3;
 	switch (dir) {
 	case CGD_CIPHER_ENCRYPT:
-		cgd_cipher_uio_cbc(&ce, c3des_cbc_enc_int, dstuio, srcuio);
+		cgd_cipher_uio(&ce, c3des_cbc_enc_int, dstuio, srcuio);
 		break;
 	case CGD_CIPHER_DECRYPT:
-		cgd_cipher_uio_cbc(&ce, c3des_cbc_dec_int, dstuio, srcuio);
+		cgd_cipher_uio(&ce, c3des_cbc_dec_int, dstuio, srcuio);
 		break;
 	default:
 		DIAGPANIC(("%s: unrecognised direction %d", __func__, dir));
@@ -398,6 +550,19 @@ cgd_cipher_bf_destroy(void *data)
 }
 
 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[8];
+
+	memset(zero_iv, 0, sizeof(zero_iv));
+	BF_cbc_encrypt(blkno_buf, iv, blocksize, &bp->bp_key, zero_iv, 1);
+	if (blocksize > 8)
+		(void)memmove(iv, iv + blocksize - 8, 8);
+}
+
+static void
 bf_cbc_enc_int(void *privdata, void *dst, const void *src, size_t len)
 {
 	struct	bf_encdata *be = privdata;
@@ -426,10 +591,10 @@ cgd_cipher_bf_cbc(void *privdata, struct
 	be.be_key = &bp->bp_key;
 	switch (dir) {
 	case CGD_CIPHER_ENCRYPT:
-		cgd_cipher_uio_cbc(&be, bf_cbc_enc_int, dstuio, srcuio);
+		cgd_cipher_uio(&be, bf_cbc_enc_int, dstuio, srcuio);
 		break;
 	case CGD_CIPHER_DECRYPT:
-		cgd_cipher_uio_cbc(&be, bf_cbc_dec_int, dstuio, srcuio);
+		cgd_cipher_uio(&be, bf_cbc_dec_int, dstuio, srcuio);
 		break;
 	default:
 		DIAGPANIC(("%s: unrecognised direction %d", __func__, dir));
Index: sys/dev/cgd.c
===================================================================
RCS file: /cvsroot/src/sys/dev/cgd.c,v
retrieving revision 1.111
diff -p -u -u -r1.111 cgd.c
--- sys/dev/cgd.c	14 Sep 2016 23:16:30 -0000	1.111
+++ sys/dev/cgd.c	12 Nov 2016 17:33:05 -0000
@@ -59,6 +59,18 @@ __KERNEL_RCSID(0, "$NetBSD: cgd.c,v 1.11
 
 #include "ioconf.h"
 
+struct selftest_params {
+	const char *alg;
+	int blocksize;	/* number of bytes */
+	int secsize;
+	daddr_t blkno;
+	int keylen;	/* number of bits */
+	int txtlen;	/* number of bytes */
+	const uint8_t *key;
+	const uint8_t *ptxt;
+	const uint8_t *ctxt;
+};
+
 /* Entry Point Functions */
 
 static dev_type_open(cgdopen);
@@ -96,6 +108,101 @@ const struct cdevsw cgd_cdevsw = {
 	.d_flag = D_DISK
 };
 
+/*
+ * Vector 5 from IEEE 1619/D16 truncated to 64 bytes, blkno 1.
+ */
+static const uint8_t selftest_aes_xts_256_ptxt[64] = {
+	0x27, 0xa7, 0x47, 0x9b, 0xef, 0xa1, 0xd4, 0x76,
+	0x48, 0x9f, 0x30, 0x8c, 0xd4, 0xcf, 0xa6, 0xe2,
+	0xa9, 0x6e, 0x4b, 0xbe, 0x32, 0x08, 0xff, 0x25,
+	0x28, 0x7d, 0xd3, 0x81, 0x96, 0x16, 0xe8, 0x9c,
+	0xc7, 0x8c, 0xf7, 0xf5, 0xe5, 0x43, 0x44, 0x5f,
+	0x83, 0x33, 0xd8, 0xfa, 0x7f, 0x56, 0x00, 0x00,
+	0x05, 0x27, 0x9f, 0xa5, 0xd8, 0xb5, 0xe4, 0xad,
+	0x40, 0xe7, 0x36, 0xdd, 0xb4, 0xd3, 0x54, 0x12,
+};
+
+static const uint8_t selftest_aes_xts_256_ctxt[512] = {
+	0x26, 0x4d, 0x3c, 0xa8, 0x51, 0x21, 0x94, 0xfe,
+	0xc3, 0x12, 0xc8, 0xc9, 0x89, 0x1f, 0x27, 0x9f,
+	0xef, 0xdd, 0x60, 0x8d, 0x0c, 0x02, 0x7b, 0x60,
+	0x48, 0x3a, 0x3f, 0xa8, 0x11, 0xd6, 0x5e, 0xe5,
+	0x9d, 0x52, 0xd9, 0xe4, 0x0e, 0xc5, 0x67, 0x2d,
+	0x81, 0x53, 0x2b, 0x38, 0xb6, 0xb0, 0x89, 0xce,
+	0x95, 0x1f, 0x0f, 0x9c, 0x35, 0x59, 0x0b, 0x8b,
+	0x97, 0x8d, 0x17, 0x52, 0x13, 0xf3, 0x29, 0xbb,
+};
+
+static const uint8_t selftest_aes_xts_256_key[33] = {
+	0x27, 0x18, 0x28, 0x18, 0x28, 0x45, 0x90, 0x45,
+	0x23, 0x53, 0x60, 0x28, 0x74, 0x71, 0x35, 0x26,
+	0x31, 0x41, 0x59, 0x26, 0x53, 0x58, 0x97, 0x93,
+	0x23, 0x84, 0x62, 0x64, 0x33, 0x83, 0x27, 0x95,
+	0
+};
+
+/*
+ * Vector 11 from IEEE 1619/D16 truncated to 64 bytes, blkno 0xffff.
+ */
+static const uint8_t selftest_aes_xts_512_ptxt[64] = {
+	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+	0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+	0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+	0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+	0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+	0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+};
+
+static const uint8_t selftest_aes_xts_512_ctxt[64] = {
+	0x77, 0xa3, 0x12, 0x51, 0x61, 0x8a, 0x15, 0xe6,
+	0xb9, 0x2d, 0x1d, 0x66, 0xdf, 0xfe, 0x7b, 0x50,
+	0xb5, 0x0b, 0xad, 0x55, 0x23, 0x05, 0xba, 0x02,
+	0x17, 0xa6, 0x10, 0x68, 0x8e, 0xff, 0x7e, 0x11,
+	0xe1, 0xd0, 0x22, 0x54, 0x38, 0xe0, 0x93, 0x24,
+	0x2d, 0x6d, 0xb2, 0x74, 0xfd, 0xe8, 0x01, 0xd4,
+	0xca, 0xe0, 0x6f, 0x20, 0x92, 0xc7, 0x28, 0xb2,
+	0x47, 0x85, 0x59, 0xdf, 0x58, 0xe8, 0x37, 0xc2,
+};
+
+static const uint8_t selftest_aes_xts_512_key[65] = {
+	0x27, 0x18, 0x28, 0x18, 0x28, 0x45, 0x90, 0x45,
+	0x23, 0x53, 0x60, 0x28, 0x74, 0x71, 0x35, 0x26,
+	0x62, 0x49, 0x77, 0x57, 0x24, 0x70, 0x93, 0x69,
+	0x99, 0x59, 0x57, 0x49, 0x66, 0x96, 0x76, 0x27,
+	0x31, 0x41, 0x59, 0x26, 0x53, 0x58, 0x97, 0x93,
+	0x23, 0x84, 0x62, 0x64, 0x33, 0x83, 0x27, 0x95,
+	0x02, 0x88, 0x41, 0x97, 0x16, 0x93, 0x99, 0x37,
+	0x51, 0x05, 0x82, 0x09, 0x74, 0x94, 0x45, 0x92,
+	0
+};
+
+const struct selftest_params selftests[] = {
+	{
+		.alg = "aes-xts",
+		.blocksize = 16,
+		.secsize = 512,
+		.blkno = 1,
+		.keylen = 256,
+		.txtlen = sizeof(selftest_aes_xts_256_ptxt),
+		.key  = selftest_aes_xts_256_key,
+		.ptxt = selftest_aes_xts_256_ptxt,
+		.ctxt = selftest_aes_xts_256_ctxt
+	},
+	{
+		.alg = "aes-xts",
+		.blocksize = 16,
+		.secsize = 512,
+		.blkno = 0xffff,
+		.keylen = 512,
+		.txtlen = sizeof(selftest_aes_xts_512_ptxt),
+		.key  = selftest_aes_xts_512_key,
+		.ptxt = selftest_aes_xts_512_ptxt,
+		.ctxt = selftest_aes_xts_512_ctxt
+	}
+};
+
 static int cgd_match(device_t, cfdata_t, void *);
 static void cgd_attach(device_t, device_t, void *);
 static int cgd_detach(device_t, int);
@@ -896,19 +1003,6 @@ bail:
  * We implement here the IV method ``encrypted block
  * number''.
  *
- * For the encryption case, we accomplish this by setting
- * up a struct uio where the first iovec of the source is
- * the blocknumber and the first iovec of the dest is a
- * sink.  We then call the cipher with an IV of zero, and
- * the right thing happens.
- *
- * For the decryption case, we use the same basic mechanism
- * for symmetry, but we encrypt the block number in the
- * first iovec.
- *
- * We mainly do this to avoid requiring the definition of
- * an ECB mode.
- *
  * XXXrcd: for now we rely on our own crypto framework defined
  *         in dev/cgd_crypto.c.  This will change when we
  *         get a generic kernel crypto framework.
@@ -945,7 +1039,8 @@ cgd_cipher(struct cgd_softc *cs, void *d
     size_t len, daddr_t blkno, size_t secsize, int dir)
 {
 	char		*dst = dstv;
-	char 		*src = srcv;
+	char		*src = srcv;
+	cfunc_cipher_prep	*ciprep = cs->sc_cfuncs->cf_cipher_prep;
 	cfunc_cipher	*cipher = cs->sc_cfuncs->cf_cipher;
 	struct uio	dstuio;
 	struct uio	srcuio;
@@ -953,9 +1048,7 @@ cgd_cipher(struct cgd_softc *cs, void *d
 	struct iovec	srciov[2];
 	size_t		blocksize = cs->sc_cdata.cf_blocksize;
 	size_t		todo;
-	char		sink[CGD_MAXBLOCKSIZE];
-	char		zero_iv[CGD_MAXBLOCKSIZE];
-	char		blkno_buf[CGD_MAXBLOCKSIZE];
+	char		blkno_buf[CGD_MAXBLOCKSIZE], *iv;
 
 	DPRINTF_FOLLOW(("cgd_cipher() dir=%d\n", dir));
 
@@ -966,46 +1059,37 @@ cgd_cipher(struct cgd_softc *cs, void *d
 	DIAGCONDPANIC(sizeof(daddr_t) > blocksize,
 	    ("cgd_cipher: sizeof(daddr_t) > blocksize"));
 
-	memset(zero_iv, 0x0, blocksize);
+	DIAGCONDPANIC(blocksize > CGD_MAXBLOCKSIZE,
+	    ("cgd_cipher: blocksize > CGD_MAXBLOCKSIZE"));
 
 	dstuio.uio_iov = dstiov;
-	dstuio.uio_iovcnt = 2;
+	dstuio.uio_iovcnt = 1;
 
 	srcuio.uio_iov = srciov;
-	srcuio.uio_iovcnt = 2;
-
-	dstiov[0].iov_base = sink;
-	dstiov[0].iov_len  = blocksize;
-	srciov[0].iov_base = blkno_buf;
-	srciov[0].iov_len  = blocksize;
+	srcuio.uio_iovcnt = 1;
 
 	for (; len > 0; len -= todo) {
 		todo = MIN(len, secsize);
 
-		dstiov[1].iov_base = dst;
-		srciov[1].iov_base = src;
-		dstiov[1].iov_len  = todo;
-		srciov[1].iov_len  = todo;
+		dstiov[0].iov_base = dst;
+		srciov[0].iov_base = src;
+		dstiov[0].iov_len  = todo;
+		srciov[0].iov_len  = todo;
 
 		memset(blkno_buf, 0x0, blocksize);
 		blkno2blkno_buf(blkno_buf, blkno);
-		if (dir == CGD_CIPHER_DECRYPT) {
-			dstuio.uio_iovcnt = 1;
-			srcuio.uio_iovcnt = 1;
-			IFDEBUG(CGDB_CRYPTO, hexprint("step 0: blkno_buf",
-			    blkno_buf, blocksize));
-			cipher(cs->sc_cdata.cf_priv, &dstuio, &srcuio,
-			    zero_iv, CGD_CIPHER_ENCRYPT);
-			memcpy(blkno_buf, sink, blocksize);
-			dstuio.uio_iovcnt = 2;
-			srcuio.uio_iovcnt = 2;
-		}
-
 		IFDEBUG(CGDB_CRYPTO, hexprint("step 1: blkno_buf",
 		    blkno_buf, blocksize));
-		cipher(cs->sc_cdata.cf_priv, &dstuio, &srcuio, zero_iv, dir);
-		IFDEBUG(CGDB_CRYPTO, hexprint("step 2: sink",
-		    sink, blocksize));
+
+		/*
+		 * Compute an initial IV. All ciphers
+		 * can convert blkno_buf in-place.
+		 */
+		iv = blkno_buf;
+		ciprep(cs->sc_cdata.cf_priv, iv, blkno_buf, blocksize, dir);
+		IFDEBUG(CGDB_CRYPTO, hexprint("step 2: iv", iv, blocksize));
+
+		cipher(cs->sc_cdata.cf_priv, &dstuio, &srcuio, iv, dir);
 
 		dst += todo;
 		src += todo;
@@ -1026,6 +1110,61 @@ hexprint(const char *start, void *buf, i
 }
 #endif
 
+static void
+selftest(void)
+{
+	struct cgd_softc cs;
+	void *buf;
+
+	printf("running cgd selftest ");
+
+	for (size_t i = 0; i < __arraycount(selftests); i++) {
+		const char *alg = selftests[i].alg;
+		const uint8_t *key = selftests[i].key;
+		int keylen = selftests[i].keylen;
+		int txtlen = selftests[i].txtlen;
+
+		printf("%s-%d ", alg, keylen);
+
+		memset(&cs, 0, sizeof(cs));
+
+		cs.sc_cfuncs = cryptfuncs_find(alg);
+		if (cs.sc_cfuncs == NULL)
+			panic("%s not implemented", alg);
+
+		cs.sc_cdata.cf_blocksize = 8 * selftests[i].blocksize;
+		cs.sc_cdata.cf_mode = CGD_CIPHER_CBC_ENCBLKNO1;
+		cs.sc_cdata.cf_keylen = keylen;
+
+		cs.sc_cdata.cf_priv = cs.sc_cfuncs->cf_init(keylen,
+		    key, &cs.sc_cdata.cf_blocksize);
+		if (cs.sc_cdata.cf_priv == NULL)
+			panic("cf_priv is NULL");
+		if (cs.sc_cdata.cf_blocksize > CGD_MAXBLOCKSIZE)
+			panic("bad block size %zu", cs.sc_cdata.cf_blocksize);
+
+		cs.sc_cdata.cf_blocksize /= 8;
+
+		buf = malloc(txtlen, M_DEVBUF, M_WAITOK);
+		memcpy(buf, selftests[i].ptxt, txtlen);
+
+		cgd_cipher(&cs, buf, buf, txtlen, selftests[i].blkno,
+				selftests[i].secsize, CGD_CIPHER_ENCRYPT);
+		if (memcmp(buf, selftests[i].ctxt, txtlen) != 0)
+			panic("encryption is broken");
+
+		cgd_cipher(&cs, buf, buf, txtlen, selftests[i].blkno,
+				selftests[i].secsize, CGD_CIPHER_DECRYPT);
+		if (memcmp(buf, selftests[i].ptxt, txtlen) != 0)
+			panic("decryption is broken");
+
+		free(buf, M_DEVBUF);
+		cs.sc_cfuncs->cf_destroy(cs.sc_cdata.cf_priv);
+	}
+
+	printf("done\n");
+}
+
 MODULE(MODULE_CLASS_DRIVER, cgd, "dk_subr");
 
 #ifdef _MODULE
@@ -1041,6 +1180,7 @@ cgd_modcmd(modcmd_t cmd, void *arg)
 
 	switch (cmd) {
 	case MODULE_CMD_INIT:
+		selftest();
 #ifdef _MODULE
 		error = config_cfdriver_attach(&cgd_cd);
 		if (error)
Index: sbin/cgdconfig/params.c
===================================================================
RCS file: /cvsroot/src/sbin/cgdconfig/params.c,v
retrieving revision 1.28
diff -p -u -u -r1.28 params.c
--- sbin/cgdconfig/params.c	24 Nov 2015 14:07:18 -0000	1.28
+++ sbin/cgdconfig/params.c	6 Nov 2016 16:43:03 -0000
@@ -70,6 +70,7 @@ static struct crypto_defaults {
 	int	keylen;
 } crypto_defaults[] = {
 	{ "aes-cbc",		128 },
+	{ "aes-xts",		256 },
 	{ "3des-cbc",		192 },
 	{ "blowfish-cbc",	128 }
 };
Index: sys/crypto/rijndael/rijndael-api-fst.h
===================================================================
RCS file: /cvsroot/src/sys/crypto/rijndael/rijndael-api-fst.h,v
retrieving revision 1.8
diff -p -u -u -r1.8 rijndael-api-fst.h
--- sys/crypto/rijndael/rijndael-api-fst.h	21 Jan 2007 23:00:08 -0000	1.8
+++ sys/crypto/rijndael/rijndael-api-fst.h	12 Nov 2016 17:46:41 -0000
@@ -48,6 +48,7 @@
 #define     MODE_ECB              1 /*  Are we ciphering in ECB mode?   */
 #define     MODE_CBC              2 /*  Are we ciphering in CBC mode?   */
 #define     MODE_CFB1             3 /*  Are we ciphering in 1-bit CFB mode? */
+#define     MODE_XTS              4 /*  Are we ciphering in XTS mode? */
 #define     TRUE                  1
 #define     FALSE                 0
 #define     BITSPERBLOCK        128 /* Default number of bits in a cipher block */
Index: sys/crypto/rijndael/rijndael-api-fst.c
===================================================================
RCS file: /cvsroot/src/sys/crypto/rijndael/rijndael-api-fst.c,v
retrieving revision 1.24
diff -p -u -u -r1.24 rijndael-api-fst.c
--- sys/crypto/rijndael/rijndael-api-fst.c	14 May 2011 16:46:55 -0000	1.24
+++ sys/crypto/rijndael/rijndael-api-fst.c	12 Nov 2016 17:46:41 -0000
@@ -52,6 +52,8 @@ __KERNEL_RCSID(0, "$NetBSD: rijndael-api
 #include <crypto/rijndael/rijndael-alg-fst.h>
 #include <crypto/rijndael/rijndael-api-fst.h>
 
+#define XTS_ALPHA 0x87
+
 static void xor16(uint8_t *d, const uint8_t *a, const uint8_t* b)
 {
 	for (size_t i = 0; i < 4; i++) {
@@ -62,6 +64,22 @@ static void xor16(uint8_t *d, const uint
 	}
 }
 
+static void
+xts_exponentiate(uint8_t *iv)
+{
+	unsigned int carry = 0;
+
+	for (size_t i = 0; i < 16; i++) {
+		unsigned int msb = iv[i] >> 7;
+
+		iv[i] = (iv[i] << 1) | carry;
+		carry = msb;
+	}
+
+	if (carry != 0)
+		iv[0] ^= XTS_ALPHA;
+}
+
 int
 rijndael_makeKey(keyInstance *key, BYTE direction, int keyLen,
     const char *keyMaterial)
@@ -102,7 +120,8 @@ rijndael_makeKey(keyInstance *key, BYTE 
 int
 rijndael_cipherInit(cipherInstance *cipher, BYTE mode, const char *IV)
 {
-	if ((mode == MODE_ECB) || (mode == MODE_CBC) || (mode == MODE_CFB1)) {
+	if ((mode == MODE_ECB) || (mode == MODE_CBC) ||
+	    (mode == MODE_XTS) || (mode == MODE_CFB1)) {
 		cipher->mode = mode;
 	} else {
 		return BAD_CIPHER_MODE;
@@ -153,6 +172,18 @@ rijndael_blockEncrypt(cipherInstance *ci
 		}
 		break;
 
+	case MODE_XTS:
+		iv = (u_int8_t *)cipher->IV;
+		for (i = numBlocks; i > 0; i--) {
+			xor16(block, input, iv);
+			rijndaelEncrypt(key->rk, key->Nr, block, block);
+			xor16(outBuffer, block, iv);
+			xts_exponentiate(iv);
+			input += 16;
+			outBuffer += 16;
+		}
+		break;
+
 	case MODE_CFB1:
 		iv = (u_int8_t *)cipher->IV;
 		for (i = numBlocks; i > 0; i--) {
@@ -284,7 +315,19 @@ rijndael_blockDecrypt(cipherInstance *ci
 		}
 		break;
 
-    case MODE_CFB1:
+	case MODE_XTS:
+		iv = (u_int8_t *)cipher->IV;
+		for (i = numBlocks; i > 0; i--) {
+			xor16(block, input, iv);
+			rijndaelDecrypt(key->rk, key->Nr, block, block);
+			xor16(outBuffer, block, iv);
+			xts_exponentiate(iv);
+			input += 16;
+			outBuffer += 16;
+		}
+		break;
+
+	case MODE_CFB1:
 		iv = (u_int8_t *)cipher->IV;
 		for (i = numBlocks; i > 0; i--) {
 			memcpy(outBuffer, input, 16);


Home | Main Index | Thread Index | Old Index