Commit 348f5669 authored by David Howells's avatar David Howells
Browse files

crypto/krb5: Implement the Kerberos5 rfc3961 get_mic and verify_mic



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: default avatarDavid 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
parent 00244da4
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -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);
+130 −0
Original line number Diff line number Diff line
@@ -653,6 +653,134 @@ int krb5_aead_decrypt(const struct krb5_enctype *krb5,
	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,
};