crypto/krb5: Implement the Kerberos5 rfc3961 get_mic and verify_mic
authorDavid Howells <dhowells@redhat.com>
Thu, 24 Sep 2020 09:23:48 +0000 (10:23 +0100)
committerDavid Howells <dhowells@redhat.com>
Sun, 2 Mar 2025 21:51:47 +0000 (21:51 +0000)
Add functions that sign and verify a message according to rfc3961 sec 5.4,
using Kc to generate a checksum and insert it into the MIC field in the
skbuff in the sign phase then checksum the data and compare it to the MIC
in the verify phase.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Herbert Xu <herbert@gondor.apana.org.au>
cc: "David S. Miller" <davem@davemloft.net>
cc: Chuck Lever <chuck.lever@oracle.com>
cc: Marc Dionne <marc.dionne@auristor.com>
cc: Eric Dumazet <edumazet@google.com>
cc: Jakub Kicinski <kuba@kernel.org>
cc: Paolo Abeni <pabeni@redhat.com>
cc: Simon Horman <horms@kernel.org>
cc: linux-afs@lists.infradead.org
cc: linux-nfs@vger.kernel.org
cc: linux-crypto@vger.kernel.org
cc: netdev@vger.kernel.org

crypto/krb5/internal.h
crypto/krb5/rfc3961_simplified.c

index c8deb112b60482c0e8f6f82da0ea356e6ae28bf5..07a47ddf3ea9a95cd033db0924e14061d5526a05 100644 (file)
@@ -169,3 +169,13 @@ int krb5_aead_decrypt(const struct krb5_enctype *krb5,
                      struct crypto_aead *aead,
                      struct scatterlist *sg, unsigned int nr_sg,
                      size_t *_offset, size_t *_len);
+ssize_t rfc3961_get_mic(const struct krb5_enctype *krb5,
+                       struct crypto_shash *shash,
+                       const struct krb5_buffer *metadata,
+                       struct scatterlist *sg, unsigned int nr_sg, size_t sg_len,
+                       size_t data_offset, size_t data_len);
+int rfc3961_verify_mic(const struct krb5_enctype *krb5,
+                      struct crypto_shash *shash,
+                      const struct krb5_buffer *metadata,
+                      struct scatterlist *sg, unsigned int nr_sg,
+                      size_t *_offset, size_t *_len);
index 58323ce5838a7f60cce41aa657a9f24a6c889bd2..c1dcb0dd3a00a41a6bbccd7a6197428d2ea8588c 100644 (file)
@@ -653,6 +653,134 @@ error:
        return ret;
 }
 
+/*
+ * Generate a checksum over some metadata and part of an skbuff and insert the
+ * MIC into the skbuff immediately prior to the data.
+ */
+ssize_t rfc3961_get_mic(const struct krb5_enctype *krb5,
+                       struct crypto_shash *shash,
+                       const struct krb5_buffer *metadata,
+                       struct scatterlist *sg, unsigned int nr_sg, size_t sg_len,
+                       size_t data_offset, size_t data_len)
+{
+       struct shash_desc *desc;
+       ssize_t ret, done;
+       size_t bsize;
+       void *buffer, *digest;
+
+       if (WARN_ON(data_offset != krb5->cksum_len))
+               return -EMSGSIZE;
+
+       bsize = krb5_shash_size(shash) +
+               krb5_digest_size(shash);
+       buffer = kzalloc(bsize, GFP_NOFS);
+       if (!buffer)
+               return -ENOMEM;
+
+       /* Calculate the MIC with key Kc and store it into the skb */
+       desc = buffer;
+       desc->tfm = shash;
+       ret = crypto_shash_init(desc);
+       if (ret < 0)
+               goto error;
+
+       if (metadata) {
+               ret = crypto_shash_update(desc, metadata->data, metadata->len);
+               if (ret < 0)
+                       goto error;
+       }
+
+       ret = crypto_shash_update_sg(desc, sg, data_offset, data_len);
+       if (ret < 0)
+               goto error;
+
+       digest = buffer + krb5_shash_size(shash);
+       ret = crypto_shash_final(desc, digest);
+       if (ret < 0)
+               goto error;
+
+       ret = -EFAULT;
+       done = sg_pcopy_from_buffer(sg, nr_sg, digest, krb5->cksum_len,
+                                   data_offset - krb5->cksum_len);
+       if (done != krb5->cksum_len)
+               goto error;
+
+       ret = krb5->cksum_len + data_len;
+
+error:
+       kfree_sensitive(buffer);
+       return ret;
+}
+
+/*
+ * Check the MIC on a region of an skbuff.  The offset and length are updated
+ * to reflect the actual content of the secure region.
+ */
+int rfc3961_verify_mic(const struct krb5_enctype *krb5,
+                      struct crypto_shash *shash,
+                      const struct krb5_buffer *metadata,
+                      struct scatterlist *sg, unsigned int nr_sg,
+                      size_t *_offset, size_t *_len)
+{
+       struct shash_desc *desc;
+       ssize_t done;
+       size_t bsize, data_offset, data_len, offset = *_offset, len = *_len;
+       void *buffer = NULL;
+       int ret;
+       u8 *cksum, *cksum2;
+
+       if (len < krb5->cksum_len)
+               return -EPROTO;
+       data_offset = offset + krb5->cksum_len;
+       data_len = len - krb5->cksum_len;
+
+       bsize = krb5_shash_size(shash) +
+               krb5_digest_size(shash) * 2;
+       buffer = kzalloc(bsize, GFP_NOFS);
+       if (!buffer)
+               return -ENOMEM;
+
+       cksum = buffer +
+               krb5_shash_size(shash);
+       cksum2 = buffer +
+               krb5_shash_size(shash) +
+               krb5_digest_size(shash);
+
+       /* Calculate the MIC */
+       desc = buffer;
+       desc->tfm = shash;
+       ret = crypto_shash_init(desc);
+       if (ret < 0)
+               goto error;
+
+       if (metadata) {
+               ret = crypto_shash_update(desc, metadata->data, metadata->len);
+               if (ret < 0)
+                       goto error;
+       }
+
+       crypto_shash_update_sg(desc, sg, data_offset, data_len);
+       crypto_shash_final(desc, cksum);
+
+       ret = -EFAULT;
+       done = sg_pcopy_to_buffer(sg, nr_sg, cksum2, krb5->cksum_len, offset);
+       if (done != krb5->cksum_len)
+               goto error;
+
+       if (memcmp(cksum, cksum2, krb5->cksum_len) != 0) {
+               ret = -EBADMSG;
+               goto error;
+       }
+
+       *_offset += krb5->cksum_len;
+       *_len -= krb5->cksum_len;
+       ret = 0;
+
+error:
+       kfree_sensitive(buffer);
+       return ret;
+}
+
 const struct krb5_crypto_profile rfc3961_simplified_profile = {
        .calc_PRF               = rfc3961_calc_PRF,
        .calc_Kc                = rfc3961_calc_DK,
@@ -664,4 +792,6 @@ const struct krb5_crypto_profile rfc3961_simplified_profile = {
        .load_checksum_key      = rfc3961_load_checksum_key,
        .encrypt                = krb5_aead_encrypt,
        .decrypt                = krb5_aead_decrypt,
+       .get_mic                = rfc3961_get_mic,
+       .verify_mic             = rfc3961_verify_mic,
 };