* [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.