All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/2] cipher: Add AEAD API with AES-CCM
@ 2016-12-22  2:09 Mat Martineau
  2016-12-22  2:09 ` [PATCH 2/2] unit: Add AEAD cipher tests Mat Martineau
  2016-12-22 18:26 ` [PATCH 1/2] cipher: Add AEAD API with AES-CCM Denis Kenzior
  0 siblings, 2 replies; 3+ messages in thread
From: Mat Martineau @ 2016-12-22  2:09 UTC (permalink / raw)
  To: ell

[-- Attachment #1: Type: text/plain, Size: 10112 bytes --]

Add new function calls to handle encryption and decryption for
authenticated encryption with associated data (AEAD).

The only AEAD cipher supported so far is AES-CCM.

Requires kernel v4.10 or later for l_aead_cipher_encrypt() and
l_aead_cipher_decrypt() to return the correct length. Earlier kernels have a
bug in AF_ALG aead sockets.
---
 ell/cipher.c | 238 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 ell/cipher.h |  24 ++++++
 2 files changed, 244 insertions(+), 18 deletions(-)

diff --git a/ell/cipher.c b/ell/cipher.c
index ac5248a..ddcd310 100644
--- a/ell/cipher.c
+++ b/ell/cipher.c
@@ -28,6 +28,7 @@
 #include <unistd.h>
 #include <errno.h>
 #include <sys/socket.h>
+#include <alloca.h>
 
 #include "util.h"
 #include "cipher.h"
@@ -85,8 +86,14 @@ struct l_cipher {
 	int decrypt_sk;
 };
 
+struct l_aead_cipher {
+	int type;
+	int encrypt_sk;
+	int decrypt_sk;
+};
+
 static int create_alg(const char *alg_type, const char *alg_name,
-			const void *key, size_t key_length)
+			const void *key, size_t key_length, size_t tag_length)
 {
 	struct sockaddr_alg salg;
 	int sk;
@@ -111,6 +118,12 @@ static int create_alg(const char *alg_type, const char *alg_name,
 		return -1;
 	}
 
+	if (tag_length && setsockopt(sk, SOL_ALG, ALG_SET_AEAD_AUTHSIZE, NULL,
+					tag_length)) {
+		close(sk);
+		return -1;
+	}
+
 	ret = accept4(sk, NULL, 0, SOCK_CLOEXEC);
 	close(sk);
 
@@ -151,11 +164,55 @@ LIB_EXPORT struct l_cipher *l_cipher_new(enum l_cipher_type type,
 		break;
 	}
 
-	cipher->encrypt_sk = create_alg("skcipher", alg_name, key, key_length);
+	cipher->encrypt_sk = create_alg("skcipher", alg_name, key, key_length,
+					0);
+	if (cipher->encrypt_sk < 0)
+		goto error_free;
+
+	cipher->decrypt_sk = create_alg("skcipher", alg_name, key, key_length,
+					0);
+	if (cipher->decrypt_sk < 0)
+		goto error_close;
+
+	return cipher;
+
+error_close:
+	close(cipher->encrypt_sk);
+error_free:
+	l_free(cipher);
+	return NULL;
+}
+
+LIB_EXPORT struct l_aead_cipher *l_aead_cipher_new(enum l_aead_cipher_type type,
+							const void *key,
+							size_t key_length,
+							size_t tag_length)
+{
+	struct l_aead_cipher *cipher;
+	const char *alg_name;
+
+	if (unlikely(!key))
+		return NULL;
+
+	if (type != L_AEAD_CIPHER_AES_CCM)
+		return NULL;
+
+	cipher = l_new(struct l_aead_cipher, 1);
+	cipher->type = type;
+
+	switch (type) {
+	case L_AEAD_CIPHER_AES_CCM:
+		alg_name = "ccm(aes)";
+		break;
+	}
+
+	cipher->encrypt_sk = create_alg("aead", alg_name, key, key_length,
+					tag_length);
 	if (cipher->encrypt_sk < 0)
 		goto error_free;
 
-	cipher->decrypt_sk = create_alg("skcipher", alg_name, key, key_length);
+	cipher->decrypt_sk = create_alg("aead", alg_name, key, key_length,
+					tag_length);
 	if (cipher->decrypt_sk < 0)
 		goto error_close;
 
@@ -179,21 +236,61 @@ LIB_EXPORT void l_cipher_free(struct l_cipher *cipher)
 	l_free(cipher);
 }
 
+LIB_EXPORT void l_aead_cipher_free(struct l_aead_cipher *cipher)
+{
+	if (unlikely(!cipher))
+		return;
+
+	close(cipher->encrypt_sk);
+	close(cipher->decrypt_sk);
+
+	l_free(cipher);
+}
+
+static ssize_t build_iv(const void *nonce, uint8_t nonce_len, uint8_t *iv,
+			uint8_t iv_len)
+{
+	const size_t iv_overhead = 2;
+
+	if (nonce_len + iv_overhead > iv_len)
+		return -EINVAL;
+
+	iv[0] = iv_len - iv_overhead - nonce_len;
+	memcpy(iv + 1, nonce, nonce_len);
+
+	/* Assumes that remaining bytes in iv were already zeroed out */
+
+	return iv_len;
+}
+
 static ssize_t operate_cipher(int sk, __u32 operation,
-				const void *in, void *out, size_t len_in,
-				size_t len_out)
+				const void *in, size_t in_len,
+				const void *ad, size_t ad_len,
+				const void *nonce, size_t nonce_len,
+				void *out, size_t out_len,
+				size_t iv_len)
 {
-	char c_msg_buf[CMSG_SPACE(sizeof(operation))];
+	char *c_msg_buf;
+	size_t c_msg_size;
 	struct msghdr msg;
 	struct cmsghdr *c_msg;
-	struct iovec iov;
+	struct iovec iov[2];
 	ssize_t result;
 
-	memset(&c_msg_buf, 0, sizeof(c_msg_buf));
+	c_msg_size = CMSG_SPACE(sizeof(operation));
+	c_msg_size += ad_len ? CMSG_SPACE(sizeof(uint32_t)) : 0;
+	c_msg_size += (nonce && iv_len) ?
+		CMSG_SPACE(sizeof(struct af_alg_iv) + iv_len) : 0;
+
+	c_msg_buf = alloca(c_msg_size);
+
+	memset(c_msg_buf, 0, c_msg_size);
 	memset(&msg, 0, sizeof(msg));
 
+	msg.msg_iov = iov;
+
 	msg.msg_control = c_msg_buf;
-	msg.msg_controllen = sizeof(c_msg_buf);
+	msg.msg_controllen = c_msg_size;
 
 	c_msg = CMSG_FIRSTHDR(&msg);
 	c_msg->cmsg_level = SOL_ALG;
@@ -201,17 +298,75 @@ static ssize_t operate_cipher(int sk, __u32 operation,
 	c_msg->cmsg_len = CMSG_LEN(sizeof(operation));
 	memcpy(CMSG_DATA(c_msg), &operation, sizeof(operation));
 
-	iov.iov_base = (void *) in;
-	iov.iov_len = len_in;
+	if (ad_len) {
+		uint32_t *ad_data;
+
+		c_msg = CMSG_NXTHDR(&msg, c_msg);
+		c_msg->cmsg_level = SOL_ALG;
+		c_msg->cmsg_type = ALG_SET_AEAD_ASSOCLEN;
+		c_msg->cmsg_len = CMSG_LEN(sizeof(*ad_data));
+		ad_data = (void *) CMSG_DATA(c_msg);
+		*ad_data = ad_len;
+
+		iov[0].iov_base = (void *) ad;
+		iov[0].iov_len = ad_len;
+		iov[1].iov_base = (void *) in;
+		iov[1].iov_len = in_len;
+		msg.msg_iovlen = 2;
+	} else {
+		iov[0].iov_base = (void *) in;
+		iov[0].iov_len = in_len;
+		msg.msg_iovlen = 1;
+	}
+
+	if (nonce && iv_len) {
+		struct af_alg_iv *algiv;
+
+		c_msg = CMSG_NXTHDR(&msg, c_msg);
+		c_msg->cmsg_level = SOL_ALG;
+		c_msg->cmsg_type = ALG_SET_IV;
+		c_msg->cmsg_len = CMSG_LEN(sizeof(*algiv) + iv_len);
 
-	msg.msg_iov = &iov;
-	msg.msg_iovlen = 1;
+		algiv = (void *)CMSG_DATA(c_msg);
+		algiv->ivlen = iv_len;
+		result = build_iv(nonce, nonce_len, &algiv->iv[0], iv_len);
+		if (result < 0)
+			return result;
+	}
 
 	result = sendmsg(sk, &msg, 0);
 	if (result < 0)
 		return -errno;
 
-	result = read(sk, out, len_out);
+	if (ad) {
+		/*
+		 * When AEAD additional data is passed to sendmsg() for
+		 * use in computing the tag, those bytes also appear at
+		 * the beginning of the encrypt or decrypt results.  Rather
+		 * than force the caller to pad their result buffer with
+		 * the correct number of bytes for the additional data,
+		 * the necessary space is allocated here and then the
+		 * duplicate AAD is discarded.
+		 */
+		iov[0].iov_base = l_malloc(ad_len);
+		iov[0].iov_len = ad_len;
+		iov[1].iov_base = (void *) out;
+		iov[1].iov_len = out_len;
+		msg.msg_iovlen = 2;
+
+		msg.msg_control = NULL;
+		msg.msg_controllen = 0;
+
+		result = recvmsg(sk, &msg, 0);
+
+		if (result > (ssize_t) ad_len)
+			result -= ad_len;
+
+		l_free(iov[0].iov_base);
+	} else {
+		result = read(sk, out, out_len);
+	}
+
 	if (result < 0)
 		return -errno;
 
@@ -227,8 +382,8 @@ LIB_EXPORT bool l_cipher_encrypt(struct l_cipher *cipher,
 	if (unlikely(!in) || unlikely(!out))
 		return false;
 
-	return operate_cipher(cipher->encrypt_sk, ALG_OP_ENCRYPT, in, out, len,
-				len) >= 0;
+	return operate_cipher(cipher->encrypt_sk, ALG_OP_ENCRYPT, in, len,
+				NULL, 0, NULL, 0, out, len, 0) >= 0;
 }
 
 LIB_EXPORT bool l_cipher_decrypt(struct l_cipher *cipher,
@@ -240,8 +395,8 @@ LIB_EXPORT bool l_cipher_decrypt(struct l_cipher *cipher,
 	if (unlikely(!in) || unlikely(!out))
 		return false;
 
-	return operate_cipher(cipher->decrypt_sk, ALG_OP_DECRYPT, in, out, len,
-				len) >= 0;
+	return operate_cipher(cipher->decrypt_sk, ALG_OP_DECRYPT, in, len,
+				NULL, 0, NULL, 0, out, len, 0) >= 0;
 }
 
 LIB_EXPORT bool l_cipher_set_iv(struct l_cipher *cipher, const uint8_t *iv,
@@ -279,3 +434,50 @@ LIB_EXPORT bool l_cipher_set_iv(struct l_cipher *cipher, const uint8_t *iv,
 
 	return true;
 }
+
+static size_t l_aead_cipher_get_ivlen(struct l_aead_cipher *cipher)
+{
+	size_t ret;
+
+	switch (cipher->type) {
+	case L_AEAD_CIPHER_AES_CCM:
+		ret = 16;
+		break;
+	}
+
+	return ret;
+}
+
+LIB_EXPORT bool l_aead_cipher_encrypt(struct l_aead_cipher *cipher,
+					const void *in, size_t in_len,
+					const void *ad, size_t ad_len,
+					const void *nonce, size_t nonce_len,
+					void *out, size_t out_len)
+{
+	if (unlikely(!cipher))
+		return false;
+
+	if (unlikely(!in) || unlikely(!out))
+		return false;
+
+	return operate_cipher(cipher->encrypt_sk, ALG_OP_ENCRYPT, in, in_len,
+				ad, ad_len, nonce, nonce_len, out, out_len,
+				l_aead_cipher_get_ivlen(cipher)) >= 0;
+}
+
+LIB_EXPORT bool l_aead_cipher_decrypt(struct l_aead_cipher *cipher,
+					const void *in, size_t in_len,
+					const void *ad, size_t ad_len,
+					const void *nonce, size_t nonce_len,
+					void *out, size_t out_len)
+{
+	if (unlikely(!cipher))
+		return false;
+
+	if (unlikely(!in) || unlikely(!out))
+		return false;
+
+	return operate_cipher(cipher->decrypt_sk, ALG_OP_DECRYPT, in, in_len,
+				ad, ad_len, nonce, nonce_len, out, out_len,
+				l_aead_cipher_get_ivlen(cipher)) >= 0;
+}
diff --git a/ell/cipher.h b/ell/cipher.h
index f9ff42f..29c9542 100644
--- a/ell/cipher.h
+++ b/ell/cipher.h
@@ -51,6 +51,30 @@ bool l_cipher_decrypt(struct l_cipher *cipher,
 bool l_cipher_set_iv(struct l_cipher *cipher, const uint8_t *iv,
 			size_t iv_length);
 
+struct l_aead_cipher;
+
+enum l_aead_cipher_type {
+	L_AEAD_CIPHER_AES_CCM = 0,
+};
+
+struct l_aead_cipher *l_aead_cipher_new(enum l_aead_cipher_type type,
+					const void *key, size_t key_length,
+					size_t tag_length);
+
+void l_aead_cipher_free(struct l_aead_cipher *cipher);
+
+bool l_aead_cipher_encrypt(struct l_aead_cipher *cipher,
+				const void *in, size_t in_len,
+				const void *ad, size_t ad_len,
+				const void *nonce, size_t nonce_len,
+				void *out, size_t out_len);
+
+bool l_aead_cipher_decrypt(struct l_aead_cipher *cipher,
+				const void *in, size_t in_len,
+				const void *ad, size_t ad_len,
+				const void *nonce, size_t nonce_len,
+				void *out, size_t out_len);
+
 #ifdef __cplusplus
 }
 #endif
-- 
2.11.0


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [PATCH 2/2] unit: Add AEAD cipher tests
  2016-12-22  2:09 [PATCH 1/2] cipher: Add AEAD API with AES-CCM Mat Martineau
@ 2016-12-22  2:09 ` Mat Martineau
  2016-12-22 18:26 ` [PATCH 1/2] cipher: Add AEAD API with AES-CCM Denis Kenzior
  1 sibling, 0 replies; 3+ messages in thread
From: Mat Martineau @ 2016-12-22  2:09 UTC (permalink / raw)
  To: ell

[-- Attachment #1: Type: text/plain, Size: 3763 bytes --]

---
 unit/test-cipher.c | 105 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 105 insertions(+)

diff --git a/unit/test-cipher.c b/unit/test-cipher.c
index eaf97a0..64216c6 100644
--- a/unit/test-cipher.c
+++ b/unit/test-cipher.c
@@ -25,6 +25,7 @@
 #endif
 
 #include <assert.h>
+#include <alloca.h>
 
 #include <ell/ell.h>
 
@@ -122,6 +123,106 @@ static void test_arc4(const void *data)
 	l_cipher_free(cipher);
 }
 
+struct ccm_test_vector {
+	char *aad;
+	char *plaintext;
+	char *key;
+	char *nonce;
+	char *ciphertext;
+	char *tag;
+};
+
+static const struct ccm_test_vector ccm_long_nonce = {
+	.aad =
+	"333b6b8fda49c6e671bad05c7e2cafa88bd47f9b0aef1a358bc87d04f26f6c82",
+	.plaintext =
+	"1293201eb30ddd693b2eb23c1e6c20d5add2202afc71679ca2eba14f73b77bcd",
+	.key = "fa536cf6c309d45c1baaa658f674758d",
+	.nonce = "e0c5241bf0014ca88511d73a30",
+	.ciphertext =
+	"2e54ebaa38da9a2b03a1147495565c31d07e793b01fd28b2adeacac6f76ae84e",
+	.tag = "e0a03b982c5afc8a937373d7d2b0e7a3"
+};
+
+static const struct ccm_test_vector ccm_short_nonce = {
+	.plaintext =
+	"a3b3fdf26d213f83c5f656b00f77253b68959c188767d584914887602c787595",
+	.aad =
+	"fcc20524894b4603fefb8029eff485a513ce4753d0d3a27c3a2c69088fa7fab7",
+	.key = "7d84efac51291e868c7b7702181a3936",
+	.nonce = "1bb3e62620462a",
+	.ciphertext =
+	"3222192ee773cef4a87175b73b3875320f18b7e016d17d52fb01f0f6ca10bb5f",
+	.tag = "ee007aafe91135c39855ebf3db96d7ff"
+};
+
+static const struct ccm_test_vector ccm_no_aad = {
+	.plaintext =
+	"90795fffab99cffdeee5cadafe448ea4df74c480f9d7e1e481ee49adeee2732a",
+	.aad = "",
+	.key = "7b3da7d5ef41b5eef19cf8fb4ca19519",
+	.nonce = "96722de7516afb",
+	.ciphertext =
+	"9160dd0e0a8ddd13bf4acb0c6f3cf4794c5459d36a378cfb4a31e6b00840d78a",
+	.tag = "efd1dc938802cd845a16f32a60eabd0f"
+};
+
+static void test_aes_ccm(const void *data)
+{
+	struct l_aead_cipher *cipher;
+	char *encbuf;
+	size_t encbuflen;
+	char *decbuf;
+	size_t decbuflen;
+	int r;
+	const struct ccm_test_vector *tv = data;
+
+	size_t ptlen;
+	uint8_t *pt = l_util_from_hexstring(tv->plaintext, &ptlen);
+	size_t aadlen;
+	uint8_t *aad = l_util_from_hexstring(tv->aad, &aadlen);
+	size_t keylen;
+	uint8_t *key = l_util_from_hexstring(tv->key, &keylen);
+	size_t noncelen;
+	uint8_t *nonce = l_util_from_hexstring(tv->nonce, &noncelen);
+	size_t ctlen;
+	uint8_t *ct = l_util_from_hexstring(tv->ciphertext, &ctlen);
+	size_t taglen;
+	uint8_t *tag = l_util_from_hexstring(tv->tag, &taglen);
+
+	encbuflen = ctlen + taglen;
+	encbuf = alloca(encbuflen);
+	memset(encbuf, 0, encbuflen);
+	decbuflen = ptlen;
+	decbuf = alloca(decbuflen);
+	memset(decbuf, 0, decbuflen);
+
+	cipher = l_aead_cipher_new(L_AEAD_CIPHER_AES_CCM, key, keylen, taglen);
+	assert(cipher);
+
+	l_aead_cipher_encrypt(cipher, pt, ptlen, aad, aadlen,
+				nonce, noncelen, encbuf, encbuflen);
+
+	r = memcmp(encbuf, ct, ctlen);
+	assert(!r);
+	r = memcmp(encbuf + ctlen, tag, taglen);
+	assert(!r);
+
+	l_aead_cipher_decrypt(cipher, encbuf, encbuflen, aad, aadlen, nonce,
+				noncelen, decbuf, decbuflen);
+
+	r = memcmp(decbuf, pt, ptlen);
+	assert(!r);
+
+	l_aead_cipher_free(cipher);
+	l_free(pt);
+	l_free(key);
+	l_free(aad);
+	l_free(nonce);
+	l_free(ct);
+	l_free(tag);
+}
+
 int main(int argc, char *argv[])
 {
 	l_test_init(&argc, &argv);
@@ -132,5 +233,9 @@ int main(int argc, char *argv[])
 
 	l_test_add("arc4", test_arc4, NULL);
 
+	l_test_add("aes_ccm long nonce", test_aes_ccm, &ccm_long_nonce);
+	l_test_add("aes_ccm short nonce", test_aes_ccm, &ccm_short_nonce);
+	l_test_add("aes_ccm no AAD", test_aes_ccm, &ccm_no_aad);
+
 	return l_test_run();
 }
-- 
2.11.0


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH 1/2] cipher: Add AEAD API with AES-CCM
  2016-12-22  2:09 [PATCH 1/2] cipher: Add AEAD API with AES-CCM Mat Martineau
  2016-12-22  2:09 ` [PATCH 2/2] unit: Add AEAD cipher tests Mat Martineau
@ 2016-12-22 18:26 ` Denis Kenzior
  1 sibling, 0 replies; 3+ messages in thread
From: Denis Kenzior @ 2016-12-22 18:26 UTC (permalink / raw)
  To: ell

[-- Attachment #1: Type: text/plain, Size: 661 bytes --]

Hi Mat,

On 12/21/2016 08:09 PM, Mat Martineau wrote:
> Add new function calls to handle encryption and decryption for
> authenticated encryption with associated data (AEAD).
>
> The only AEAD cipher supported so far is AES-CCM.
>
> Requires kernel v4.10 or later for l_aead_cipher_encrypt() and
> l_aead_cipher_decrypt() to return the correct length. Earlier kernels have a
> bug in AF_ALG aead sockets.
> ---
>  ell/cipher.c | 238 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
>  ell/cipher.h |  24 ++++++
>  2 files changed, 244 insertions(+), 18 deletions(-)
>

Looks fine to me.  Both applied, thanks.

Regards,
-Denis


^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2016-12-22 18:26 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-12-22  2:09 [PATCH 1/2] cipher: Add AEAD API with AES-CCM Mat Martineau
2016-12-22  2:09 ` [PATCH 2/2] unit: Add AEAD cipher tests Mat Martineau
2016-12-22 18:26 ` [PATCH 1/2] cipher: Add AEAD API with AES-CCM Denis Kenzior

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.