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 input data.
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 <smuel...@chronox.de>
---
 crypto/algif_skcipher.c | 158 +++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 148 insertions(+), 10 deletions(-)

diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c
index 9637365..26367f4 100644
--- a/crypto/algif_skcipher.c
+++ b/crypto/algif_skcipher.c
@@ -384,6 +384,9 @@ static int skcipher_sendmsg(struct kiocb *unused, struct 
socket *sock,
 
                if (con.iv && con.iv->ivlen != ivsize)
                        return -EINVAL;
+
+               if (ctx->aead && !con.aead_authsize && !con.aead_assoclen)
+                       return -EINVAL;
        }
 
        err = -EINVAL;
@@ -396,6 +399,16 @@ 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;
+                       ctx->aead_assoclen = con.aead_assoclen;
+               }
        }
 
        while (size) {
@@ -539,15 +552,58 @@ static int skcipher_recvmsg(struct kiocb *unused, struct 
socket *sock,
        unsigned bs = skcipher_crypto_blocksize(ctx);
        struct skcipher_sg_list *sgl;
        struct scatterlist *sg;
+       struct scatterlist assoc;
        unsigned long iovlen;
        struct iovec *iov;
        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.
+       *
+       * The memory structure for cipher operation has the following
+       * structure:
+       *       Symmetric encryption input:  plaintext
+       *       Symmetric encryption output: ciphertext
+       *       AEAD encryption input:  assoc data || plaintext
+       *       AEAD encryption output: cipherntext || auth tag
+       *       Symmetric decryption input:  ciphertext
+       *       Symmetric decryption output: plaintext
+       *       AEAD decryption input:  assoc data || ciphertext || auth tag
+       *       AEAD decryption output: plaintext
+       */
+       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;
 
@@ -559,14 +615,43 @@ 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) {
+                       if (used <= ctx->aead_assoclen) {
                                err = skcipher_wait_for_data(sk, flags);
                                if (err)
                                        goto unlock;
                        }
 
-                       used = min_t(unsigned long, used, seglen);
+                       /*
+                        * The cipher operation input data is reduced by
+                        * the associated data length as the data pointers will
+                        * be moved forward by the associated data length later
+                        * on.
+                        */
+                       used -= ctx->aead_assoclen;
+                       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;
@@ -580,9 +665,29 @@ 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);
+                       if (ctx->aead) {
+                               /* first chunk of input is AD */
+                               sg_init_table(&assoc, 2);
+                               sg_set_page(&assoc, sg_page(sg),
+                                           ctx->aead_assoclen, 0);
+                               aead_request_set_assoc(&ctx->u.aead_req, &assoc,
+                                                      ctx->aead_assoclen);
+                               /* point sg to cipher/plaintext */
+                               sg_set_page(sg, sg_page(sg),
+                                           sg->length - ctx->aead_assoclen,
+                                           ctx->aead_assoclen);
+                       }
+
+                       /*
+                        * 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 ?
@@ -596,10 +701,22 @@ 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. The associated data
+                        * is also released as a new cipher operation must
+                        * provide new associated data.
+                        */
+                       skcipher_pull_sgl(sk, used - aead_authsize_enc +
+                                         ctx->aead_assoclen);
                }
        }
 
@@ -732,15 +849,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);
-- 
2.1.0


--
To unsubscribe from this list: send the line "unsubscribe linux-crypto" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to