2020-11-12 12:58:36

by David Howells

[permalink] [raw]
Subject: [RFC][PATCH 00/18] crypto: Add generic Kerberos library


Hi Herbert, Bruce,

Here's my first cut at a generic Kerberos crypto library in the kernel so
that I can share code between rxrpc and sunrpc (and cifs?).

I derived some of the parts from the sunrpc gss library and added more
advanced AES and Camellia crypto. I haven't ported across the DES-based
crypto yet - I figure that can wait a bit till the interface is sorted.

Whilst I have put it into a directory under crypto/, I haven't made an
interface that goes and loads it (analogous to crypto_alloc_skcipher,
say). Instead, you call:

const struct krb5_enctype *crypto_krb5_find_enctype(u32 enctype);

to go and get a handler table and then use a bunch of accessor functions to
jump through the hoops. This is basically the way the sunrpc gsslib does
things. It might be worth making it so you do something like:

struct crypto_mech *ctx = crypto_mech_alloc("krb5(18)");

to get enctype 18, but I'm not sure if it's worth the effort. Also, I'm
not sure if there are any alternatives to kerberos we will need to support.

There are three main interfaces to it:

(*) I/O crypto: encrypt, decrypt, get_mic and verify_mic.

These all do in-place crypto, using an sglist to define the buffer
with the data in it. Is it necessary to make it able to take separate
input and output buffers?

(*) PRF+ calculation for key derivation.
(*) Kc, Ke, Ki derivation.

These use krb5_buffer structs to pass objects around. This is akin to
the xdr_netobj, but has a void* instead of a u8* data pointer.

In terms of rxrpc's rxgk, there's another step in key derivation that isn't
part of the kerberos standard, but uses the PRF+ function to generate a key
that is then used to generate Kc, Ke and Ki. Is it worth putting this into
the directory or maybe having a callout to insert an intermediate step in
key derivation?

Note that, for purposes of illustration, I've included some rxrpc patches
that use this interface to implement the rxgk Rx security class. The
branch also is based on some rxrpc patches that are a prerequisite for
this, but the crypto patches don't need it.

---
The patches can be found here also:

http://git.kernel.org/cgit/linux/kernel/git/dhowells/linux-fs.git/log/?h=crypto-krb5

David
---
David Howells (18):
crypto/krb5: Implement Kerberos crypto core
crypto/krb5: Add some constants out of sunrpc headers
crypto/krb5: Provide infrastructure and key derivation
crypto/krb5: Implement the Kerberos5 rfc3961 key derivation
crypto/krb5: Implement the Kerberos5 rfc3961 encrypt and decrypt functions
crypto/krb5: Implement the Kerberos5 rfc3961 get_mic and verify_mic
crypto/krb5: Implement the AES enctypes from rfc3962
crypto/krb5: Implement crypto self-testing
crypto/krb5: Implement the AES enctypes from rfc8009
crypto/krb5: Implement the AES encrypt/decrypt from rfc8009
crypto/krb5: Add the AES self-testing data from rfc8009
crypto/krb5: Implement the Camellia enctypes from rfc6803
rxrpc: Add the security index for yfs-rxgk
rxrpc: Add YFS RxGK (GSSAPI) security class
rxrpc: rxgk: Provide infrastructure and key derivation
rxrpc: rxgk: Implement the yfs-rxgk security class (GSSAPI)
rxrpc: rxgk: Implement connection rekeying
rxgk: Support OpenAFS's rxgk implementation


crypto/krb5/Kconfig | 9 +
crypto/krb5/Makefile | 11 +-
crypto/krb5/internal.h | 101 +++
crypto/krb5/kdf.c | 223 ++++++
crypto/krb5/main.c | 190 +++++
crypto/krb5/rfc3961_simplified.c | 732 ++++++++++++++++++
crypto/krb5/rfc3962_aes.c | 140 ++++
crypto/krb5/rfc6803_camellia.c | 249 ++++++
crypto/krb5/rfc8009_aes2.c | 440 +++++++++++
crypto/krb5/selftest.c | 543 +++++++++++++
crypto/krb5/selftest_data.c | 289 +++++++
fs/afs/misc.c | 13 +
include/crypto/krb5.h | 100 +++
include/keys/rxrpc-type.h | 17 +
include/trace/events/rxrpc.h | 4 +
include/uapi/linux/rxrpc.h | 17 +
net/rxrpc/Kconfig | 10 +
net/rxrpc/Makefile | 5 +
net/rxrpc/ar-internal.h | 20 +
net/rxrpc/conn_object.c | 2 +
net/rxrpc/key.c | 319 ++++++++
net/rxrpc/rxgk.c | 1232 ++++++++++++++++++++++++++++++
net/rxrpc/rxgk_app.c | 424 ++++++++++
net/rxrpc/rxgk_common.h | 164 ++++
net/rxrpc/rxgk_kdf.c | 271 +++++++
net/rxrpc/security.c | 6 +
26 files changed, 5530 insertions(+), 1 deletion(-)
create mode 100644 crypto/krb5/kdf.c
create mode 100644 crypto/krb5/rfc3961_simplified.c
create mode 100644 crypto/krb5/rfc3962_aes.c
create mode 100644 crypto/krb5/rfc6803_camellia.c
create mode 100644 crypto/krb5/rfc8009_aes2.c
create mode 100644 crypto/krb5/selftest.c
create mode 100644 crypto/krb5/selftest_data.c
create mode 100644 net/rxrpc/rxgk.c
create mode 100644 net/rxrpc/rxgk_app.c
create mode 100644 net/rxrpc/rxgk_common.h
create mode 100644 net/rxrpc/rxgk_kdf.c



2020-11-12 12:58:55

by David Howells

[permalink] [raw]
Subject: [PATCH 01/18] crypto/krb5: Implement Kerberos crypto core

Provide core structures, an encoding-type registry and basic module and
config bits for a generic Kerberos crypto library.

Signed-off-by: David Howells <[email protected]>
---

crypto/Kconfig | 1 +
crypto/Makefile | 1 +
crypto/krb5/Kconfig | 11 ++++++
crypto/krb5/Makefile | 9 +++++
crypto/krb5/internal.h | 87 ++++++++++++++++++++++++++++++++++++++++++++++++
crypto/krb5/main.c | 42 +++++++++++++++++++++++
include/crypto/krb5.h | 67 +++++++++++++++++++++++++++++++++++++
7 files changed, 218 insertions(+)
create mode 100644 crypto/krb5/Kconfig
create mode 100644 crypto/krb5/Makefile
create mode 100644 crypto/krb5/internal.h
create mode 100644 crypto/krb5/main.c
create mode 100644 include/crypto/krb5.h

diff --git a/crypto/Kconfig b/crypto/Kconfig
index 094ef56ab7b4..0d5ca023bb77 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -1940,5 +1940,6 @@ source "lib/crypto/Kconfig"
source "drivers/crypto/Kconfig"
source "crypto/asymmetric_keys/Kconfig"
source "certs/Kconfig"
+source "crypto/krb5/Kconfig"

endif # if CRYPTO
diff --git a/crypto/Makefile b/crypto/Makefile
index b279483fba50..732467ed3c94 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -197,3 +197,4 @@ obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += asymmetric_keys/
obj-$(CONFIG_CRYPTO_HASH_INFO) += hash_info.o
crypto_simd-y := simd.o
obj-$(CONFIG_CRYPTO_SIMD) += crypto_simd.o
+obj-$(CONFIG_CRYPTO_KRB5) += krb5/
diff --git a/crypto/krb5/Kconfig b/crypto/krb5/Kconfig
new file mode 100644
index 000000000000..881754500732
--- /dev/null
+++ b/crypto/krb5/Kconfig
@@ -0,0 +1,11 @@
+config CRYPTO_KRB5
+ tristate "Kerberos 5 crypto"
+ select CRYPTO_MANAGER
+ select CRYPTO_SKCIPHER
+ select CRYPTO_HASH_INFO
+ select CRYPTO_SHA1
+ select CRYPTO_CBC
+ select CRYPTO_CTS
+ select CRYPTO_AES
+ help
+ Provide Kerberos-5-based security.
diff --git a/crypto/krb5/Makefile b/crypto/krb5/Makefile
new file mode 100644
index 000000000000..071ce2ff82e5
--- /dev/null
+++ b/crypto/krb5/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for asymmetric cryptographic keys
+#
+
+krb5-y += \
+ main.o
+
+obj-$(CONFIG_CRYPTO_KRB5) += krb5.o
diff --git a/crypto/krb5/internal.h b/crypto/krb5/internal.h
new file mode 100644
index 000000000000..d2e3da7f101e
--- /dev/null
+++ b/crypto/krb5/internal.h
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Kerberos5 crypto internals
+ *
+ * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ */
+
+#include <crypto/krb5.h>
+
+/*
+ * Profile used for key derivation and encryption.
+ */
+struct krb5_crypto_profile {
+ /* Pseudo-random function */
+ int (*calc_PRF)(const struct krb5_enctype *krb5,
+ const struct krb5_buffer *protocol_key,
+ const struct krb5_buffer *octet_string,
+ struct krb5_buffer *result,
+ gfp_t gfp);
+
+ /* Checksum key derivation */
+ int (*calc_Kc)(const struct krb5_enctype *krb5,
+ const struct krb5_buffer *TK,
+ const struct krb5_buffer *usage_constant,
+ struct krb5_buffer *Kc,
+ gfp_t gfp);
+
+ /* Encryption key derivation */
+ int (*calc_Ke)(const struct krb5_enctype *krb5,
+ const struct krb5_buffer *TK,
+ const struct krb5_buffer *usage_constant,
+ struct krb5_buffer *Ke,
+ gfp_t gfp);
+
+ /* Integrity key derivation */
+ int (*calc_Ki)(const struct krb5_enctype *krb5,
+ const struct krb5_buffer *TK,
+ const struct krb5_buffer *usage_constant,
+ struct krb5_buffer *Ki,
+ gfp_t gfp);
+
+ /* Encrypt data in-place, inserting confounder and checksum. */
+ ssize_t (*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);
+
+ /* Decrypt data in-place, removing confounder and checksum */
+ int (*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);
+
+ /* Generate a MIC on part of a packet, inserting the checksum */
+ ssize_t (*get_mic)(const struct krb5_enctype *krb5,
+ struct crypto_shash *shash,
+ const struct krb5_buffer *metadata,
+ struct scatterlist *sg, unsigned nr_sg, size_t sg_len,
+ size_t data_offset, size_t data_len);
+
+ /* Verify the MIC on a piece of data, removing the checksum */
+ int (*verify_mic)(const struct krb5_enctype *krb5,
+ struct crypto_shash *shash,
+ const struct krb5_buffer *metadata,
+ struct scatterlist *sg, unsigned nr_sg,
+ size_t *_offset, size_t *_len,
+ int *_error_code);
+};
+
+/*
+ * Crypto size/alignment rounding convenience macros.
+ */
+#define crypto_roundup(X) round_up((X), CRYPTO_MINALIGN)
+
+#define krb5_shash_size(TFM) \
+ crypto_roundup(sizeof(struct shash_desc) + crypto_shash_descsize(TFM))
+#define krb5_skcipher_size(TFM) \
+ crypto_roundup(sizeof(struct skcipher_request) + crypto_skcipher_reqsize(TFM))
+#define krb5_digest_size(TFM) \
+ crypto_roundup(crypto_shash_digestsize(TFM))
+#define krb5_sync_skcipher_size(TFM) \
+ krb5_skcipher_size(&(TFM)->base)
+#define krb5_sync_skcipher_ivsize(TFM) \
+ crypto_roundup(crypto_sync_skcipher_ivsize(TFM))
+#define round16(x) (((x) + 15) & ~15)
diff --git a/crypto/krb5/main.c b/crypto/krb5/main.c
new file mode 100644
index 000000000000..58d40252adc9
--- /dev/null
+++ b/crypto/krb5/main.c
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* RxGK transport key derivation.
+ *
+ * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include "internal.h"
+
+MODULE_DESCRIPTION("Kerberos 5 crypto");
+MODULE_AUTHOR("Red Hat, Inc.");
+MODULE_LICENSE("GPL");
+
+static const struct krb5_enctype *const krb5_supported_enctypes[] = {
+};
+
+/**
+ * crypto_krb5_find_enctype - Find the handler for a Kerberos5 encryption type
+ * @enctype: The standard Kerberos encryption type number
+ *
+ * Look up a Kerberos encryption type by number. If successful, returns a
+ * pointer to the type tables; returns NULL otherwise.
+ */
+const struct krb5_enctype *crypto_krb5_find_enctype(u32 enctype)
+{
+ const struct krb5_enctype *krb5;
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(krb5_supported_enctypes); i++) {
+ krb5 = krb5_supported_enctypes[i];
+ if (krb5->etype == enctype)
+ return krb5;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL(crypto_krb5_find_enctype);
diff --git a/include/crypto/krb5.h b/include/crypto/krb5.h
new file mode 100644
index 000000000000..2bd6cfe50b85
--- /dev/null
+++ b/include/crypto/krb5.h
@@ -0,0 +1,67 @@
+/* Kerberos 5 crypto
+ *
+ * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#ifndef _CRYPTO_KRB5_H
+#define _CRYPTO_KRB5_H
+
+struct crypto_shash;
+struct scatterlist;
+
+struct krb5_buffer {
+ unsigned int len;
+ void *data;
+};
+
+/*
+ * Encryption key and checksum for RxGK encryption. These always come
+ * as a pair as per RFC3961 encrypt().
+ */
+struct krb5_enc_keys {
+ struct crypto_sync_skcipher *Ke; /* Encryption key */
+ struct crypto_shash *Ki; /* Checksum key */
+};
+
+/*
+ * Kerberos encoding type definition.
+ */
+struct krb5_enctype {
+ int etype; /* Encryption (key) type */
+ int ctype; /* Checksum type */
+ const char *name; /* "Friendly" name */
+ const char *encrypt_name; /* Crypto encrypt name */
+ const char *cksum_name; /* Crypto checksum name */
+ const char *hash_name; /* Crypto hash name */
+ u16 block_len; /* Length of encryption block */
+ u16 conf_len; /* Length of confounder (normally == block_len) */
+ u16 cksum_len; /* Length of checksum */
+ u16 key_bytes; /* Length of raw key, in bytes */
+ u16 key_len; /* Length of final key, in bytes */
+ u16 hash_len; /* Length of hash in bytes */
+ u16 prf_len; /* Length of PRF() result in bytes */
+ u16 Kc_len; /* Length of Kc in bytes */
+ u16 Ke_len; /* Length of Ke in bytes */
+ u16 Ki_len; /* Length of Ki in bytes */
+ bool keyed_cksum; /* T if a keyed cksum */
+ bool pad; /* T if should pad */
+
+ const struct krb5_crypto_profile *profile;
+
+ int (*random_to_key)(const struct krb5_enctype *krb5,
+ const struct krb5_buffer *in,
+ struct krb5_buffer *out); /* complete key generation */
+};
+
+/*
+ * main.c
+ */
+extern const struct krb5_enctype *crypto_krb5_find_enctype(u32 enctype);
+
+#endif /* _CRYPTO_KRB5_H */


2020-11-12 12:59:29

by David Howells

[permalink] [raw]
Subject: [PATCH 05/18] crypto/krb5: Implement the Kerberos5 rfc3961 encrypt and decrypt functions

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 <[email protected]>
---

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
*/


2020-11-12 12:59:39

by David Howells

[permalink] [raw]
Subject: [PATCH 06/18] crypto/krb5: Implement the Kerberos5 rfc3961 get_mic and verify_mic

Add functions that sign and verify a piece of an skbuff 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 <[email protected]>
---

crypto/krb5/internal.h | 11 +++
crypto/krb5/main.c | 70 ++++++++++++++++++++
crypto/krb5/rfc3961_simplified.c | 134 ++++++++++++++++++++++++++++++++++++++
include/crypto/krb5.h | 12 +++
4 files changed, 227 insertions(+)

diff --git a/crypto/krb5/internal.h b/crypto/krb5/internal.h
index ce07decf19f0..20b506327491 100644
--- a/crypto/krb5/internal.h
+++ b/crypto/krb5/internal.h
@@ -109,3 +109,14 @@ int rfc3961_decrypt(const struct krb5_enctype *krb5,
struct scatterlist *sg, unsigned nr_sg,
size_t *_offset, size_t *_len,
int *_error_code);
+ssize_t rfc3961_get_mic(const struct krb5_enctype *krb5,
+ struct crypto_shash *shash,
+ const struct krb5_buffer *metadata,
+ struct scatterlist *sg, unsigned 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 nr_sg,
+ size_t *_offset, size_t *_len,
+ int *_error_code);
diff --git a/crypto/krb5/main.c b/crypto/krb5/main.c
index db3fc34be272..97b28e40f6d7 100644
--- a/crypto/krb5/main.c
+++ b/crypto/krb5/main.c
@@ -142,3 +142,73 @@ int crypto_krb5_decrypt(const struct krb5_enctype *krb5,
_offset, _len, _error_code);
}
EXPORT_SYMBOL(crypto_krb5_decrypt);
+
+/**
+ * crypto_krb5_get_mic - Apply Kerberos integrity checksum.
+ * @krb5: The encoding to use.
+ * @shash: The keyed hash to use.
+ * @metadata: Metadata to add into the hash before adding the data.
+ * @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.
+ *
+ * Using the specified Kerberos encoding, calculate and insert an integrity
+ * checksum into the buffer.
+ *
+ * The buffer must include space for the checksum at the front.
+ *
+ * Returns the size of the secure blob if successful, -ENOMEM on an allocation
+ * failure, -EFAULT if there is insufficient space, -EMSGSIZE if the gap for
+ * the checksum is too short. Other errors may also be returned from the
+ * crypto layer.
+ */
+ssize_t crypto_krb5_get_mic(const struct krb5_enctype *krb5,
+ struct crypto_shash *shash,
+ const struct krb5_buffer *metadata,
+ struct scatterlist *sg, unsigned nr_sg, size_t sg_len,
+ size_t data_offset, size_t data_len)
+{
+ if (WARN_ON(data_offset > sg_len ||
+ data_len > sg_len ||
+ data_offset > sg_len - data_len))
+ return -EMSGSIZE;
+ return krb5->profile->get_mic(krb5, shash, metadata, sg, nr_sg, sg_len,
+ data_offset, data_len);
+}
+EXPORT_SYMBOL(crypto_krb5_get_mic);
+
+/**
+ * crypto_krb5_verify_mic - Validate and remove Kerberos integrity checksum.
+ * @krb5: The encoding to use.
+ * @shash: The keyed hash to use.
+ * @metadata: Metadata to add into the hash before adding the data.
+ * @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.
+ *
+ * If successful, @_offset and @_len are updated to outline the region in which
+ * the data is stored.
+ *
+ * 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_verify_mic(const struct krb5_enctype *krb5,
+ struct crypto_shash *shash,
+ const struct krb5_buffer *metadata,
+ struct scatterlist *sg, unsigned nr_sg,
+ size_t *_offset, size_t *_len,
+ int *_error_code)
+{
+ return krb5->profile->verify_mic(krb5, shash, metadata, sg, nr_sg,
+ _offset, _len, _error_code);
+}
+EXPORT_SYMBOL(crypto_krb5_verify_mic);
diff --git a/crypto/krb5/rfc3961_simplified.c b/crypto/krb5/rfc3961_simplified.c
index 0a5c19b83f51..f779f962b921 100644
--- a/crypto/krb5/rfc3961_simplified.c
+++ b/crypto/krb5/rfc3961_simplified.c
@@ -588,6 +588,138 @@ int rfc3961_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 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 nr_sg,
+ size_t *_offset, size_t *_len,
+ int *_error_code)
+{
+ 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) {
+ *_error_code = 1; //RXGK_SEALED_INCON;
+ 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) {
+ *_error_code = 1; //RXGK_SEALED_INCON;
+ ret = -EPROTO;
+ 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,
@@ -595,4 +727,6 @@ const struct krb5_crypto_profile rfc3961_simplified_profile = {
.calc_Ki = rfc3961_calc_DK,
.encrypt = rfc3961_encrypt,
.decrypt = rfc3961_decrypt,
+ .get_mic = rfc3961_get_mic,
+ .verify_mic = rfc3961_verify_mic,
};
diff --git a/include/crypto/krb5.h b/include/crypto/krb5.h
index fb77f70117c1..b83d3d487753 100644
--- a/include/crypto/krb5.h
+++ b/include/crypto/krb5.h
@@ -115,6 +115,18 @@ extern int crypto_krb5_decrypt(const struct krb5_enctype *krb5,
struct scatterlist *sg, unsigned nr_sg,
size_t *_offset, size_t *_len,
int *_error_code);
+extern ssize_t crypto_krb5_get_mic(const struct krb5_enctype *krb5,
+ struct crypto_shash *shash,
+ const struct krb5_buffer *metadata,
+ struct scatterlist *sg, unsigned nr_sg, size_t sg_len,
+ size_t data_offset, size_t data_len);
+extern int crypto_krb5_verify_mic(const struct krb5_enctype *krb5,
+ struct crypto_shash *shash,
+ const struct krb5_buffer *metadata,
+ struct scatterlist *sg, unsigned nr_sg,
+ size_t *_offset, size_t *_len,
+ int *_error_code);
+
/*
* kdf.c
*/


2020-11-12 12:59:55

by David Howells

[permalink] [raw]
Subject: [PATCH 07/18] crypto/krb5: Implement the AES enctypes from rfc3962

Implement the aes128-cts-hmac-sha1-96 and aes256-cts-hmac-sha1-96 enctypes
from rfc3962, using the rfc3961 kerberos 5 simplified crypto scheme.

Signed-off-by: David Howells <[email protected]>
---

crypto/krb5/Makefile | 3 +
crypto/krb5/internal.h | 6 ++
crypto/krb5/main.c | 2 +
crypto/krb5/rfc3962_aes.c | 140 +++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 150 insertions(+), 1 deletion(-)
create mode 100644 crypto/krb5/rfc3962_aes.c

diff --git a/crypto/krb5/Makefile b/crypto/krb5/Makefile
index 67824c44aac3..b81e2efac3c8 100644
--- a/crypto/krb5/Makefile
+++ b/crypto/krb5/Makefile
@@ -6,6 +6,7 @@
krb5-y += \
kdf.o \
main.o \
- rfc3961_simplified.o
+ rfc3961_simplified.o \
+ rfc3962_aes.o

obj-$(CONFIG_CRYPTO_KRB5) += krb5.o
diff --git a/crypto/krb5/internal.h b/crypto/krb5/internal.h
index 20b506327491..5d55a574536e 100644
--- a/crypto/krb5/internal.h
+++ b/crypto/krb5/internal.h
@@ -120,3 +120,9 @@ int rfc3961_verify_mic(const struct krb5_enctype *krb5,
struct scatterlist *sg, unsigned nr_sg,
size_t *_offset, size_t *_len,
int *_error_code);
+
+/*
+ * rfc3962_aes.c
+ */
+extern const struct krb5_enctype krb5_aes128_cts_hmac_sha1_96;
+extern const struct krb5_enctype krb5_aes256_cts_hmac_sha1_96;
diff --git a/crypto/krb5/main.c b/crypto/krb5/main.c
index 97b28e40f6d7..bce47580c33f 100644
--- a/crypto/krb5/main.c
+++ b/crypto/krb5/main.c
@@ -18,6 +18,8 @@ MODULE_AUTHOR("Red Hat, Inc.");
MODULE_LICENSE("GPL");

static const struct krb5_enctype *const krb5_supported_enctypes[] = {
+ &krb5_aes128_cts_hmac_sha1_96,
+ &krb5_aes256_cts_hmac_sha1_96,
};

/**
diff --git a/crypto/krb5/rfc3962_aes.c b/crypto/krb5/rfc3962_aes.c
new file mode 100644
index 000000000000..99297a698178
--- /dev/null
+++ b/crypto/krb5/rfc3962_aes.c
@@ -0,0 +1,140 @@
+/* rfc3962 Advanced Encryption Standard (AES) Encryption for Kerberos 5
+ *
+ * Parts borrowed from net/sunrpc/auth_gss/.
+ */
+/*
+ * COPYRIGHT (c) 2008
+ * The Regents of the University of Michigan
+ * ALL RIGHTS RESERVED
+ *
+ * Permission is granted to use, copy, create derivative works
+ * and redistribute this software and such derivative works
+ * for any purpose, so long as the name of The University of
+ * Michigan is not used in any advertising or publicity
+ * pertaining to the use of distribution of this software
+ * without specific, written prior authorization. If the
+ * above copyright notice or any other identification of the
+ * University of Michigan is included in any copy of any
+ * portion of this software, then the disclaimer below must
+ * also be included.
+ *
+ * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
+ * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
+ * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
+ * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
+ * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
+ * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
+ * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGES.
+ */
+
+/*
+ * Copyright (C) 1998 by the FundsXpress, INC.
+ *
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may require
+ * a specific license from the United States Government. It is the
+ * responsibility of any person or organization contemplating export to
+ * obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of FundsXpress. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. FundsXpress makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/*
+ * RxGK bits:
+ * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <crypto/skcipher.h>
+#include <crypto/hash.h>
+#include <linux/net.h>
+#include <linux/skbuff.h>
+#include <linux/key-type.h>
+#include <linux/slab.h>
+#include <linux/lcm.h>
+#include <linux/ctype.h>
+#include "internal.h"
+
+/*
+ * AES random-to-key function. For AES, this is an identity operation.
+ */
+static int rfc3962_random_to_key(const struct krb5_enctype *krb5,
+ const struct krb5_buffer *randombits,
+ struct krb5_buffer *result)
+{
+ if (randombits->len != 16 && randombits->len != 32)
+ return -EINVAL;
+
+ if (result->len != randombits->len)
+ return -EINVAL;
+
+ memcpy(result->data, randombits->data, randombits->len);
+ return 0;
+}
+
+const struct krb5_enctype krb5_aes128_cts_hmac_sha1_96 = {
+ .etype = KRB5_ENCTYPE_AES128_CTS_HMAC_SHA1_96,
+ .ctype = KRB5_CKSUMTYPE_HMAC_SHA1_96_AES128,
+ .name = "aes128-cts-hmac-sha1-96",
+ .encrypt_name = "cts(cbc(aes))",
+ .cksum_name = "hmac(sha1)",
+ .hash_name = "sha1",
+ .key_bytes = 16,
+ .key_len = 16,
+ .Kc_len = 16,
+ .Ke_len = 16,
+ .Ki_len = 16,
+ .block_len = 16,
+ .conf_len = 16,
+ .cksum_len = 12,
+ .hash_len = 20,
+ .prf_len = 16,
+ .keyed_cksum = true,
+ .pad = true,
+ .random_to_key = rfc3962_random_to_key,
+ .profile = &rfc3961_simplified_profile,
+};
+
+const struct krb5_enctype krb5_aes256_cts_hmac_sha1_96 = {
+ .etype = KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96,
+ .ctype = KRB5_CKSUMTYPE_HMAC_SHA1_96_AES256,
+ .name = "aes256-cts-hmac-sha1-96",
+ .encrypt_name = "cts(cbc(aes))",
+ .cksum_name = "hmac(sha1)",
+ .hash_name = "sha1",
+ .key_bytes = 32,
+ .key_len = 32,
+ .Kc_len = 32,
+ .Ke_len = 32,
+ .Ki_len = 32,
+ .block_len = 16,
+ .conf_len = 16,
+ .cksum_len = 12,
+ .hash_len = 20,
+ .prf_len = 16,
+ .keyed_cksum = true,
+ .pad = true,
+ .random_to_key = rfc3962_random_to_key,
+ .profile = &rfc3961_simplified_profile,
+};


2020-11-12 13:00:09

by David Howells

[permalink] [raw]
Subject: [PATCH 08/18] crypto/krb5: Implement crypto self-testing

Implement self-testing infrastructure to test the pseudo-random function,
key derivation, encryption and checksumming.

Signed-off-by: David Howells <[email protected]>
---

crypto/krb5/Kconfig | 4
crypto/krb5/Makefile | 4
crypto/krb5/internal.h | 48 ++++
crypto/krb5/main.c | 12 +
crypto/krb5/selftest.c | 543 +++++++++++++++++++++++++++++++++++++++++++
crypto/krb5/selftest_data.c | 38 +++
6 files changed, 649 insertions(+)
create mode 100644 crypto/krb5/selftest.c
create mode 100644 crypto/krb5/selftest_data.c

diff --git a/crypto/krb5/Kconfig b/crypto/krb5/Kconfig
index 881754500732..e2eba1d689ab 100644
--- a/crypto/krb5/Kconfig
+++ b/crypto/krb5/Kconfig
@@ -9,3 +9,7 @@ config CRYPTO_KRB5
select CRYPTO_AES
help
Provide Kerberos-5-based security.
+
+config CRYPTO_KRB5_SELFTESTS
+ bool "Kerberos 5 crypto selftests"
+ depends on CRYPTO_KRB5
diff --git a/crypto/krb5/Makefile b/crypto/krb5/Makefile
index b81e2efac3c8..b7da03cae6d1 100644
--- a/crypto/krb5/Makefile
+++ b/crypto/krb5/Makefile
@@ -9,4 +9,8 @@ krb5-y += \
rfc3961_simplified.o \
rfc3962_aes.o

+krb5-$(CONFIG_CRYPTO_KRB5_SELFTESTS) += \
+ selftest.o \
+ selftest_data.o
+
obj-$(CONFIG_CRYPTO_KRB5) += krb5.o
diff --git a/crypto/krb5/internal.h b/crypto/krb5/internal.h
index 5d55a574536e..47424b433778 100644
--- a/crypto/krb5/internal.h
+++ b/crypto/krb5/internal.h
@@ -88,6 +88,37 @@ struct krb5_crypto_profile {
crypto_roundup(crypto_sync_skcipher_ivsize(TFM))
#define round16(x) (((x) + 15) & ~15)

+/*
+ * Self-testing data.
+ */
+struct krb5_prf_test {
+ const struct krb5_enctype *krb5;
+ const char *name, *key, *octet, *prf;
+};
+
+struct krb5_key_test_one {
+ u32 use;
+ const char *key;
+};
+
+struct krb5_key_test {
+ const struct krb5_enctype *krb5;
+ const char *name, *key;
+ struct krb5_key_test_one Kc, Ke, Ki;
+};
+
+struct krb5_enc_test {
+ const struct krb5_enctype *krb5;
+ const char *name, *plain, *conf, *K0, *Ke, *Ki, *ct;
+ __be32 usage;
+};
+
+struct krb5_mic_test {
+ const struct krb5_enctype *krb5;
+ const char *name, *plain, *K0, *Kc, *mic;
+ __be32 usage;
+};
+
/*
* main.c
*/
@@ -126,3 +157,20 @@ int rfc3961_verify_mic(const struct krb5_enctype *krb5,
*/
extern const struct krb5_enctype krb5_aes128_cts_hmac_sha1_96;
extern const struct krb5_enctype krb5_aes256_cts_hmac_sha1_96;
+
+/*
+ * selftest.c
+ */
+#ifdef CONFIG_CRYPTO_KRB5_SELFTESTS
+void krb5_selftest(void);
+#else
+static inline void krb5_selftest(void) {}
+#endif
+
+/*
+ * selftest_data.c
+ */
+extern const struct krb5_prf_test krb5_prf_tests[];
+extern const struct krb5_key_test krb5_key_tests[];
+extern const struct krb5_enc_test krb5_enc_tests[];
+extern const struct krb5_mic_test krb5_mic_tests[];
diff --git a/crypto/krb5/main.c b/crypto/krb5/main.c
index bce47580c33f..b79127027551 100644
--- a/crypto/krb5/main.c
+++ b/crypto/krb5/main.c
@@ -214,3 +214,15 @@ int crypto_krb5_verify_mic(const struct krb5_enctype *krb5,
_offset, _len, _error_code);
}
EXPORT_SYMBOL(crypto_krb5_verify_mic);
+
+static int __init crypto_krb5_init(void)
+{
+ krb5_selftest();
+ return 0;
+}
+module_init(crypto_krb5_init);
+
+static void __exit crypto_krb5_exit(void)
+{
+}
+module_exit(crypto_krb5_exit);
diff --git a/crypto/krb5/selftest.c b/crypto/krb5/selftest.c
new file mode 100644
index 000000000000..df57ab24cc6e
--- /dev/null
+++ b/crypto/krb5/selftest.c
@@ -0,0 +1,543 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* RxGK self-testing
+ *
+ * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/slab.h>
+#include <crypto/skcipher.h>
+#include <crypto/hash.h>
+#include "internal.h"
+
+#define VALID(X) \
+ ({ \
+ bool __x = (X); \
+ if (__x) { \
+ pr_warn("!!! TESTINVAL %s:%u\n", __FILE__, __LINE__); \
+ } \
+ __x; \
+ })
+
+#define CHECK(X) \
+ ({ \
+ bool __x = (X); \
+ if (__x) { \
+ pr_warn("!!! TESTFAIL %s:%u\n", __FILE__, __LINE__); \
+ } \
+ __x; \
+ })
+
+enum which_key {
+ TEST_KC, TEST_KE, TEST_KI,
+};
+
+static int prep_buf(struct krb5_buffer *buf)
+{
+ buf->data = kmalloc(buf->len, GFP_KERNEL);
+ if (!buf->data)
+ return -ENOMEM;
+ return 0;
+}
+
+#define PREP_BUF(BUF, LEN) \
+ do { \
+ (BUF)->len = (LEN); \
+ if ((ret = prep_buf((BUF))) < 0) \
+ goto out; \
+ } while(0)
+
+static int load_buf(struct krb5_buffer *buf, const char *from)
+{
+ size_t len = strlen(from);
+ int ret;
+
+ if (len > 1 && from[0] == '\'') {
+ PREP_BUF(buf, len - 1);
+ memcpy(buf->data, from + 1, len - 1);
+ ret = 0;
+ goto out;
+ }
+
+ if (VALID(len & 1))
+ return -EINVAL;
+
+ PREP_BUF(buf, len / 2);
+ if ((ret = hex2bin(buf->data, from, buf->len)) < 0) {
+ VALID(1);
+ goto out;
+ }
+out:
+ return ret;
+}
+
+#define LOAD_BUF(BUF, FROM) do { if ((ret = load_buf(BUF, FROM)) < 0) goto out; } while(0)
+
+static void clear_buf(struct krb5_buffer *buf)
+{
+ kfree(buf->data);
+ buf->len = 0;
+ buf->data = NULL;
+}
+
+/*
+ * Perform a pseudo-random function check.
+ */
+static int krb5_test_one_prf(const struct krb5_prf_test *test)
+{
+ const struct krb5_enctype *krb5 = test->krb5;
+ struct krb5_buffer key = {}, octet = {}, result = {}, prf = {};
+ int ret;
+
+ pr_notice("Running %s %s\n", krb5->name, test->name);
+
+ LOAD_BUF(&key, test->key);
+ LOAD_BUF(&octet, test->octet);
+ LOAD_BUF(&prf, test->prf);
+ PREP_BUF(&result, krb5->prf_len);
+
+ if (VALID(result.len != prf.len)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if ((ret = krb5->profile->calc_PRF(krb5, &key, &octet, &result, GFP_KERNEL)) < 0) {
+ CHECK(1);
+ pr_warn("PRF calculation failed %d\n", ret);
+ goto out;
+ }
+
+ if (memcmp(result.data, prf.data, result.len) != 0) {
+ CHECK(1);
+ ret = -EKEYREJECTED;
+ goto out;
+ }
+
+ ret = 0;
+
+out:
+ clear_buf(&result);
+ clear_buf(&octet);
+ clear_buf(&key);
+ return ret;
+}
+
+/*
+ * Perform a key derivation check.
+ */
+static int krb5_test_key(const struct krb5_enctype *krb5,
+ const struct krb5_buffer *base_key,
+ const struct krb5_key_test_one *test,
+ enum which_key which)
+{
+ struct krb5_buffer key = {}, result = {};
+ int ret;
+
+ LOAD_BUF(&key, test->key);
+ PREP_BUF(&result, key.len);
+
+ switch (which) {
+ case TEST_KC:
+ ret = crypto_krb5_get_Kc(krb5, base_key, test->use, &result,
+ NULL, GFP_KERNEL);
+ break;
+ case TEST_KE:
+ ret = crypto_krb5_get_Ke(krb5, base_key, test->use, &result,
+ NULL, GFP_KERNEL);
+ break;
+ case TEST_KI:
+ ret = crypto_krb5_get_Ki(krb5, base_key, test->use, &result,
+ NULL, GFP_KERNEL);
+ break;
+ default:
+ VALID(1);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (ret < 0) {
+ CHECK(1);
+ pr_warn("Key derivation failed %d\n", ret);
+ goto out;
+ }
+
+ if (memcmp(result.data, key.data, result.len) != 0) {
+ CHECK(1);
+ ret = -EKEYREJECTED;
+ goto out;
+ }
+
+out:
+ clear_buf(&key);
+ clear_buf(&result);
+ return ret;
+}
+
+static int krb5_test_one_key(const struct krb5_key_test *test)
+{
+ const struct krb5_enctype *krb5 = test->krb5;
+ struct krb5_buffer base_key = {};
+ int ret;
+
+ pr_notice("Running %s %s\n", krb5->name, test->name);
+
+ LOAD_BUF(&base_key, test->key);
+
+ if ((ret = krb5_test_key(krb5, &base_key, &test->Kc, TEST_KC)) < 0)
+ goto out;
+ if ((ret = krb5_test_key(krb5, &base_key, &test->Ke, TEST_KE)) < 0)
+ goto out;
+ if ((ret = krb5_test_key(krb5, &base_key, &test->Ki, TEST_KI)) < 0)
+ goto out;
+
+out:
+ clear_buf(&base_key);
+ return ret;
+}
+
+static int krb5_test_get_Kc(const struct krb5_mic_test *test,
+ struct crypto_shash **_Kc)
+{
+ const struct krb5_enctype *krb5 = test->krb5;
+ struct crypto_shash *shash;
+ struct krb5_buffer K0 = {}, key = {};
+ int ret;
+
+ shash = crypto_alloc_shash(krb5->cksum_name, 0, 0);
+ if (IS_ERR(shash))
+ return (PTR_ERR(shash) == -ENOENT) ? -ENOPKG : PTR_ERR(shash);
+ *_Kc = shash;
+
+ if (test->Kc) {
+ LOAD_BUF(&key, test->Kc);
+ } else {
+ char usage_data[5];
+ struct krb5_buffer usage = { .len = 5, .data = usage_data };
+ memcpy(usage_data, &test->usage, 4);
+ usage_data[4] = 0x99;
+ LOAD_BUF(&K0, test->K0);
+ PREP_BUF(&key, krb5->Kc_len);
+ ret = krb5->profile->calc_Kc(krb5, &K0, &usage, &key, GFP_KERNEL);
+ }
+
+ ret = crypto_shash_setkey(shash, key.data, key.len);
+out:
+ clear_buf(&key);
+ clear_buf(&K0);
+ return ret;
+}
+
+static int krb5_test_get_Ke(const struct krb5_enc_test *test,
+ struct krb5_enc_keys *keys)
+{
+ const struct krb5_enctype *krb5 = test->krb5;
+ struct crypto_sync_skcipher *ci;
+ struct krb5_buffer K0 = {}, key = {};
+ int ret;
+
+ ci = crypto_alloc_sync_skcipher(krb5->encrypt_name, 0, 0);
+ if (IS_ERR(ci))
+ return (PTR_ERR(ci) == -ENOENT) ? -ENOPKG : PTR_ERR(ci);
+ keys->Ke = ci;
+
+ if (test->Ke) {
+ LOAD_BUF(&key, test->Ke);
+ } else {
+ char usage_data[5];
+ struct krb5_buffer usage = { .len = 5, .data = usage_data };
+ memcpy(usage_data, &test->usage, 4);
+ usage_data[4] = 0xAA;
+ LOAD_BUF(&K0, test->K0);
+ PREP_BUF(&key, krb5->Ke_len);
+ ret = krb5->profile->calc_Ke(krb5, &K0, &usage, &key, GFP_KERNEL);
+ }
+
+ ret = crypto_sync_skcipher_setkey(ci, key.data, key.len);
+out:
+ clear_buf(&key);
+ clear_buf(&K0);
+ return ret;
+}
+
+static int krb5_test_get_Ki(const struct krb5_enc_test *test,
+ struct krb5_enc_keys *keys)
+{
+ const struct krb5_enctype *krb5 = test->krb5;
+ struct crypto_shash *shash;
+ struct krb5_buffer K0 = {}, key = {};
+ int ret;
+
+ shash = crypto_alloc_shash(krb5->cksum_name, 0, 0);
+ if (IS_ERR(shash))
+ return (PTR_ERR(shash) == -ENOENT) ? -ENOPKG : PTR_ERR(shash);
+ keys->Ki = shash;
+
+ if (test->Ki) {
+ LOAD_BUF(&key, test->Ki);
+ } else {
+ char usage_data[5];
+ struct krb5_buffer usage = { .len = 5, .data = usage_data };
+ memcpy(usage_data, &test->usage, 4);
+ usage_data[4] = 0x55;
+ LOAD_BUF(&K0, test->K0);
+ PREP_BUF(&key, krb5->Ki_len);
+ ret = krb5->profile->calc_Ki(krb5, &K0, &usage, &key, GFP_KERNEL);
+ }
+
+ ret = crypto_shash_setkey(shash, key.data, key.len);
+out:
+ clear_buf(&key);
+ clear_buf(&K0);
+ return ret;
+}
+
+/*
+ * Generate a buffer containing encryption test data.
+ */
+static int krb5_load_enc_buf(const struct krb5_enc_test *test,
+ const struct krb5_buffer *plain,
+ void *buf)
+{
+ const struct krb5_enctype *krb5 = test->krb5;
+ unsigned int conf_len, pad_len, enc_len, ct_len;
+ int ret;
+
+ conf_len = strlen(test->conf);
+ if (VALID((conf_len & 1) || conf_len / 2 != krb5->conf_len))
+ return -EINVAL;
+
+ if (krb5->pad) {
+ enc_len = round_up(krb5->conf_len + plain->len, krb5->block_len);
+ pad_len = enc_len - (krb5->conf_len + plain->len);
+ } else {
+ enc_len = krb5->conf_len + plain->len;
+ pad_len = 0;
+ }
+
+ ct_len = strlen(test->ct);
+ if (VALID((ct_len & 1) || ct_len / 2 != enc_len + krb5->cksum_len))
+ return -EINVAL;
+ ct_len = enc_len + krb5->cksum_len;
+
+ if ((ret = hex2bin(buf, test->conf, krb5->conf_len)) < 0)
+ return ret;
+ buf += krb5->conf_len;
+ memcpy(buf, plain->data, plain->len);
+ return 0;
+}
+
+/*
+ * Load checksum test data into a buffer.
+ */
+static int krb5_load_mic_buf(const struct krb5_mic_test *test,
+ const struct krb5_buffer *plain,
+ void *buf)
+{
+ const struct krb5_enctype *krb5 = test->krb5;
+
+ memcpy(buf + krb5->cksum_len, plain->data, plain->len);
+ return 0;
+}
+
+/*
+ * Perform an encryption test.
+ */
+static int krb5_test_one_enc(const struct krb5_enc_test *test, void *buf)
+{
+ const struct krb5_enctype *krb5 = test->krb5;
+ struct krb5_enc_keys keys = {};
+ struct krb5_buffer plain = {}, ct = {};
+ struct scatterlist sg[1];
+ size_t offset, len;
+ int ret, error_code;
+
+ pr_notice("Running %s %s\n", krb5->name, test->name);
+
+ if ((ret = krb5_test_get_Ke(test, &keys)) < 0 ||
+ (ret = krb5_test_get_Ki(test, &keys)) < 0)
+ goto out;
+
+ LOAD_BUF(&plain, test->plain);
+ LOAD_BUF(&ct, test->ct);
+
+ ret = krb5_load_enc_buf(test, &plain, buf);
+ if (ret < 0)
+ goto out;
+
+ sg_init_one(sg, buf, 1024);
+ ret = crypto_krb5_encrypt(krb5, &keys, sg, 1, 1024,
+ krb5->conf_len, plain.len, true);
+ if (ret < 0) {
+ CHECK(1);
+ pr_warn("Encryption failed %d\n", ret);
+ goto out;
+ }
+ len = ret;
+
+ if (CHECK(len != ct.len)) {
+ pr_warn("Encrypted length mismatch %zu != %u\n", len, ct.len);
+ goto out;
+ }
+
+ if (memcmp(buf, ct.data, ct.len) != 0) {
+ CHECK(1);
+ pr_warn("Ciphertext mismatch\n");
+ pr_warn("BUF %*phN\n", ct.len, buf);
+ pr_warn("CT %*phN\n", ct.len, ct.data);
+ ret = -EKEYREJECTED;
+ goto out;
+ }
+
+ offset = 0;
+ ret = crypto_krb5_decrypt(krb5, &keys, sg, 1, &offset, &len, &error_code);
+ if (ret < 0) {
+ CHECK(1);
+ pr_warn("Decryption failed %d\n", ret);
+ goto out;
+ }
+
+ if (CHECK(len != plain.len))
+ goto out;
+
+ if (memcmp(buf + offset, plain.data, plain.len) != 0) {
+ CHECK(1);
+ pr_warn("Plaintext mismatch\n");
+ pr_warn("BUF %*phN\n", plain.len, buf + offset);
+ pr_warn("PT %*phN\n", plain.len, plain.data);
+ ret = -EKEYREJECTED;
+ goto out;
+ }
+
+ ret = 0;
+
+out:
+ clear_buf(&ct);
+ clear_buf(&plain);
+ crypto_krb5_free_enc_keys(&keys);
+ return ret;
+}
+
+static int krb5_test_one_mic(const struct krb5_mic_test *test, void *buf)
+{
+ const struct krb5_enctype *krb5 = test->krb5;
+ struct crypto_shash *Kc = NULL;
+ struct scatterlist sg[1];
+ struct krb5_buffer plain = {}, mic = {};
+ size_t offset, len;
+ int ret, error_code;
+
+ pr_notice("Running %s %s\n", krb5->name, test->name);
+
+ if ((ret = krb5_test_get_Kc(test, &Kc)) < 0)
+ goto out;
+
+ LOAD_BUF(&plain, test->plain);
+ LOAD_BUF(&mic, test->mic);
+
+ ret = krb5_load_mic_buf(test, &plain, buf);
+ if (ret < 0)
+ goto out;
+
+ sg_init_one(sg, buf, 1024);
+
+ ret = crypto_krb5_get_mic(krb5, Kc, NULL, sg, 1, 1024,
+ krb5->cksum_len, plain.len);
+ if (ret < 0) {
+ CHECK(1);
+ pr_warn("Get MIC failed %d\n", ret);
+ goto out;
+ }
+ len = ret;
+
+ if (CHECK(len != plain.len + mic.len)) {
+ pr_warn("MIC length mismatch %zu != %u\n", len, plain.len + mic.len);
+ goto out;
+ }
+
+ if (memcmp(buf, mic.data, mic.len) != 0) {
+ CHECK(1);
+ pr_warn("MIC mismatch\n");
+ pr_warn("BUF %*phN\n", mic.len, buf);
+ pr_warn("MIC %*phN\n", mic.len, mic.data);
+ ret = -EKEYREJECTED;
+ goto out;
+ }
+
+ offset = 0;
+ ret = crypto_krb5_verify_mic(krb5, Kc, NULL, sg, 1,
+ &offset, &len, &error_code);
+ if (ret < 0) {
+ CHECK(1);
+ pr_warn("Verify MIC failed %d\n", ret);
+ goto out;
+ }
+
+ if (CHECK(len != plain.len))
+ goto out;
+ if (CHECK(offset != mic.len))
+ goto out;
+
+ if (memcmp(buf + offset, plain.data, plain.len) != 0) {
+ CHECK(1);
+ pr_warn("Plaintext mismatch\n");
+ pr_warn("BUF %*phN\n", plain.len, buf + offset);
+ pr_warn("PT %*phN\n", plain.len, plain.data);
+ ret = -EKEYREJECTED;
+ goto out;
+ }
+
+ ret = 0;
+
+out:
+ clear_buf(&mic);
+ clear_buf(&plain);
+ if (Kc)
+ crypto_free_shash(Kc);
+ return ret;
+}
+
+void krb5_selftest(void)
+{
+ void *buf;
+ bool fail = false;
+ int i;
+
+ buf = kmalloc(1024, GFP_KERNEL);
+ if (!buf)
+ return;
+
+ printk("\n");
+ pr_notice("Running selftests\n");
+
+ for (i = 0; krb5_prf_tests[i].krb5; i++) {
+ fail |= krb5_test_one_prf(&krb5_prf_tests[i]) < 0;
+ if (fail)
+ goto out;
+ }
+
+ for (i = 0; krb5_key_tests[i].krb5; i++) {
+ fail |= krb5_test_one_key(&krb5_key_tests[i]) < 0;
+ if (fail)
+ goto out;
+ }
+
+ for (i = 0; krb5_enc_tests[i].krb5; i++) {
+ memset(buf, 0x5a, 1024);
+ fail |= krb5_test_one_enc(&krb5_enc_tests[i], buf) < 0;
+ if (fail)
+ goto out;
+ }
+
+ for (i = 0; krb5_mic_tests[i].krb5; i++) {
+ memset(buf, 0x5a, 1024);
+ fail |= krb5_test_one_mic(&krb5_mic_tests[i], buf) < 0;
+ if (fail)
+ goto out;
+ }
+
+out:
+ pr_notice("Selftests %s\n", fail ? "failed" : "succeeded");
+ kfree(buf);
+}
diff --git a/crypto/krb5/selftest_data.c b/crypto/krb5/selftest_data.c
new file mode 100644
index 000000000000..9085723b730b
--- /dev/null
+++ b/crypto/krb5/selftest_data.c
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Data for RxGK self-testing
+ *
+ * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "internal.h"
+
+/*
+ * Pseudo-random function tests.
+ */
+const struct krb5_prf_test krb5_prf_tests[] = {
+ {/* END */}
+};
+
+/*
+ * Key derivation tests.
+ */
+const struct krb5_key_test krb5_key_tests[] = {
+ {/* END */}
+};
+
+/*
+ * Encryption tests.
+ */
+const struct krb5_enc_test krb5_enc_tests[] = {
+ {/* END */}
+};
+
+/*
+ * Checksum generation tests.
+ */
+const struct krb5_mic_test krb5_mic_tests[] = {
+ {/* END */}
+};


2020-11-12 13:00:46

by David Howells

[permalink] [raw]
Subject: [PATCH 10/18] crypto/krb5: Implement the AES encrypt/decrypt from rfc8009

Implement encryption and decryption functions for AES + HMAC-SHA2 as
described in rfc8009 sec 5.

Signed-off-by: David Howells <[email protected]>
---

crypto/krb5/rfc8009_aes2.c | 205 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 203 insertions(+), 2 deletions(-)

diff --git a/crypto/krb5/rfc8009_aes2.c b/crypto/krb5/rfc8009_aes2.c
index 9f0f0f410d91..df517435be73 100644
--- a/crypto/krb5/rfc8009_aes2.c
+++ b/crypto/krb5/rfc8009_aes2.c
@@ -10,6 +10,7 @@
#include <crypto/skcipher.h>
#include <crypto/hash.h>
#include <linux/slab.h>
+#include <linux/random.h>
#include "internal.h"

static const struct krb5_buffer rfc8009_no_context = { .len = 0, .data = "" };
@@ -183,13 +184,213 @@ static int rfc8009_random_to_key(const struct krb5_enctype *krb5,
return 0;
}

+/*
+ * Apply encryption and checksumming functions to part of an skbuff.
+ */
+static ssize_t rfc8009_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 */
+
+ base_len = krb5->conf_len + data_len;
+ secure_len = base_len;
+ pad_len = secure_len - base_len;
+ secure_offset = 0;
+ cksum_offset = secure_offset + secure_len;
+
+ 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_to_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;
+ }
+
+ /* 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;
+
+ /* 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;
+
+ memset(iv, 0, crypto_sync_skcipher_ivsize(keys->Ke));
+ ret = crypto_shash_update(desc, iv, crypto_sync_skcipher_ivsize(keys->Ke));
+ 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;
+ sg_zero_buffer(sg, nr_sg, 3, cksum_offset);
+ done = sg_pcopy_from_buffer(sg, nr_sg, cksum, krb5->cksum_len, cksum_offset);
+ if (done != krb5->cksum_len)
+ 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.
+ */
+static int rfc8009_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;
+
+ cksum = buffer +
+ krb5_shash_size(keys->Ki);
+ cksum2 = buffer +
+ krb5_shash_size(keys->Ki) +
+ krb5_digest_size(keys->Ki);
+ 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);
+
+ /* Calculate the checksum using key Ki */
+ desc = buffer;
+ desc->tfm = keys->Ki;
+ ret = crypto_shash_init(desc);
+ if (ret < 0)
+ goto error;
+
+ ret = crypto_shash_update(desc, iv, crypto_sync_skcipher_ivsize(keys->Ke));
+ 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;
+ }
+
+ /* Decrypt the secure region with key 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);
+
+ *_offset += krb5->conf_len;
+ *_len -= krb5->conf_len + krb5->cksum_len;
+ ret = 0;
+
+error:
+ kfree_sensitive(buffer);
+ return ret;
+}
+
static const struct krb5_crypto_profile rfc8009_crypto_profile = {
.calc_PRF = rfc8009_calc_PRF,
.calc_Kc = rfc8009_calc_Ki,
.calc_Ke = rfc8009_calc_Ke,
.calc_Ki = rfc8009_calc_Ki,
- .encrypt = NULL, //rfc8009_encrypt,
- .decrypt = NULL, //rfc8009_decrypt,
+ .encrypt = rfc8009_encrypt,
+ .decrypt = rfc8009_decrypt,
.get_mic = rfc3961_get_mic,
.verify_mic = rfc3961_verify_mic,
};


2020-11-12 13:00:46

by David Howells

[permalink] [raw]
Subject: [PATCH 09/18] crypto/krb5: Implement the AES enctypes from rfc8009

Implement the aes128-cts-hmac-sha256-128 and aes256-cts-hmac-sha384-192
enctypes from rfc8009, overriding the rfc3961 kerberos 5 simplified crypto
scheme.

Signed-off-by: David Howells <[email protected]>
---

crypto/krb5/Kconfig | 2
crypto/krb5/Makefile | 3 -
crypto/krb5/internal.h | 6 +
crypto/krb5/main.c | 2
crypto/krb5/rfc8009_aes2.c | 239 ++++++++++++++++++++++++++++++++++++++++++++
include/crypto/krb5.h | 4 +
6 files changed, 255 insertions(+), 1 deletion(-)
create mode 100644 crypto/krb5/rfc8009_aes2.c

diff --git a/crypto/krb5/Kconfig b/crypto/krb5/Kconfig
index e2eba1d689ab..5607c0c81049 100644
--- a/crypto/krb5/Kconfig
+++ b/crypto/krb5/Kconfig
@@ -4,6 +4,8 @@ config CRYPTO_KRB5
select CRYPTO_SKCIPHER
select CRYPTO_HASH_INFO
select CRYPTO_SHA1
+ select CRYPTO_SHA256
+ select CRYPTO_SHA512
select CRYPTO_CBC
select CRYPTO_CTS
select CRYPTO_AES
diff --git a/crypto/krb5/Makefile b/crypto/krb5/Makefile
index b7da03cae6d1..85763131f7b6 100644
--- a/crypto/krb5/Makefile
+++ b/crypto/krb5/Makefile
@@ -7,7 +7,8 @@ krb5-y += \
kdf.o \
main.o \
rfc3961_simplified.o \
- rfc3962_aes.o
+ rfc3962_aes.o \
+ rfc8009_aes2.o

krb5-$(CONFIG_CRYPTO_KRB5_SELFTESTS) += \
selftest.o \
diff --git a/crypto/krb5/internal.h b/crypto/krb5/internal.h
index 47424b433778..e64f5e58199f 100644
--- a/crypto/krb5/internal.h
+++ b/crypto/krb5/internal.h
@@ -158,6 +158,12 @@ int rfc3961_verify_mic(const struct krb5_enctype *krb5,
extern const struct krb5_enctype krb5_aes128_cts_hmac_sha1_96;
extern const struct krb5_enctype krb5_aes256_cts_hmac_sha1_96;

+/*
+ * rfc8009_aes2.c
+ */
+extern const struct krb5_enctype krb5_aes128_cts_hmac_sha256_128;
+extern const struct krb5_enctype krb5_aes256_cts_hmac_sha384_192;
+
/*
* selftest.c
*/
diff --git a/crypto/krb5/main.c b/crypto/krb5/main.c
index b79127027551..9914d3417c21 100644
--- a/crypto/krb5/main.c
+++ b/crypto/krb5/main.c
@@ -20,6 +20,8 @@ MODULE_LICENSE("GPL");
static const struct krb5_enctype *const krb5_supported_enctypes[] = {
&krb5_aes128_cts_hmac_sha1_96,
&krb5_aes256_cts_hmac_sha1_96,
+ &krb5_aes128_cts_hmac_sha256_128,
+ &krb5_aes256_cts_hmac_sha384_192,
};

/**
diff --git a/crypto/krb5/rfc8009_aes2.c b/crypto/krb5/rfc8009_aes2.c
new file mode 100644
index 000000000000..9f0f0f410d91
--- /dev/null
+++ b/crypto/krb5/rfc8009_aes2.c
@@ -0,0 +1,239 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* rfc8009 AES Encryption with HMAC-SHA2 for Kerberos 5
+ *
+ * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <crypto/skcipher.h>
+#include <crypto/hash.h>
+#include <linux/slab.h>
+#include "internal.h"
+
+static const struct krb5_buffer rfc8009_no_context = { .len = 0, .data = "" };
+
+/*
+ * Calculate the key derivation function KDF-HMAC-SHA2(key, label, [context,] k)
+ *
+ * KDF-HMAC-SHA2(key, label, [context,] k) = k-truncate(K1)
+ *
+ * Using the appropriate one of:
+ * K1 = HMAC-SHA-256(key, 0x00000001 | label | 0x00 | k)
+ * K1 = HMAC-SHA-384(key, 0x00000001 | label | 0x00 | k)
+ * K1 = HMAC-SHA-256(key, 0x00000001 | label | 0x00 | context | k)
+ * K1 = HMAC-SHA-384(key, 0x00000001 | label | 0x00 | context | k)
+ * [rfc8009 sec 3]
+ */
+static int rfc8009_calc_KDF_HMAC_SHA2(const struct krb5_enctype *krb5,
+ const struct krb5_buffer *key,
+ const struct krb5_buffer *label,
+ const struct krb5_buffer *context,
+ unsigned int k,
+ struct krb5_buffer *result,
+ gfp_t gfp)
+{
+ struct crypto_shash *shash;
+ struct krb5_buffer K1, data;
+ struct shash_desc *desc;
+ __be32 tmp;
+ size_t bsize;
+ void *buffer;
+ u8 *p;
+ int ret = -ENOMEM;
+
+ if (WARN_ON(result->len != k / 8))
+ return -EINVAL;
+
+ shash = crypto_alloc_shash(krb5->cksum_name, 0, 0);
+ if (IS_ERR(shash))
+ return (PTR_ERR(shash) == -ENOENT) ? -ENOPKG : PTR_ERR(shash);
+ ret = crypto_shash_setkey(shash, key->data, key->len);
+ if (ret < 0)
+ goto error_shash;
+
+ ret = -EINVAL;
+ if (WARN_ON(crypto_shash_digestsize(shash) * 8 < k))
+ goto error_shash;
+
+ ret = -ENOMEM;
+ data.len = 4 + label->len + 1 + context->len + 4;
+ bsize = krb5_shash_size(shash) +
+ krb5_digest_size(shash) +
+ crypto_roundup(data.len);
+ buffer = kzalloc(bsize, GFP_NOFS);
+ if (!buffer)
+ goto error_shash;
+
+ desc = buffer;
+ desc->tfm = shash;
+ ret = crypto_shash_init(desc);
+ if (ret < 0)
+ goto error;
+
+ p = data.data = buffer +
+ krb5_shash_size(shash) +
+ krb5_digest_size(shash);
+ *(__be32 *)p = htonl(0x00000001);
+ p += 4;
+ memcpy(p, label->data, label->len);
+ p += label->len;
+ *p++ = 0;
+ memcpy(p, context->data, context->len);
+ p += context->len;
+ tmp = htonl(k);
+ memcpy(p, &tmp, 4);
+ p += 4;
+
+ ret = -EINVAL;
+ if (WARN_ON(p - (u8 *)data.data != data.len))
+ goto error;
+
+ K1.len = crypto_shash_digestsize(shash);
+ K1.data = buffer +
+ krb5_shash_size(shash);
+
+ ret = crypto_shash_finup(desc, data.data, data.len, K1.data);
+ if (ret < 0)
+ goto error;
+
+ memcpy(result->data, K1.data, result->len);
+
+error:
+ kfree_sensitive(buffer);
+error_shash:
+ crypto_free_shash(shash);
+ return ret;
+}
+
+/*
+ * Calculate the pseudo-random function, PRF().
+ *
+ * PRF = KDF-HMAC-SHA2(input-key, "prf", octet-string, 256)
+ * PRF = KDF-HMAC-SHA2(input-key, "prf", octet-string, 384)
+ *
+ * The "prfconstant" used in the PRF operation is the three-octet string
+ * "prf".
+ * [rfc8009 sec 5]
+ */
+static int rfc8009_calc_PRF(const struct krb5_enctype *krb5,
+ const struct krb5_buffer *input_key,
+ const struct krb5_buffer *octet_string,
+ struct krb5_buffer *result,
+ gfp_t gfp)
+{
+ static const struct krb5_buffer prfconstant = { 3, "prf" };
+
+ return rfc8009_calc_KDF_HMAC_SHA2(krb5, input_key, &prfconstant,
+ octet_string, krb5->prf_len * 8,
+ result, gfp);
+}
+
+/*
+ * Derive Ke.
+ * Ke = KDF-HMAC-SHA2(base-key, usage | 0xAA, 128)
+ * Ke = KDF-HMAC-SHA2(base-key, usage | 0xAA, 256)
+ * [rfc8009 sec 5]
+ */
+static int rfc8009_calc_Ke(const struct krb5_enctype *krb5,
+ const struct krb5_buffer *base_key,
+ const struct krb5_buffer *usage_constant,
+ struct krb5_buffer *result,
+ gfp_t gfp)
+{
+ return rfc8009_calc_KDF_HMAC_SHA2(krb5, base_key, usage_constant,
+ &rfc8009_no_context, krb5->key_bytes * 8,
+ result, gfp);
+}
+
+/*
+ * Derive Kc/Ki
+ * Kc = KDF-HMAC-SHA2(base-key, usage | 0x99, 128)
+ * Ki = KDF-HMAC-SHA2(base-key, usage | 0x55, 128)
+ * Kc = KDF-HMAC-SHA2(base-key, usage | 0x99, 192)
+ * Ki = KDF-HMAC-SHA2(base-key, usage | 0x55, 192)
+ * [rfc8009 sec 5]
+ */
+static int rfc8009_calc_Ki(const struct krb5_enctype *krb5,
+ const struct krb5_buffer *base_key,
+ const struct krb5_buffer *usage_constant,
+ struct krb5_buffer *result,
+ gfp_t gfp)
+{
+ return rfc8009_calc_KDF_HMAC_SHA2(krb5, base_key, usage_constant,
+ &rfc8009_no_context, krb5->cksum_len * 8,
+ result, gfp);
+}
+
+/*
+ * AES random-to-key function. For AES, this is an identity operation.
+ */
+static int rfc8009_random_to_key(const struct krb5_enctype *krb5,
+ const struct krb5_buffer *randombits,
+ struct krb5_buffer *result)
+{
+ if (randombits->len != 16 && randombits->len != 32)
+ return -EINVAL;
+
+ if (result->len != randombits->len)
+ return -EINVAL;
+
+ memcpy(result->data, randombits->data, randombits->len);
+ return 0;
+}
+
+static const struct krb5_crypto_profile rfc8009_crypto_profile = {
+ .calc_PRF = rfc8009_calc_PRF,
+ .calc_Kc = rfc8009_calc_Ki,
+ .calc_Ke = rfc8009_calc_Ke,
+ .calc_Ki = rfc8009_calc_Ki,
+ .encrypt = NULL, //rfc8009_encrypt,
+ .decrypt = NULL, //rfc8009_decrypt,
+ .get_mic = rfc3961_get_mic,
+ .verify_mic = rfc3961_verify_mic,
+};
+
+const struct krb5_enctype krb5_aes128_cts_hmac_sha256_128 = {
+ .etype = KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128,
+ .ctype = KRB5_CKSUMTYPE_HMAC_SHA256_128_AES128,
+ .name = "aes128-cts-hmac-sha256-128",
+ .encrypt_name = "cts(cbc(aes))",
+ .cksum_name = "hmac(sha256)",
+ .hash_name = "sha256",
+ .key_bytes = 16,
+ .key_len = 16,
+ .Kc_len = 16,
+ .Ke_len = 16,
+ .Ki_len = 16,
+ .block_len = 16,
+ .conf_len = 16,
+ .cksum_len = 16,
+ .hash_len = 20,
+ .prf_len = 32,
+ .keyed_cksum = true,
+ .random_to_key = rfc8009_random_to_key,
+ .profile = &rfc8009_crypto_profile,
+};
+
+const struct krb5_enctype krb5_aes256_cts_hmac_sha384_192 = {
+ .etype = KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192,
+ .ctype = KRB5_CKSUMTYPE_HMAC_SHA384_192_AES256,
+ .name = "aes256-cts-hmac-sha384-192",
+ .encrypt_name = "cts(cbc(aes))",
+ .cksum_name = "hmac(sha384)",
+ .hash_name = "sha384",
+ .key_bytes = 32,
+ .key_len = 32,
+ .Kc_len = 24,
+ .Ke_len = 32,
+ .Ki_len = 24,
+ .block_len = 16,
+ .conf_len = 16,
+ .cksum_len = 24,
+ .hash_len = 20,
+ .prf_len = 48,
+ .keyed_cksum = true,
+ .random_to_key = rfc8009_random_to_key,
+ .profile = &rfc8009_crypto_profile,
+};
diff --git a/include/crypto/krb5.h b/include/crypto/krb5.h
index b83d3d487753..f38a5b4d97ee 100644
--- a/include/crypto/krb5.h
+++ b/include/crypto/krb5.h
@@ -32,6 +32,8 @@ struct scatterlist;
#define KRB5_ENCTYPE_DES3_CBC_SHA1 0x0010
#define KRB5_ENCTYPE_AES128_CTS_HMAC_SHA1_96 0x0011
#define KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96 0x0012
+#define KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128 0x0013
+#define KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192 0x0014
#define KRB5_ENCTYPE_ARCFOUR_HMAC 0x0017
#define KRB5_ENCTYPE_ARCFOUR_HMAC_EXP 0x0018
#define KRB5_ENCTYPE_UNKNOWN 0x01ff
@@ -46,6 +48,8 @@ struct scatterlist;
#define KRB5_CKSUMTYPE_HMAC_SHA1_DES3 0x000c
#define KRB5_CKSUMTYPE_HMAC_SHA1_96_AES128 0x000f
#define KRB5_CKSUMTYPE_HMAC_SHA1_96_AES256 0x0010
+#define KRB5_CKSUMTYPE_HMAC_SHA256_128_AES128 0x0013
+#define KRB5_CKSUMTYPE_HMAC_SHA384_192_AES256 0x0014
#define KRB5_CKSUMTYPE_HMAC_MD5_ARCFOUR -138 /* Microsoft md5 hmac cksumtype */

/*


2020-11-12 13:00:52

by David Howells

[permalink] [raw]
Subject: [PATCH 11/18] crypto/krb5: Add the AES self-testing data from rfc8009

Add the self-testing data from rfc8009 to test AES + HMAC-SHA2.

Signed-off-by: David Howells <[email protected]>
---

crypto/krb5/selftest_data.c | 116 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 116 insertions(+)

diff --git a/crypto/krb5/selftest_data.c b/crypto/krb5/selftest_data.c
index 9085723b730b..00c3b38c01d8 100644
--- a/crypto/krb5/selftest_data.c
+++ b/crypto/krb5/selftest_data.c
@@ -13,6 +13,20 @@
* Pseudo-random function tests.
*/
const struct krb5_prf_test krb5_prf_tests[] = {
+ /* rfc8009 Appendix A */
+ {
+ .krb5 = &krb5_aes128_cts_hmac_sha256_128,
+ .name = "prf",
+ .key = "3705D96080C17728A0E800EAB6E0D23C",
+ .octet = "74657374",
+ .prf = "9D188616F63852FE86915BB840B4A886FF3E6BB0F819B49B893393D393854295",
+ }, {
+ .krb5 = &krb5_aes256_cts_hmac_sha384_192,
+ .name = "prf",
+ .key = "6D404D37FAF79F9DF0D33568D320669800EB4836472EA8A026D16B7182460C52",
+ .octet = "74657374",
+ .prf = "9801F69A368C2BF675E59521E177D9A07F67EFE1CFDE8D3C8D6F6A0256E3B17DB3C1B62AD1B8553360D17367EB1514D2",
+ },
{/* END */}
};

@@ -20,6 +34,28 @@ const struct krb5_prf_test krb5_prf_tests[] = {
* Key derivation tests.
*/
const struct krb5_key_test krb5_key_tests[] = {
+ /* rfc8009 Appendix A */
+ {
+ .krb5 = &krb5_aes128_cts_hmac_sha256_128,
+ .name = "key",
+ .key = "3705D96080C17728A0E800EAB6E0D23C",
+ .Kc.use = 0x00000002,
+ .Kc.key = "B31A018A48F54776F403E9A396325DC3",
+ .Ke.use = 0x00000002,
+ .Ke.key = "9B197DD1E8C5609D6E67C3E37C62C72E",
+ .Ki.use = 0x00000002,
+ .Ki.key = "9FDA0E56AB2D85E1569A688696C26A6C",
+ }, {
+ .krb5 = &krb5_aes256_cts_hmac_sha384_192,
+ .name = "key",
+ .key = "6D404D37FAF79F9DF0D33568D320669800EB4836472EA8A026D16B7182460C52",
+ .Kc.use = 0x00000002,
+ .Kc.key = "EF5718BE86CC84963D8BBB5031E9F5C4BA41F28FAF69E73D",
+ .Ke.use = 0x00000002,
+ .Ke.key = "56AB22BEE63D82D7BC5227F6773F8EA7A5EB1C825160C38312980C442E5C7E49",
+ .Ki.use = 0x00000002,
+ .Ki.key = "69B16514E3CD8E56B82010D5C73012B622C4D00FFC23ED1F",
+ },
{/* END */}
};

@@ -27,6 +63,72 @@ const struct krb5_key_test krb5_key_tests[] = {
* Encryption tests.
*/
const struct krb5_enc_test krb5_enc_tests[] = {
+ /* rfc8009 Appendix A */
+ {
+ .krb5 = &krb5_aes128_cts_hmac_sha256_128,
+ .name = "enc no plain",
+ .plain = "",
+ .conf = "7E5895EAF2672435BAD817F545A37148",
+ .Ke = "9B197DD1E8C5609D6E67C3E37C62C72E",
+ .Ki = "9FDA0E56AB2D85E1569A688696C26A6C",
+ .ct = "EF85FB890BB8472F4DAB20394DCA781DAD877EDA39D50C870C0D5A0A8E48C718",
+ }, {
+ .krb5 = &krb5_aes128_cts_hmac_sha256_128,
+ .name = "enc plain<block",
+ .plain = "000102030405",
+ .conf = "7BCA285E2FD4130FB55B1A5C83BC5B24",
+ .Ke = "9B197DD1E8C5609D6E67C3E37C62C72E",
+ .Ki = "9FDA0E56AB2D85E1569A688696C26A6C",
+ .ct = "84D7F30754ED987BAB0BF3506BEB09CFB55402CEF7E6877CE99E247E52D16ED4421DFDF8976C",
+ }, {
+ .krb5 = &krb5_aes128_cts_hmac_sha256_128,
+ .name = "enc plain==block",
+ .plain = "000102030405060708090A0B0C0D0E0F",
+ .conf = "56AB21713FF62C0A1457200F6FA9948F",
+ .Ke = "9B197DD1E8C5609D6E67C3E37C62C72E",
+ .Ki = "9FDA0E56AB2D85E1569A688696C26A6C",
+ .ct = "3517D640F50DDC8AD3628722B3569D2AE07493FA8263254080EA65C1008E8FC295FB4852E7D83E1E7C48C37EEBE6B0D3",
+ }, {
+ .krb5 = &krb5_aes128_cts_hmac_sha256_128,
+ .name = "enc plain>block",
+ .plain = "000102030405060708090A0B0C0D0E0F1011121314",
+ .conf = "A7A4E29A4728CE10664FB64E49AD3FAC",
+ .Ke = "9B197DD1E8C5609D6E67C3E37C62C72E",
+ .Ki = "9FDA0E56AB2D85E1569A688696C26A6C",
+ .ct = "720F73B18D9859CD6CCB4346115CD336C70F58EDC0C4437C5573544C31C813BCE1E6D072C186B39A413C2F92CA9B8334A287FFCBFC",
+ }, {
+ .krb5 = &krb5_aes256_cts_hmac_sha384_192,
+ .name = "enc no plain",
+ .plain = "",
+ .conf = "F764E9FA15C276478B2C7D0C4E5F58E4",
+ .Ke = "56AB22BEE63D82D7BC5227F6773F8EA7A5EB1C825160C38312980C442E5C7E49",
+ .Ki = "69B16514E3CD8E56B82010D5C73012B622C4D00FFC23ED1F",
+ .ct = "41F53FA5BFE7026D91FAF9BE959195A058707273A96A40F0A01960621AC612748B9BBFBE7EB4CE3C",
+ }, {
+ .krb5 = &krb5_aes256_cts_hmac_sha384_192,
+ .name = "enc plain<block",
+ .plain = "000102030405",
+ .conf = "B80D3251C1F6471494256FFE712D0B9A",
+ .Ke = "56AB22BEE63D82D7BC5227F6773F8EA7A5EB1C825160C38312980C442E5C7E49",
+ .Ki = "69B16514E3CD8E56B82010D5C73012B622C4D00FFC23ED1F",
+ .ct = "4ED7B37C2BCAC8F74F23C1CF07E62BC7B75FB3F637B9F559C7F664F69EAB7B6092237526EA0D1F61CB20D69D10F2",
+ }, {
+ .krb5 = &krb5_aes256_cts_hmac_sha384_192,
+ .name = "enc plain==block",
+ .plain = "000102030405060708090A0B0C0D0E0F",
+ .conf = "53BF8A0D105265D4E276428624CE5E63",
+ .Ke = "56AB22BEE63D82D7BC5227F6773F8EA7A5EB1C825160C38312980C442E5C7E49",
+ .Ki = "69B16514E3CD8E56B82010D5C73012B622C4D00FFC23ED1F",
+ .ct = "BC47FFEC7998EB91E8115CF8D19DAC4BBBE2E163E87DD37F49BECA92027764F68CF51F14D798C2273F35DF574D1F932E40C4FF255B36A266",
+ }, {
+ .krb5 = &krb5_aes256_cts_hmac_sha384_192,
+ .name = "enc plain>block",
+ .plain = "000102030405060708090A0B0C0D0E0F1011121314",
+ .conf = "763E65367E864F02F55153C7E3B58AF1",
+ .Ke = "56AB22BEE63D82D7BC5227F6773F8EA7A5EB1C825160C38312980C442E5C7E49",
+ .Ki = "69B16514E3CD8E56B82010D5C73012B622C4D00FFC23ED1F",
+ .ct = "40013E2DF58E8751957D2878BCD2D6FE101CCFD556CB1EAE79DB3C3EE86429F2B2A602AC86FEF6ECB647D6295FAE077A1FEB517508D2C16B4192E01F62",
+ },
{/* END */}
};

@@ -34,5 +136,19 @@ const struct krb5_enc_test krb5_enc_tests[] = {
* Checksum generation tests.
*/
const struct krb5_mic_test krb5_mic_tests[] = {
+ /* rfc8009 Appendix A */
+ {
+ .krb5 = &krb5_aes128_cts_hmac_sha256_128,
+ .name = "mic",
+ .plain = "000102030405060708090A0B0C0D0E0F1011121314",
+ .Kc = "B31A018A48F54776F403E9A396325DC3",
+ .mic = "D78367186643D67B411CBA9139FC1DEE",
+ }, {
+ .krb5 = &krb5_aes256_cts_hmac_sha384_192,
+ .name = "mic",
+ .plain = "000102030405060708090A0B0C0D0E0F1011121314",
+ .Kc = "EF5718BE86CC84963D8BBB5031E9F5C4BA41F28FAF69E73D",
+ .mic = "45EE791567EEFCA37F4AC1E0222DE80D43C3BFA06699672A",
+ },
{/* END */}
};


2020-11-12 13:01:07

by David Howells

[permalink] [raw]
Subject: [PATCH 16/18] rxrpc: rxgk: Implement the yfs-rxgk security class (GSSAPI)

Implement the basic parts of the yfs-rxgk security class (security index 6)
to support GSSAPI-negotiated security.

Signed-off-by: David Howells <[email protected]>
---

include/trace/events/rxrpc.h | 4
net/rxrpc/Makefile | 2
net/rxrpc/ar-internal.h | 12
net/rxrpc/rxgk.c | 1063 ++++++++++++++++++++++++++++++++++++++++++
net/rxrpc/rxgk_app.c | 289 +++++++++++
net/rxrpc/rxgk_common.h | 118 +++++
net/rxrpc/security.c | 3
7 files changed, 1491 insertions(+)
create mode 100644 net/rxrpc/rxgk.c
create mode 100644 net/rxrpc/rxgk_app.c

diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h
index e70c90116eda..dd541c6d5ea3 100644
--- a/include/trace/events/rxrpc.h
+++ b/include/trace/events/rxrpc.h
@@ -210,6 +210,8 @@ enum rxrpc_tx_point {
rxrpc_tx_point_call_data_nofrag,
rxrpc_tx_point_call_final_resend,
rxrpc_tx_point_conn_abort,
+ rxrpc_tx_point_rxgk_challenge,
+ rxrpc_tx_point_rxgk_response,
rxrpc_tx_point_rxkad_challenge,
rxrpc_tx_point_rxkad_response,
rxrpc_tx_point_reject,
@@ -440,6 +442,8 @@ enum rxrpc_tx_point {
EM(rxrpc_tx_point_call_final_resend, "CallFinalResend") \
EM(rxrpc_tx_point_conn_abort, "ConnAbort") \
EM(rxrpc_tx_point_reject, "Reject") \
+ EM(rxrpc_tx_point_rxgk_challenge, "RxGKChall") \
+ EM(rxrpc_tx_point_rxgk_response, "RxGKResp") \
EM(rxrpc_tx_point_rxkad_challenge, "RxkadChall") \
EM(rxrpc_tx_point_rxkad_response, "RxkadResp") \
EM(rxrpc_tx_point_version_keepalive, "VerKeepalive") \
diff --git a/net/rxrpc/Makefile b/net/rxrpc/Makefile
index 08636858e77f..4be98775dc7f 100644
--- a/net/rxrpc/Makefile
+++ b/net/rxrpc/Makefile
@@ -37,4 +37,6 @@ rxrpc-$(CONFIG_RXKAD) += rxkad.o
rxrpc-$(CONFIG_SYSCTL) += sysctl.o

rxrpc-$(CONFIG_RXGK) += \
+ rxgk.o \
+ rxgk_app.o \
rxgk_kdf.o
diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
index 4e0766b4a714..efdb3334ad88 100644
--- a/net/rxrpc/ar-internal.h
+++ b/net/rxrpc/ar-internal.h
@@ -37,6 +37,7 @@ struct rxrpc_crypt {

struct key_preparsed_payload;
struct rxrpc_connection;
+struct rxgk_context;

/*
* Mark applied to socket buffers in skb->mark. skb->priority is used
@@ -264,6 +265,10 @@ struct rxrpc_security {

/* clear connection security */
void (*clear)(struct rxrpc_connection *);
+
+ /* Default ticket -> key decoder */
+ int (*default_decode_ticket)(struct sk_buff *, unsigned int, unsigned int,
+ u32 *, struct key **);
};

/*
@@ -457,7 +462,9 @@ struct rxrpc_connection {
u32 nonce; /* response re-use preventer */
} rxkad;
struct {
+ struct rxgk_context *keys[1];
u64 start_time; /* The start time for TK derivation */
+ u8 nonce[20]; /* Response re-use preventer */
} rxgk;
};
unsigned long flags;
@@ -1056,6 +1063,11 @@ void rxrpc_peer_add_rtt(struct rxrpc_call *, enum rxrpc_rtt_rx_trace, int,
unsigned long rxrpc_get_rto_backoff(struct rxrpc_peer *, bool);
void rxrpc_peer_init_rtt(struct rxrpc_peer *);

+/*
+ * rxgk.c
+ */
+extern const struct rxrpc_security rxgk_yfs;
+
/*
* rxkad.c
*/
diff --git a/net/rxrpc/rxgk.c b/net/rxrpc/rxgk.c
new file mode 100644
index 000000000000..703e46e8b508
--- /dev/null
+++ b/net/rxrpc/rxgk.c
@@ -0,0 +1,1063 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* GSSAPI-based RxRPC security
+ *
+ * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/net.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/key-type.h>
+#include "ar-internal.h"
+#include "rxgk_common.h"
+
+struct rxgk_header {
+ __be32 epoch;
+ __be32 cid;
+ __be32 call_number;
+ __be32 seq;
+ __be32 sec_index;
+ __be32 data_len;
+} __packed;
+
+struct rxgk_response {
+ __be64 start_time;
+ __be32 token_len;
+} __packed;
+
+/*
+ * Parse the information from a server key
+ */
+static int rxgk_preparse_server_key(struct key_preparsed_payload *prep)
+{
+ const struct krb5_enctype *krb5;
+ struct krb5_buffer *server_key = (void *)&prep->payload.data[2];
+ unsigned int service, sec_class, kvno, enctype;
+ int n = 0;
+
+ _enter("%zu", prep->datalen);
+
+ if (sscanf(prep->orig_description, "%u:%u:%u:%u%n",
+ &service, &sec_class, &kvno, &enctype, &n) != 4)
+ return -EINVAL;
+
+ if (prep->orig_description[n])
+ return -EINVAL;
+
+ krb5 = crypto_krb5_find_enctype(enctype);
+ if (!krb5)
+ return -ENOPKG;
+
+ prep->payload.data[0] = (struct krb5_enctype *)krb5;
+
+ if (prep->datalen != krb5->key_len)
+ return -EKEYREJECTED;
+
+ server_key->len = prep->datalen;
+ server_key->data = kmemdup(prep->data, prep->datalen, GFP_KERNEL);
+ if (!server_key->data)
+ return -ENOMEM;
+
+ _leave(" = 0");
+ return 0;
+}
+
+static void rxgk_free_server_key(union key_payload *payload)
+{
+ struct krb5_buffer *server_key = (void *)&payload->data[2];
+
+ kfree_sensitive(server_key->data);
+}
+
+static void rxgk_free_preparse_server_key(struct key_preparsed_payload *prep)
+{
+ rxgk_free_server_key(&prep->payload);
+}
+
+static void rxgk_destroy_server_key(struct key *key)
+{
+ rxgk_free_server_key(&key->payload);
+}
+
+static void rxgk_describe_server_key(const struct key *key, struct seq_file *m)
+{
+ const struct krb5_enctype *krb5 = key->payload.data[0];
+
+ if (krb5)
+ seq_printf(m, ": %s", krb5->name);
+}
+
+static struct rxgk_context *rxgk_get_key(struct rxrpc_connection *conn,
+ u16 *specific_key_number)
+{
+ refcount_inc(&conn->rxgk.keys[0]->usage);
+ return conn->rxgk.keys[0];
+}
+
+/*
+ * initialise connection security
+ */
+static int rxgk_init_connection_security(struct rxrpc_connection *conn,
+ struct rxrpc_key_token *token)
+{
+ struct rxgk_context *gk;
+ int ret;
+
+ _enter("{%d},{%x}", conn->debug_id, key_serial(conn->params.key));
+
+ conn->security_ix = token->security_index;
+ conn->params.security_level = token->rxgk->level;
+
+ if (rxrpc_conn_is_client(conn)) {
+ conn->rxgk.start_time = ktime_get();
+ do_div(conn->rxgk.start_time, 100);
+ }
+
+ gk = rxgk_generate_transport_key(conn, token->rxgk, 0, GFP_NOFS);
+ if (IS_ERR(gk))
+ return PTR_ERR(gk);
+ conn->rxgk.keys[0] = gk;
+
+ switch (conn->params.security_level) {
+ case RXRPC_SECURITY_PLAIN:
+ break;
+ case RXRPC_SECURITY_AUTH:
+ conn->security_size = gk->krb5->cksum_len;
+ break;
+ case RXRPC_SECURITY_ENCRYPT:
+ if (gk->krb5->pad)
+ conn->size_align = gk->krb5->block_len;
+ conn->security_size = gk->krb5->conf_len + sizeof(struct rxgk_header);
+ conn->security_trailer = gk->krb5->cksum_len;
+ break;
+ default:
+ ret = -EKEYREJECTED;
+ goto error;
+ }
+
+ ret = 0;
+error:
+ _leave(" = %d", ret);
+ return ret;
+}
+
+/*
+ * Clean up the crypto on a call.
+ */
+static void rxgk_free_call_crypto(struct rxrpc_call *call)
+{
+}
+
+/*
+ * Integrity mode (sign a packet - level 1 security)
+ */
+static int rxgk_secure_packet_integrity(const struct rxrpc_call *call,
+ struct rxgk_context *gk,
+ struct sk_buff *skb, u32 data_size)
+{
+ struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
+ struct rxgk_header *hdr;
+ struct krb5_buffer metadata;
+ int ret = -ENOMEM;
+
+ _enter("");
+
+ hdr = kzalloc(sizeof(*hdr), GFP_NOFS);
+ if (!hdr)
+ goto error_gk;
+
+ hdr->epoch = htonl(call->conn->proto.epoch);
+ hdr->cid = htonl(call->cid);
+ hdr->call_number = htonl(call->call_id);
+ hdr->seq = htonl(sp->hdr.seq);
+ hdr->sec_index = htonl(call->security_ix);
+ hdr->data_len = htonl(data_size);
+
+ metadata.len = sizeof(*hdr);
+ metadata.data = hdr;
+ ret = rxgk_get_mic_skb(gk->krb5, gk->tx_Kc, &metadata, skb,
+ 0, skb->len, gk->krb5->cksum_len, data_size);
+ if (ret >= 0)
+ gk->bytes_remaining -= ret;
+ kfree(hdr);
+error_gk:
+ rxgk_put(gk);
+ _leave(" = %d", ret);
+ return ret;
+}
+
+/*
+ * wholly encrypt a packet (level 2 security)
+ */
+static int rxgk_secure_packet_encrypted(const struct rxrpc_call *call,
+ struct rxgk_context *gk,
+ struct sk_buff *skb, u32 data_size)
+{
+ struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
+ struct rxgk_header hdr;
+ int ret;
+
+ _enter("%x,%x", skb->len, data_size);
+
+ /* Insert the header into the skb */
+ hdr.epoch = htonl(call->conn->proto.epoch);
+ hdr.cid = htonl(call->cid);
+ hdr.call_number = htonl(call->call_id);
+ hdr.seq = htonl(sp->hdr.seq);
+ hdr.sec_index = htonl(call->security_ix);
+ hdr.data_len = htonl(data_size);
+
+ ret = skb_store_bits(skb, gk->krb5->conf_len, &hdr, sizeof(hdr));
+ if (ret < 0)
+ goto error;
+
+ /* Increase the buffer size to allow for the checksum to be written in */
+ skb->len += gk->krb5->cksum_len;
+
+ ret = rxgk_encrypt_skb(gk->krb5, &gk->tx_enc, skb,
+ 0, skb->len, gk->krb5->conf_len, sizeof(hdr) + data_size,
+ false);
+ if (ret >= 0)
+ gk->bytes_remaining -= ret;
+
+error:
+ rxgk_put(gk);
+ _leave(" = %d", ret);
+ return ret;
+}
+
+/*
+ * checksum an RxRPC packet header
+ */
+static int rxgk_secure_packet(struct rxrpc_call *call,
+ struct sk_buff *skb,
+ size_t data_size)
+{
+ struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
+ struct rxgk_context *gk;
+ int ret;
+
+ sp = rxrpc_skb(skb);
+
+ _enter("{%d{%x}},{#%u},%zu,",
+ call->debug_id, key_serial(call->conn->params.key),
+ sp->hdr.seq, data_size);
+
+ gk = rxgk_get_key(call->conn, NULL);
+ if (IS_ERR(gk))
+ return PTR_ERR(gk) == -ESTALE ? -EKEYREJECTED : PTR_ERR(gk);
+
+ ret = key_validate(call->conn->params.key);
+ if (ret < 0)
+ return ret;
+
+ sp->hdr.cksum = gk->key_number;
+
+ switch (call->conn->params.security_level) {
+ case RXRPC_SECURITY_PLAIN:
+ rxgk_put(gk);
+ return 0;
+ case RXRPC_SECURITY_AUTH:
+ return rxgk_secure_packet_integrity(call, gk, skb, data_size);
+ case RXRPC_SECURITY_ENCRYPT:
+ return rxgk_secure_packet_encrypted(call, gk, skb, data_size);
+ default:
+ rxgk_put(gk);
+ return -EPERM;
+ }
+}
+
+/*
+ * Integrity mode (check the signature on a packet - level 1 security)
+ */
+static int rxgk_verify_packet_integrity(struct rxrpc_call *call,
+ struct rxgk_context *gk,
+ struct sk_buff *skb,
+ unsigned int offset, unsigned int len,
+ rxrpc_seq_t seq)
+{
+ struct rxgk_header *hdr;
+ struct krb5_buffer metadata;
+ bool aborted;
+ u32 ac;
+ int ret = -ENOMEM;
+
+ _enter("");
+
+ hdr = kzalloc(sizeof(*hdr), GFP_NOFS);
+ if (!hdr)
+ goto error;
+
+ hdr->epoch = htonl(call->conn->proto.epoch);
+ hdr->cid = htonl(call->cid);
+ hdr->call_number = htonl(call->call_id);
+ hdr->seq = htonl(seq);
+ hdr->sec_index = htonl(call->security_ix);
+ hdr->data_len = htonl(len - gk->krb5->cksum_len);
+
+ metadata.len = sizeof(*hdr);
+ metadata.data = hdr;
+ ret = rxgk_verify_mic_skb(gk->krb5, gk->rx_Kc, &metadata,
+ skb, &offset, &len, &ac);
+ kfree(hdr);
+ if (ret < 0) {
+ if (ret == -EPROTO) {
+ aborted = rxrpc_abort_eproto(call, skb, "rxgk_2_vfy",
+ "V1V", ac);
+ goto protocol_error;
+ }
+ goto error;
+ }
+
+error:
+ rxgk_put(gk);
+ _leave(" = %d", ret);
+ return ret;
+
+protocol_error:
+ if (aborted)
+ rxrpc_send_abort_packet(call);
+ ret = -EPROTO;
+ goto error;
+}
+
+/*
+ * Decrypt an encrypted packet (level 2 security).
+ */
+static int rxgk_verify_packet_encrypted(struct rxrpc_call *call,
+ struct rxgk_context *gk,
+ struct sk_buff *skb,
+ unsigned int offset, unsigned int len,
+ rxrpc_seq_t seq)
+{
+ struct rxgk_header hdr;
+ bool aborted;
+ int ret;
+ u32 ac;
+
+ _enter("");
+
+ ret = rxgk_decrypt_skb(gk->krb5, &gk->rx_enc, skb, &offset, &len, &ac);
+ if (ret < 0) {
+ if (ret == -EPROTO) {
+ aborted = rxrpc_abort_eproto(call, skb, "rxgk_2_dec",
+ "V2D", ac);
+ goto protocol_error;
+ }
+ goto error;
+ }
+
+ if (len < sizeof(hdr)) {
+ aborted = rxrpc_abort_eproto(call, skb, "rxgk_2_hdr",
+ "V2L", RXGK_PACKETSHORT);
+ goto protocol_error;
+ }
+
+ /* Extract the header from the skb */
+ ret = skb_copy_bits(skb, offset, &hdr, sizeof(hdr));
+ if (ret < 0)
+ goto error;
+ len -= sizeof(hdr);
+
+ if (ntohl(hdr.epoch) != call->conn->proto.epoch ||
+ ntohl(hdr.cid) != call->cid ||
+ ntohl(hdr.call_number) != call->call_id ||
+ ntohl(hdr.seq) != seq ||
+ ntohl(hdr.sec_index) != call->security_ix ||
+ ntohl(hdr.data_len) > len) {
+ aborted = rxrpc_abort_eproto(call, skb, "rxgk_2_hdr", "V2H",
+ RXGK_SEALED_INCON);
+ goto protocol_error;
+ }
+
+ ret = 0;
+
+error:
+ rxgk_put(gk);
+ _leave(" = %d", ret);
+ return ret;
+
+protocol_error:
+ if (aborted)
+ rxrpc_send_abort_packet(call);
+ ret = -EPROTO;
+ goto error;
+}
+
+/*
+ * Verify the security on a received packet or subpacket (if part of a
+ * jumbo packet).
+ */
+static int rxgk_verify_packet(struct rxrpc_call *call, struct sk_buff *skb,
+ unsigned int offset, unsigned int len,
+ rxrpc_seq_t seq, u16 key_number)
+{
+ struct rxgk_context *gk;
+ bool aborted;
+
+ _enter("{%d{%x}},{#%u}",
+ call->debug_id, key_serial(call->conn->params.key), seq);
+
+ gk = rxgk_get_key(call->conn, &key_number);
+ if (IS_ERR(gk)) {
+ switch (PTR_ERR(gk)) {
+ case -ESTALE:
+ aborted = rxrpc_abort_eproto(call, skb, "rxgk_csum", "VKY",
+ RXGK_BADKEYNO);
+ gk = NULL;
+ goto protocol_error;
+ default:
+ return PTR_ERR(gk);
+ }
+ }
+
+ switch (call->conn->params.security_level) {
+ case RXRPC_SECURITY_PLAIN:
+ return 0;
+ case RXRPC_SECURITY_AUTH:
+ return rxgk_verify_packet_integrity(call, gk, skb, offset, len, seq);
+ case RXRPC_SECURITY_ENCRYPT:
+ return rxgk_verify_packet_encrypted(call, gk, skb, offset, len, seq);
+ default:
+ rxgk_put(gk);
+ return -ENOANO;
+ }
+
+protocol_error:
+ if (aborted)
+ rxrpc_send_abort_packet(call);
+ rxgk_put(gk);
+ return -EPROTO;
+}
+
+/*
+ * Locate the data contained in a packet that was partially encrypted.
+ */
+static void rxgk_locate_data_1(struct rxrpc_call *call, struct sk_buff *skb,
+ unsigned int *_offset, unsigned int *_len)
+{
+ *_offset += call->conn->security_size;
+ *_len -= call->conn->security_size;
+}
+
+/*
+ * Locate the data contained in a packet that was completely encrypted.
+ */
+static void rxgk_locate_data_2(struct rxrpc_call *call, struct sk_buff *skb,
+ unsigned int *_offset, unsigned int *_len)
+{
+ unsigned int off = call->conn->security_size - sizeof(__be32);
+ __be32 data_length_be;
+ u32 data_length;
+
+ if (skb_copy_bits(skb, *_offset + off, &data_length_be, sizeof(u32)) < 0)
+ BUG();
+ data_length = ntohl(data_length_be);
+ *_offset += call->conn->security_size;
+ *_len = data_length;
+}
+
+/*
+ * Locate the data contained in an already decrypted packet.
+ */
+static void rxgk_locate_data(struct rxrpc_call *call, struct sk_buff *skb,
+ unsigned int *_offset, unsigned int *_len)
+{
+ switch (call->conn->params.security_level) {
+ case RXRPC_SECURITY_AUTH:
+ rxgk_locate_data_1(call, skb, _offset, _len);
+ return;
+ case RXRPC_SECURITY_ENCRYPT:
+ rxgk_locate_data_2(call, skb, _offset, _len);
+ return;
+ default:
+ return;
+ }
+}
+
+/*
+ * issue a challenge
+ */
+static int rxgk_issue_challenge(struct rxrpc_connection *conn)
+{
+ struct rxrpc_wire_header whdr;
+ struct msghdr msg;
+ struct kvec iov[2];
+ size_t len;
+ u32 serial;
+ int ret;
+
+ _enter("{%d}", conn->debug_id);
+
+ get_random_bytes(&conn->rxgk.nonce, sizeof(conn->rxgk.nonce));
+
+ msg.msg_name = &conn->params.peer->srx.transport;
+ msg.msg_namelen = conn->params.peer->srx.transport_len;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ msg.msg_flags = 0;
+
+ whdr.epoch = htonl(conn->proto.epoch);
+ whdr.cid = htonl(conn->proto.cid);
+ whdr.callNumber = 0;
+ whdr.seq = 0;
+ whdr.type = RXRPC_PACKET_TYPE_CHALLENGE;
+ whdr.flags = conn->out_clientflag;
+ whdr.userStatus = 0;
+ whdr.securityIndex = conn->security_ix;
+ whdr._rsvd = 0;
+ whdr.serviceId = htons(conn->service_id);
+
+ iov[0].iov_base = &whdr;
+ iov[0].iov_len = sizeof(whdr);
+ iov[1].iov_base = conn->rxgk.nonce;
+ iov[1].iov_len = sizeof(conn->rxgk.nonce);
+
+ len = iov[0].iov_len + iov[1].iov_len;
+
+ serial = atomic_inc_return(&conn->serial);
+ whdr.serial = htonl(serial);
+ _proto("Tx CHALLENGE %%%u", serial);
+
+ ret = kernel_sendmsg(conn->params.local->socket, &msg, iov, 2, len);
+ if (ret < 0) {
+ trace_rxrpc_tx_fail(conn->debug_id, serial, ret,
+ rxrpc_tx_point_rxgk_challenge);
+ return -EAGAIN;
+ }
+
+ conn->params.peer->last_tx_at = ktime_get_seconds();
+ trace_rxrpc_tx_packet(conn->debug_id, &whdr,
+ rxrpc_tx_point_rxgk_challenge);
+ _leave(" = 0");
+ return 0;
+}
+
+/*
+ * Send a response packet.
+ */
+static int rxgk_send_response(struct rxrpc_connection *conn,
+ struct sk_buff *skb)
+{
+ struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
+ struct rxrpc_wire_header whdr;
+ struct msghdr msg;
+ struct kvec iov[2];
+ size_t len;
+ u32 serial;
+ int ret, i;
+
+ _enter("");
+
+ msg.msg_name = &conn->params.peer->srx.transport;
+ msg.msg_namelen = conn->params.peer->srx.transport_len;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ msg.msg_flags = 0;
+
+ memset(&whdr, 0, sizeof(whdr));
+ whdr.epoch = htonl(sp->hdr.epoch);
+ whdr.cid = htonl(sp->hdr.cid);
+ whdr.type = RXRPC_PACKET_TYPE_RESPONSE;
+ whdr.flags = sp->hdr.flags;
+ whdr.securityIndex = sp->hdr.securityIndex;
+ whdr.cksum = htons(sp->hdr.cksum);
+ whdr.serviceId = htons(sp->hdr.serviceId);
+
+ iov[0].iov_base = &whdr;
+ iov[0].iov_len = sizeof(whdr);
+ iov[1].iov_base = skb->head;
+ iov[1].iov_len = skb->len;
+
+ len = 0;
+ for (i = 0; i < ARRAY_SIZE(iov); i++)
+ len += iov[i].iov_len;
+
+ serial = atomic_inc_return(&conn->serial);
+ whdr.serial = htonl(serial);
+ _proto("Tx RESPONSE %%%u", serial);
+
+ ret = kernel_sendmsg(conn->params.local->socket, &msg,
+ iov, ARRAY_SIZE(iov), len);
+ if (ret < 0) {
+ trace_rxrpc_tx_fail(conn->debug_id, serial, ret,
+ rxrpc_tx_point_rxgk_response);
+ return -EAGAIN;
+ }
+
+ conn->params.peer->last_tx_at = ktime_get_seconds();
+ _leave(" = 0");
+ return 0;
+}
+
+/*
+ * Construct the authenticator to go in the response packet
+ *
+ * struct RXGK_Authenticator {
+ * opaque nonce[20];
+ * opaque appdata<>;
+ * RXGK_Level level;
+ * unsigned int epoch;
+ * unsigned int cid;
+ * unsigned int call_numbers<>;
+ * };
+ */
+static void rxgk_construct_authenticator(struct rxrpc_connection *conn,
+ const u8 *nonce,
+ struct sk_buff *skb)
+{
+ __be32 xdr[9];
+
+ __skb_put_data(skb, nonce, 20);
+
+ xdr[0] = htonl(0); /* appdata len */
+ xdr[1] = htonl(conn->params.security_level);
+ xdr[2] = htonl(conn->proto.epoch);
+ xdr[3] = htonl(conn->proto.cid);
+ xdr[4] = htonl(4); /* # call_numbers */
+ xdr[5] = htonl(conn->channels[0].call_counter);
+ xdr[6] = htonl(conn->channels[1].call_counter);
+ xdr[7] = htonl(conn->channels[2].call_counter);
+ xdr[8] = htonl(conn->channels[3].call_counter);
+
+ __skb_put_data(skb, xdr, sizeof(xdr));
+}
+
+/*
+ * Construct the response.
+ *
+ * struct RXGK_Response {
+ * rxgkTime start_time;
+ * RXGK_Data token;
+ * opaque authenticator<RXGK_MAXAUTHENTICATOR>
+ * };
+ */
+static int rxgk_construct_response(struct rxrpc_connection *conn,
+ struct sk_buff *challenge,
+ const u8 *nonce)
+{
+ struct rxrpc_skb_priv *csp = rxrpc_skb(challenge), *rsp;
+ struct rxgk_context *gk;
+ struct sk_buff *skb;
+ unsigned short resp_len, auth_len, pad_len, enc_len, auth_pad_len, authx_len;
+ unsigned short auth_offset, authx_offset;
+ __be64 start_time;
+ __be32 tmp;
+ void *p;
+ int ret;
+
+ gk = rxgk_get_key(conn, NULL);
+ if (IS_ERR(gk))
+ return PTR_ERR(gk);
+
+ auth_len = 20 + 4 /* appdatalen */ + 12 + (1 + 4) * 4;
+ if (gk->krb5->pad) {
+ enc_len = round_up(gk->krb5->conf_len + auth_len, gk->krb5->block_len);
+ pad_len = enc_len - (gk->krb5->conf_len + auth_len);
+ } else {
+ enc_len = gk->krb5->conf_len + auth_len;
+ pad_len = 0;
+ }
+ authx_len = enc_len + gk->krb5->cksum_len;
+ auth_pad_len = xdr_round_up(authx_len) - authx_len;
+
+ resp_len = 8;
+ resp_len += 4 + xdr_round_up(gk->key->ticket.len);
+ resp_len += 4 + xdr_round_up(authx_len);
+
+ ret = -ENOMEM;
+ skb = alloc_skb(resp_len, GFP_NOFS);
+ if (!skb)
+ goto error_gk;
+
+ rsp = rxrpc_skb(skb);
+ rsp->hdr = csp->hdr;
+ rsp->hdr.flags = conn->out_clientflag;
+ rsp->hdr.cksum = gk->key_number;
+
+ start_time = cpu_to_be64(conn->rxgk.start_time);
+ p = __skb_put_data(skb, &start_time, 8);
+
+ tmp = htonl(gk->key->ticket.len);
+ __skb_put_data(skb, &tmp, 4);
+ __skb_put_data(skb, gk->key->ticket.data, xdr_round_up(gk->key->ticket.len));
+ tmp = htonl(authx_len);
+ __skb_put_data(skb, &tmp, 4);
+ authx_offset = skb->len;
+ __skb_put_zero(skb, gk->krb5->conf_len);
+ auth_offset = skb->len;
+ rxgk_construct_authenticator(conn, nonce, skb);
+ __skb_put_zero(skb, pad_len + gk->krb5->cksum_len + auth_pad_len);
+
+ ret = rxgk_encrypt_skb(gk->krb5, &gk->resp_enc, skb,
+ authx_offset, authx_len,
+ auth_offset, auth_len, false);
+ if (ret < 0)
+ goto error;
+
+ ret = rxgk_send_response(conn, skb);
+error:
+ kfree_skb(skb);
+error_gk:
+ rxgk_put(gk);
+ _leave(" = %d", ret);
+ return ret;
+}
+
+/*
+ * Respond to a challenge packet
+ */
+static int rxgk_respond_to_challenge(struct rxrpc_connection *conn,
+ struct sk_buff *skb,
+ u32 *_abort_code)
+{
+ struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
+ const char *eproto;
+ u32 abort_code;
+ u8 nonce[20];
+ int ret;
+
+ _enter("{%d,%x}", conn->debug_id, key_serial(conn->params.key));
+
+ eproto = tracepoint_string("chall_no_key");
+ abort_code = RX_PROTOCOL_ERROR;
+ if (!conn->params.key)
+ goto protocol_error;
+
+ abort_code = RXGK_EXPIRED;
+ ret = key_validate(conn->params.key);
+ if (ret < 0)
+ goto other_error;
+
+ eproto = tracepoint_string("chall_short");
+ abort_code = RXGK_PACKETSHORT;
+ if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header),
+ nonce, sizeof(nonce)) < 0)
+ goto protocol_error;
+
+ _proto("Rx CHALLENGE %%%u { n=%20phN }", sp->hdr.serial, nonce);
+
+ ret = rxgk_construct_response(conn, skb, nonce);
+ if (ret < 0)
+ goto error;
+ return ret;
+
+protocol_error:
+ trace_rxrpc_rx_eproto(NULL, sp->hdr.serial, eproto);
+ ret = -EPROTO;
+other_error:
+ *_abort_code = abort_code;
+error:
+ return ret;
+}
+
+/*
+ * Verify the authenticator.
+ *
+ * struct RXGK_Authenticator {
+ * opaque nonce[20];
+ * opaque appdata<>;
+ * RXGK_Level level;
+ * unsigned int epoch;
+ * unsigned int cid;
+ * unsigned int call_numbers<>;
+ * };
+ */
+static int rxgk_verify_authenticator(struct rxrpc_connection *conn,
+ const struct krb5_enctype *krb5,
+ struct sk_buff *skb,
+ unsigned int auth_offset, unsigned int auth_len,
+ u32 *_abort_code, const char **_eproto)
+{
+ void *auth;
+ __be32 *p, *end;
+ u32 app_len, call_count, level, epoch, cid, i;
+ int ret;
+
+ _enter("");
+
+ auth = kmalloc(auth_len, GFP_NOFS);
+ if (!auth)
+ return -ENOMEM;
+
+ ret = skb_copy_bits(skb, auth_offset, auth, auth_len);
+ if (ret < 0)
+ goto error;
+
+ *_eproto = tracepoint_string("rxgk_rsp_nonce");
+ p = auth;
+ end = auth + auth_len;
+ if (memcmp(auth, conn->rxgk.nonce, 20) != 0)
+ goto bad_auth;
+ p += 20 / sizeof(__be32);
+
+ *_eproto = tracepoint_string("rxgk_rsp_applen");
+ app_len = ntohl(*p++);
+ if (app_len > (end - p) * sizeof(__be32))
+ goto bad_auth;
+ p += xdr_round_up(app_len) / sizeof(__be32);
+ if (end - p < 4)
+ goto bad_auth;
+ level = ntohl(*p++);
+ epoch = ntohl(*p++);
+ cid = ntohl(*p++);
+ call_count = ntohl(*p++);
+
+ *_eproto = tracepoint_string("rxgk_rsp_params");
+ if (level != conn->params.security_level ||
+ epoch != conn->proto.epoch ||
+ cid != conn->proto.cid ||
+ call_count > 4)
+ goto bad_auth;
+ if (end - p < call_count)
+ goto bad_auth;
+
+ spin_lock(&conn->bundle->channel_lock);
+ for (i = 0; i < call_count; i++) {
+ struct rxrpc_call *call;
+ u32 call_id = ntohl(*p++);
+
+ *_eproto = tracepoint_string("rxgk_rsp_callid");
+ if (call_id > INT_MAX)
+ goto bad_auth_unlock;
+
+ *_eproto = tracepoint_string("rxgk_rsp_callctr");
+ if (call_id < conn->channels[i].call_counter)
+ goto bad_auth_unlock;
+
+ *_eproto = tracepoint_string("rxgk_rsp_callst");
+ if (call_id > conn->channels[i].call_counter) {
+ call = rcu_dereference_protected(
+ conn->channels[i].call,
+ lockdep_is_held(&conn->bundle->channel_lock));
+ if (call && call->state < RXRPC_CALL_COMPLETE)
+ goto bad_auth_unlock;
+ conn->channels[i].call_counter = call_id;
+ }
+ }
+ spin_unlock(&conn->bundle->channel_lock);
+ ret = 0;
+error:
+ kfree(auth);
+ _leave(" = %d", ret);
+ return ret;
+
+bad_auth_unlock:
+ spin_unlock(&conn->bundle->channel_lock);
+bad_auth:
+ *_abort_code = RXGK_NOTAUTH;
+ ret = -EPROTO;
+ goto error;
+}
+
+/*
+ * Verify a response.
+ *
+ * struct RXGK_Response {
+ * rxgkTime start_time;
+ * RXGK_Data token;
+ * opaque authenticator<RXGK_MAXAUTHENTICATOR>
+ * };
+ */
+static int rxgk_verify_response(struct rxrpc_connection *conn,
+ struct sk_buff *skb,
+ u32 *_abort_code)
+{
+ const struct krb5_enctype *krb5;
+ struct rxrpc_key_token *token;
+ struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
+ struct krb5_enc_keys token_enc = {};
+ struct rxgk_context *gk;
+ struct key *key = NULL;
+ const char *eproto;
+ unsigned int offset = sizeof(struct rxrpc_wire_header);
+ unsigned int len = skb->len - sizeof(struct rxrpc_wire_header);
+ unsigned int token_offset, token_len;
+ unsigned int auth_offset, auth_len;
+ __be32 xauth_len;
+ u32 abort_code;
+ int ret;
+
+ struct rxgk_response rhdr;
+
+ _enter("{%d}", conn->debug_id);
+
+ /* Parse the RXGK_Response object */
+ if (sizeof(rhdr) + sizeof(__be32) > len)
+ goto short_packet;
+
+ if (skb_copy_bits(skb, offset, &rhdr, sizeof(rhdr)) < 0)
+ goto short_packet;
+ offset += sizeof(rhdr);
+ len -= sizeof(rhdr);
+
+ token_offset = offset;
+ token_len = ntohl(rhdr.token_len);
+ if (xdr_round_up(token_len) + sizeof(__be32) > len)
+ goto short_packet;
+
+ offset += xdr_round_up(token_len);
+ len -= xdr_round_up(token_len);
+
+ if (skb_copy_bits(skb, offset, &xauth_len, sizeof(xauth_len)) < 0)
+ goto short_packet;
+ offset += sizeof(xauth_len);
+ len -= sizeof(xauth_len);
+
+ auth_offset = offset;
+ auth_len = ntohl(xauth_len);
+ if (auth_len < len)
+ goto short_packet;
+ if (auth_len & 3)
+ goto inconsistent;
+ if (auth_len < 20 + 9 * 4)
+ goto auth_too_short;
+
+ /* We need to extract and decrypt the token and instantiate a session
+ * key for it. This bit, however, is application-specific. If
+ * possible, we use a default parser, but we might end up bumping this
+ * to the app to deal with - which might mean a round trip to
+ * userspace.
+ */
+ ret = rxgk_extract_token(conn, skb, token_offset, token_len, &key,
+ &abort_code, &eproto);
+ if (ret < 0)
+ goto protocol_error;
+
+ /* We now have a key instantiated from the decrypted ticket. We can
+ * pass this to the application so that they can parse the ticket
+ * content and we can use the session key it contains to derive the
+ * keys we need.
+ *
+ * Note that we have to switch enctype at this point as the enctype of
+ * the ticket doesn't necessarily match that of the transport.
+ */
+ token = key->payload.data[0];
+ conn->params.security_level = token->rxgk->level;
+ conn->rxgk.start_time = __be64_to_cpu(rhdr.start_time);
+
+ gk = rxgk_generate_transport_key(conn, token->rxgk, sp->hdr.cksum, GFP_NOFS);
+ if (IS_ERR(gk)) {
+ ret = PTR_ERR(gk);
+ goto cant_get_token;
+ }
+
+ krb5 = gk->krb5;
+
+ /* Decrypt, parse and verify the authenticator. */
+ eproto = tracepoint_string("rxgk_rsp_dec_auth");
+ ret = rxgk_decrypt_skb(krb5, &gk->resp_enc, skb,
+ &auth_offset, &auth_len, &abort_code);
+ if (ret < 0)
+ goto protocol_error;
+
+ ret = rxgk_verify_authenticator(conn, krb5, skb, auth_offset, auth_len,
+ &abort_code, &eproto);
+ if (ret < 0)
+ goto protocol_error;
+
+ conn->params.key = key;
+ key = NULL;
+ ret = 0;
+out:
+ key_put(key);
+ crypto_krb5_free_enc_keys(&token_enc);
+ _leave(" = %d", ret);
+ return ret;
+
+inconsistent:
+ eproto = tracepoint_string("rxgk_rsp_xdr_align");
+ abort_code = RXGK_INCONSISTENCY;
+ ret = -EPROTO;
+ goto protocol_error;
+auth_too_short:
+ eproto = tracepoint_string("rxgk_rsp_short_auth");
+ abort_code = RXGK_PACKETSHORT;
+ ret = -EPROTO;
+ goto protocol_error;
+short_packet:
+ eproto = tracepoint_string("rxgk_rsp_short");
+ abort_code = RXGK_PACKETSHORT;
+ ret = -EPROTO;
+protocol_error:
+ trace_rxrpc_rx_eproto(NULL, sp->hdr.serial, eproto);
+ *_abort_code = abort_code;
+ goto out;
+
+cant_get_token:
+ switch (ret) {
+ case -ENOMEM:
+ goto temporary_error;
+ case -EINVAL:
+ eproto = tracepoint_string("rxgk_rsp_internal_error");
+ abort_code = RXGK_NOTAUTH;
+ ret = -EKEYREJECTED;
+ goto protocol_error;
+ case -ENOPKG:
+ eproto = tracepoint_string("rxgk_rsp_nopkg");
+ abort_code = RXGK_BADETYPE;
+ ret = -EKEYREJECTED;
+ goto protocol_error;
+ }
+
+temporary_error:
+ /* Ignore the response packet if we got a temporary error such as
+ * ENOMEM. We just want to send the challenge again. Note that we
+ * also come out this way if the ticket decryption fails.
+ */
+ goto out;
+}
+
+/*
+ * clear the connection security
+ */
+static void rxgk_clear(struct rxrpc_connection *conn)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(conn->rxgk.keys); i++)
+ rxgk_put(conn->rxgk.keys[i]);
+}
+
+/*
+ * Initialise the RxGK security service.
+ */
+static int rxgk_init(void)
+{
+ return 0;
+}
+
+/*
+ * Clean up the RxGK security service.
+ */
+static void rxgk_exit(void)
+{
+}
+
+/*
+ * RxRPC YFS GSSAPI-based security
+ */
+const struct rxrpc_security rxgk_yfs = {
+ .name = "yfs-rxgk",
+ .security_index = RXRPC_SECURITY_YFS_RXGK,
+ .no_key_abort = RXGK_NOTAUTH,
+ .init = rxgk_init,
+ .exit = rxgk_exit,
+ .preparse_server_key = rxgk_preparse_server_key,
+ .free_preparse_server_key = rxgk_free_preparse_server_key,
+ .destroy_server_key = rxgk_destroy_server_key,
+ .describe_server_key = rxgk_describe_server_key,
+ .init_connection_security = rxgk_init_connection_security,
+ .secure_packet = rxgk_secure_packet,
+ .verify_packet = rxgk_verify_packet,
+ .free_call_crypto = rxgk_free_call_crypto,
+ .locate_data = rxgk_locate_data,
+ .issue_challenge = rxgk_issue_challenge,
+ .respond_to_challenge = rxgk_respond_to_challenge,
+ .verify_response = rxgk_verify_response,
+ .clear = rxgk_clear,
+ .default_decode_ticket = rxgk_yfs_decode_ticket,
+};
diff --git a/net/rxrpc/rxgk_app.c b/net/rxrpc/rxgk_app.c
new file mode 100644
index 000000000000..895879f3acfb
--- /dev/null
+++ b/net/rxrpc/rxgk_app.c
@@ -0,0 +1,289 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Application-specific bits for GSSAPI-based RxRPC security
+ *
+ * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/net.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/key-type.h>
+#include "ar-internal.h"
+#include "rxgk_common.h"
+
+/*
+ * Decode a default-style YFS ticket in a response and turn it into an
+ * rxrpc-type key.
+ *
+ * struct rxgk_key {
+ * afs_uint32 enctype;
+ * opaque key<>;
+ * };
+ *
+ * struct RXGK_AuthName {
+ * afs_int32 kind;
+ * opaque data<AUTHDATAMAX>;
+ * opaque display<AUTHPRINTABLEMAX>;
+ * };
+ *
+ * struct RXGK_Token {
+ * rxgk_key K0;
+ * RXGK_Level level;
+ * rxgkTime starttime;
+ * afs_int32 lifetime;
+ * afs_int32 bytelife;
+ * rxgkTime expirationtime;
+ * struct RXGK_AuthName identities<>;
+ * };
+ */
+int rxgk_yfs_decode_ticket(struct sk_buff *skb,
+ unsigned int ticket_offset, unsigned int ticket_len,
+ u32 *_abort_code,
+ struct key **_key)
+{
+ struct rxrpc_key_token *token;
+ const struct cred *cred = current_cred(); // TODO - use socket creds
+ struct key *key;
+ size_t pre_ticket_len, payload_len;
+ unsigned int klen, enctype;
+ void *payload, *ticket;
+ __be32 *t, *p, *q, tmp[2];
+ int ret;
+
+ _enter("");
+
+ /* Get the session key length */
+ ret = skb_copy_bits(skb, ticket_offset, tmp, sizeof(tmp));
+ if (ret < 0)
+ goto error_out;
+ enctype = ntohl(tmp[0]);
+ klen = ntohl(tmp[1]);
+
+ if (klen > ticket_len - 10 * sizeof(__be32)) {
+ *_abort_code = RXGK_INCONSISTENCY;
+ return -EPROTO;
+ }
+
+ pre_ticket_len = ((5 + 14) * sizeof(__be32) +
+ xdr_round_up(klen) +
+ sizeof(__be32));
+ payload_len = pre_ticket_len + xdr_round_up(ticket_len);
+
+ payload = kzalloc(payload_len, GFP_NOFS);
+ if (!payload)
+ return -ENOMEM;
+
+ /* We need to fill out the XDR form for a key payload that we can pass
+ * to add_key(). Start by copying in the ticket so that we can parse
+ * it.
+ */
+ ticket = payload + pre_ticket_len;
+ ret = skb_copy_bits(skb, ticket_offset, ticket, ticket_len);
+ if (ret < 0)
+ goto error;
+
+ /* Fill out the form header. */
+ p = payload;
+ p[0] = htonl(0); /* Flags */
+ p[1] = htonl(1); /* len(cellname) */
+ p[2] = htonl(0x20000000); /* Cellname " " */
+ p[3] = htonl(1); /* #tokens */
+ p[4] = htonl(15 * sizeof(__be32) + xdr_round_up(klen) + xdr_round_up(ticket_len)); /* Token len */
+
+ /* Now fill in the body. Most of this we can just scrape directly from
+ * the ticket.
+ */
+ t = ticket + sizeof(__be32) * 2 + xdr_round_up(klen);
+ q = payload + 5 * sizeof(__be32);
+ q[ 0] = htonl(RXRPC_SECURITY_YFS_RXGK);
+ q[ 1] = t[1]; /* begintime - msw */
+ q[ 2] = t[2]; /* - lsw */
+ q[ 3] = t[5]; /* endtime - msw */
+ q[ 4] = t[6]; /* - lsw */
+ q[ 5] = 0; /* level - msw */
+ q[ 6] = t[0]; /* - lsw */
+ q[ 7] = 0; /* lifetime - msw */
+ q[ 8] = t[3]; /* - lsw */
+ q[ 9] = 0; /* bytelife - msw */
+ q[10] = t[4]; /* - lsw */
+ q[11] = 0; /* enctype - msw */
+ q[12] = htonl(enctype); /* - lsw */
+ q[13] = htonl(klen); /* Key length */
+
+ q += 14;
+
+ memcpy(q, ticket + sizeof(__be32) * 2, klen);
+ q += xdr_round_up(klen) / 4;
+ q[0] = ntohl(ticket_len);
+ q++;
+ if (WARN_ON((unsigned long)q != (unsigned long)ticket)) {
+ ret = -EIO;
+ goto error;
+ }
+
+ /* Ticket read in with skb_copy_bits above */
+ q += xdr_round_up(ticket_len) / 4;
+ if (WARN_ON((unsigned long)q - (unsigned long)payload != payload_len)) {
+ ret = -EIO;
+ goto error;
+ }
+
+ /* Now turn that into a key. */
+ key = key_alloc(&key_type_rxrpc, "x",
+ GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, 0, // TODO: Use socket owner
+ KEY_ALLOC_NOT_IN_QUOTA, NULL);
+ if (IS_ERR(key)) {
+ _leave(" = -ENOMEM [alloc %ld]", PTR_ERR(key));
+ goto error;
+ }
+
+ _debug("key %d", key_serial(key));
+
+ ret = key_instantiate_and_link(key, payload, payload_len, NULL, NULL);
+ if (ret < 0)
+ goto error_key;
+
+ token = key->payload.data[0];
+ token->no_leak_key = true;
+ *_key = key;
+ key = NULL;
+ ret = 0;
+ goto error;
+
+error_key:
+ key_put(key);
+error:
+ kfree_sensitive(payload);
+error_out:
+ _leave(" = %d", ret);
+ return ret;
+}
+
+/*
+ * Extract the token and set up a session key from the details.
+ *
+ * struct RXGK_TokenContainer {
+ * afs_int32 kvno;
+ * afs_int32 enctype;
+ * opaque encrypted_token<>;
+ * };
+ *
+ * [tools.ietf.org/html/draft-wilkinson-afs3-rxgk-afs-08 sec 6.1]
+ */
+int rxgk_extract_token(struct rxrpc_connection *conn,
+ struct sk_buff *skb,
+ unsigned int token_offset, unsigned int token_len,
+ struct key **_key,
+ u32 *_abort_code, const char **_eproto)
+{
+ const struct krb5_enctype *krb5;
+ const struct krb5_buffer *server_secret;
+ struct krb5_enc_keys token_enc = {};
+ struct key *server_key;
+ unsigned int ticket_offset, ticket_len;
+ u32 kvno, enctype;
+ int ret;
+
+ struct {
+ __be32 kvno;
+ __be32 enctype;
+ __be32 token_len;
+ } container;
+
+ /* Decode the RXGK_TokenContainer object. This tells us which server
+ * key we should be using. We can then fetch the key, get the secret
+ * and set up the crypto to extract the token.
+ */
+ if (skb_copy_bits(skb, token_offset, &container, sizeof(container)) < 0)
+ goto short_packet;
+
+ kvno = ntohl(container.kvno);
+ enctype = ntohl(container.enctype);
+ ticket_len = ntohl(container.token_len);
+ ticket_offset = token_offset + sizeof(container);
+
+ if (xdr_round_up(ticket_len) > token_len - 3 * 4)
+ goto short_packet;
+
+ _debug("KVNO %u", kvno);
+ _debug("ENC %u", enctype);
+ _debug("TLEN %u", ticket_len);
+
+ server_key = rxrpc_look_up_server_security(conn, skb, kvno, enctype);
+ if (IS_ERR(server_key))
+ goto cant_get_server_key;
+
+ down_read(&server_key->sem);
+ server_secret = (const void *)&server_key->payload.data[2];
+ ret = rxgk_set_up_token_cipher(server_secret, &token_enc, enctype, &krb5, GFP_NOFS);
+ up_read(&server_key->sem);
+ key_put(server_key);
+ if (ret < 0)
+ goto cant_get_token;
+
+ /* We can now decrypt and parse the token/ticket. This allows us to
+ * gain access to K0, from which we can derive the transport key and
+ * thence decode the authenticator.
+ */
+ *_eproto = tracepoint_string("rxgk_rsp_dec_tkt");
+ ret = rxgk_decrypt_skb(krb5, &token_enc, skb,
+ &ticket_offset, &ticket_len, _abort_code);
+ if (ret < 0)
+ return ret;
+
+ ret = conn->security->default_decode_ticket(skb, ticket_offset, ticket_len,
+ _abort_code, _key);
+ if (ret < 0)
+ goto cant_get_token;
+
+ _leave(" = 0");
+ return ret;
+
+short_packet:
+ *_eproto = tracepoint_string("rxgk_rsp_short");
+ *_abort_code = RXGK_PACKETSHORT;
+ return -EPROTO;
+
+cant_get_server_key:
+ ret = PTR_ERR(server_key);
+ switch (ret) {
+ case -ENOMEM:
+ goto temporary_error;
+ case -ENOKEY:
+ case -EKEYREJECTED:
+ case -EKEYEXPIRED:
+ case -EKEYREVOKED:
+ case -EPERM:
+ *_eproto = tracepoint_string("rxgk_rsp_nokey");
+ *_abort_code = RXGK_BADKEYNO;
+ return -EKEYREJECTED;
+ default:
+ *_eproto = tracepoint_string("rxgk_rsp_keyerr");
+ *_abort_code = RXGK_NOTAUTH;
+ return -EKEYREJECTED;
+ }
+
+cant_get_token:
+ switch (ret) {
+ case -ENOMEM:
+ goto temporary_error;
+ case -EINVAL:
+ *_eproto = tracepoint_string("rxgk_rsp_internal_error");
+ *_abort_code = RXGK_NOTAUTH;
+ return -EKEYREJECTED;
+ case -ENOPKG:
+ *_eproto = tracepoint_string("rxgk_rsp_nopkg");
+ *_abort_code = RXGK_BADETYPE;
+ return -EKEYREJECTED;
+ }
+
+temporary_error:
+ /* Ignore the response packet if we got a temporary error such as
+ * ENOMEM. We just want to send the challenge again. Note that we
+ * also come out this way if the ticket decryption fails.
+ */
+ return ret;
+}
diff --git a/net/rxrpc/rxgk_common.h b/net/rxrpc/rxgk_common.h
index 3047ad531877..38473b13e67d 100644
--- a/net/rxrpc/rxgk_common.h
+++ b/net/rxrpc/rxgk_common.h
@@ -33,6 +33,17 @@ struct rxgk_context {
struct krb5_enc_keys resp_enc; /* Response packet enc key */
};

+#define xdr_round_up(x) (round_up((x), sizeof(__be32)))
+
+/*
+ * rxgk_app.c
+ */
+int rxgk_yfs_decode_ticket(struct sk_buff *, unsigned int, unsigned int,
+ u32 *, struct key **);
+int rxgk_extract_token(struct rxrpc_connection *,
+ struct sk_buff *, unsigned int, unsigned int,
+ struct key **, u32 *, const char **);
+
/*
* rxgk_kdf.c
*/
@@ -42,3 +53,110 @@ int rxgk_set_up_token_cipher(const struct krb5_buffer *, struct krb5_enc_keys *,
unsigned int, const struct krb5_enctype **,
gfp_t);
void rxgk_put(struct rxgk_context *);
+
+/*
+ * Apply encryption and checksumming functions to part of an skbuff.
+ */
+static inline
+int rxgk_encrypt_skb(const struct krb5_enctype *krb5,
+ struct krb5_enc_keys *keys,
+ struct sk_buff *skb,
+ u16 secure_offset, u16 secure_len,
+ u16 data_offset, u16 data_len,
+ bool preconfounded)
+{
+ struct scatterlist sg[16];
+ int nr_sg;
+
+ sg_init_table(sg, ARRAY_SIZE(sg));
+ nr_sg = skb_to_sgvec(skb, sg, secure_offset, secure_len);
+ if (unlikely(nr_sg < 0))
+ return nr_sg;
+
+ data_offset -= secure_offset;
+ return crypto_krb5_encrypt(krb5, keys, sg, nr_sg, secure_len,
+ data_offset, data_len, preconfounded);
+}
+
+/*
+ * 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.
+ */
+static inline
+int rxgk_decrypt_skb(const struct krb5_enctype *krb5,
+ struct krb5_enc_keys *keys,
+ struct sk_buff *skb,
+ unsigned int *_offset, unsigned int *_len,
+ int *_error_code)
+{
+ struct scatterlist sg[16];
+ size_t offset = 0, len = *_len;
+ int nr_sg, ret;
+
+ sg_init_table(sg, ARRAY_SIZE(sg));
+ nr_sg = skb_to_sgvec(skb, sg, *_offset, len);
+ if (unlikely(nr_sg < 0))
+ return nr_sg;
+
+ ret = crypto_krb5_decrypt(krb5, keys, sg, nr_sg,
+ &offset, &len, _error_code);
+
+ *_offset += offset;
+ *_len = len;
+ 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.
+ */
+static inline
+int rxgk_get_mic_skb(const struct krb5_enctype *krb5,
+ struct crypto_shash *shash,
+ const struct krb5_buffer *metadata,
+ struct sk_buff *skb,
+ u16 secure_offset, u16 secure_len,
+ u16 data_offset, u16 data_len)
+{
+ struct scatterlist sg[16];
+ int nr_sg;
+
+ sg_init_table(sg, ARRAY_SIZE(sg));
+ nr_sg = skb_to_sgvec(skb, sg, secure_offset, secure_len);
+ if (unlikely(nr_sg < 0))
+ return nr_sg;
+
+ data_offset -= secure_offset;
+ return crypto_krb5_get_mic(krb5, shash, metadata, sg, nr_sg, secure_len,
+ data_offset, data_len);
+}
+
+/*
+ * Check the MIC on a region of an skbuff. The offset and length are updated
+ * to reflect the actual content of the secure region.
+ */
+static inline
+int rxgk_verify_mic_skb(const struct krb5_enctype *krb5,
+ struct crypto_shash *shash,
+ const struct krb5_buffer *metadata,
+ struct sk_buff *skb,
+ unsigned int *_offset, unsigned int *_len,
+ u32 *_error_code)
+{
+ struct scatterlist sg[16];
+ size_t offset = 0, len = *_len;
+ int nr_sg, ret;
+
+ sg_init_table(sg, ARRAY_SIZE(sg));
+ nr_sg = skb_to_sgvec(skb, sg, *_offset, len);
+ if (unlikely(nr_sg < 0))
+ return nr_sg;
+
+ ret = crypto_krb5_verify_mic(krb5, shash, metadata, sg, nr_sg,
+ &offset, &len, _error_code);
+
+ *_offset += offset;
+ *_len = len;
+ return 0;
+}
diff --git a/net/rxrpc/security.c b/net/rxrpc/security.c
index 50cb5f1ee0c0..278a510b2956 100644
--- a/net/rxrpc/security.c
+++ b/net/rxrpc/security.c
@@ -20,6 +20,9 @@ static const struct rxrpc_security *rxrpc_security_types[] = {
#ifdef CONFIG_RXKAD
[RXRPC_SECURITY_RXKAD] = &rxkad,
#endif
+#ifdef CONFIG_RXGK
+ [RXRPC_SECURITY_YFS_RXGK] = &rxgk_yfs,
+#endif
};

int __init rxrpc_init_security(void)


2020-11-12 13:44:59

by David Howells

[permalink] [raw]
Subject: Re: [RFC][PATCH 00/18] crypto: Add generic Kerberos library

Would it be possible/practical to make the skcipher encrypt functions take an
offset into the scatterlist rather than always starting at the beginning?

David

2020-11-12 14:37:50

by Chuck Lever III

[permalink] [raw]
Subject: Re: [RFC][PATCH 00/18] crypto: Add generic Kerberos library



> On Nov 12, 2020, at 7:57 AM, David Howells <[email protected]> wrote:
>
>
> Hi Herbert, Bruce,
>
> Here's my first cut at a generic Kerberos crypto library in the kernel so
> that I can share code between rxrpc and sunrpc (and cifs?).
>
> I derived some of the parts from the sunrpc gss library and added more
> advanced AES and Camellia crypto. I haven't ported across the DES-based
> crypto yet - I figure that can wait a bit till the interface is sorted.
>
> Whilst I have put it into a directory under crypto/, I haven't made an
> interface that goes and loads it (analogous to crypto_alloc_skcipher,
> say). Instead, you call:
>
> const struct krb5_enctype *crypto_krb5_find_enctype(u32 enctype);
>
> to go and get a handler table and then use a bunch of accessor functions to
> jump through the hoops. This is basically the way the sunrpc gsslib does
> things. It might be worth making it so you do something like:
>
> struct crypto_mech *ctx = crypto_mech_alloc("krb5(18)");
>
> to get enctype 18, but I'm not sure if it's worth the effort. Also, I'm
> not sure if there are any alternatives to kerberos we will need to support.
>
> There are three main interfaces to it:
>
> (*) I/O crypto: encrypt, decrypt, get_mic and verify_mic.
>
> These all do in-place crypto, using an sglist to define the buffer
> with the data in it. Is it necessary to make it able to take separate
> input and output buffers?

Hi David, Wondering if these "I/O" APIs use synchronous or async
crypto under the covers. For small data items like MICs, synchronous
might be a better choice, especially if asynchronous crypto could
result in incoming requests getting re-ordered and falling out of
the GSS sequence number window.

What say ye?


> (*) PRF+ calculation for key derivation.
> (*) Kc, Ke, Ki derivation.
>
> These use krb5_buffer structs to pass objects around. This is akin to
> the xdr_netobj, but has a void* instead of a u8* data pointer.
>
> In terms of rxrpc's rxgk, there's another step in key derivation that isn't
> part of the kerberos standard, but uses the PRF+ function to generate a key
> that is then used to generate Kc, Ke and Ki. Is it worth putting this into
> the directory or maybe having a callout to insert an intermediate step in
> key derivation?
>
> Note that, for purposes of illustration, I've included some rxrpc patches
> that use this interface to implement the rxgk Rx security class. The
> branch also is based on some rxrpc patches that are a prerequisite for
> this, but the crypto patches don't need it.
>
> ---
> The patches can be found here also:
>
> http://git.kernel.org/cgit/linux/kernel/git/dhowells/linux-fs.git/log/?h=crypto-krb5
>
> David
> ---
> David Howells (18):
> crypto/krb5: Implement Kerberos crypto core
> crypto/krb5: Add some constants out of sunrpc headers
> crypto/krb5: Provide infrastructure and key derivation
> crypto/krb5: Implement the Kerberos5 rfc3961 key derivation
> crypto/krb5: Implement the Kerberos5 rfc3961 encrypt and decrypt functions
> crypto/krb5: Implement the Kerberos5 rfc3961 get_mic and verify_mic
> crypto/krb5: Implement the AES enctypes from rfc3962
> crypto/krb5: Implement crypto self-testing
> crypto/krb5: Implement the AES enctypes from rfc8009
> crypto/krb5: Implement the AES encrypt/decrypt from rfc8009
> crypto/krb5: Add the AES self-testing data from rfc8009
> crypto/krb5: Implement the Camellia enctypes from rfc6803
> rxrpc: Add the security index for yfs-rxgk
> rxrpc: Add YFS RxGK (GSSAPI) security class
> rxrpc: rxgk: Provide infrastructure and key derivation
> rxrpc: rxgk: Implement the yfs-rxgk security class (GSSAPI)
> rxrpc: rxgk: Implement connection rekeying
> rxgk: Support OpenAFS's rxgk implementation
>
>
> crypto/krb5/Kconfig | 9 +
> crypto/krb5/Makefile | 11 +-
> crypto/krb5/internal.h | 101 +++
> crypto/krb5/kdf.c | 223 ++++++
> crypto/krb5/main.c | 190 +++++
> crypto/krb5/rfc3961_simplified.c | 732 ++++++++++++++++++
> crypto/krb5/rfc3962_aes.c | 140 ++++
> crypto/krb5/rfc6803_camellia.c | 249 ++++++
> crypto/krb5/rfc8009_aes2.c | 440 +++++++++++
> crypto/krb5/selftest.c | 543 +++++++++++++
> crypto/krb5/selftest_data.c | 289 +++++++
> fs/afs/misc.c | 13 +
> include/crypto/krb5.h | 100 +++
> include/keys/rxrpc-type.h | 17 +
> include/trace/events/rxrpc.h | 4 +
> include/uapi/linux/rxrpc.h | 17 +
> net/rxrpc/Kconfig | 10 +
> net/rxrpc/Makefile | 5 +
> net/rxrpc/ar-internal.h | 20 +
> net/rxrpc/conn_object.c | 2 +
> net/rxrpc/key.c | 319 ++++++++
> net/rxrpc/rxgk.c | 1232 ++++++++++++++++++++++++++++++
> net/rxrpc/rxgk_app.c | 424 ++++++++++
> net/rxrpc/rxgk_common.h | 164 ++++
> net/rxrpc/rxgk_kdf.c | 271 +++++++
> net/rxrpc/security.c | 6 +
> 26 files changed, 5530 insertions(+), 1 deletion(-)
> create mode 100644 crypto/krb5/kdf.c
> create mode 100644 crypto/krb5/rfc3961_simplified.c
> create mode 100644 crypto/krb5/rfc3962_aes.c
> create mode 100644 crypto/krb5/rfc6803_camellia.c
> create mode 100644 crypto/krb5/rfc8009_aes2.c
> create mode 100644 crypto/krb5/selftest.c
> create mode 100644 crypto/krb5/selftest_data.c
> create mode 100644 net/rxrpc/rxgk.c
> create mode 100644 net/rxrpc/rxgk_app.c
> create mode 100644 net/rxrpc/rxgk_common.h
> create mode 100644 net/rxrpc/rxgk_kdf.c
>
>

--
Chuck Lever



2020-11-12 16:54:53

by David Howells

[permalink] [raw]
Subject: Re: [RFC][PATCH 00/18] crypto: Add generic Kerberos library

Chuck Lever <[email protected]> wrote:

> Really? My understanding of the Linux kernel SUNRPC implementation is
> that it uses asynchronous, even for small data items. Maybe I'm using
> the terminology incorrectly.

Seems to be synchronous, at least in its use of skcipher:

grep -e skcipher *
gss_krb5_crypto.c:#include <crypto/skcipher.h>
gss_krb5_crypto.c: struct crypto_sync_skcipher *tfm,
gss_krb5_crypto.c: if (length % crypto_sync_skcipher_blocksize(tfm) != 0)
gss_krb5_crypto.c: if (crypto_sync_skcipher_ivsize(tfm) > GSS_KRB5_MAX_BLOCKSIZE) {
gss_krb5_crypto.c: crypto_sync_skcipher_ivsize(tfm));
gss_krb5_crypto.c: memcpy(local_iv, iv, crypto_sync_skcipher_ivsize(tfm));
gss_krb5_crypto.c: skcipher_request_set_sync_tfm(req, tfm);
gss_krb5_crypto.c: skcipher_request_set_callback(req, 0, NULL, NULL);
gss_krb5_crypto.c: skcipher_request_set_crypt(req, sg, sg, length, local_iv);
gss_krb5_crypto.c: ret = crypto_skcipher_encrypt(req);
gss_krb5_crypto.c: skcipher_request_zero(req);
gss_krb5_crypto.c: struct crypto_sync_skcipher *tfm,
gss_krb5_crypto.c: if (length % crypto_sync_skcipher_blocksize(tfm) != 0)
gss_krb5_crypto.c: if (crypto_sync_skcipher_ivsize(tfm) > GSS_KRB5_MAX_BLOCKSIZE) {
gss_krb5_crypto.c: crypto_sync_skcipher_ivsize(tfm));
gss_krb5_crypto.c: memcpy(local_iv, iv, crypto_sync_skcipher_ivsize(tfm));
gss_krb5_crypto.c: skcipher_request_set_sync_tfm(req, tfm);
gss_krb5_crypto.c: skcipher_request_set_callback(req, 0, NULL, NULL);
gss_krb5_crypto.c: skcipher_request_set_crypt(req, sg, sg, length, local_iv);
gss_krb5_crypto.c: ret = crypto_skcipher_decrypt(req);
gss_krb5_crypto.c: skcipher_request_zero(req);
gss_krb5_crypto.c: struct skcipher_request *req;
gss_krb5_crypto.c: struct crypto_sync_skcipher *tfm =
gss_krb5_crypto.c: crypto_sync_skcipher_reqtfm(desc->req);
gss_krb5_crypto.c: fraglen = thislen & (crypto_sync_skcipher_blocksize(tfm) - 1);
gss_krb5_crypto.c: skcipher_request_set_crypt(desc->req, desc->infrags, desc->outfrags,
gss_krb5_crypto.c: ret = crypto_skcipher_encrypt(desc->req);
gss_krb5_crypto.c:gss_encrypt_xdr_buf(struct crypto_sync_skcipher *tfm, struct xdr_buf *buf,
gss_krb5_crypto.c: BUG_ON((buf->len - offset) % crypto_sync_skcipher_blocksize(tfm) != 0);
gss_krb5_crypto.c: skcipher_request_set_sync_tfm(req, tfm);
gss_krb5_crypto.c: skcipher_request_set_callback(req, 0, NULL, NULL);
gss_krb5_crypto.c: skcipher_request_zero(req);
gss_krb5_crypto.c: struct skcipher_request *req;
gss_krb5_crypto.c: struct crypto_sync_skcipher *tfm =
gss_krb5_crypto.c: crypto_sync_skcipher_reqtfm(desc->req);
gss_krb5_crypto.c: fraglen = thislen & (crypto_sync_skcipher_blocksize(tfm) - 1);
gss_krb5_crypto.c: skcipher_request_set_crypt(desc->req, desc->frags, desc->frags,
gss_krb5_crypto.c: ret = crypto_skcipher_decrypt(desc->req);
gss_krb5_crypto.c:gss_decrypt_xdr_buf(struct crypto_sync_skcipher *tfm, struct xdr_buf *buf,
gss_krb5_crypto.c: BUG_ON((buf->len - offset) % crypto_sync_skcipher_blocksize(tfm) != 0);
gss_krb5_crypto.c: skcipher_request_set_sync_tfm(req, tfm);
gss_krb5_crypto.c: skcipher_request_set_callback(req, 0, NULL, NULL);
gss_krb5_crypto.c: skcipher_request_zero(req);
gss_krb5_crypto.c:gss_krb5_cts_crypt(struct crypto_sync_skcipher *cipher, struct xdr_buf *buf,
gss_krb5_crypto.c: skcipher_request_set_sync_tfm(req, cipher);
gss_krb5_crypto.c: skcipher_request_set_callback(req, 0, NULL, NULL);
gss_krb5_crypto.c: skcipher_request_set_crypt(req, sg, sg, len, iv);
gss_krb5_crypto.c: ret = crypto_skcipher_encrypt(req);
gss_krb5_crypto.c: ret = crypto_skcipher_decrypt(req);
gss_krb5_crypto.c: skcipher_request_zero(req);
gss_krb5_crypto.c: struct crypto_sync_skcipher *cipher, *aux_cipher;
gss_krb5_crypto.c: blocksize = crypto_sync_skcipher_blocksize(cipher);
gss_krb5_crypto.c: skcipher_request_set_sync_tfm(req, aux_cipher);
gss_krb5_crypto.c: skcipher_request_set_callback(req, 0, NULL, NULL);
gss_krb5_crypto.c: skcipher_request_zero(req);
gss_krb5_crypto.c: struct crypto_sync_skcipher *cipher, *aux_cipher;
gss_krb5_crypto.c: blocksize = crypto_sync_skcipher_blocksize(cipher);
gss_krb5_crypto.c: skcipher_request_set_sync_tfm(req, aux_cipher);
gss_krb5_crypto.c: skcipher_request_set_callback(req, 0, NULL, NULL);
gss_krb5_crypto.c: skcipher_request_zero(req);
gss_krb5_keys.c:#include <crypto/skcipher.h>
gss_krb5_keys.c: struct crypto_sync_skcipher *cipher;
gss_krb5_keys.c: cipher = crypto_alloc_sync_skcipher(gk5e->encrypt_name, 0, 0);
gss_krb5_keys.c: if (crypto_sync_skcipher_setkey(cipher, inkey->data, inkey->len))
gss_krb5_keys.c: crypto_free_sync_skcipher(cipher);
gss_krb5_mech.c:#include <crypto/skcipher.h>
gss_krb5_mech.c: struct krb5_ctx *ctx, struct crypto_sync_skcipher **res)
gss_krb5_mech.c: *res = crypto_alloc_sync_skcipher(ctx->gk5e->encrypt_name, 0, 0);
gss_krb5_mech.c: if (crypto_sync_skcipher_setkey(*res, key.data, key.len)) {
gss_krb5_mech.c: crypto_free_sync_skcipher(*res);
gss_krb5_mech.c: crypto_free_sync_skcipher(ctx->seq);
gss_krb5_mech.c: crypto_free_sync_skcipher(ctx->enc);
gss_krb5_mech.c:static struct crypto_sync_skcipher *
gss_krb5_mech.c: struct crypto_sync_skcipher *cp;
gss_krb5_mech.c: cp = crypto_alloc_sync_skcipher(cname, 0, 0);
gss_krb5_mech.c: if (crypto_sync_skcipher_setkey(cp, key, ctx->gk5e->keylength)) {
gss_krb5_mech.c: crypto_free_sync_skcipher(cp);
gss_krb5_mech.c: crypto_free_sync_skcipher(ctx->enc);
gss_krb5_mech.c: crypto_free_sync_skcipher(ctx->seq);
gss_krb5_mech.c: crypto_free_sync_skcipher(ctx->initiator_enc_aux);
gss_krb5_mech.c: crypto_free_sync_skcipher(ctx->acceptor_enc);
gss_krb5_mech.c: crypto_free_sync_skcipher(ctx->initiator_enc);
gss_krb5_mech.c: crypto_free_sync_skcipher(kctx->seq);
gss_krb5_mech.c: crypto_free_sync_skcipher(kctx->enc);
gss_krb5_mech.c: crypto_free_sync_skcipher(kctx->acceptor_enc);
gss_krb5_mech.c: crypto_free_sync_skcipher(kctx->initiator_enc);
gss_krb5_mech.c: crypto_free_sync_skcipher(kctx->acceptor_enc_aux);
gss_krb5_mech.c: crypto_free_sync_skcipher(kctx->initiator_enc_aux);
gss_krb5_seqnum.c:#include <crypto/skcipher.h>
gss_krb5_seqnum.c: struct crypto_sync_skcipher *key,
gss_krb5_seqnum.c: struct crypto_sync_skcipher *key = kctx->seq;
gss_krb5_wrap.c:#include <crypto/skcipher.h>
gss_krb5_wrap.c: blocksize = crypto_sync_skcipher_blocksize(kctx->enc);
gss_krb5_wrap.c: blocksize = crypto_sync_skcipher_blocksize(kctx->enc);

David

2020-11-12 18:38:04

by J. Bruce Fields

[permalink] [raw]
Subject: Re: [RFC][PATCH 00/18] crypto: Add generic Kerberos library

On Thu, Nov 12, 2020 at 12:57:45PM +0000, David Howells wrote:
>
> Hi Herbert, Bruce,
>
> Here's my first cut at a generic Kerberos crypto library in the kernel so
> that I can share code between rxrpc and sunrpc (and cifs?).
>
> I derived some of the parts from the sunrpc gss library and added more
> advanced AES and Camellia crypto. I haven't ported across the DES-based
> crypto yet - I figure that can wait a bit till the interface is sorted.
>
> Whilst I have put it into a directory under crypto/, I haven't made an
> interface that goes and loads it (analogous to crypto_alloc_skcipher,
> say). Instead, you call:
>
> const struct krb5_enctype *crypto_krb5_find_enctype(u32 enctype);
>
> to go and get a handler table and then use a bunch of accessor functions to
> jump through the hoops. This is basically the way the sunrpc gsslib does
> things. It might be worth making it so you do something like:
>
> struct crypto_mech *ctx = crypto_mech_alloc("krb5(18)");
>
> to get enctype 18, but I'm not sure if it's worth the effort. Also, I'm
> not sure if there are any alternatives to kerberos we will need to support.

We did have code for a non-krb5 mechanism at some point, but it was torn
out. So I don't think that's a priority.

(Chuck, will RPC-over-SSL need a new non-krb5 mechanism?)

> There are three main interfaces to it:
>
> (*) I/O crypto: encrypt, decrypt, get_mic and verify_mic.
>
> These all do in-place crypto, using an sglist to define the buffer
> with the data in it. Is it necessary to make it able to take separate
> input and output buffers?

I don't know. My memory is that the buffer management in the existing
rpcsec_gss code is complex and fragile. See e.g. the long comment in
gss_krb5_remove_padding.

--b.

> (*) PRF+ calculation for key derivation.
> (*) Kc, Ke, Ki derivation.
>
> These use krb5_buffer structs to pass objects around. This is akin to
> the xdr_netobj, but has a void* instead of a u8* data pointer.
>
> In terms of rxrpc's rxgk, there's another step in key derivation that isn't
> part of the kerberos standard, but uses the PRF+ function to generate a key
> that is then used to generate Kc, Ke and Ki. Is it worth putting this into
> the directory or maybe having a callout to insert an intermediate step in
> key derivation?
>
> Note that, for purposes of illustration, I've included some rxrpc patches
> that use this interface to implement the rxgk Rx security class. The
> branch also is based on some rxrpc patches that are a prerequisite for
> this, but the crypto patches don't need it.
>
> ---
> The patches can be found here also:
>
> http://git.kernel.org/cgit/linux/kernel/git/dhowells/linux-fs.git/log/?h=crypto-krb5
>
> David
> ---
> David Howells (18):
> crypto/krb5: Implement Kerberos crypto core
> crypto/krb5: Add some constants out of sunrpc headers
> crypto/krb5: Provide infrastructure and key derivation
> crypto/krb5: Implement the Kerberos5 rfc3961 key derivation
> crypto/krb5: Implement the Kerberos5 rfc3961 encrypt and decrypt functions
> crypto/krb5: Implement the Kerberos5 rfc3961 get_mic and verify_mic
> crypto/krb5: Implement the AES enctypes from rfc3962
> crypto/krb5: Implement crypto self-testing
> crypto/krb5: Implement the AES enctypes from rfc8009
> crypto/krb5: Implement the AES encrypt/decrypt from rfc8009
> crypto/krb5: Add the AES self-testing data from rfc8009
> crypto/krb5: Implement the Camellia enctypes from rfc6803
> rxrpc: Add the security index for yfs-rxgk
> rxrpc: Add YFS RxGK (GSSAPI) security class
> rxrpc: rxgk: Provide infrastructure and key derivation
> rxrpc: rxgk: Implement the yfs-rxgk security class (GSSAPI)
> rxrpc: rxgk: Implement connection rekeying
> rxgk: Support OpenAFS's rxgk implementation
>
>
> crypto/krb5/Kconfig | 9 +
> crypto/krb5/Makefile | 11 +-
> crypto/krb5/internal.h | 101 +++
> crypto/krb5/kdf.c | 223 ++++++
> crypto/krb5/main.c | 190 +++++
> crypto/krb5/rfc3961_simplified.c | 732 ++++++++++++++++++
> crypto/krb5/rfc3962_aes.c | 140 ++++
> crypto/krb5/rfc6803_camellia.c | 249 ++++++
> crypto/krb5/rfc8009_aes2.c | 440 +++++++++++
> crypto/krb5/selftest.c | 543 +++++++++++++
> crypto/krb5/selftest_data.c | 289 +++++++
> fs/afs/misc.c | 13 +
> include/crypto/krb5.h | 100 +++
> include/keys/rxrpc-type.h | 17 +
> include/trace/events/rxrpc.h | 4 +
> include/uapi/linux/rxrpc.h | 17 +
> net/rxrpc/Kconfig | 10 +
> net/rxrpc/Makefile | 5 +
> net/rxrpc/ar-internal.h | 20 +
> net/rxrpc/conn_object.c | 2 +
> net/rxrpc/key.c | 319 ++++++++
> net/rxrpc/rxgk.c | 1232 ++++++++++++++++++++++++++++++
> net/rxrpc/rxgk_app.c | 424 ++++++++++
> net/rxrpc/rxgk_common.h | 164 ++++
> net/rxrpc/rxgk_kdf.c | 271 +++++++
> net/rxrpc/security.c | 6 +
> 26 files changed, 5530 insertions(+), 1 deletion(-)
> create mode 100644 crypto/krb5/kdf.c
> create mode 100644 crypto/krb5/rfc3961_simplified.c
> create mode 100644 crypto/krb5/rfc3962_aes.c
> create mode 100644 crypto/krb5/rfc6803_camellia.c
> create mode 100644 crypto/krb5/rfc8009_aes2.c
> create mode 100644 crypto/krb5/selftest.c
> create mode 100644 crypto/krb5/selftest_data.c
> create mode 100644 net/rxrpc/rxgk.c
> create mode 100644 net/rxrpc/rxgk_app.c
> create mode 100644 net/rxrpc/rxgk_common.h
> create mode 100644 net/rxrpc/rxgk_kdf.c
>

2020-11-12 18:40:25

by Chuck Lever III

[permalink] [raw]
Subject: Re: [RFC][PATCH 00/18] crypto: Add generic Kerberos library



> On Nov 12, 2020, at 1:37 PM, J. Bruce Fields <[email protected]> wrote:
>
> On Thu, Nov 12, 2020 at 12:57:45PM +0000, David Howells wrote:
>>
>> Hi Herbert, Bruce,
>>
>> Here's my first cut at a generic Kerberos crypto library in the kernel so
>> that I can share code between rxrpc and sunrpc (and cifs?).
>>
>> I derived some of the parts from the sunrpc gss library and added more
>> advanced AES and Camellia crypto. I haven't ported across the DES-based
>> crypto yet - I figure that can wait a bit till the interface is sorted.
>>
>> Whilst I have put it into a directory under crypto/, I haven't made an
>> interface that goes and loads it (analogous to crypto_alloc_skcipher,
>> say). Instead, you call:
>>
>> const struct krb5_enctype *crypto_krb5_find_enctype(u32 enctype);
>>
>> to go and get a handler table and then use a bunch of accessor functions to
>> jump through the hoops. This is basically the way the sunrpc gsslib does
>> things. It might be worth making it so you do something like:
>>
>> struct crypto_mech *ctx = crypto_mech_alloc("krb5(18)");
>>
>> to get enctype 18, but I'm not sure if it's worth the effort. Also, I'm
>> not sure if there are any alternatives to kerberos we will need to support.
>
> We did have code for a non-krb5 mechanism at some point, but it was torn
> out. So I don't think that's a priority.
>
> (Chuck, will RPC-over-SSL need a new non-krb5 mechanism?)

No, RPC-over-TLS does not involve the GSS infrastructure in any way.


>> There are three main interfaces to it:
>>
>> (*) I/O crypto: encrypt, decrypt, get_mic and verify_mic.
>>
>> These all do in-place crypto, using an sglist to define the buffer
>> with the data in it. Is it necessary to make it able to take separate
>> input and output buffers?
>
> I don't know. My memory is that the buffer management in the existing
> rpcsec_gss code is complex and fragile. See e.g. the long comment in
> gss_krb5_remove_padding.

And even worse, the buffer handling is slightly different in the NFS
client and server code paths.


> --b.
>
>> (*) PRF+ calculation for key derivation.
>> (*) Kc, Ke, Ki derivation.
>>
>> These use krb5_buffer structs to pass objects around. This is akin to
>> the xdr_netobj, but has a void* instead of a u8* data pointer.
>>
>> In terms of rxrpc's rxgk, there's another step in key derivation that isn't
>> part of the kerberos standard, but uses the PRF+ function to generate a key
>> that is then used to generate Kc, Ke and Ki. Is it worth putting this into
>> the directory or maybe having a callout to insert an intermediate step in
>> key derivation?
>>
>> Note that, for purposes of illustration, I've included some rxrpc patches
>> that use this interface to implement the rxgk Rx security class. The
>> branch also is based on some rxrpc patches that are a prerequisite for
>> this, but the crypto patches don't need it.
>>
>> ---
>> The patches can be found here also:
>>
>> http://git.kernel.org/cgit/linux/kernel/git/dhowells/linux-fs.git/log/?h=crypto-krb5
>>
>> David
>> ---
>> David Howells (18):
>> crypto/krb5: Implement Kerberos crypto core
>> crypto/krb5: Add some constants out of sunrpc headers
>> crypto/krb5: Provide infrastructure and key derivation
>> crypto/krb5: Implement the Kerberos5 rfc3961 key derivation
>> crypto/krb5: Implement the Kerberos5 rfc3961 encrypt and decrypt functions
>> crypto/krb5: Implement the Kerberos5 rfc3961 get_mic and verify_mic
>> crypto/krb5: Implement the AES enctypes from rfc3962
>> crypto/krb5: Implement crypto self-testing
>> crypto/krb5: Implement the AES enctypes from rfc8009
>> crypto/krb5: Implement the AES encrypt/decrypt from rfc8009
>> crypto/krb5: Add the AES self-testing data from rfc8009
>> crypto/krb5: Implement the Camellia enctypes from rfc6803
>> rxrpc: Add the security index for yfs-rxgk
>> rxrpc: Add YFS RxGK (GSSAPI) security class
>> rxrpc: rxgk: Provide infrastructure and key derivation
>> rxrpc: rxgk: Implement the yfs-rxgk security class (GSSAPI)
>> rxrpc: rxgk: Implement connection rekeying
>> rxgk: Support OpenAFS's rxgk implementation
>>
>>
>> crypto/krb5/Kconfig | 9 +
>> crypto/krb5/Makefile | 11 +-
>> crypto/krb5/internal.h | 101 +++
>> crypto/krb5/kdf.c | 223 ++++++
>> crypto/krb5/main.c | 190 +++++
>> crypto/krb5/rfc3961_simplified.c | 732 ++++++++++++++++++
>> crypto/krb5/rfc3962_aes.c | 140 ++++
>> crypto/krb5/rfc6803_camellia.c | 249 ++++++
>> crypto/krb5/rfc8009_aes2.c | 440 +++++++++++
>> crypto/krb5/selftest.c | 543 +++++++++++++
>> crypto/krb5/selftest_data.c | 289 +++++++
>> fs/afs/misc.c | 13 +
>> include/crypto/krb5.h | 100 +++
>> include/keys/rxrpc-type.h | 17 +
>> include/trace/events/rxrpc.h | 4 +
>> include/uapi/linux/rxrpc.h | 17 +
>> net/rxrpc/Kconfig | 10 +
>> net/rxrpc/Makefile | 5 +
>> net/rxrpc/ar-internal.h | 20 +
>> net/rxrpc/conn_object.c | 2 +
>> net/rxrpc/key.c | 319 ++++++++
>> net/rxrpc/rxgk.c | 1232 ++++++++++++++++++++++++++++++
>> net/rxrpc/rxgk_app.c | 424 ++++++++++
>> net/rxrpc/rxgk_common.h | 164 ++++
>> net/rxrpc/rxgk_kdf.c | 271 +++++++
>> net/rxrpc/security.c | 6 +
>> 26 files changed, 5530 insertions(+), 1 deletion(-)
>> create mode 100644 crypto/krb5/kdf.c
>> create mode 100644 crypto/krb5/rfc3961_simplified.c
>> create mode 100644 crypto/krb5/rfc3962_aes.c
>> create mode 100644 crypto/krb5/rfc6803_camellia.c
>> create mode 100644 crypto/krb5/rfc8009_aes2.c
>> create mode 100644 crypto/krb5/selftest.c
>> create mode 100644 crypto/krb5/selftest_data.c
>> create mode 100644 net/rxrpc/rxgk.c
>> create mode 100644 net/rxrpc/rxgk_app.c
>> create mode 100644 net/rxrpc/rxgk_common.h
>> create mode 100644 net/rxrpc/rxgk_kdf.c

--
Chuck Lever



2020-11-12 21:08:21

by J. Bruce Fields

[permalink] [raw]
Subject: Re: [RFC][PATCH 00/18] crypto: Add generic Kerberos library

On Thu, Nov 12, 2020 at 04:54:06PM +0000, David Howells wrote:
> Chuck Lever <[email protected]> wrote:
>
> > Really? My understanding of the Linux kernel SUNRPC implementation is
> > that it uses asynchronous, even for small data items. Maybe I'm using
> > the terminology incorrectly.
>
> Seems to be synchronous, at least in its use of skcipher:

Yes, it's all synchronous. The only cases where we defer and revisit a
request is when we need to do upcalls to userspace.

(And those upcalls mostly come after we're done with unwrapping and
verifying a request, so now I'm sort of curious exactly what Chuck was
seeing.)

--b.

2020-11-27 08:49:13

by Herbert Xu

[permalink] [raw]
Subject: Re: [RFC][PATCH 00/18] crypto: Add generic Kerberos library

On Thu, Nov 26, 2020 at 08:19:41AM +0000, David Howells wrote:
>
> I haven't done that yet. Sorry, I should've been more explicit with what I
> was after. I was wanting to find out if the nfs/nfsd people are okay with
> this (and if there are any gotchas I should know about - it turns out, if I
> understand it correctly, the relevant code may being being rewritten a bit
> anyway).
>
> And from you, I was wanting to find out if you're okay with an interface of
> this kind in crypto/ where the code is just used directly - or whether I'll
> be required to wrap it up in the autoloading, module-handling mechanisms.

I don't have any problems with it living under crypto. However,
I'd like to see what the sunrpc code looks like before going one
way or another.

Cheers,
--
Email: Herbert Xu <[email protected]>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

2020-12-01 08:48:44

by Herbert Xu

[permalink] [raw]
Subject: Re: [RFC][PATCH 00/18] crypto: Add generic Kerberos library

On Tue, Dec 01, 2020 at 08:44:33AM +0000, David Howells wrote:
> Btw, would it be feasible to make it so that an extra parameter can be added
> to the cipher buffer-supplying functions, e.g.:
>
> skcipher_request_set_crypt(req, input, ciphertext_sg, esize, iv);
>
> such that we can pass in an offset into the output sg as well?

Couldn't you just change the output sg to include the offset?

Cheers,
--
Email: Herbert Xu <[email protected]>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

2020-12-01 10:40:25

by Herbert Xu

[permalink] [raw]
Subject: Re: [RFC][PATCH 00/18] crypto: Add generic Kerberos library

On Tue, Dec 01, 2020 at 09:12:38AM +0000, David Howells wrote:
>
> That depends on whether the caller has passed it elsewhere for some other
> parallel purpose, but I think I'm going to have to go down that road and
> restore it afterwards.

Sure but even if you added it to the API the underlying
implementataions would just have to do the same thing.

Since this is particular to your use-case it's better to leave
the complexity where it's needed rather than propagting it to
all the crypto drivers.

Cheers,
--
Email: Herbert Xu <[email protected]>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

2020-12-04 15:03:20

by David Howells

[permalink] [raw]
Subject: Why the auxiliary cipher in gss_krb5_crypto.c?

Hi Chuck, Bruce,

Why is gss_krb5_crypto.c using an auxiliary cipher? For reference, the
gss_krb5_aes_encrypt() code looks like the attached.

From what I can tell, in AES mode, the difference between the main cipher and
the auxiliary cipher is that the latter is "cbc(aes)" whereas the former is
"cts(cbc(aes))" - but they have the same key.

Reading up on CTS, I'm guessing the reason it's like this is that CTS is the
same as the non-CTS, except for the last two blocks, but the non-CTS one is
more efficient.

David
---
nbytes = buf->len - offset - GSS_KRB5_TOK_HDR_LEN;
nblocks = (nbytes + blocksize - 1) / blocksize;
cbcbytes = 0;
if (nblocks > 2)
cbcbytes = (nblocks - 2) * blocksize;

memset(desc.iv, 0, sizeof(desc.iv));

if (cbcbytes) {
SYNC_SKCIPHER_REQUEST_ON_STACK(req, aux_cipher);

desc.pos = offset + GSS_KRB5_TOK_HDR_LEN;
desc.fragno = 0;
desc.fraglen = 0;
desc.pages = pages;
desc.outbuf = buf;
desc.req = req;

skcipher_request_set_sync_tfm(req, aux_cipher);
skcipher_request_set_callback(req, 0, NULL, NULL);

sg_init_table(desc.infrags, 4);
sg_init_table(desc.outfrags, 4);

err = xdr_process_buf(buf, offset + GSS_KRB5_TOK_HDR_LEN,
cbcbytes, encryptor, &desc);
skcipher_request_zero(req);
if (err)
goto out_err;
}

/* Make sure IV carries forward from any CBC results. */
err = gss_krb5_cts_crypt(cipher, buf,
offset + GSS_KRB5_TOK_HDR_LEN + cbcbytes,
desc.iv, pages, 1);
if (err) {
err = GSS_S_FAILURE;
goto out_err;
}

2020-12-04 16:18:32

by J. Bruce Fields

[permalink] [raw]
Subject: Re: Why the auxiliary cipher in gss_krb5_crypto.c?

On Fri, Dec 04, 2020 at 10:46:26AM -0500, Bruce Fields wrote:
> On Fri, Dec 04, 2020 at 02:59:35PM +0000, David Howells wrote:
> > Hi Chuck, Bruce,
> >
> > Why is gss_krb5_crypto.c using an auxiliary cipher? For reference, the
> > gss_krb5_aes_encrypt() code looks like the attached.
> >
> > >From what I can tell, in AES mode, the difference between the main cipher and
> > the auxiliary cipher is that the latter is "cbc(aes)" whereas the former is
> > "cts(cbc(aes))" - but they have the same key.
> >
> > Reading up on CTS, I'm guessing the reason it's like this is that CTS is the
> > same as the non-CTS, except for the last two blocks, but the non-CTS one is
> > more efficient.
>
> CTS is cipher-text stealing, isn't it? I think it was Kevin Coffman
> that did that, and I don't remember the history. I thought it was
> required by some spec or peer implementation (maybe Windows?) but I
> really don't remember. It may predate git. I'll dig around and see
> what I can find.

Like I say, I've got no insight here, I'm just grepping through
mailboxes and stuff, but maybe some of this history's useful;

Addition of CTS mode:

https://lore.kernel.org/linux-crypto/[email protected]/

This rpc/krb5 code went in with 934a95aa1c9c "gss_krb5: add remaining
pieces to enable AES encryption support"; may be worth looking at that
and the series leading up to it, I see the changelogs have some RFC
references that might explain why it's using the crypto it is.

--b.

2020-12-04 17:22:14

by David Howells

[permalink] [raw]
Subject: Re: Why the auxiliary cipher in gss_krb5_crypto.c?

Ard Biesheuvel <[email protected]> wrote:

> The tricky thing with CTS is that you have to ensure that the final
> full and partial blocks are presented to the crypto driver as one
> chunk, or it won't be able to perform the ciphertext stealing. This
> might be the reason for the current approach. If the sunrpc code has
> multiple disjoint chunks of data to encrypto, it is always better to
> wrap it in a single scatterlist and call into the skcipher only once.

Yeah - the problem with that is that for sunrpc, we might be dealing with 1MB
plus bits of non-contiguous pages, requiring >8K of scatterlist elements
(admittedly, we can chain them, but we may have to do one or more large
allocations).

> However, I would recommend against it:

Sorry, recommend against what?

> at least for ARM and arm64, I
> have already contributed SIMD based implementations that use SIMD
> permutation instructions and overlapping loads and stores to perform
> the ciphertext stealing, which means that there is only a single layer
> which implements CTS+CBC+AES, and this layer can consume the entire
> scatterlist in one go. We could easily do something similar in the
> AES-NI driver as well.

Can you point me at that in the sources?

Can you also do SHA at the same time in the same loop?

Note that the rfc3962 AES does the checksum over the plaintext, but rfc8009
does it over the ciphertext.

David

2020-12-04 18:14:58

by Theodore Ts'o

[permalink] [raw]
Subject: Re: Why the auxiliary cipher in gss_krb5_crypto.c?

On Fri, Dec 04, 2020 at 02:59:35PM +0000, David Howells wrote:
> Hi Chuck, Bruce,
>
> Why is gss_krb5_crypto.c using an auxiliary cipher? For reference, the
> gss_krb5_aes_encrypt() code looks like the attached.
>
> From what I can tell, in AES mode, the difference between the main cipher and
> the auxiliary cipher is that the latter is "cbc(aes)" whereas the former is
> "cts(cbc(aes))" - but they have the same key.
>
> Reading up on CTS, I'm guessing the reason it's like this is that CTS is the
> same as the non-CTS, except for the last two blocks, but the non-CTS one is
> more efficient.

The reason to use CTS is if you don't want to expand the size of the
cipher text to the cipher block size. e.g., if you have a 53 byte
plaintext, and you can't afford to let the ciphertext be 56 bytes, the
cryptographic engineer will reach for CTS instead of CBC.

So that probably explains the explanation to use CTS (and it's
required by the spec in any case). As far as why CBC is being used
instead of CTS, the only reason I can think of is the one you posted.
Perhaps there was some hardware or software configureation where
cbc(aes) was hardware accelerated, and cts(cbc(aes)) would not be?

In any case, using cbc(aes) for all but the last two blocks, and using
cts(cbc(aes)) for the last two blocks, is identical to using
cts(cbc(aes)) for the whole encryption. So the only reason to do this
in the more complex way would be because for performance reasons.

- Ted

2020-12-04 21:11:21

by Herbert Xu

[permalink] [raw]
Subject: Re: Why the auxiliary cipher in gss_krb5_crypto.c?

On Fri, Dec 04, 2020 at 06:35:48PM +0100, Ard Biesheuvel wrote:
>
> Herbert recently made some changes for MSG_MORE support in the AF_ALG
> code, which permits a skcipher encryption to be split into several
> invocations of the skcipher layer without the need for this complexity
> on the side of the caller. Maybe there is a way to reuse that here.
> Herbert?

Yes this was one of the reasons I was persuing the continuation
work. It should allow us to kill the special case for CTS in the
krb5 code.

Hopefully I can get some time to restart work on this soon.

Cheers,
--
Email: Herbert Xu <[email protected]>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

2020-12-07 13:10:50

by Ard Biesheuvel

[permalink] [raw]
Subject: Re: Why the auxiliary cipher in gss_krb5_crypto.c?

On Mon, 7 Dec 2020 at 13:02, David Howells <[email protected]> wrote:
>
> Ard Biesheuvel <[email protected]> wrote:
>
> > > Yeah - the problem with that is that for sunrpc, we might be dealing with 1MB
> > > plus bits of non-contiguous pages, requiring >8K of scatterlist elements
> > > (admittedly, we can chain them, but we may have to do one or more large
> > > allocations).
> > >
> > > > However, I would recommend against it:
> > >
> > > Sorry, recommend against what?
> > >
> >
> > Recommend against the current approach of manipulating the input like
> > this and feeding it into the skcipher piecemeal.
>
> Right. I understand the problem, but as I mentioned above, the scatterlist
> itself becomes a performance issue as it may exceed two pages in size. Double
> that as there may need to be separate input and output scatterlists.
>

I wasn't aware that Herbert's work hadn't been merged yet. So that
means it is entirely reasonable to split the input like this and feed
the first part into a cbc(aes) skcipher and the last part into a
cts(cbc(aes)) skcipher, provided that you ensure that the last part
covers the final two blocks (one full block and one block that is
either full or partial)

With Herbert's changes, you will be able to use the same skcipher, and
pass a flag to all but the final part that more data is coming. But
for lack of that, the current approach is optimal for cases where
having to cover the entire input with a single scatterlist is
undesirable.

> > Herbert recently made some changes for MSG_MORE support in the AF_ALG
> > code, which permits a skcipher encryption to be split into several
> > invocations of the skcipher layer without the need for this complexity
> > on the side of the caller. Maybe there is a way to reuse that here.
> > Herbert?
>
> I wonder if it would help if the input buffer and output buffer didn't have to
> correspond exactly in usage - ie. the output buffer could be used at a slower
> rate than the input to allow for buffering inside the crypto algorithm.
>

I don't follow - how could one be used at a slower rate?

> > > Can you also do SHA at the same time in the same loop?
> >
> > SHA-1 or HMAC-SHA1? The latter could probably be modeled as an AEAD.
> > The former doesn't really fit the current API so we'd have to invent
> > something for it.
>
> The hashes corresponding to the kerberos enctypes I'm supporting are:
>
> HMAC-SHA1 for aes128-cts-hmac-sha1-96 and aes256-cts-hmac-sha1-96.
>
> HMAC-SHA256 for aes128-cts-hmac-sha256-128
>
> HMAC-SHA384 for aes256-cts-hmac-sha384-192
>
> CMAC-CAMELLIA for camellia128-cts-cmac and camellia256-cts-cmac
>
> I'm not sure you can support all of those with the instructions available.
>

It depends on whether the caller can make use of the authenc()
pattern, which is a type of AEAD we support. There are numerous
implementations of authenc(hmac(shaXXX),cbc(aes)), including h/w
accelerated ones, but none that implement ciphertext stealing. So that
means that, even if you manage to use the AEAD layer to perform both
at the same time, the generic authenc() template will perform the
cts(cbc(aes)) and hmac(shaXXX) by calling into skciphers and ahashes,
respectively, which won't give you any benefit until accelerated
implementations turn up that perform the whole operation in one pass
over the input. And even then, I don't think the performance benefit
will be worth it.

2020-12-07 14:17:11

by David Howells

[permalink] [raw]
Subject: Re: Why the auxiliary cipher in gss_krb5_crypto.c?

Ard Biesheuvel <[email protected]> wrote:

> > I wonder if it would help if the input buffer and output buffer didn't
> > have to correspond exactly in usage - ie. the output buffer could be used
> > at a slower rate than the input to allow for buffering inside the crypto
> > algorithm.
> >
>
> I don't follow - how could one be used at a slower rate?

I mean that the crypto algorithm might need to buffer the last part of the
input until it has a block's worth before it can write to the output.

> > The hashes corresponding to the kerberos enctypes I'm supporting are:
> >
> > HMAC-SHA1 for aes128-cts-hmac-sha1-96 and aes256-cts-hmac-sha1-96.
> >
> > HMAC-SHA256 for aes128-cts-hmac-sha256-128
> >
> > HMAC-SHA384 for aes256-cts-hmac-sha384-192
> >
> > CMAC-CAMELLIA for camellia128-cts-cmac and camellia256-cts-cmac
> >
> > I'm not sure you can support all of those with the instructions available.
>
> It depends on whether the caller can make use of the authenc()
> pattern, which is a type of AEAD we support.

Interesting. I didn't realise AEAD was an API.

> There are numerous implementations of authenc(hmac(shaXXX),cbc(aes)),
> including h/w accelerated ones, but none that implement ciphertext
> stealing. So that means that, even if you manage to use the AEAD layer to
> perform both at the same time, the generic authenc() template will perform
> the cts(cbc(aes)) and hmac(shaXXX) by calling into skciphers and ahashes,
> respectively, which won't give you any benefit until accelerated
> implementations turn up that perform the whole operation in one pass over
> the input. And even then, I don't think the performance benefit will be
> worth it.

Also, the rfc8009 variants that use AES with SHA256/384 hash the ciphertext,
not the plaintext.

For the moment, it's probably not worth worrying about, then. If I can manage
to abstract the sunrpc bits out into a krb5 library, we can improve the
library later.

David

2020-12-08 13:29:08

by David Howells

[permalink] [raw]
Subject: Re: Why the auxiliary cipher in gss_krb5_crypto.c?

I wonder - would it make sense to reserve two arrays of scatterlist structs
and a mutex per CPU sufficient to map up to 1MiB of pages with each array
while the krb5 service is in use?

That way sunrpc could, say, grab the mutex, map the input and output buffers,
do the entire crypto op in one go and then release the mutex - at least for
big ops, small ops needn't use this service.

For rxrpc/afs's use case this would probably be overkill - it's doing crypto
on each packet, not on whole operations - but I could still make use of it
there.

However, that then limits the maximum size of an op to 1MiB, plus dangly bits
on either side (which can be managed with chained scatterlist structs) and
also limits the number of large simultaneous krb5 crypto ops we can do.

David

2020-12-08 14:05:39

by Ard Biesheuvel

[permalink] [raw]
Subject: Re: Why the auxiliary cipher in gss_krb5_crypto.c?

On Tue, 8 Dec 2020 at 14:25, David Howells <[email protected]> wrote:
>
> I wonder - would it make sense to reserve two arrays of scatterlist structs
> and a mutex per CPU sufficient to map up to 1MiB of pages with each array
> while the krb5 service is in use?
>
> That way sunrpc could, say, grab the mutex, map the input and output buffers,
> do the entire crypto op in one go and then release the mutex - at least for
> big ops, small ops needn't use this service.
>
> For rxrpc/afs's use case this would probably be overkill - it's doing crypto
> on each packet, not on whole operations - but I could still make use of it
> there.
>
> However, that then limits the maximum size of an op to 1MiB, plus dangly bits
> on either side (which can be managed with chained scatterlist structs) and
> also limits the number of large simultaneous krb5 crypto ops we can do.
>

Apparently, it is permitted for gss_krb5_cts_crypt() to do a
kmalloc(GFP_NOFS) in the context from where gss_krb5_aes_encrypt() is
being invoked, and so I don't see why it wouldn't be possible to
simply kmalloc() a scatterlist[] of the appropriate size, populate it
with all the pages, bufs and whatever else gets passed into the
skcipher, and pass it into the skcipher in one go.

2020-12-08 14:17:49

by David Howells

[permalink] [raw]
Subject: Re: Why the auxiliary cipher in gss_krb5_crypto.c?

Ard Biesheuvel <[email protected]> wrote:

> Apparently, it is permitted for gss_krb5_cts_crypt() to do a
> kmalloc(GFP_NOFS) in the context from where gss_krb5_aes_encrypt() is
> being invoked, and so I don't see why it wouldn't be possible to
> simply kmalloc() a scatterlist[] of the appropriate size, populate it
> with all the pages, bufs and whatever else gets passed into the
> skcipher, and pass it into the skcipher in one go.

I never said it wasn't possible. But doing a pair of order-1 allocations from
there might have a significant detrimental effect on performance - in which
case Trond and co. will say "no".

Remember: to crypt 1MiB of data on a 64-bit machine requires 2 x minimum 8KiB
scatterlist arrays. That's assuming the pages in the middle are contiguous,
which might not be the case for a direct I/O read/write. So for the DIO case,
it could be involve an order-2 allocation (or chaining of single pages).

David