linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: David Howells <dhowells@redhat.com>
To: herbert@gondor.apana.org.au, bfields@fieldses.org
Cc: dhowells@redhat.com, trond.myklebust@hammerspace.com,
	linux-crypto@vger.kernel.org, linux-afs@lists.infradead.org,
	linux-cifs@vger.kernel.org, linux-nfs@vger.kernel.org,
	linux-fsdevel@vger.kernel.org, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org
Subject: [PATCH 05/18] crypto/krb5: Implement the Kerberos5 rfc3961 encrypt and decrypt functions
Date: Thu, 12 Nov 2020 12:58:26 +0000	[thread overview]
Message-ID: <160518590606.2277919.18393717448457481276.stgit@warthog.procyon.org.uk> (raw)
In-Reply-To: <160518586534.2277919.14475638653680231924.stgit@warthog.procyon.org.uk>

Add functions that encrypt and decrypt a piece of an skbuff according to
rfc3961 sec 5.3, using Ki to checksum the data to be secured and Ke to
encrypt it during the encryption phase, then decrypting with Ke and
verifying the checksum with Ki in the decryption phase.

Signed-off-by: David Howells <dhowells@redhat.com>
---

 crypto/krb5/internal.h           |   18 +++
 crypto/krb5/main.c               |  102 +++++++++++++++++++
 crypto/krb5/rfc3961_simplified.c |  204 ++++++++++++++++++++++++++++++++++++++
 include/crypto/krb5.h            |   12 ++
 4 files changed, 336 insertions(+)

diff --git a/crypto/krb5/internal.h b/crypto/krb5/internal.h
index 874dddada713..ce07decf19f0 100644
--- a/crypto/krb5/internal.h
+++ b/crypto/krb5/internal.h
@@ -7,6 +7,7 @@
 
 #include <linux/scatterlist.h>
 #include <crypto/krb5.h>
+#include <crypto/hash.h>
 
 /*
  * Profile used for key derivation and encryption.
@@ -87,7 +88,24 @@ struct krb5_crypto_profile {
 	crypto_roundup(crypto_sync_skcipher_ivsize(TFM))
 #define round16(x) (((x) + 15) & ~15)
 
+/*
+ * main.c
+ */
+int crypto_shash_update_sg(struct shash_desc *desc, struct scatterlist *sg,
+			   size_t offset, size_t len);
+
 /*
  * rfc3961_simplified.c
  */
 extern const struct krb5_crypto_profile rfc3961_simplified_profile;
+
+ssize_t rfc3961_encrypt(const struct krb5_enctype *krb5,
+			struct krb5_enc_keys *keys,
+			struct scatterlist *sg, unsigned nr_sg, size_t sg_len,
+			size_t data_offset, size_t data_len,
+			bool preconfounded);
+int rfc3961_decrypt(const struct krb5_enctype *krb5,
+		    struct krb5_enc_keys *keys,
+		    struct scatterlist *sg, unsigned nr_sg,
+		    size_t *_offset, size_t *_len,
+		    int *_error_code);
diff --git a/crypto/krb5/main.c b/crypto/krb5/main.c
index 58d40252adc9..db3fc34be272 100644
--- a/crypto/krb5/main.c
+++ b/crypto/krb5/main.c
@@ -10,6 +10,7 @@
 #include <linux/module.h>
 #include <linux/export.h>
 #include <linux/kernel.h>
+#include <linux/highmem.h>
 #include "internal.h"
 
 MODULE_DESCRIPTION("Kerberos 5 crypto");
@@ -40,3 +41,104 @@ const struct krb5_enctype *crypto_krb5_find_enctype(u32 enctype)
 	return NULL;
 }
 EXPORT_SYMBOL(crypto_krb5_find_enctype);
+
+int crypto_shash_update_sg(struct shash_desc *desc, struct scatterlist *sg,
+			   size_t offset, size_t len)
+{
+	for (;; sg++) {
+		int ret;
+
+		if (offset < sg->length) {
+			struct page *page = sg_page(sg);
+			void *p = kmap_atomic(page);
+			size_t seg = min_t(size_t, len, sg->length - offset);
+
+			ret = crypto_shash_update(desc, p + sg->offset + offset, seg);
+			kunmap_atomic(p);
+			if (ret < 0)
+				return ret;
+			len -= seg;
+			offset = 0;
+		} else {
+			offset -= sg->length;
+		}
+		if (sg_is_last(sg) || len > 0)
+			break;
+	}
+
+	return 0;
+}
+
+/**
+ * crypto_krb5_encrypt - Apply Kerberos encryption and integrity.
+ * @krb5: The encoding to use.
+ * @keys: The encryption and integrity keys to use.
+ * @sg: Scatterlist defining the crypto buffer.
+ * @nr_sg: The number of elements in @sg.
+ * @sg_len: The size of the buffer.
+ * @data_offset: The offset of the data in the @sg buffer.
+ * @data_len: The length of the data.
+ * @preconfounded: True if the confounder is already inserted.
+ *
+ * Using the specified Kerberos encoding, insert a confounder and padding as
+ * needed, encrypt this and the data in place and insert an integrity checksum
+ * into the buffer.
+ *
+ * The buffer must include space for the confounder, the checksum and any
+ * padding required.  The caller can preinsert the confounder into the buffer
+ * (for testing, for example).
+ *
+ * The resulting secured blob may be less than the size of the buffer.
+ *
+ * Returns the size of the secure blob if successful, -ENOMEM on an allocation
+ * failure, -EFAULT if there is insufficient space, -EMSGSIZE if the confounder
+ * is too short or the data is misaligned.  Other errors may also be returned
+ * from the crypto layer.
+ */
+ssize_t crypto_krb5_encrypt(const struct krb5_enctype *krb5,
+			    struct krb5_enc_keys *keys,
+			    struct scatterlist *sg, unsigned nr_sg, size_t sg_len,
+			    size_t data_offset, size_t data_len,
+			    bool preconfounded)
+{
+	if (WARN_ON(data_offset > sg_len ||
+		    data_len > sg_len ||
+		    data_offset > sg_len - data_len))
+		return -EMSGSIZE;
+	return krb5->profile->encrypt(krb5, keys, sg, nr_sg, sg_len,
+				      data_offset, data_len, preconfounded);
+}
+EXPORT_SYMBOL(crypto_krb5_encrypt);
+
+/**
+ * crypto_krb5_decrypt - Validate and remove Kerberos encryption and integrity.
+ * @krb5: The encoding to use.
+ * @keys: The encryption and integrity keys to use.
+ * @sg: Scatterlist defining the crypto buffer.
+ * @nr_sg: The number of elements in @sg.
+ * @_offset: Offset of the secure blob in the buffer; updated to data offset.
+ * @_len: The length of the secure blob; updated to data length.
+ * @_error_code: Set to a Kerberos error code for parsing/validation errors.
+ *
+ * Using the specified Kerberos encoding, check and remove the integrity
+ * checksum and decrypt the secure region, stripping off the confounder.
+ *
+ * If successful, @_offset and @_len are updated to outline the region in which
+ * the data plus the trailing padding are stored.  The caller is responsible
+ * for working out how much padding there is and removing it.
+ *
+ * Returns the 0 if successful, -ENOMEM on an allocation failure; sets
+ * *_error_code and returns -EPROTO if the data cannot be parsed or if the
+ * integrity checksum doesn't match).  Other errors may also be returned from
+ * the crypto layer.
+ */
+int crypto_krb5_decrypt(const struct krb5_enctype *krb5,
+			struct krb5_enc_keys *keys,
+			struct scatterlist *sg, unsigned nr_sg,
+			size_t *_offset, size_t *_len,
+			int *_error_code)
+{
+	return krb5->profile->decrypt(krb5, keys, sg, nr_sg,
+				      _offset, _len, _error_code);
+}
+EXPORT_SYMBOL(crypto_krb5_decrypt);
diff --git a/crypto/krb5/rfc3961_simplified.c b/crypto/krb5/rfc3961_simplified.c
index 0a5c689f6354..0a5c19b83f51 100644
--- a/crypto/krb5/rfc3961_simplified.c
+++ b/crypto/krb5/rfc3961_simplified.c
@@ -65,6 +65,8 @@
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
+#include <linux/random.h>
+#include <linux/skbuff.h>
 #include <linux/slab.h>
 #include <linux/lcm.h>
 #include <crypto/skcipher.h>
@@ -386,9 +388,211 @@ static int rfc3961_calc_PRF(const struct krb5_enctype *krb5,
 	return ret;
 }
 
+/*
+ * Apply encryption and checksumming functions to part of a scatterlist.
+ */
+ssize_t rfc3961_encrypt(const struct krb5_enctype *krb5,
+			struct krb5_enc_keys *keys,
+			struct scatterlist *sg, unsigned nr_sg, size_t sg_len,
+			size_t data_offset, size_t data_len,
+			bool preconfounded)
+{
+	struct skcipher_request	*req;
+	struct shash_desc *desc;
+	ssize_t ret, done;
+	size_t bsize, base_len, secure_offset, secure_len, pad_len, cksum_offset;
+	void *buffer;
+	u8 *cksum, *iv;
+
+	if (WARN_ON(data_offset != krb5->conf_len))
+		return -EINVAL; /* Can't set offset on skcipher */
+
+	secure_offset = 0;
+	base_len = krb5->conf_len + data_len;
+	if (krb5->pad) {
+		secure_len = round_up(base_len, krb5->block_len);
+		pad_len    = secure_len - base_len;
+	} else {
+		secure_len = base_len;
+		pad_len    = 0;
+	}
+	cksum_offset = secure_len;
+	if (WARN_ON(cksum_offset + krb5->cksum_len > sg_len))
+		return -EFAULT;
+
+	bsize = krb5_shash_size(keys->Ki) +
+		krb5_digest_size(keys->Ki) +
+		krb5_sync_skcipher_size(keys->Ke) +
+		krb5_sync_skcipher_ivsize(keys->Ke);
+	bsize = max_t(size_t, bsize, krb5->conf_len);
+	bsize = max_t(size_t, bsize, krb5->block_len);
+	buffer = kzalloc(bsize, GFP_NOFS);
+	if (!buffer)
+		return -ENOMEM;
+
+	/* Insert the confounder into the skb */
+	ret = -EFAULT;
+	if (!preconfounded) {
+		get_random_bytes(buffer, krb5->conf_len);
+		done = sg_pcopy_from_buffer(sg, nr_sg, buffer, krb5->conf_len,
+					    secure_offset);
+		if (done != krb5->conf_len)
+			goto error;
+	}
+
+	/* We need to pad out to the crypto blocksize. */
+	if (pad_len) {
+		done = sg_zero_buffer(sg, nr_sg, pad_len, data_offset + data_len);
+		if (done != pad_len)
+			goto error;
+	}
+
+	/* Calculate the checksum using key Ki */
+	cksum = buffer + krb5_shash_size(keys->Ki);
+
+	desc = buffer;
+	desc->tfm = keys->Ki;
+	ret = crypto_shash_init(desc);
+	if (ret < 0)
+		goto error;
+
+	ret = crypto_shash_update_sg(desc, sg, secure_offset, secure_len);
+	if (ret < 0)
+		goto error;
+
+	ret = crypto_shash_final(desc, cksum);
+	if (ret < 0)
+		goto error;
+
+	/* Append the checksum into the buffer. */
+	ret = -EFAULT;
+	done = sg_pcopy_from_buffer(sg, nr_sg, cksum, krb5->cksum_len, cksum_offset);
+	if (done != krb5->cksum_len)
+		goto error;
+
+	/* Encrypt the secure region with key Ke. */
+	req = buffer +
+		krb5_shash_size(keys->Ki) +
+		krb5_digest_size(keys->Ki);
+	iv = buffer +
+		krb5_shash_size(keys->Ki) +
+		krb5_digest_size(keys->Ki) +
+		krb5_sync_skcipher_size(keys->Ke);
+
+	skcipher_request_set_sync_tfm(req, keys->Ke);
+	skcipher_request_set_callback(req, 0, NULL, NULL);
+	skcipher_request_set_crypt(req, sg, sg, secure_len, iv);
+	ret = crypto_skcipher_encrypt(req);
+	if (ret < 0)
+		goto error;
+
+	ret = secure_len + krb5->cksum_len;
+
+error:
+	kfree_sensitive(buffer);
+	return ret;
+}
+
+/*
+ * Apply decryption and checksumming functions to part of an skbuff.  The
+ * offset and length are updated to reflect the actual content of the encrypted
+ * region.
+ */
+int rfc3961_decrypt(const struct krb5_enctype *krb5,
+		    struct krb5_enc_keys *keys,
+		    struct scatterlist *sg, unsigned nr_sg,
+		    size_t *_offset, size_t *_len,
+		    int *_error_code)
+{
+	struct skcipher_request	*req;
+	struct shash_desc *desc;
+	ssize_t done;
+	size_t bsize, secure_len, offset = *_offset, len = *_len;
+	void *buffer = NULL;
+	int ret;
+	u8 *cksum, *cksum2, *iv;
+
+	if (WARN_ON(*_offset != 0))
+		return -EINVAL; /* Can't set offset on skcipher */
+
+	if (len < krb5->conf_len + krb5->cksum_len) {
+		*_error_code = 1; //RXGK_SEALED_INCON;
+		return -EPROTO;
+	}
+	secure_len = len - krb5->cksum_len;
+
+	bsize = krb5_shash_size(keys->Ki) +
+		krb5_digest_size(keys->Ki) * 2 +
+		krb5_sync_skcipher_size(keys->Ke) +
+		krb5_sync_skcipher_ivsize(keys->Ke);
+	buffer = kzalloc(bsize, GFP_NOFS);
+	if (!buffer)
+		return -ENOMEM;
+
+	/* Decrypt the secure region with key Ke. */
+	req = buffer +
+		krb5_shash_size(keys->Ki) +
+		krb5_digest_size(keys->Ki) * 2;
+	iv = buffer +
+		krb5_shash_size(keys->Ki) +
+		krb5_digest_size(keys->Ki) * 2 +
+		krb5_sync_skcipher_size(keys->Ke);
+
+	skcipher_request_set_sync_tfm(req, keys->Ke);
+	skcipher_request_set_callback(req, 0, NULL, NULL);
+	skcipher_request_set_crypt(req, sg, sg, secure_len, iv);
+	ret = crypto_skcipher_decrypt(req);
+	if (ret < 0)
+		goto error;
+
+	/* Calculate the checksum using key Ki */
+	cksum = buffer +
+		krb5_shash_size(keys->Ki);
+	cksum2 = buffer +
+		krb5_shash_size(keys->Ki) +
+		krb5_digest_size(keys->Ki);
+
+	desc = buffer;
+	desc->tfm = keys->Ki;
+	ret = crypto_shash_init(desc);
+	if (ret < 0)
+		goto error;
+
+	ret = crypto_shash_update_sg(desc, sg, 0, secure_len);
+	if (ret < 0)
+		goto error;
+
+	ret = crypto_shash_final(desc, cksum);
+	if (ret < 0)
+		goto error;
+
+	/* Get the checksum from the buffer. */
+	ret = -EFAULT;
+	done = sg_pcopy_to_buffer(sg, nr_sg, cksum2, krb5->cksum_len,
+				  offset + len - krb5->cksum_len);
+	if (done != krb5->cksum_len)
+		goto error;
+
+	if (memcmp(cksum, cksum2, krb5->cksum_len) != 0) {
+		*_error_code = 1; //RXGK_SEALED_INCON;
+		ret = -EPROTO;
+		goto error;
+	}
+
+	*_offset += krb5->conf_len;
+	*_len -= krb5->conf_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,
 	.calc_Ke	= rfc3961_calc_DK,
 	.calc_Ki	= rfc3961_calc_DK,
+	.encrypt	= rfc3961_encrypt,
+	.decrypt	= rfc3961_decrypt,
 };
diff --git a/include/crypto/krb5.h b/include/crypto/krb5.h
index 04286bacaf06..fb77f70117c1 100644
--- a/include/crypto/krb5.h
+++ b/include/crypto/krb5.h
@@ -12,6 +12,8 @@
 #ifndef _CRYPTO_KRB5_H
 #define _CRYPTO_KRB5_H
 
+#include <linux/crypto.h>
+
 struct crypto_shash;
 struct scatterlist;
 
@@ -103,6 +105,16 @@ struct krb5_enctype {
  */
 extern const struct krb5_enctype *crypto_krb5_find_enctype(u32 enctype);
 
+extern ssize_t crypto_krb5_encrypt(const struct krb5_enctype *krb5,
+				   struct krb5_enc_keys *keys,
+				   struct scatterlist *sg, unsigned nr_sg, size_t sg_len,
+				   size_t data_offset, size_t data_len,
+				   bool preconfounded);
+extern int crypto_krb5_decrypt(const struct krb5_enctype *krb5,
+			       struct krb5_enc_keys *keys,
+			       struct scatterlist *sg, unsigned nr_sg,
+			       size_t *_offset, size_t *_len,
+			       int *_error_code);
 /*
  * kdf.c
  */



  parent reply	other threads:[~2020-11-12 12:58 UTC|newest]

Thread overview: 57+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-11-12 12:57 [RFC][PATCH 00/18] crypto: Add generic Kerberos library David Howells
2020-11-12 12:57 ` [PATCH 01/18] crypto/krb5: Implement Kerberos crypto core David Howells
2020-11-12 12:58 ` [PATCH 02/18] crypto/krb5: Add some constants out of sunrpc headers David Howells
2020-11-12 12:58 ` [PATCH 03/18] crypto/krb5: Provide infrastructure and key derivation David Howells
2020-11-12 12:58 ` [PATCH 04/18] crypto/krb5: Implement the Kerberos5 rfc3961 " David Howells
2020-11-12 12:58 ` David Howells [this message]
2020-11-12 12:58 ` [PATCH 06/18] crypto/krb5: Implement the Kerberos5 rfc3961 get_mic and verify_mic David Howells
2020-11-12 12:58 ` [PATCH 07/18] crypto/krb5: Implement the AES enctypes from rfc3962 David Howells
2020-11-12 12:58 ` [PATCH 08/18] crypto/krb5: Implement crypto self-testing David Howells
2020-11-12 12:58 ` [PATCH 09/18] crypto/krb5: Implement the AES enctypes from rfc8009 David Howells
2020-11-12 12:59 ` [PATCH 10/18] crypto/krb5: Implement the AES encrypt/decrypt " David Howells
2020-11-12 12:59 ` [PATCH 11/18] crypto/krb5: Add the AES self-testing data " David Howells
2020-11-12 12:59 ` [PATCH 12/18] crypto/krb5: Implement the Camellia enctypes from rfc6803 David Howells
2020-11-12 12:59 ` [PATCH 13/18] rxrpc: Add the security index for yfs-rxgk David Howells
2020-11-12 12:59 ` [PATCH 14/18] rxrpc: Add YFS RxGK (GSSAPI) security class David Howells
2020-11-12 12:59 ` [PATCH 15/18] rxrpc: rxgk: Provide infrastructure and key derivation David Howells
2020-11-12 12:59 ` [PATCH 16/18] rxrpc: rxgk: Implement the yfs-rxgk security class (GSSAPI) David Howells
2020-11-12 13:00 ` [PATCH 17/18] rxrpc: rxgk: Implement connection rekeying David Howells
2020-11-12 13:00 ` [PATCH 18/18] rxgk: Support OpenAFS's rxgk implementation David Howells
2020-11-12 13:44 ` [RFC][PATCH 00/18] crypto: Add generic Kerberos library David Howells
2020-11-12 14:36 ` Chuck Lever
2020-11-12 15:42 ` David Howells
2020-11-12 15:49   ` Chuck Lever
2020-11-12 16:54   ` David Howells
2020-11-12 21:07     ` Bruce Fields
2020-11-12 21:09       ` Chuck Lever
2020-11-12 18:37 ` J. Bruce Fields
2020-11-12 18:39   ` Chuck Lever
2020-11-26  6:33 ` Herbert Xu
2020-11-26  8:19 ` David Howells
2020-11-27  5:07   ` Herbert Xu
2020-12-01  8:44   ` David Howells
2020-12-01  8:46     ` Herbert Xu
2020-12-01  9:12     ` David Howells
2020-12-01 10:36       ` Herbert Xu
2020-12-04 14:59 ` Why the auxiliary cipher in gss_krb5_crypto.c? David Howells
2020-12-04 15:46   ` Bruce Fields
2020-12-04 16:05     ` Chuck Lever
2020-12-04 16:14     ` Bruce Fields
2020-12-04 16:01   ` David Howells
2020-12-04 16:03     ` Bruce Fields
2020-12-04 16:50     ` David Howells
2020-12-04 17:06       ` Ard Biesheuvel
2020-12-04 17:19       ` David Howells
2020-12-04 17:35         ` Ard Biesheuvel
2020-12-04 21:08           ` Herbert Xu
2020-12-07  8:24           ` David Howells
2020-12-07 12:01         ` David Howells
2020-12-07 13:08           ` Ard Biesheuvel
2020-12-07 14:15           ` David Howells
2020-12-08  8:27             ` Ard Biesheuvel
2020-12-08  9:18             ` David Howells
2020-12-04 18:13   ` Theodore Y. Ts'o
2020-12-08 13:25 ` David Howells
2020-12-08 14:04   ` Ard Biesheuvel
2020-12-08 14:13   ` David Howells
2020-12-08 14:02 ` David Howells

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=160518590606.2277919.18393717448457481276.stgit@warthog.procyon.org.uk \
    --to=dhowells@redhat.com \
    --cc=bfields@fieldses.org \
    --cc=herbert@gondor.apana.org.au \
    --cc=linux-afs@lists.infradead.org \
    --cc=linux-cifs@vger.kernel.org \
    --cc=linux-crypto@vger.kernel.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-nfs@vger.kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=trond.myklebust@hammerspace.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).