diff mbox

[v2,05/10] crypto: AF_ALG: add AEAD support

Message ID 13113185.ASLrB6vjVX@tachyon.chronox.de (mailing list archive)
State Changes Requested
Delegated to: Herbert Xu
Headers show

Commit Message

Stephan Mueller Nov. 16, 2014, 2:26 a.m. UTC
This patch adds the AEAD support for AF_ALG.

The AEAD implementation uses the entire memory handling and
infrastructure of the existing skcipher implementation.

To use AEAD, the user space consumer has to use the salg_type named
"aead". The AEAD extension only uses the bind callback as the key
differentiator. The previously added functions that select whether to
use AEAD or ablkcipher crypto API functions depend on the TFM type
allocated during the bind() call.

The addition of AEAD brings a bit of overhead to calculate the size of
the ciphertext, because the AEAD implementation of the kernel crypto API
makes implied assumption on the location of the authentication tag. When
performing an encryption, the tag will be added to the created
ciphertext (note, the tag is placed adjacent to the ciphertext). For
decryption, the caller must hand in the ciphertext with the tag appended
to the ciphertext. Therefore, the selection of the used memory
needs to add/subtract the tag size from the source/destination buffers
depending on the encryption type. The code is provided with comments
explainint when and how that operation is performed.

Note: The AF_ALG interface does not support zero length plaintext or
zero length ciphertext. Such zero length input data may be used if one
wants to access the hash implementation of an AEAD directly (e.g. the
GHASH of GCM or CMAC for CCM). However, this is a use case that is not
of interest. GHASH or CMAC is directly available via the hash AF_ALG
interface and we therefore do not need to take precautions for this
use case.

A fully working example using all aspects of AEAD is provided at
http://www.chronox.de/libkcapi.html

Signed-off-by: Stephan Mueller <smueller@chronox.de>
---
 crypto/algif_skcipher.c | 155 ++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 145 insertions(+), 10 deletions(-)
diff mbox

Patch

diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c
index d0e31ab..4bb7556 100644
--- a/crypto/algif_skcipher.c
+++ b/crypto/algif_skcipher.c
@@ -49,7 +49,7 @@  struct skcipher_ctx {
 	bool aead;
 	void *aead_assoc;
 	/* define arbitrary maximum length of associated data */
-	#define MAX_AEAD_ASSOCLEN 128
+#define MAX_AEAD_ASSOCLEN 128
 	struct scatterlist sg_aead_assoc;
 	union {
 		struct ablkcipher_request ablkcipher_req;
@@ -387,6 +387,17 @@  static int skcipher_sendmsg(struct kiocb *unused, struct socket *sock,
 
 		if (con.iv && con.iv->ivlen != ivsize)
 			return -EINVAL;
+
+		/*
+		 * AEAD associated data is limited to a sensible size
+		 * Size limit is set to some arbitrary length to avoid
+		 * user space eating up memory
+		 */
+		if (ctx->aead &&
+		    (con.aead_assoc->aead_assoclen > MAX_AEAD_ASSOCLEN ||
+		     !con.aead_assoc->aead_assoclen ||
+		     !con.aead_assoc || !con.aead_authsize))
+			return -EINVAL;
 	}
 
 	err = -EINVAL;
@@ -399,6 +410,25 @@  static int skcipher_sendmsg(struct kiocb *unused, struct socket *sock,
 		ctx->enc = enc;
 		if (con.iv)
 			memcpy(ctx->iv, con.iv->iv, ivsize);
+		/* AEAD authentication data handling */
+		if (ctx->aead) {
+			if (con.aead_authsize)
+				err = crypto_aead_setauthsize(
+					crypto_aead_reqtfm(&ctx->u.aead_req),
+							   con.aead_authsize);
+			if (err)
+				goto unlock;
+			/* set associated data */
+			memcpy(ctx->aead_assoc,
+			       con.aead_assoc->aead_assoc,
+			       con.aead_assoc->aead_assoclen);
+			sg_init_one(&ctx->sg_aead_assoc,
+				    ctx->aead_assoc,
+				    con.aead_assoc->aead_assoclen);
+			aead_request_set_assoc(&ctx->u.aead_req,
+					       &ctx->sg_aead_assoc,
+					       con.aead_assoc->aead_assoclen);
+		}
 	}
 
 	while (size) {
@@ -547,10 +577,41 @@  static int skcipher_recvmsg(struct kiocb *unused, struct socket *sock,
 	int err = -EAGAIN;
 	int used;
 	long copied = 0;
+	unsigned int aead_authsize_enc = 0;
+	unsigned int aead_authsize_dec = 0;
 
 	lock_sock(sk);
+	/*
+	* AEAD memory structure: For encryption, the tag is appended to the
+	* ciphertext which implies that the memory allocated for the ciphertext
+	* must be increased by the tag length. For decryption, the tag
+	* is expected to be concatenated to the ciphertext. The plaintext
+	* therefore has a memory size of the ciphertext minus the tag length.
+	*
+	* Note: this memory calculation only works because we require the
+	* user space caller to:
+	*	* perform encryption by invoking the recv function with a buffer
+	*	  length of ciphertext + tag size -- the send function can be
+	*	  invoked normally with just the plaintext.
+	*	* perform a decryption by invoking the the write function with
+	*	  a buffer holding the ciphertext + tag (and setting the
+	*	  buffer size accordingly) -- the recv function can be invoked
+	*	  normally with just the space needed for the ciphertext.
+	*	  Though, the caller should check for EBADMSG to catch integiry
+	*	  violations.
+	*/
+	if (ctx->aead) {
+		if (ctx->enc)
+			aead_authsize_enc = crypto_aead_authsize(
+					crypto_aead_reqtfm(&ctx->u.aead_req));
+		else
+			aead_authsize_dec = crypto_aead_authsize(
+					crypto_aead_reqtfm(&ctx->u.aead_req));
+	}
+
 	for (iov = msg->msg_iov, iovlen = msg->msg_iovlen; iovlen > 0;
 	     iovlen--, iov++) {
+		/* size of the output data memory */
 		unsigned long seglen = iov->iov_len;
 		char __user *from = iov->iov_base;
 
@@ -562,6 +623,7 @@  static int skcipher_recvmsg(struct kiocb *unused, struct socket *sock,
 			while (!sg->length)
 				sg++;
 
+			/* size of the input data memory */
 			used = ctx->used;
 			if (!used) {
 				err = skcipher_wait_for_data(sk, flags);
@@ -569,7 +631,28 @@  static int skcipher_recvmsg(struct kiocb *unused, struct socket *sock,
 					goto unlock;
 			}
 
-			used = min_t(unsigned long, used, seglen);
+			used = min_t(unsigned long,
+					     /*
+					      * In case of encryption, add
+					      * the memory needed for the tag
+					      * to the input data length to
+					      * give the cipher the necessary
+					      * space to add the tag.
+					      */
+					     used + aead_authsize_enc,
+					     /*
+					      * In case of decryption, add the
+					      * memory needed for the tag
+					      * calculations to the output
+					      * buffer.
+					      */
+					     seglen + aead_authsize_dec);
+
+			if (used < aead_authsize_enc ||
+			    seglen < aead_authsize_dec) {
+				err = -ENOMEM;
+				goto unlock;
+			}
 
 			used = af_alg_make_sg(&ctx->rsgl, from, used, 1);
 			err = used;
@@ -583,9 +666,16 @@  static int skcipher_recvmsg(struct kiocb *unused, struct socket *sock,
 			if (!used)
 				goto free;
 
-			ablkcipher_request_set_crypt(&ctx->req, sg,
-						     ctx->rsgl.sg, used,
-						     ctx->iv);
+			/*
+			 * See API specification of the AEAD API: for
+			 * encryption, we need to tell the encrypt function
+			 * what the size of the plaintext is. But we have
+			 * ensured that we have sufficient memory allocated for
+			 * the operation.
+			 */
+			skcipher_crypto_set_crypt(ctx, sg, ctx->rsgl.sg,
+						  used - aead_authsize_enc,
+						  ctx->iv);
 
 			err = af_alg_wait_for_completion(
 				ctx->enc ?
@@ -599,10 +689,19 @@  free:
 			if (err)
 				goto unlock;
 
-			copied += used;
-			from += used;
-			seglen -= used;
-			skcipher_pull_sgl(sk, used);
+			/*
+			 * Adjust the output buffer counters by only the size
+			 * needed for the plaintext in case of a decryption
+			 */
+			copied += (used - aead_authsize_dec);
+			from += (used - aead_authsize_dec);
+			seglen -= (used - aead_authsize_dec);
+			/*
+			 * Adjust the input buffer by how much we have encrypted
+			 * or decrypted. In case of encryption, we only credit
+			 * the memory of the plaintext.
+			 */
+			skcipher_pull_sgl(sk, used - aead_authsize_enc);
 		}
 	}
 
@@ -724,6 +823,10 @@  static void skcipher_sock_destruct(struct sock *sk)
 	unsigned int ivlen = skcipher_crypto_ivsize_ctx(ctx);
 
 	skcipher_free_sgl(sk);
+	if (ctx->aead) {
+		memzero_explicit(ctx->aead_assoc, MAX_AEAD_ASSOCLEN);
+		sock_kfree_s(sk, ctx->aead_assoc, MAX_AEAD_ASSOCLEN);
+	}
 	memzero_explicit(ctx->iv, ivlen);
 	sock_kfree_s(sk, ctx->iv, ivlen);
 	sock_kfree_s(sk, ctx, ctx->len);
@@ -749,6 +852,17 @@  static int skcipher_accept_parent(void *private, struct sock *sk)
 
 	memset(ctx->iv, 0, ivlen);
 
+	if (skcipher_is_aead(private)) {
+		ctx->aead_assoc = sock_kmalloc(sk, MAX_AEAD_ASSOCLEN,
+					       GFP_KERNEL);
+		if (!ctx->aead_assoc) {
+			sock_kfree_s(sk, ctx->iv, ivlen);
+			sock_kfree_s(sk, ctx, len);
+			return -ENOMEM;
+		}
+		memset(ctx->aead_assoc, 0, MAX_AEAD_ASSOCLEN);
+	}
+
 	INIT_LIST_HEAD(&ctx->tsgl);
 	ctx->len = len;
 	ctx->used = 0;
@@ -779,15 +893,36 @@  static const struct af_alg_type algif_type_skcipher = {
 	.owner		=	THIS_MODULE
 };
 
+static void *aead_bind(const char *name, u32 type, u32 mask)
+{
+	return crypto_alloc_aead(name, type, mask);
+}
+
+static const struct af_alg_type algif_type_aead = {
+	.bind		=	aead_bind,
+	.release	=	skcipher_release,
+	.setkey		=	skcipher_setkey,
+	.accept		=	skcipher_accept_parent,
+	.ops		=	&algif_skcipher_ops,
+	.name		=	"aead",
+	.owner		=	THIS_MODULE
+};
+
 static int __init algif_skcipher_init(void)
 {
-	return af_alg_register_type(&algif_type_skcipher);
+	int ret = af_alg_register_type(&algif_type_skcipher);
+
+	if (ret)
+		return ret;
+	return af_alg_register_type(&algif_type_aead);
 }
 
 static void __exit algif_skcipher_exit(void)
 {
 	int err = af_alg_unregister_type(&algif_type_skcipher);
 	BUG_ON(err);
+	err = af_alg_unregister_type(&algif_type_aead);
+	BUG_ON(err);
 }
 
 module_init(algif_skcipher_init);