crypto: Add Kerberos crypto lib

-----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEEqG5UsNXhtOCrfGQP+7dXa6fLC2sFAmfFa1UACgkQ+7dXa6fL
 C2ucgA//YRhCKDCQXe9Sf8l7PY8gDS15oLKbldwCHdKl2QZRasIVvWO+/67nXWi0
 jarNXOxHXSI9gPf5V/W28Ya8l3ANNCNRksd/78OqHfblKGHiMmWk+Lvs8E0upTnD
 C2zjorbMoJBcMTmDQF602ENlSJdI/Qn+zqSZlulhI+LFwCzU5xPzg9LUfCNu6MzJ
 b9RKEYd2OC9Nst/pcIRZjz7M/TsOy8Fj457QOYjKXmNh6zTP8E5QMOyb4MThOacz
 uu4K7JWpxJaQHv9BpYCLCCl1Mfjq8xUDxLwh3uMXbh2RUi4ZjCjnAnaZNF71kj/C
 sRL6wm1U0QwPfaG9WZ0r2qytgtEGru5+roTdPF1ieXnZzLAy7fNU/fNXpaoHpRuI
 g3cpxzYHD+lQg97uIFDi79uYm5t8NLRBxqh4ORXLobB2Yx4XMmpdG8sZvgRY/RIs
 y3i6XAL3Te04sDywknNB+qb9Pc5AKdYLv4g7hWBvAqYq6fkZW7QBDNR3IobnnvXi
 88w1wHQIrpr3o23be4v5GtrZYENlaQpDSa8oHcKd4yN5xs6Mj/6oGOzbII9aQWaM
 +wgPkX6M02mJgoi2msL6a6WBnDBo5Jnq+OTY8VIw0eeMUMytHBkr46/3mY/cfbyl
 yXgWrZxI9VsbD7JBjP8OkZreBTyYK1XjXrMzhHhvJozl2YJSWmw=
 =7+o7
 -----END PGP SIGNATURE-----

Merge tag 'crypto-krb5-20250303' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs.git

Pull Kerberos crypto library from David Howells:

crypto: Add Kerberos crypto lib

It does a couple of things:

 (1) Provide an AEAD crypto driver, krb5enc, that mirrors the authenc
     driver, but that hashes the plaintext, not the ciphertext.  This was
     made a separate module rather than just being a part of the authenc
     driver because it has to do all of the constituent operations in the
     opposite order - which impacts the async op handling.

     Testmgr data is provided for AES+SHA2 and Camellia combinations of
     authenc and krb5enc used by the krb5 library.  AES+SHA1 is not
     provided as the RFCs don't contain usable test vectors.

 (2) Provide a Kerberos 5 crypto library.  This is an extract from the
     sunrpc driver as that code can be shared between sunrpc/nfs and
     rxrpc/afs.  This provides encryption, decryption, get MIC and verify
     MIC routines that use and wrap the crypto functions, along with some
     functions to provide layout management.

     This supports AES+SHA1, AES+SHA2 and Camellia encryption types.

     Self-testing is provided that goes further than is possible with
     testmgr, doing subkey derivation as well.

The patches were previously posted here:

    https://lore.kernel.org/r/20250203142343.248839-1-dhowells@redhat.com/

as part of a larger series, but the networking guys would prefer these to
go through the crypto tree.  If you want them reposting independently, I
can do that.
This commit is contained in:
Herbert Xu 2025-03-08 15:03:20 +08:00
commit d4880fe6fd
20 changed files with 4546 additions and 0 deletions

View File

@ -26,3 +26,4 @@ for cryptographic use cases, as well as programming examples.
api-samples
descore-readme
device_drivers/index
krb5

View File

@ -0,0 +1,262 @@
.. SPDX-License-Identifier: GPL-2.0
===========================
Kerberos V Cryptography API
===========================
.. Contents:
- Overview.
- Small Buffer.
- Encoding Type.
- Key Derivation.
- PRF+ Calculation.
- Kc, Ke And Ki Derivation.
- Crypto Functions.
- Preparation Functions.
- Encryption Mode.
- Checksum Mode.
- The krb5enc AEAD algorithm
Overview
========
This API provides Kerberos 5-style cryptography for key derivation, encryption
and checksumming for use in network filesystems and can be used to implement
the low-level crypto that's needed for GSSAPI.
The following crypto types are supported::
KRB5_ENCTYPE_AES128_CTS_HMAC_SHA1_96
KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96
KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128
KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192
KRB5_ENCTYPE_CAMELLIA128_CTS_CMAC
KRB5_ENCTYPE_CAMELLIA256_CTS_CMAC
KRB5_CKSUMTYPE_HMAC_SHA1_96_AES128
KRB5_CKSUMTYPE_HMAC_SHA1_96_AES256
KRB5_CKSUMTYPE_CMAC_CAMELLIA128
KRB5_CKSUMTYPE_CMAC_CAMELLIA256
KRB5_CKSUMTYPE_HMAC_SHA256_128_AES128
KRB5_CKSUMTYPE_HMAC_SHA384_192_AES256
The API can be included by::
#include <crypto/krb5.h>
Small Buffer
------------
To pass small pieces of data about, such as keys, a buffer structure is
defined, giving a pointer to the data and the size of that data::
struct krb5_buffer {
unsigned int len;
void *data;
};
Encoding Type
=============
The encoding type is defined by the following structure::
struct krb5_enctype {
int etype;
int ctype;
const char *name;
u16 key_bytes;
u16 key_len;
u16 Kc_len;
u16 Ke_len;
u16 Ki_len;
u16 prf_len;
u16 block_len;
u16 conf_len;
u16 cksum_len;
...
};
The fields of interest to the user of the API are as follows:
* ``etype`` and ``ctype`` indicate the protocol number for this encoding
type for encryption and checksumming respectively. They hold
``KRB5_ENCTYPE_*`` and ``KRB5_CKSUMTYPE_*`` constants.
* ``name`` is the formal name of the encoding.
* ``key_len`` and ``key_bytes`` are the input key length and the derived key
length. (I think they only differ for DES, which isn't supported here).
* ``Kc_len``, ``Ke_len`` and ``Ki_len`` are the sizes of the derived Kc, Ke
and Ki keys. Kc is used for in checksum mode; Ke and Ki are used in
encryption mode.
* ``prf_len`` is the size of the result from the PRF+ function calculation.
* ``block_len``, ``conf_len`` and ``cksum_len`` are the encryption block
length, confounder length and checksum length respectively. All three are
used in encryption mode, but only the checksum length is used in checksum
mode.
The encoding type is looked up by number using the following function::
const struct krb5_enctype *crypto_krb5_find_enctype(u32 enctype);
Key Derivation
==============
Once the application has selected an encryption type, the keys that will be
used to do the actual crypto can be derived from the transport key.
PRF+ Calculation
----------------
To aid in key derivation, a function to calculate the Kerberos GSSAPI
mechanism's PRF+ is provided::
int crypto_krb5_calc_PRFplus(const struct krb5_enctype *krb5,
const struct krb5_buffer *K,
unsigned int L,
const struct krb5_buffer *S,
struct krb5_buffer *result,
gfp_t gfp);
This can be used to derive the transport key from a source key plus additional
data to limit its use.
Crypto Functions
================
Once the keys have been derived, crypto can be performed on the data. The
caller must leave gaps in the buffer for the storage of the confounder (if
needed) and the checksum when preparing a message for transmission. An enum
and a pair of functions are provided to aid in this::
enum krb5_crypto_mode {
KRB5_CHECKSUM_MODE,
KRB5_ENCRYPT_MODE,
};
size_t crypto_krb5_how_much_buffer(const struct krb5_enctype *krb5,
enum krb5_crypto_mode mode,
size_t data_size, size_t *_offset);
size_t crypto_krb5_how_much_data(const struct krb5_enctype *krb5,
enum krb5_crypto_mode mode,
size_t *_buffer_size, size_t *_offset);
All these functions take the encoding type and an indication the mode of crypto
(checksum-only or full encryption).
The first function returns how big the buffer will need to be to house a given
amount of data; the second function returns how much data will fit in a buffer
of a particular size, and adjusts down the size of the required buffer
accordingly. In both cases, the offset of the data within the buffer is also
returned.
When a message has been received, the location and size of the data with the
message can be determined by calling::
void crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5,
enum krb5_crypto_mode mode,
size_t *_offset, size_t *_len);
The caller provides the offset and length of the message to the function, which
then alters those values to indicate the region containing the data (plus any
padding). It is up to the caller to determine how much padding there is.
Preparation Functions
---------------------
Two functions are provided to allocated and prepare a crypto object for use by
the action functions::
struct crypto_aead *
crypto_krb5_prepare_encryption(const struct krb5_enctype *krb5,
const struct krb5_buffer *TK,
u32 usage, gfp_t gfp);
struct crypto_shash *
crypto_krb5_prepare_checksum(const struct krb5_enctype *krb5,
const struct krb5_buffer *TK,
u32 usage, gfp_t gfp);
Both of these functions take the encoding type, the transport key and the usage
value used to derive the appropriate subkey(s). They create an appropriate
crypto object, an AEAD template for encryption and a synchronous hash for
checksumming, set the key(s) on it and configure it. The caller is expected to
pass these handles to the action functions below.
Encryption Mode
---------------
A pair of functions are provided to encrypt and decrypt a message::
ssize_t crypto_krb5_encrypt(const struct krb5_enctype *krb5,
struct crypto_aead *aead,
struct scatterlist *sg, unsigned int nr_sg,
size_t sg_len,
size_t data_offset, size_t data_len,
bool preconfounded);
int crypto_krb5_decrypt(const struct krb5_enctype *krb5,
struct crypto_aead *aead,
struct scatterlist *sg, unsigned int nr_sg,
size_t *_offset, size_t *_len);
In both cases, the input and output buffers are indicated by the same
scatterlist.
For the encryption function, the output buffer may be larger than is needed
(the amount of output generated is returned) and the location and size of the
data are indicated (which must match the encoding). If no confounder is set,
the function will insert one.
For the decryption function, the offset and length of the message in buffer are
supplied and these are shrunk to fit the data. The decryption function will
verify any checksums within the message and give an error if they don't match.
Checksum Mode
-------------
A pair of function are provided to generate the checksum on a message and to
verify that checksum::
ssize_t crypto_krb5_get_mic(const struct krb5_enctype *krb5,
struct crypto_shash *shash,
const struct krb5_buffer *metadata,
struct scatterlist *sg, unsigned int nr_sg,
size_t sg_len,
size_t data_offset, size_t data_len);
int crypto_krb5_verify_mic(const struct krb5_enctype *krb5,
struct crypto_shash *shash,
const struct krb5_buffer *metadata,
struct scatterlist *sg, unsigned int nr_sg,
size_t *_offset, size_t *_len);
In both cases, the input and output buffers are indicated by the same
scatterlist. Additional metadata can be passed in which will get added to the
hash before the data.
For the get_mic function, the output buffer may be larger than is needed (the
amount of output generated is returned) and the location and size of the data
are indicated (which must match the encoding).
For the verification function, the offset and length of the message in buffer
are supplied and these are shrunk to fit the data. An error will be returned
if the checksums don't match.
The krb5enc AEAD algorithm
==========================
A template AEAD crypto algorithm, called "krb5enc", is provided that hashes the
plaintext before encrypting it (the reverse of authenc). The handle returned
by ``crypto_krb5_prepare_encryption()`` may be one of these, but there's no
requirement for the user of this API to interact with it directly.
For reference, its key format begins with a BE32 of the format number. Only
format 1 is provided and that continues with a BE32 of the Ke key length
followed by a BE32 of the Ki key length, followed by the bytes from the Ke key
and then the Ki key.
Using specifically ordered words means that the static test data doesn't
require byteswapping.

View File

@ -228,6 +228,18 @@ config CRYPTO_AUTHENC
This is required for IPSec ESP (XFRM_ESP).
config CRYPTO_KRB5ENC
tristate "Kerberos 5 combined hash+cipher support"
select CRYPTO_AEAD
select CRYPTO_SKCIPHER
select CRYPTO_MANAGER
select CRYPTO_HASH
select CRYPTO_NULL
help
Combined hash and cipher support for Kerberos 5 RFC3961 simplified
profile. This is required for Kerberos 5-style encryption, used by
sunrpc/NFS and rxrpc/AFS.
config CRYPTO_TEST
tristate "Testing module"
depends on m || EXPERT
@ -1460,5 +1472,6 @@ endif
source "drivers/crypto/Kconfig"
source "crypto/asymmetric_keys/Kconfig"
source "certs/Kconfig"
source "crypto/krb5/Kconfig"
endif # if CRYPTO

View File

@ -159,6 +159,7 @@ obj-$(CONFIG_CRYPTO_CRCT10DIF) += crct10dif_generic.o
CFLAGS_crct10dif_generic.o += -DARCH=$(ARCH)
obj-$(CONFIG_CRYPTO_CRC64_ROCKSOFT) += crc64_rocksoft_generic.o
obj-$(CONFIG_CRYPTO_AUTHENC) += authenc.o authencesn.o
obj-$(CONFIG_CRYPTO_KRB5ENC) += krb5enc.o
obj-$(CONFIG_CRYPTO_LZO) += lzo.o lzo-rle.o
obj-$(CONFIG_CRYPTO_LZ4) += lz4.o
obj-$(CONFIG_CRYPTO_LZ4HC) += lz4hc.o
@ -212,3 +213,5 @@ obj-$(CONFIG_CRYPTO_SIMD) += crypto_simd.o
# Key derivation function
#
obj-$(CONFIG_CRYPTO_KDF800108_CTR) += kdf_sp800108.o
obj-$(CONFIG_CRYPTO_KRB5) += krb5/

26
crypto/krb5/Kconfig Normal file
View File

@ -0,0 +1,26 @@
config CRYPTO_KRB5
tristate "Kerberos 5 crypto"
select CRYPTO_MANAGER
select CRYPTO_KRB5ENC
select CRYPTO_AUTHENC
select CRYPTO_SKCIPHER
select CRYPTO_HASH_INFO
select CRYPTO_HMAC
select CRYPTO_CMAC
select CRYPTO_SHA1
select CRYPTO_SHA256
select CRYPTO_SHA512
select CRYPTO_CBC
select CRYPTO_CTS
select CRYPTO_AES
select CRYPTO_CAMELLIA
help
Provide a library for provision of Kerberos-5-based crypto. This is
intended for network filesystems to use.
config CRYPTO_KRB5_SELFTESTS
bool "Kerberos 5 crypto selftests"
depends on CRYPTO_KRB5
help
Turn on some self-testing for the kerberos 5 crypto functions. These
will be performed on module load or boot, if compiled in.

18
crypto/krb5/Makefile Normal file
View File

@ -0,0 +1,18 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for asymmetric cryptographic keys
#
krb5-y += \
krb5_kdf.o \
krb5_api.o \
rfc3961_simplified.o \
rfc3962_aes.o \
rfc6803_camellia.o \
rfc8009_aes2.o
krb5-$(CONFIG_CRYPTO_KRB5_SELFTESTS) += \
selftest.o \
selftest_data.o
obj-$(CONFIG_CRYPTO_KRB5) += krb5.o

247
crypto/krb5/internal.h Normal file
View File

@ -0,0 +1,247 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/* Kerberos5 crypto internals
*
* Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*/
#include <linux/scatterlist.h>
#include <crypto/krb5.h>
#include <crypto/hash.h>
#include <crypto/skcipher.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);
/* Derive the keys needed for an encryption AEAD object. */
int (*derive_encrypt_keys)(const struct krb5_enctype *krb5,
const struct krb5_buffer *TK,
unsigned int usage,
struct krb5_buffer *setkey,
gfp_t gfp);
/* Directly load the keys needed for an encryption AEAD object. */
int (*load_encrypt_keys)(const struct krb5_enctype *krb5,
const struct krb5_buffer *Ke,
const struct krb5_buffer *Ki,
struct krb5_buffer *setkey,
gfp_t gfp);
/* Derive the key needed for a checksum hash object. */
int (*derive_checksum_key)(const struct krb5_enctype *krb5,
const struct krb5_buffer *TK,
unsigned int usage,
struct krb5_buffer *setkey,
gfp_t gfp);
/* Directly load the keys needed for a checksum hash object. */
int (*load_checksum_key)(const struct krb5_enctype *krb5,
const struct krb5_buffer *Kc,
struct krb5_buffer *setkey,
gfp_t gfp);
/* Encrypt data in-place, inserting confounder and checksum. */
ssize_t (*encrypt)(const struct krb5_enctype *krb5,
struct crypto_aead *aead,
struct scatterlist *sg, unsigned int 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 crypto_aead *aead,
struct scatterlist *sg, unsigned int nr_sg,
size_t *_offset, size_t *_len);
/* 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 int 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 int nr_sg,
size_t *_offset, size_t *_len);
};
/*
* Crypto size/alignment rounding convenience macros.
*/
#define crypto_roundup(X) ((unsigned int)round_up((X), CRYPTO_MINALIGN))
#define krb5_aead_size(TFM) \
crypto_roundup(sizeof(struct aead_request) + crypto_aead_reqsize(TFM))
#define krb5_aead_ivsize(TFM) \
crypto_roundup(crypto_aead_ivsize(TFM))
#define krb5_shash_size(TFM) \
crypto_roundup(sizeof(struct shash_desc) + crypto_shash_descsize(TFM))
#define krb5_digest_size(TFM) \
crypto_roundup(crypto_shash_digestsize(TFM))
#define round16(x) (((x) + 15) & ~15)
/*
* Self-testing data.
*/
struct krb5_prf_test {
u32 etype;
const char *name, *key, *octet, *prf;
};
struct krb5_key_test_one {
u32 use;
const char *key;
};
struct krb5_key_test {
u32 etype;
const char *name, *key;
struct krb5_key_test_one Kc, Ke, Ki;
};
struct krb5_enc_test {
u32 etype;
u32 usage;
const char *name, *plain, *conf, *K0, *Ke, *Ki, *ct;
};
struct krb5_mic_test {
u32 etype;
u32 usage;
const char *name, *plain, *K0, *Kc, *mic;
};
/*
* krb5_api.c
*/
struct crypto_aead *krb5_prepare_encryption(const struct krb5_enctype *krb5,
const struct krb5_buffer *keys,
gfp_t gfp);
struct crypto_shash *krb5_prepare_checksum(const struct krb5_enctype *krb5,
const struct krb5_buffer *Kc,
gfp_t gfp);
/*
* krb5_kdf.c
*/
int krb5_derive_Kc(const struct krb5_enctype *krb5, const struct krb5_buffer *TK,
u32 usage, struct krb5_buffer *key, gfp_t gfp);
int krb5_derive_Ke(const struct krb5_enctype *krb5, const struct krb5_buffer *TK,
u32 usage, struct krb5_buffer *key, gfp_t gfp);
int krb5_derive_Ki(const struct krb5_enctype *krb5, const struct krb5_buffer *TK,
u32 usage, struct krb5_buffer *key, gfp_t gfp);
/*
* rfc3961_simplified.c
*/
extern const struct krb5_crypto_profile rfc3961_simplified_profile;
int crypto_shash_update_sg(struct shash_desc *desc, struct scatterlist *sg,
size_t offset, size_t len);
int authenc_derive_encrypt_keys(const struct krb5_enctype *krb5,
const struct krb5_buffer *TK,
unsigned int usage,
struct krb5_buffer *setkey,
gfp_t gfp);
int authenc_load_encrypt_keys(const struct krb5_enctype *krb5,
const struct krb5_buffer *Ke,
const struct krb5_buffer *Ki,
struct krb5_buffer *setkey,
gfp_t gfp);
int rfc3961_derive_checksum_key(const struct krb5_enctype *krb5,
const struct krb5_buffer *TK,
unsigned int usage,
struct krb5_buffer *setkey,
gfp_t gfp);
int rfc3961_load_checksum_key(const struct krb5_enctype *krb5,
const struct krb5_buffer *Kc,
struct krb5_buffer *setkey,
gfp_t gfp);
ssize_t krb5_aead_encrypt(const struct krb5_enctype *krb5,
struct crypto_aead *aead,
struct scatterlist *sg, unsigned int nr_sg, size_t sg_len,
size_t data_offset, size_t data_len,
bool preconfounded);
int krb5_aead_decrypt(const struct krb5_enctype *krb5,
struct crypto_aead *aead,
struct scatterlist *sg, unsigned int nr_sg,
size_t *_offset, size_t *_len);
ssize_t rfc3961_get_mic(const struct krb5_enctype *krb5,
struct crypto_shash *shash,
const struct krb5_buffer *metadata,
struct scatterlist *sg, unsigned int nr_sg, size_t sg_len,
size_t data_offset, size_t data_len);
int rfc3961_verify_mic(const struct krb5_enctype *krb5,
struct crypto_shash *shash,
const struct krb5_buffer *metadata,
struct scatterlist *sg, unsigned int nr_sg,
size_t *_offset, size_t *_len);
/*
* 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;
/*
* rfc6803_camellia.c
*/
extern const struct krb5_enctype krb5_camellia128_cts_cmac;
extern const struct krb5_enctype krb5_camellia256_cts_cmac;
/*
* 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
*/
#ifdef CONFIG_CRYPTO_KRB5_SELFTESTS
int krb5_selftest(void);
#else
static inline int krb5_selftest(void) { return 0; }
#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[];

452
crypto/krb5/krb5_api.c Normal file
View File

@ -0,0 +1,452 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/* Kerberos 5 crypto library.
*
* Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*/
#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[] = {
&krb5_aes128_cts_hmac_sha1_96,
&krb5_aes256_cts_hmac_sha1_96,
&krb5_aes128_cts_hmac_sha256_128,
&krb5_aes256_cts_hmac_sha384_192,
&krb5_camellia128_cts_cmac,
&krb5_camellia256_cts_cmac,
};
/**
* 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);
/**
* crypto_krb5_how_much_buffer - Work out how much buffer is required for an amount of data
* @krb5: The encoding to use.
* @mode: The mode in which to operated (checksum/encrypt)
* @data_size: How much data we want to allow for
* @_offset: Where to place the offset into the buffer
*
* Calculate how much buffer space is required to wrap a given amount of data.
* This allows for a confounder, padding and checksum as appropriate. The
* amount of buffer required is returned and the offset into the buffer at
* which the data will start is placed in *_offset.
*/
size_t crypto_krb5_how_much_buffer(const struct krb5_enctype *krb5,
enum krb5_crypto_mode mode,
size_t data_size, size_t *_offset)
{
switch (mode) {
case KRB5_CHECKSUM_MODE:
*_offset = krb5->cksum_len;
return krb5->cksum_len + data_size;
case KRB5_ENCRYPT_MODE:
*_offset = krb5->conf_len;
return krb5->conf_len + data_size + krb5->cksum_len;
default:
WARN_ON(1);
*_offset = 0;
return 0;
}
}
EXPORT_SYMBOL(crypto_krb5_how_much_buffer);
/**
* crypto_krb5_how_much_data - Work out how much data can fit in an amount of buffer
* @krb5: The encoding to use.
* @mode: The mode in which to operated (checksum/encrypt)
* @_buffer_size: How much buffer we want to allow for (may be reduced)
* @_offset: Where to place the offset into the buffer
*
* Calculate how much data can be fitted into given amount of buffer. This
* allows for a confounder, padding and checksum as appropriate. The amount of
* data that will fit is returned, the amount of buffer required is shrunk to
* allow for alignment and the offset into the buffer at which the data will
* start is placed in *_offset.
*/
size_t crypto_krb5_how_much_data(const struct krb5_enctype *krb5,
enum krb5_crypto_mode mode,
size_t *_buffer_size, size_t *_offset)
{
size_t buffer_size = *_buffer_size, data_size;
switch (mode) {
case KRB5_CHECKSUM_MODE:
if (WARN_ON(buffer_size < krb5->cksum_len + 1))
goto bad;
*_offset = krb5->cksum_len;
return buffer_size - krb5->cksum_len;
case KRB5_ENCRYPT_MODE:
if (WARN_ON(buffer_size < krb5->conf_len + 1 + krb5->cksum_len))
goto bad;
data_size = buffer_size - krb5->cksum_len;
*_offset = krb5->conf_len;
return data_size - krb5->conf_len;
default:
WARN_ON(1);
goto bad;
}
bad:
*_offset = 0;
return 0;
}
EXPORT_SYMBOL(crypto_krb5_how_much_data);
/**
* crypto_krb5_where_is_the_data - Find the data in a decrypted message
* @krb5: The encoding to use.
* @mode: Mode of operation
* @_offset: Offset of the secure blob in the buffer; updated to data offset.
* @_len: The length of the secure blob; updated to data length.
*
* Find the offset and size of the data in a secure message so that this
* information can be used in the metadata buffer which will get added to the
* digest by crypto_krb5_verify_mic().
*/
void crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5,
enum krb5_crypto_mode mode,
size_t *_offset, size_t *_len)
{
switch (mode) {
case KRB5_CHECKSUM_MODE:
*_offset += krb5->cksum_len;
*_len -= krb5->cksum_len;
return;
case KRB5_ENCRYPT_MODE:
*_offset += krb5->conf_len;
*_len -= krb5->conf_len + krb5->cksum_len;
return;
default:
WARN_ON_ONCE(1);
return;
}
}
EXPORT_SYMBOL(crypto_krb5_where_is_the_data);
/*
* Prepare the encryption with derived key data.
*/
struct crypto_aead *krb5_prepare_encryption(const struct krb5_enctype *krb5,
const struct krb5_buffer *keys,
gfp_t gfp)
{
struct crypto_aead *ci = NULL;
int ret = -ENOMEM;
ci = crypto_alloc_aead(krb5->encrypt_name, 0, 0);
if (IS_ERR(ci)) {
ret = PTR_ERR(ci);
if (ret == -ENOENT)
ret = -ENOPKG;
goto err;
}
ret = crypto_aead_setkey(ci, keys->data, keys->len);
if (ret < 0) {
pr_err("Couldn't set AEAD key %s: %d\n", krb5->encrypt_name, ret);
goto err_ci;
}
ret = crypto_aead_setauthsize(ci, krb5->cksum_len);
if (ret < 0) {
pr_err("Couldn't set AEAD authsize %s: %d\n", krb5->encrypt_name, ret);
goto err_ci;
}
return ci;
err_ci:
crypto_free_aead(ci);
err:
return ERR_PTR(ret);
}
/**
* crypto_krb5_prepare_encryption - Prepare AEAD crypto object for encryption-mode
* @krb5: The encoding to use.
* @TK: The transport key to use.
* @usage: The usage constant for key derivation.
* @gfp: Allocation flags.
*
* Allocate a crypto object that does all the necessary crypto, key it and set
* its parameters and return the crypto handle to it. This can then be used to
* dispatch encrypt and decrypt operations.
*/
struct crypto_aead *crypto_krb5_prepare_encryption(const struct krb5_enctype *krb5,
const struct krb5_buffer *TK,
u32 usage, gfp_t gfp)
{
struct crypto_aead *ci = NULL;
struct krb5_buffer keys = {};
int ret;
ret = krb5->profile->derive_encrypt_keys(krb5, TK, usage, &keys, gfp);
if (ret < 0)
goto err;
ci = krb5_prepare_encryption(krb5, &keys, gfp);
if (IS_ERR(ci)) {
ret = PTR_ERR(ci);
goto err;
}
kfree(keys.data);
return ci;
err:
kfree(keys.data);
return ERR_PTR(ret);
}
EXPORT_SYMBOL(crypto_krb5_prepare_encryption);
/*
* Prepare the checksum with derived key data.
*/
struct crypto_shash *krb5_prepare_checksum(const struct krb5_enctype *krb5,
const struct krb5_buffer *Kc,
gfp_t gfp)
{
struct crypto_shash *ci = NULL;
int ret = -ENOMEM;
ci = crypto_alloc_shash(krb5->cksum_name, 0, 0);
if (IS_ERR(ci)) {
ret = PTR_ERR(ci);
if (ret == -ENOENT)
ret = -ENOPKG;
goto err;
}
ret = crypto_shash_setkey(ci, Kc->data, Kc->len);
if (ret < 0) {
pr_err("Couldn't set shash key %s: %d\n", krb5->cksum_name, ret);
goto err_ci;
}
return ci;
err_ci:
crypto_free_shash(ci);
err:
return ERR_PTR(ret);
}
/**
* crypto_krb5_prepare_checksum - Prepare AEAD crypto object for checksum-mode
* @krb5: The encoding to use.
* @TK: The transport key to use.
* @usage: The usage constant for key derivation.
* @gfp: Allocation flags.
*
* Allocate a crypto object that does all the necessary crypto, key it and set
* its parameters and return the crypto handle to it. This can then be used to
* dispatch get_mic and verify_mic operations.
*/
struct crypto_shash *crypto_krb5_prepare_checksum(const struct krb5_enctype *krb5,
const struct krb5_buffer *TK,
u32 usage, gfp_t gfp)
{
struct crypto_shash *ci = NULL;
struct krb5_buffer keys = {};
int ret;
ret = krb5->profile->derive_checksum_key(krb5, TK, usage, &keys, gfp);
if (ret < 0) {
pr_err("get_Kc failed %d\n", ret);
goto err;
}
ci = krb5_prepare_checksum(krb5, &keys, gfp);
if (IS_ERR(ci)) {
ret = PTR_ERR(ci);
goto err;
}
kfree(keys.data);
return ci;
err:
kfree(keys.data);
return ERR_PTR(ret);
}
EXPORT_SYMBOL(crypto_krb5_prepare_checksum);
/**
* crypto_krb5_encrypt - Apply Kerberos encryption and integrity.
* @krb5: The encoding to use.
* @aead: The keyed crypto object 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 crypto_aead *aead,
struct scatterlist *sg, unsigned int 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, aead, 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.
* @aead: The keyed crypto object 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.
*
* 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 -EBADMSG
* 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 crypto_aead *aead,
struct scatterlist *sg, unsigned int nr_sg,
size_t *_offset, size_t *_len)
{
return krb5->profile->decrypt(krb5, aead, sg, nr_sg, _offset, _len);
}
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 int 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.
*
* 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 -EBADMSG
* if the 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 int nr_sg,
size_t *_offset, size_t *_len)
{
return krb5->profile->verify_mic(krb5, shash, metadata, sg, nr_sg,
_offset, _len);
}
EXPORT_SYMBOL(crypto_krb5_verify_mic);
static int __init crypto_krb5_init(void)
{
return krb5_selftest();
}
module_init(crypto_krb5_init);
static void __exit crypto_krb5_exit(void)
{
}
module_exit(crypto_krb5_exit);

145
crypto/krb5/krb5_kdf.c Normal file
View File

@ -0,0 +1,145 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/* Kerberos key derivation.
*
* Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/export.h>
#include <linux/slab.h>
#include <crypto/skcipher.h>
#include <crypto/hash.h>
#include "internal.h"
/**
* crypto_krb5_calc_PRFplus - Calculate PRF+ [RFC4402]
* @krb5: The encryption type to use
* @K: The protocol key for the pseudo-random function
* @L: The length of the output
* @S: The input octet string
* @result: Result buffer, sized to krb5->prf_len
* @gfp: Allocation restrictions
*
* Calculate the kerberos pseudo-random function, PRF+() by the following
* method:
*
* PRF+(K, L, S) = truncate(L, T1 || T2 || .. || Tn)
* Tn = PRF(K, n || S)
* [rfc4402 sec 2]
*/
int crypto_krb5_calc_PRFplus(const struct krb5_enctype *krb5,
const struct krb5_buffer *K,
unsigned int L,
const struct krb5_buffer *S,
struct krb5_buffer *result,
gfp_t gfp)
{
struct krb5_buffer T_series, Tn, n_S;
void *buffer;
int ret, n = 1;
Tn.len = krb5->prf_len;
T_series.len = 0;
n_S.len = 4 + S->len;
buffer = kzalloc(round16(L + Tn.len) + round16(n_S.len), gfp);
if (!buffer)
return -ENOMEM;
T_series.data = buffer;
n_S.data = buffer + round16(L + Tn.len);
memcpy(n_S.data + 4, S->data, S->len);
while (T_series.len < L) {
*(__be32 *)(n_S.data) = htonl(n);
Tn.data = T_series.data + Tn.len * (n - 1);
ret = krb5->profile->calc_PRF(krb5, K, &n_S, &Tn, gfp);
if (ret < 0)
goto err;
T_series.len += Tn.len;
n++;
}
/* Truncate to L */
memcpy(result->data, T_series.data, L);
ret = 0;
err:
kfree_sensitive(buffer);
return ret;
}
EXPORT_SYMBOL(crypto_krb5_calc_PRFplus);
/**
* krb5_derive_Kc - Derive key Kc and install into a hash
* @krb5: The encryption type to use
* @TK: The base key
* @usage: The key usage number
* @key: Prepped buffer to store the key into
* @gfp: Allocation restrictions
*
* Derive the Kerberos Kc checksumming key. The key is stored into the
* prepared buffer.
*/
int krb5_derive_Kc(const struct krb5_enctype *krb5, const struct krb5_buffer *TK,
u32 usage, struct krb5_buffer *key, gfp_t gfp)
{
u8 buf[5] __aligned(CRYPTO_MINALIGN);
struct krb5_buffer usage_constant = { .len = 5, .data = buf };
*(__be32 *)buf = cpu_to_be32(usage);
buf[4] = KEY_USAGE_SEED_CHECKSUM;
key->len = krb5->Kc_len;
return krb5->profile->calc_Kc(krb5, TK, &usage_constant, key, gfp);
}
/**
* krb5_derive_Ke - Derive key Ke and install into an skcipher
* @krb5: The encryption type to use
* @TK: The base key
* @usage: The key usage number
* @key: Prepped buffer to store the key into
* @gfp: Allocation restrictions
*
* Derive the Kerberos Ke encryption key. The key is stored into the prepared
* buffer.
*/
int krb5_derive_Ke(const struct krb5_enctype *krb5, const struct krb5_buffer *TK,
u32 usage, struct krb5_buffer *key, gfp_t gfp)
{
u8 buf[5] __aligned(CRYPTO_MINALIGN);
struct krb5_buffer usage_constant = { .len = 5, .data = buf };
*(__be32 *)buf = cpu_to_be32(usage);
buf[4] = KEY_USAGE_SEED_ENCRYPTION;
key->len = krb5->Ke_len;
return krb5->profile->calc_Ke(krb5, TK, &usage_constant, key, gfp);
}
/**
* krb5_derive_Ki - Derive key Ki and install into a hash
* @krb5: The encryption type to use
* @TK: The base key
* @usage: The key usage number
* @key: Prepped buffer to store the key into
* @gfp: Allocation restrictions
*
* Derive the Kerberos Ki integrity checksum key. The key is stored into the
* prepared buffer.
*/
int krb5_derive_Ki(const struct krb5_enctype *krb5, const struct krb5_buffer *TK,
u32 usage, struct krb5_buffer *key, gfp_t gfp)
{
u8 buf[5] __aligned(CRYPTO_MINALIGN);
struct krb5_buffer usage_constant = { .len = 5, .data = buf };
*(__be32 *)buf = cpu_to_be32(usage);
buf[4] = KEY_USAGE_SEED_INTEGRITY;
key->len = krb5->Ki_len;
return krb5->profile->calc_Ki(krb5, TK, &usage_constant, key, gfp);
}

View File

@ -0,0 +1,797 @@
// SPDX-License-Identifier: BSD-3-Clause
/* rfc3961 Kerberos 5 simplified crypto profile.
*
* 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.
*/
/*
* Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/random.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <linux/highmem.h>
#include <linux/lcm.h>
#include <linux/rtnetlink.h>
#include <crypto/authenc.h>
#include <crypto/skcipher.h>
#include <crypto/hash.h>
#include "internal.h"
/* Maximum blocksize for the supported crypto algorithms */
#define KRB5_MAX_BLOCKSIZE (16)
int crypto_shash_update_sg(struct shash_desc *desc, struct scatterlist *sg,
size_t offset, size_t len)
{
do {
int ret;
if (offset < sg->length) {
struct page *page = sg_page(sg);
void *p = kmap_local_page(page);
void *q = p + sg->offset + offset;
size_t seg = min_t(size_t, len, sg->length - offset);
ret = crypto_shash_update(desc, q, seg);
kunmap_local(p);
if (ret < 0)
return ret;
len -= seg;
offset = 0;
} else {
offset -= sg->length;
}
} while (len > 0 && (sg = sg_next(sg)));
return 0;
}
static int rfc3961_do_encrypt(struct crypto_sync_skcipher *tfm, void *iv,
const struct krb5_buffer *in, struct krb5_buffer *out)
{
struct scatterlist sg[1];
u8 local_iv[KRB5_MAX_BLOCKSIZE] __aligned(KRB5_MAX_BLOCKSIZE) = {0};
SYNC_SKCIPHER_REQUEST_ON_STACK(req, tfm);
int ret;
if (WARN_ON(in->len != out->len))
return -EINVAL;
if (out->len % crypto_sync_skcipher_blocksize(tfm) != 0)
return -EINVAL;
if (crypto_sync_skcipher_ivsize(tfm) > KRB5_MAX_BLOCKSIZE)
return -EINVAL;
if (iv)
memcpy(local_iv, iv, crypto_sync_skcipher_ivsize(tfm));
memcpy(out->data, in->data, out->len);
sg_init_one(sg, out->data, out->len);
skcipher_request_set_sync_tfm(req, tfm);
skcipher_request_set_callback(req, 0, NULL, NULL);
skcipher_request_set_crypt(req, sg, sg, out->len, local_iv);
ret = crypto_skcipher_encrypt(req);
skcipher_request_zero(req);
return ret;
}
/*
* Calculate an unkeyed basic hash.
*/
static int rfc3961_calc_H(const struct krb5_enctype *krb5,
const struct krb5_buffer *data,
struct krb5_buffer *digest,
gfp_t gfp)
{
struct crypto_shash *tfm;
struct shash_desc *desc;
size_t desc_size;
int ret = -ENOMEM;
tfm = crypto_alloc_shash(krb5->hash_name, 0, 0);
if (IS_ERR(tfm))
return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm);
desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
desc = kzalloc(desc_size, gfp);
if (!desc)
goto error_tfm;
digest->len = crypto_shash_digestsize(tfm);
digest->data = kzalloc(digest->len, gfp);
if (!digest->data)
goto error_desc;
desc->tfm = tfm;
ret = crypto_shash_init(desc);
if (ret < 0)
goto error_digest;
ret = crypto_shash_finup(desc, data->data, data->len, digest->data);
if (ret < 0)
goto error_digest;
goto error_desc;
error_digest:
kfree_sensitive(digest->data);
error_desc:
kfree_sensitive(desc);
error_tfm:
crypto_free_shash(tfm);
return ret;
}
/*
* This is the n-fold function as described in rfc3961, sec 5.1
* Taken from MIT Kerberos and modified.
*/
static void rfc3961_nfold(const struct krb5_buffer *source, struct krb5_buffer *result)
{
const u8 *in = source->data;
u8 *out = result->data;
unsigned long ulcm;
unsigned int inbits, outbits;
int byte, i, msbit;
/* the code below is more readable if I make these bytes instead of bits */
inbits = source->len;
outbits = result->len;
/* first compute lcm(n,k) */
ulcm = lcm(inbits, outbits);
/* now do the real work */
memset(out, 0, outbits);
byte = 0;
/* this will end up cycling through k lcm(k,n)/k times, which
* is correct.
*/
for (i = ulcm-1; i >= 0; i--) {
/* compute the msbit in k which gets added into this byte */
msbit = (
/* first, start with the msbit in the first,
* unrotated byte
*/
((inbits << 3) - 1) +
/* then, for each byte, shift to the right
* for each repetition
*/
(((inbits << 3) + 13) * (i/inbits)) +
/* last, pick out the correct byte within
* that shifted repetition
*/
((inbits - (i % inbits)) << 3)
) % (inbits << 3);
/* pull out the byte value itself */
byte += (((in[((inbits - 1) - (msbit >> 3)) % inbits] << 8) |
(in[((inbits) - (msbit >> 3)) % inbits]))
>> ((msbit & 7) + 1)) & 0xff;
/* do the addition */
byte += out[i % outbits];
out[i % outbits] = byte & 0xff;
/* keep around the carry bit, if any */
byte >>= 8;
}
/* if there's a carry bit left over, add it back in */
if (byte) {
for (i = outbits - 1; i >= 0; i--) {
/* do the addition */
byte += out[i];
out[i] = byte & 0xff;
/* keep around the carry bit, if any */
byte >>= 8;
}
}
}
/*
* Calculate a derived key, DK(Base Key, Well-Known Constant)
*
* DK(Key, Constant) = random-to-key(DR(Key, Constant))
* DR(Key, Constant) = k-truncate(E(Key, Constant, initial-cipher-state))
* K1 = E(Key, n-fold(Constant), initial-cipher-state)
* K2 = E(Key, K1, initial-cipher-state)
* K3 = E(Key, K2, initial-cipher-state)
* K4 = ...
* DR(Key, Constant) = k-truncate(K1 | K2 | K3 | K4 ...)
* [rfc3961 sec 5.1]
*/
static int rfc3961_calc_DK(const struct krb5_enctype *krb5,
const struct krb5_buffer *inkey,
const struct krb5_buffer *in_constant,
struct krb5_buffer *result,
gfp_t gfp)
{
unsigned int blocksize, keybytes, keylength, n;
struct krb5_buffer inblock, outblock, rawkey;
struct crypto_sync_skcipher *cipher;
int ret = -EINVAL;
blocksize = krb5->block_len;
keybytes = krb5->key_bytes;
keylength = krb5->key_len;
if (inkey->len != keylength || result->len != keylength)
return -EINVAL;
if (!krb5->random_to_key && result->len != keybytes)
return -EINVAL;
cipher = crypto_alloc_sync_skcipher(krb5->derivation_enc, 0, 0);
if (IS_ERR(cipher)) {
ret = (PTR_ERR(cipher) == -ENOENT) ? -ENOPKG : PTR_ERR(cipher);
goto err_return;
}
ret = crypto_sync_skcipher_setkey(cipher, inkey->data, inkey->len);
if (ret < 0)
goto err_free_cipher;
ret = -ENOMEM;
inblock.data = kzalloc(blocksize * 2 + keybytes, gfp);
if (!inblock.data)
goto err_free_cipher;
inblock.len = blocksize;
outblock.data = inblock.data + blocksize;
outblock.len = blocksize;
rawkey.data = outblock.data + blocksize;
rawkey.len = keybytes;
/* initialize the input block */
if (in_constant->len == inblock.len)
memcpy(inblock.data, in_constant->data, inblock.len);
else
rfc3961_nfold(in_constant, &inblock);
/* loop encrypting the blocks until enough key bytes are generated */
n = 0;
while (n < rawkey.len) {
rfc3961_do_encrypt(cipher, NULL, &inblock, &outblock);
if (keybytes - n <= outblock.len) {
memcpy(rawkey.data + n, outblock.data, keybytes - n);
break;
}
memcpy(rawkey.data + n, outblock.data, outblock.len);
memcpy(inblock.data, outblock.data, outblock.len);
n += outblock.len;
}
/* postprocess the key */
if (!krb5->random_to_key) {
/* Identity random-to-key function. */
memcpy(result->data, rawkey.data, rawkey.len);
ret = 0;
} else {
ret = krb5->random_to_key(krb5, &rawkey, result);
}
kfree_sensitive(inblock.data);
err_free_cipher:
crypto_free_sync_skcipher(cipher);
err_return:
return ret;
}
/*
* Calculate single encryption, E()
*
* E(Key, octets)
*/
static int rfc3961_calc_E(const struct krb5_enctype *krb5,
const struct krb5_buffer *key,
const struct krb5_buffer *in_data,
struct krb5_buffer *result,
gfp_t gfp)
{
struct crypto_sync_skcipher *cipher;
int ret;
cipher = crypto_alloc_sync_skcipher(krb5->derivation_enc, 0, 0);
if (IS_ERR(cipher)) {
ret = (PTR_ERR(cipher) == -ENOENT) ? -ENOPKG : PTR_ERR(cipher);
goto err;
}
ret = crypto_sync_skcipher_setkey(cipher, key->data, key->len);
if (ret < 0)
goto err_free;
ret = rfc3961_do_encrypt(cipher, NULL, in_data, result);
err_free:
crypto_free_sync_skcipher(cipher);
err:
return ret;
}
/*
* Calculate the pseudo-random function, PRF().
*
* tmp1 = H(octet-string)
* tmp2 = truncate tmp1 to multiple of m
* PRF = E(DK(protocol-key, prfconstant), tmp2, initial-cipher-state)
*
* The "prfconstant" used in the PRF operation is the three-octet string
* "prf".
* [rfc3961 sec 5.3]
*/
static int rfc3961_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)
{
static const struct krb5_buffer prfconstant = { 3, "prf" };
struct krb5_buffer derived_key;
struct krb5_buffer tmp1, tmp2;
unsigned int m = krb5->block_len;
void *buffer;
int ret;
if (result->len != krb5->prf_len)
return -EINVAL;
tmp1.len = krb5->hash_len;
derived_key.len = krb5->key_bytes;
buffer = kzalloc(round16(tmp1.len) + round16(derived_key.len), gfp);
if (!buffer)
return -ENOMEM;
tmp1.data = buffer;
derived_key.data = buffer + round16(tmp1.len);
ret = rfc3961_calc_H(krb5, octet_string, &tmp1, gfp);
if (ret < 0)
goto err;
tmp2.len = tmp1.len & ~(m - 1);
tmp2.data = tmp1.data;
ret = rfc3961_calc_DK(krb5, protocol_key, &prfconstant, &derived_key, gfp);
if (ret < 0)
goto err;
ret = rfc3961_calc_E(krb5, &derived_key, &tmp2, result, gfp);
err:
kfree_sensitive(buffer);
return ret;
}
/*
* Derive the Ke and Ki keys and package them into a key parameter that can be
* given to the setkey of a authenc AEAD crypto object.
*/
int authenc_derive_encrypt_keys(const struct krb5_enctype *krb5,
const struct krb5_buffer *TK,
unsigned int usage,
struct krb5_buffer *setkey,
gfp_t gfp)
{
struct crypto_authenc_key_param *param;
struct krb5_buffer Ke, Ki;
struct rtattr *rta;
int ret;
Ke.len = krb5->Ke_len;
Ki.len = krb5->Ki_len;
setkey->len = RTA_LENGTH(sizeof(*param)) + Ke.len + Ki.len;
setkey->data = kzalloc(setkey->len, GFP_KERNEL);
if (!setkey->data)
return -ENOMEM;
rta = setkey->data;
rta->rta_type = CRYPTO_AUTHENC_KEYA_PARAM;
rta->rta_len = RTA_LENGTH(sizeof(*param));
param = RTA_DATA(rta);
param->enckeylen = htonl(Ke.len);
Ki.data = (void *)(param + 1);
Ke.data = Ki.data + Ki.len;
ret = krb5_derive_Ke(krb5, TK, usage, &Ke, gfp);
if (ret < 0) {
pr_err("get_Ke failed %d\n", ret);
return ret;
}
ret = krb5_derive_Ki(krb5, TK, usage, &Ki, gfp);
if (ret < 0)
pr_err("get_Ki failed %d\n", ret);
return ret;
}
/*
* Package predefined Ke and Ki keys and into a key parameter that can be given
* to the setkey of an authenc AEAD crypto object.
*/
int authenc_load_encrypt_keys(const struct krb5_enctype *krb5,
const struct krb5_buffer *Ke,
const struct krb5_buffer *Ki,
struct krb5_buffer *setkey,
gfp_t gfp)
{
struct crypto_authenc_key_param *param;
struct rtattr *rta;
setkey->len = RTA_LENGTH(sizeof(*param)) + Ke->len + Ki->len;
setkey->data = kzalloc(setkey->len, GFP_KERNEL);
if (!setkey->data)
return -ENOMEM;
rta = setkey->data;
rta->rta_type = CRYPTO_AUTHENC_KEYA_PARAM;
rta->rta_len = RTA_LENGTH(sizeof(*param));
param = RTA_DATA(rta);
param->enckeylen = htonl(Ke->len);
memcpy((void *)(param + 1), Ki->data, Ki->len);
memcpy((void *)(param + 1) + Ki->len, Ke->data, Ke->len);
return 0;
}
/*
* Derive the Kc key for checksum-only mode and package it into a key parameter
* that can be given to the setkey of a hash crypto object.
*/
int rfc3961_derive_checksum_key(const struct krb5_enctype *krb5,
const struct krb5_buffer *TK,
unsigned int usage,
struct krb5_buffer *setkey,
gfp_t gfp)
{
int ret;
setkey->len = krb5->Kc_len;
setkey->data = kzalloc(setkey->len, GFP_KERNEL);
if (!setkey->data)
return -ENOMEM;
ret = krb5_derive_Kc(krb5, TK, usage, setkey, gfp);
if (ret < 0)
pr_err("get_Kc failed %d\n", ret);
return ret;
}
/*
* Package a predefined Kc key for checksum-only mode into a key parameter that
* can be given to the setkey of a hash crypto object.
*/
int rfc3961_load_checksum_key(const struct krb5_enctype *krb5,
const struct krb5_buffer *Kc,
struct krb5_buffer *setkey,
gfp_t gfp)
{
setkey->len = krb5->Kc_len;
setkey->data = kmemdup(Kc->data, Kc->len, GFP_KERNEL);
if (!setkey->data)
return -ENOMEM;
return 0;
}
/*
* Apply encryption and checksumming functions to part of a scatterlist.
*/
ssize_t krb5_aead_encrypt(const struct krb5_enctype *krb5,
struct crypto_aead *aead,
struct scatterlist *sg, unsigned int nr_sg, size_t sg_len,
size_t data_offset, size_t data_len,
bool preconfounded)
{
struct aead_request *req;
ssize_t ret, done;
size_t bsize, base_len, secure_offset, secure_len, pad_len, cksum_offset;
void *buffer;
u8 *iv;
if (WARN_ON(data_offset != krb5->conf_len))
return -EINVAL; /* Data is in wrong place */
secure_offset = 0;
base_len = krb5->conf_len + data_len;
pad_len = 0;
secure_len = base_len + pad_len;
cksum_offset = secure_len;
if (WARN_ON(cksum_offset + krb5->cksum_len > sg_len))
return -EFAULT;
bsize = krb5_aead_size(aead) +
krb5_aead_ivsize(aead);
buffer = kzalloc(bsize, GFP_NOFS);
if (!buffer)
return -ENOMEM;
/* Insert the confounder into the buffer */
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 may 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;
}
/* Hash and encrypt the message. */
req = buffer;
iv = buffer + krb5_aead_size(aead);
aead_request_set_tfm(req, aead);
aead_request_set_callback(req, 0, NULL, NULL);
aead_request_set_crypt(req, sg, sg, secure_len, iv);
ret = crypto_aead_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 a message. The offset and
* length are updated to reflect the actual content of the encrypted region.
*/
int krb5_aead_decrypt(const struct krb5_enctype *krb5,
struct crypto_aead *aead,
struct scatterlist *sg, unsigned int nr_sg,
size_t *_offset, size_t *_len)
{
struct aead_request *req;
size_t bsize;
void *buffer;
int ret;
u8 *iv;
if (WARN_ON(*_offset != 0))
return -EINVAL; /* Can't set offset on aead */
if (*_len < krb5->conf_len + krb5->cksum_len)
return -EPROTO;
bsize = krb5_aead_size(aead) +
krb5_aead_ivsize(aead);
buffer = kzalloc(bsize, GFP_NOFS);
if (!buffer)
return -ENOMEM;
/* Decrypt the message and verify its checksum. */
req = buffer;
iv = buffer + krb5_aead_size(aead);
aead_request_set_tfm(req, aead);
aead_request_set_callback(req, 0, NULL, NULL);
aead_request_set_crypt(req, sg, sg, *_len, iv);
ret = crypto_aead_decrypt(req);
if (ret < 0)
goto error;
/* Adjust the boundaries of the data. */
*_offset += krb5->conf_len;
*_len -= krb5->conf_len + krb5->cksum_len;
ret = 0;
error:
kfree_sensitive(buffer);
return ret;
}
/*
* Generate a checksum over some metadata and part of an skbuff and insert the
* MIC into the skbuff immediately prior to the data.
*/
ssize_t rfc3961_get_mic(const struct krb5_enctype *krb5,
struct crypto_shash *shash,
const struct krb5_buffer *metadata,
struct scatterlist *sg, unsigned int nr_sg, size_t sg_len,
size_t data_offset, size_t data_len)
{
struct shash_desc *desc;
ssize_t ret, done;
size_t bsize;
void *buffer, *digest;
if (WARN_ON(data_offset != krb5->cksum_len))
return -EMSGSIZE;
bsize = krb5_shash_size(shash) +
krb5_digest_size(shash);
buffer = kzalloc(bsize, GFP_NOFS);
if (!buffer)
return -ENOMEM;
/* Calculate the MIC with key Kc and store it into the skb */
desc = buffer;
desc->tfm = shash;
ret = crypto_shash_init(desc);
if (ret < 0)
goto error;
if (metadata) {
ret = crypto_shash_update(desc, metadata->data, metadata->len);
if (ret < 0)
goto error;
}
ret = crypto_shash_update_sg(desc, sg, data_offset, data_len);
if (ret < 0)
goto error;
digest = buffer + krb5_shash_size(shash);
ret = crypto_shash_final(desc, digest);
if (ret < 0)
goto error;
ret = -EFAULT;
done = sg_pcopy_from_buffer(sg, nr_sg, digest, krb5->cksum_len,
data_offset - krb5->cksum_len);
if (done != krb5->cksum_len)
goto error;
ret = krb5->cksum_len + data_len;
error:
kfree_sensitive(buffer);
return ret;
}
/*
* Check the MIC on a region of an skbuff. The offset and length are updated
* to reflect the actual content of the secure region.
*/
int rfc3961_verify_mic(const struct krb5_enctype *krb5,
struct crypto_shash *shash,
const struct krb5_buffer *metadata,
struct scatterlist *sg, unsigned int nr_sg,
size_t *_offset, size_t *_len)
{
struct shash_desc *desc;
ssize_t done;
size_t bsize, data_offset, data_len, offset = *_offset, len = *_len;
void *buffer = NULL;
int ret;
u8 *cksum, *cksum2;
if (len < krb5->cksum_len)
return -EPROTO;
data_offset = offset + krb5->cksum_len;
data_len = len - krb5->cksum_len;
bsize = krb5_shash_size(shash) +
krb5_digest_size(shash) * 2;
buffer = kzalloc(bsize, GFP_NOFS);
if (!buffer)
return -ENOMEM;
cksum = buffer +
krb5_shash_size(shash);
cksum2 = buffer +
krb5_shash_size(shash) +
krb5_digest_size(shash);
/* Calculate the MIC */
desc = buffer;
desc->tfm = shash;
ret = crypto_shash_init(desc);
if (ret < 0)
goto error;
if (metadata) {
ret = crypto_shash_update(desc, metadata->data, metadata->len);
if (ret < 0)
goto error;
}
crypto_shash_update_sg(desc, sg, data_offset, data_len);
crypto_shash_final(desc, cksum);
ret = -EFAULT;
done = sg_pcopy_to_buffer(sg, nr_sg, cksum2, krb5->cksum_len, offset);
if (done != krb5->cksum_len)
goto error;
if (memcmp(cksum, cksum2, krb5->cksum_len) != 0) {
ret = -EBADMSG;
goto error;
}
*_offset += krb5->cksum_len;
*_len -= krb5->cksum_len;
ret = 0;
error:
kfree_sensitive(buffer);
return ret;
}
const struct krb5_crypto_profile rfc3961_simplified_profile = {
.calc_PRF = rfc3961_calc_PRF,
.calc_Kc = rfc3961_calc_DK,
.calc_Ke = rfc3961_calc_DK,
.calc_Ki = rfc3961_calc_DK,
.derive_encrypt_keys = authenc_derive_encrypt_keys,
.load_encrypt_keys = authenc_load_encrypt_keys,
.derive_checksum_key = rfc3961_derive_checksum_key,
.load_checksum_key = rfc3961_load_checksum_key,
.encrypt = krb5_aead_encrypt,
.decrypt = krb5_aead_decrypt,
.get_mic = rfc3961_get_mic,
.verify_mic = rfc3961_verify_mic,
};

115
crypto/krb5/rfc3962_aes.c Normal file
View File

@ -0,0 +1,115 @@
// SPDX-License-Identifier: BSD-3-Clause
/* 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.
*/
/*
* Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include "internal.h"
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 = "krb5enc(hmac(sha1),cts(cbc(aes)))",
.cksum_name = "hmac(sha1)",
.hash_name = "sha1",
.derivation_enc = "cts(cbc(aes))",
.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,
.random_to_key = NULL, /* Identity */
.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 = "krb5enc(hmac(sha1),cts(cbc(aes)))",
.cksum_name = "hmac(sha1)",
.hash_name = "sha1",
.derivation_enc = "cts(cbc(aes))",
.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,
.random_to_key = NULL, /* Identity */
.profile = &rfc3961_simplified_profile,
};

View File

@ -0,0 +1,237 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/* rfc6803 Camellia Encryption for Kerberos 5
*
* Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/slab.h>
#include "internal.h"
/*
* Calculate the key derivation function KDF-FEEDBACK_CMAC(key, constant)
*
* n = ceiling(k / 128)
* K(0) = zeros
* K(i) = CMAC(key, K(i-1) | i | constant | 0x00 | k)
* DR(key, constant) = k-truncate(K(1) | K(2) | ... | K(n))
* KDF-FEEDBACK-CMAC(key, constant) = random-to-key(DR(key, constant))
*
* [rfc6803 sec 3]
*/
static int rfc6803_calc_KDF_FEEDBACK_CMAC(const struct krb5_enctype *krb5,
const struct krb5_buffer *key,
const struct krb5_buffer *constant,
struct krb5_buffer *result,
gfp_t gfp)
{
struct crypto_shash *shash;
struct krb5_buffer K, data;
struct shash_desc *desc;
__be32 tmp;
size_t bsize, offset, seg;
void *buffer;
u32 i = 0, k = result->len * 8;
u8 *p;
int ret = -ENOMEM;
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 = -ENOMEM;
K.len = crypto_shash_digestsize(shash);
data.len = K.len + 4 + constant->len + 1 + 4;
bsize = krb5_shash_size(shash) +
krb5_digest_size(shash) +
crypto_roundup(K.len) +
crypto_roundup(data.len);
buffer = kzalloc(bsize, GFP_NOFS);
if (!buffer)
goto error_shash;
desc = buffer;
desc->tfm = shash;
K.data = buffer +
krb5_shash_size(shash) +
krb5_digest_size(shash);
data.data = buffer +
krb5_shash_size(shash) +
krb5_digest_size(shash) +
crypto_roundup(K.len);
p = data.data + K.len + 4;
memcpy(p, constant->data, constant->len);
p += constant->len;
*p++ = 0x00;
tmp = htonl(k);
memcpy(p, &tmp, 4);
p += 4;
ret = -EINVAL;
if (WARN_ON(p - (u8 *)data.data != data.len))
goto error;
offset = 0;
do {
i++;
p = data.data;
memcpy(p, K.data, K.len);
p += K.len;
*(__be32 *)p = htonl(i);
ret = crypto_shash_init(desc);
if (ret < 0)
goto error;
ret = crypto_shash_finup(desc, data.data, data.len, K.data);
if (ret < 0)
goto error;
seg = min_t(size_t, result->len - offset, K.len);
memcpy(result->data + offset, K.data, seg);
offset += seg;
} while (offset < result->len);
error:
kfree_sensitive(buffer);
error_shash:
crypto_free_shash(shash);
return ret;
}
/*
* Calculate the pseudo-random function, PRF().
*
* Kp = KDF-FEEDBACK-CMAC(protocol-key, "prf")
* PRF = CMAC(Kp, octet-string)
* [rfc6803 sec 6]
*/
static int rfc6803_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)
{
static const struct krb5_buffer prfconstant = { 3, "prf" };
struct crypto_shash *shash;
struct krb5_buffer Kp;
struct shash_desc *desc;
size_t bsize;
void *buffer;
int ret;
Kp.len = krb5->prf_len;
shash = crypto_alloc_shash(krb5->cksum_name, 0, 0);
if (IS_ERR(shash))
return (PTR_ERR(shash) == -ENOENT) ? -ENOPKG : PTR_ERR(shash);
ret = -EINVAL;
if (result->len != crypto_shash_digestsize(shash))
goto out_shash;
ret = -ENOMEM;
bsize = krb5_shash_size(shash) +
krb5_digest_size(shash) +
crypto_roundup(Kp.len);
buffer = kzalloc(bsize, GFP_NOFS);
if (!buffer)
goto out_shash;
Kp.data = buffer +
krb5_shash_size(shash) +
krb5_digest_size(shash);
ret = rfc6803_calc_KDF_FEEDBACK_CMAC(krb5, protocol_key, &prfconstant,
&Kp, gfp);
if (ret < 0)
goto out;
ret = crypto_shash_setkey(shash, Kp.data, Kp.len);
if (ret < 0)
goto out;
desc = buffer;
desc->tfm = shash;
ret = crypto_shash_init(desc);
if (ret < 0)
goto out;
ret = crypto_shash_finup(desc, octet_string->data, octet_string->len, result->data);
if (ret < 0)
goto out;
out:
kfree_sensitive(buffer);
out_shash:
crypto_free_shash(shash);
return ret;
}
static const struct krb5_crypto_profile rfc6803_crypto_profile = {
.calc_PRF = rfc6803_calc_PRF,
.calc_Kc = rfc6803_calc_KDF_FEEDBACK_CMAC,
.calc_Ke = rfc6803_calc_KDF_FEEDBACK_CMAC,
.calc_Ki = rfc6803_calc_KDF_FEEDBACK_CMAC,
.derive_encrypt_keys = authenc_derive_encrypt_keys,
.load_encrypt_keys = authenc_load_encrypt_keys,
.derive_checksum_key = rfc3961_derive_checksum_key,
.load_checksum_key = rfc3961_load_checksum_key,
.encrypt = krb5_aead_encrypt,
.decrypt = krb5_aead_decrypt,
.get_mic = rfc3961_get_mic,
.verify_mic = rfc3961_verify_mic,
};
const struct krb5_enctype krb5_camellia128_cts_cmac = {
.etype = KRB5_ENCTYPE_CAMELLIA128_CTS_CMAC,
.ctype = KRB5_CKSUMTYPE_CMAC_CAMELLIA128,
.name = "camellia128-cts-cmac",
.encrypt_name = "krb5enc(cmac(camellia),cts(cbc(camellia)))",
.cksum_name = "cmac(camellia)",
.hash_name = NULL,
.derivation_enc = "cts(cbc(camellia))",
.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 = 16,
.prf_len = 16,
.keyed_cksum = true,
.random_to_key = NULL, /* Identity */
.profile = &rfc6803_crypto_profile,
};
const struct krb5_enctype krb5_camellia256_cts_cmac = {
.etype = KRB5_ENCTYPE_CAMELLIA256_CTS_CMAC,
.ctype = KRB5_CKSUMTYPE_CMAC_CAMELLIA256,
.name = "camellia256-cts-cmac",
.encrypt_name = "krb5enc(cmac(camellia),cts(cbc(camellia)))",
.cksum_name = "cmac(camellia)",
.hash_name = NULL,
.derivation_enc = "cts(cbc(camellia))",
.key_bytes = 32,
.key_len = 32,
.Kc_len = 32,
.Ke_len = 32,
.Ki_len = 32,
.block_len = 16,
.conf_len = 16,
.cksum_len = 16,
.hash_len = 16,
.prf_len = 16,
.keyed_cksum = true,
.random_to_key = NULL, /* Identity */
.profile = &rfc6803_crypto_profile,
};

362
crypto/krb5/rfc8009_aes2.c Normal file
View File

@ -0,0 +1,362 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/* rfc8009 AES Encryption with HMAC-SHA2 for Kerberos 5
*
* Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/slab.h>
#include <crypto/authenc.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);
}
/*
* Apply encryption and checksumming functions to a message. Unlike for
* RFC3961, for RFC8009, we have to chuck the starting IV into the hash first.
*/
static ssize_t rfc8009_encrypt(const struct krb5_enctype *krb5,
struct crypto_aead *aead,
struct scatterlist *sg, unsigned int nr_sg, size_t sg_len,
size_t data_offset, size_t data_len,
bool preconfounded)
{
struct aead_request *req;
struct scatterlist bsg[2];
ssize_t ret, done;
size_t bsize, base_len, secure_offset, secure_len, pad_len, cksum_offset;
void *buffer;
u8 *iv, *ad;
if (WARN_ON(data_offset != krb5->conf_len))
return -EINVAL; /* Data is in wrong place */
secure_offset = 0;
base_len = krb5->conf_len + data_len;
pad_len = 0;
secure_len = base_len + pad_len;
cksum_offset = secure_len;
if (WARN_ON(cksum_offset + krb5->cksum_len > sg_len))
return -EFAULT;
bsize = krb5_aead_size(aead) +
krb5_aead_ivsize(aead) * 2;
buffer = kzalloc(bsize, GFP_NOFS);
if (!buffer)
return -ENOMEM;
req = buffer;
iv = buffer + krb5_aead_size(aead);
ad = buffer + krb5_aead_size(aead) + krb5_aead_ivsize(aead);
/* Insert the confounder into the buffer */
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 may 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;
}
/* We need to include the starting IV in the hash. */
sg_init_table(bsg, 2);
sg_set_buf(&bsg[0], ad, krb5_aead_ivsize(aead));
sg_chain(bsg, 2, sg);
/* Hash and encrypt the message. */
aead_request_set_tfm(req, aead);
aead_request_set_callback(req, 0, NULL, NULL);
aead_request_set_ad(req, krb5_aead_ivsize(aead));
aead_request_set_crypt(req, bsg, bsg, secure_len, iv);
ret = crypto_aead_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 a message. Unlike for
* RFC3961, for RFC8009, we have to chuck the starting IV into the hash first.
*
* 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 crypto_aead *aead,
struct scatterlist *sg, unsigned int nr_sg,
size_t *_offset, size_t *_len)
{
struct aead_request *req;
struct scatterlist bsg[2];
size_t bsize;
void *buffer;
int ret;
u8 *iv, *ad;
if (WARN_ON(*_offset != 0))
return -EINVAL; /* Can't set offset on aead */
if (*_len < krb5->conf_len + krb5->cksum_len)
return -EPROTO;
bsize = krb5_aead_size(aead) +
krb5_aead_ivsize(aead) * 2;
buffer = kzalloc(bsize, GFP_NOFS);
if (!buffer)
return -ENOMEM;
req = buffer;
iv = buffer + krb5_aead_size(aead);
ad = buffer + krb5_aead_size(aead) + krb5_aead_ivsize(aead);
/* We need to include the starting IV in the hash. */
sg_init_table(bsg, 2);
sg_set_buf(&bsg[0], ad, krb5_aead_ivsize(aead));
sg_chain(bsg, 2, sg);
/* Decrypt the message and verify its checksum. */
aead_request_set_tfm(req, aead);
aead_request_set_callback(req, 0, NULL, NULL);
aead_request_set_ad(req, krb5_aead_ivsize(aead));
aead_request_set_crypt(req, bsg, bsg, *_len, iv);
ret = crypto_aead_decrypt(req);
if (ret < 0)
goto error;
/* Adjust the boundaries of the data. */
*_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,
.derive_encrypt_keys = authenc_derive_encrypt_keys,
.load_encrypt_keys = authenc_load_encrypt_keys,
.derive_checksum_key = rfc3961_derive_checksum_key,
.load_checksum_key = rfc3961_load_checksum_key,
.encrypt = rfc8009_encrypt,
.decrypt = 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 = "authenc(hmac(sha256),cts(cbc(aes)))",
.cksum_name = "hmac(sha256)",
.hash_name = "sha256",
.derivation_enc = "cts(cbc(aes))",
.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 = NULL, /* Identity */
.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 = "authenc(hmac(sha384),cts(cbc(aes)))",
.cksum_name = "hmac(sha384)",
.hash_name = "sha384",
.derivation_enc = "cts(cbc(aes))",
.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 = NULL, /* Identity */
.profile = &rfc8009_crypto_profile,
};

544
crypto/krb5/selftest.c Normal file
View File

@ -0,0 +1,544 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/* Kerberos library self-testing
*
* Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*/
#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__); \
ret = -EBADMSG; \
} \
__x; \
})
#define CHECK(X) \
({ \
bool __x = (X); \
if (__x) { \
pr_warn("!!! TESTFAIL %s:%u\n", __FILE__, __LINE__); \
ret = -EBADMSG; \
} \
__x; \
})
enum which_key {
TEST_KC, TEST_KE, TEST_KI,
};
#if 0
static void dump_sg(struct scatterlist *sg, unsigned int limit)
{
unsigned int index = 0, n = 0;
for (; sg && limit > 0; sg = sg_next(sg)) {
unsigned int off = sg->offset, len = umin(sg->length, limit);
const void *p = kmap_local_page(sg_page(sg));
limit -= len;
while (len > 0) {
unsigned int part = umin(len, 32);
pr_notice("[%x] %04x: %*phN\n", n, index, part, p + off);
index += part;
off += part;
len -= part;
}
kunmap_local(p);
n++;
}
}
#endif
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); \
ret = prep_buf((BUF)); \
if (ret < 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);
ret = hex2bin(buf->data, from, buf->len);
if (ret < 0) {
VALID(1);
goto out;
}
out:
return ret;
}
#define LOAD_BUF(BUF, FROM) do { ret = load_buf(BUF, FROM); if (ret < 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 = crypto_krb5_find_enctype(test->etype);
struct krb5_buffer key = {}, octet = {}, result = {}, prf = {};
int ret;
if (!krb5)
return -EOPNOTSUPP;
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;
}
ret = krb5->profile->calc_PRF(krb5, &key, &octet, &result, GFP_KERNEL);
if (ret < 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 = krb5_derive_Kc(krb5, base_key, test->use, &result, GFP_KERNEL);
break;
case TEST_KE:
ret = krb5_derive_Ke(krb5, base_key, test->use, &result, GFP_KERNEL);
break;
case TEST_KI:
ret = krb5_derive_Ki(krb5, base_key, test->use, &result, 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 = crypto_krb5_find_enctype(test->etype);
struct krb5_buffer base_key = {};
int ret;
if (!krb5)
return -EOPNOTSUPP;
pr_notice("Running %s %s\n", krb5->name, test->name);
LOAD_BUF(&base_key, test->key);
ret = krb5_test_key(krb5, &base_key, &test->Kc, TEST_KC);
if (ret < 0)
goto out;
ret = krb5_test_key(krb5, &base_key, &test->Ke, TEST_KE);
if (ret < 0)
goto out;
ret = krb5_test_key(krb5, &base_key, &test->Ki, TEST_KI);
if (ret < 0)
goto out;
out:
clear_buf(&base_key);
return ret;
}
/*
* Perform an encryption test.
*/
static int krb5_test_one_enc(const struct krb5_enc_test *test, void *buf)
{
const struct krb5_enctype *krb5 = crypto_krb5_find_enctype(test->etype);
struct crypto_aead *ci = NULL;
struct krb5_buffer K0 = {}, Ke = {}, Ki = {}, keys = {};
struct krb5_buffer conf = {}, plain = {}, ct = {};
struct scatterlist sg[1];
size_t data_len, data_offset, message_len;
int ret;
if (!krb5)
return -EOPNOTSUPP;
pr_notice("Running %s %s\n", krb5->name, test->name);
/* Load the test data into binary buffers. */
LOAD_BUF(&conf, test->conf);
LOAD_BUF(&plain, test->plain);
LOAD_BUF(&ct, test->ct);
if (test->K0) {
LOAD_BUF(&K0, test->K0);
} else {
LOAD_BUF(&Ke, test->Ke);
LOAD_BUF(&Ki, test->Ki);
ret = krb5->profile->load_encrypt_keys(krb5, &Ke, &Ki, &keys, GFP_KERNEL);
if (ret < 0)
goto out;
}
if (VALID(conf.len != krb5->conf_len) ||
VALID(ct.len != krb5->conf_len + plain.len + krb5->cksum_len))
goto out;
data_len = plain.len;
message_len = crypto_krb5_how_much_buffer(krb5, KRB5_ENCRYPT_MODE,
data_len, &data_offset);
if (CHECK(message_len != ct.len)) {
pr_warn("Encrypted length mismatch %zu != %u\n", message_len, ct.len);
goto out;
}
if (CHECK(data_offset != conf.len)) {
pr_warn("Data offset mismatch %zu != %u\n", data_offset, conf.len);
goto out;
}
memcpy(buf, conf.data, conf.len);
memcpy(buf + data_offset, plain.data, plain.len);
/* Allocate a crypto object and set its key. */
if (test->K0)
ci = crypto_krb5_prepare_encryption(krb5, &K0, test->usage, GFP_KERNEL);
else
ci = krb5_prepare_encryption(krb5, &keys, GFP_KERNEL);
if (IS_ERR(ci)) {
ret = PTR_ERR(ci);
ci = NULL;
pr_err("Couldn't alloc AEAD %s: %d\n", krb5->encrypt_name, ret);
goto out;
}
/* Encrypt the message. */
sg_init_one(sg, buf, message_len);
ret = crypto_krb5_encrypt(krb5, ci, sg, 1, message_len,
data_offset, data_len, true);
if (ret < 0) {
CHECK(1);
pr_warn("Encryption failed %d\n", ret);
goto out;
}
if (ret != message_len) {
CHECK(1);
pr_warn("Encrypted message wrong size %x != %zx\n", ret, message_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);
pr_warn("PT %*phN%*phN\n", conf.len, conf.data, plain.len, plain.data);
ret = -EKEYREJECTED;
goto out;
}
/* Decrypt the encrypted message. */
data_offset = 0;
data_len = message_len;
ret = crypto_krb5_decrypt(krb5, ci, sg, 1, &data_offset, &data_len);
if (ret < 0) {
CHECK(1);
pr_warn("Decryption failed %d\n", ret);
goto out;
}
if (CHECK(data_offset != conf.len) ||
CHECK(data_len != plain.len))
goto out;
if (memcmp(buf, conf.data, conf.len) != 0) {
CHECK(1);
pr_warn("Confounder mismatch\n");
pr_warn("ENC %*phN\n", conf.len, buf);
pr_warn("DEC %*phN\n", conf.len, conf.data);
ret = -EKEYREJECTED;
goto out;
}
if (memcmp(buf + conf.len, plain.data, plain.len) != 0) {
CHECK(1);
pr_warn("Plaintext mismatch\n");
pr_warn("BUF %*phN\n", plain.len, buf + conf.len);
pr_warn("PT %*phN\n", plain.len, plain.data);
ret = -EKEYREJECTED;
goto out;
}
ret = 0;
out:
clear_buf(&ct);
clear_buf(&plain);
clear_buf(&conf);
clear_buf(&keys);
clear_buf(&Ki);
clear_buf(&Ke);
clear_buf(&K0);
if (ci)
crypto_free_aead(ci);
return ret;
}
/*
* Perform a checksum test.
*/
static int krb5_test_one_mic(const struct krb5_mic_test *test, void *buf)
{
const struct krb5_enctype *krb5 = crypto_krb5_find_enctype(test->etype);
struct crypto_shash *ci = NULL;
struct scatterlist sg[1];
struct krb5_buffer K0 = {}, Kc = {}, keys = {}, plain = {}, mic = {};
size_t offset, len, message_len;
int ret;
if (!krb5)
return -EOPNOTSUPP;
pr_notice("Running %s %s\n", krb5->name, test->name);
/* Allocate a crypto object and set its key. */
if (test->K0) {
LOAD_BUF(&K0, test->K0);
ci = crypto_krb5_prepare_checksum(krb5, &K0, test->usage, GFP_KERNEL);
} else {
LOAD_BUF(&Kc, test->Kc);
ret = krb5->profile->load_checksum_key(krb5, &Kc, &keys, GFP_KERNEL);
if (ret < 0)
goto out;
ci = krb5_prepare_checksum(krb5, &Kc, GFP_KERNEL);
}
if (IS_ERR(ci)) {
ret = PTR_ERR(ci);
ci = NULL;
pr_err("Couldn't alloc shash %s: %d\n", krb5->cksum_name, ret);
goto out;
}
/* Load the test data into binary buffers. */
LOAD_BUF(&plain, test->plain);
LOAD_BUF(&mic, test->mic);
len = plain.len;
message_len = crypto_krb5_how_much_buffer(krb5, KRB5_CHECKSUM_MODE,
len, &offset);
if (CHECK(message_len != mic.len + plain.len)) {
pr_warn("MIC length mismatch %zu != %u\n",
message_len, mic.len + plain.len);
goto out;
}
memcpy(buf + offset, plain.data, plain.len);
/* Generate a MIC generation request. */
sg_init_one(sg, buf, 1024);
ret = crypto_krb5_get_mic(krb5, ci, 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;
}
/* Generate a verification request. */
offset = 0;
ret = crypto_krb5_verify_mic(krb5, ci, NULL, sg, 1, &offset, &len);
if (ret < 0) {
CHECK(1);
pr_warn("Verify MIC failed %d\n", ret);
goto out;
}
if (CHECK(offset != mic.len) ||
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(&mic);
clear_buf(&plain);
clear_buf(&keys);
clear_buf(&K0);
clear_buf(&Kc);
if (ci)
crypto_free_shash(ci);
return ret;
}
int krb5_selftest(void)
{
void *buf;
int ret = 0, i;
buf = kmalloc(4096, GFP_KERNEL);
if (!buf)
return -ENOMEM;
pr_notice("\n");
pr_notice("Running selftests\n");
for (i = 0; krb5_prf_tests[i].name; i++) {
ret = krb5_test_one_prf(&krb5_prf_tests[i]);
if (ret < 0) {
if (ret != -EOPNOTSUPP)
goto out;
pr_notice("Skipping %s\n", krb5_prf_tests[i].name);
}
}
for (i = 0; krb5_key_tests[i].name; i++) {
ret = krb5_test_one_key(&krb5_key_tests[i]);
if (ret < 0) {
if (ret != -EOPNOTSUPP)
goto out;
pr_notice("Skipping %s\n", krb5_key_tests[i].name);
}
}
for (i = 0; krb5_enc_tests[i].name; i++) {
memset(buf, 0x5a, 4096);
ret = krb5_test_one_enc(&krb5_enc_tests[i], buf);
if (ret < 0) {
if (ret != -EOPNOTSUPP)
goto out;
pr_notice("Skipping %s\n", krb5_enc_tests[i].name);
}
}
for (i = 0; krb5_mic_tests[i].name; i++) {
memset(buf, 0x5a, 4096);
ret = krb5_test_one_mic(&krb5_mic_tests[i], buf);
if (ret < 0) {
if (ret != -EOPNOTSUPP)
goto out;
pr_notice("Skipping %s\n", krb5_mic_tests[i].name);
}
}
ret = 0;
out:
pr_notice("Selftests %s\n", ret == 0 ? "succeeded" : "failed");
kfree(buf);
return ret;
}

291
crypto/krb5/selftest_data.c Normal file
View File

@ -0,0 +1,291 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/* Data for Kerberos library self-testing
*
* Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include "internal.h"
/*
* Pseudo-random function tests.
*/
const struct krb5_prf_test krb5_prf_tests[] = {
/* rfc8009 Appendix A */
{
.etype = KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128,
.name = "prf",
.key = "3705D96080C17728A0E800EAB6E0D23C",
.octet = "74657374",
.prf = "9D188616F63852FE86915BB840B4A886FF3E6BB0F819B49B893393D393854295",
}, {
.etype = KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192,
.name = "prf",
.key = "6D404D37FAF79F9DF0D33568D320669800EB4836472EA8A026D16B7182460C52",
.octet = "74657374",
.prf =
"9801F69A368C2BF675E59521E177D9A07F67EFE1CFDE8D3C8D6F6A0256E3B17D"
"B3C1B62AD1B8553360D17367EB1514D2",
},
{/* END */}
};
/*
* Key derivation tests.
*/
const struct krb5_key_test krb5_key_tests[] = {
/* rfc8009 Appendix A */
{
.etype = KRB5_ENCTYPE_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",
}, {
.etype = KRB5_ENCTYPE_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",
},
/* rfc6803 sec 10 */
{
.etype = KRB5_ENCTYPE_CAMELLIA128_CTS_CMAC,
.name = "key",
.key = "57D0297298FFD9D35DE5A47FB4BDE24B",
.Kc.use = 0x00000002,
.Kc.key = "D155775A209D05F02B38D42A389E5A56",
.Ke.use = 0x00000002,
.Ke.key = "64DF83F85A532F17577D8C37035796AB",
.Ki.use = 0x00000002,
.Ki.key = "3E4FBDF30FB8259C425CB6C96F1F4635",
},
{
.etype = KRB5_ENCTYPE_CAMELLIA256_CTS_CMAC,
.name = "key",
.key = "B9D6828B2056B7BE656D88A123B1FAC68214AC2B727ECF5F69AFE0C4DF2A6D2C",
.Kc.use = 0x00000002,
.Kc.key = "E467F9A9552BC7D3155A6220AF9C19220EEED4FF78B0D1E6A1544991461A9E50",
.Ke.use = 0x00000002,
.Ke.key = "412AEFC362A7285FC3966C6A5181E7605AE675235B6D549FBFC9AB6630A4C604",
.Ki.use = 0x00000002,
.Ki.key = "FA624FA0E523993FA388AEFDC67E67EBCD8C08E8A0246B1D73B0D1DD9FC582B0",
},
{/* END */}
};
/*
* Encryption tests.
*/
const struct krb5_enc_test krb5_enc_tests[] = {
/* rfc8009 Appendix A */
{
.etype = KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128,
.name = "enc no plain",
.plain = "",
.conf = "7E5895EAF2672435BAD817F545A37148",
.Ke = "9B197DD1E8C5609D6E67C3E37C62C72E",
.Ki = "9FDA0E56AB2D85E1569A688696C26A6C",
.ct = "EF85FB890BB8472F4DAB20394DCA781DAD877EDA39D50C870C0D5A0A8E48C718",
}, {
.etype = KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128,
.name = "enc plain<block",
.plain = "000102030405",
.conf = "7BCA285E2FD4130FB55B1A5C83BC5B24",
.Ke = "9B197DD1E8C5609D6E67C3E37C62C72E",
.Ki = "9FDA0E56AB2D85E1569A688696C26A6C",
.ct = "84D7F30754ED987BAB0BF3506BEB09CFB55402CEF7E6877CE99E247E52D16ED4421DFDF8976C",
}, {
.etype = KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128,
.name = "enc plain==block",
.plain = "000102030405060708090A0B0C0D0E0F",
.conf = "56AB21713FF62C0A1457200F6FA9948F",
.Ke = "9B197DD1E8C5609D6E67C3E37C62C72E",
.Ki = "9FDA0E56AB2D85E1569A688696C26A6C",
.ct = "3517D640F50DDC8AD3628722B3569D2AE07493FA8263254080EA65C1008E8FC295FB4852E7D83E1E7C48C37EEBE6B0D3",
}, {
.etype = KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128,
.name = "enc plain>block",
.plain = "000102030405060708090A0B0C0D0E0F1011121314",
.conf = "A7A4E29A4728CE10664FB64E49AD3FAC",
.Ke = "9B197DD1E8C5609D6E67C3E37C62C72E",
.Ki = "9FDA0E56AB2D85E1569A688696C26A6C",
.ct = "720F73B18D9859CD6CCB4346115CD336C70F58EDC0C4437C5573544C31C813BCE1E6D072C186B39A413C2F92CA9B8334A287FFCBFC",
}, {
.etype = KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192,
.name = "enc no plain",
.plain = "",
.conf = "F764E9FA15C276478B2C7D0C4E5F58E4",
.Ke = "56AB22BEE63D82D7BC5227F6773F8EA7A5EB1C825160C38312980C442E5C7E49",
.Ki = "69B16514E3CD8E56B82010D5C73012B622C4D00FFC23ED1F",
.ct = "41F53FA5BFE7026D91FAF9BE959195A058707273A96A40F0A01960621AC612748B9BBFBE7EB4CE3C",
}, {
.etype = KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192,
.name = "enc plain<block",
.plain = "000102030405",
.conf = "B80D3251C1F6471494256FFE712D0B9A",
.Ke = "56AB22BEE63D82D7BC5227F6773F8EA7A5EB1C825160C38312980C442E5C7E49",
.Ki = "69B16514E3CD8E56B82010D5C73012B622C4D00FFC23ED1F",
.ct = "4ED7B37C2BCAC8F74F23C1CF07E62BC7B75FB3F637B9F559C7F664F69EAB7B6092237526EA0D1F61CB20D69D10F2",
}, {
.etype = KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192,
.name = "enc plain==block",
.plain = "000102030405060708090A0B0C0D0E0F",
.conf = "53BF8A0D105265D4E276428624CE5E63",
.Ke = "56AB22BEE63D82D7BC5227F6773F8EA7A5EB1C825160C38312980C442E5C7E49",
.Ki = "69B16514E3CD8E56B82010D5C73012B622C4D00FFC23ED1F",
.ct = "BC47FFEC7998EB91E8115CF8D19DAC4BBBE2E163E87DD37F49BECA92027764F68CF51F14D798C2273F35DF574D1F932E40C4FF255B36A266",
}, {
.etype = KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192,
.name = "enc plain>block",
.plain = "000102030405060708090A0B0C0D0E0F1011121314",
.conf = "763E65367E864F02F55153C7E3B58AF1",
.Ke = "56AB22BEE63D82D7BC5227F6773F8EA7A5EB1C825160C38312980C442E5C7E49",
.Ki = "69B16514E3CD8E56B82010D5C73012B622C4D00FFC23ED1F",
.ct = "40013E2DF58E8751957D2878BCD2D6FE101CCFD556CB1EAE79DB3C3EE86429F2B2A602AC86FEF6ECB647D6295FAE077A1FEB517508D2C16B4192E01F62",
},
/* rfc6803 sec 10 */
{
.etype = KRB5_ENCTYPE_CAMELLIA128_CTS_CMAC,
.name = "enc no plain",
.plain = "",
.conf = "B69822A19A6B09C0EBC8557D1F1B6C0A",
.K0 = "1DC46A8D763F4F93742BCBA3387576C3",
.usage = 0,
.ct = "C466F1871069921EDB7C6FDE244A52DB0BA10EDC197BDB8006658CA3CCCE6EB8",
}, {
.etype = KRB5_ENCTYPE_CAMELLIA128_CTS_CMAC,
.name = "enc 1 plain",
.plain = "'1",
.conf = "6F2FC3C2A166FD8898967A83DE9596D9",
.K0 = "5027BC231D0F3A9D23333F1CA6FDBE7C",
.usage = 1,
.ct = "842D21FD950311C0DD464A3F4BE8D6DA88A56D559C9B47D3F9A85067AF661559B8",
}, {
.etype = KRB5_ENCTYPE_CAMELLIA128_CTS_CMAC,
.name = "enc 9 plain",
.plain = "'9 bytesss",
.conf = "A5B4A71E077AEEF93C8763C18FDB1F10",
.K0 = "A1BB61E805F9BA6DDE8FDBDDC05CDEA0",
.usage = 2,
.ct = "619FF072E36286FF0A28DEB3A352EC0D0EDF5C5160D663C901758CCF9D1ED33D71DB8F23AABF8348A0",
}, {
.etype = KRB5_ENCTYPE_CAMELLIA128_CTS_CMAC,
.name = "enc 13 plain",
.plain = "'13 bytes byte",
.conf = "19FEE40D810C524B5B22F01874C693DA",
.K0 = "2CA27A5FAF5532244506434E1CEF6676",
.usage = 3,
.ct = "B8ECA3167AE6315512E59F98A7C500205E5F63FF3BB389AF1C41A21D640D8615C9ED3FBEB05AB6ACB67689B5EA",
}, {
.etype = KRB5_ENCTYPE_CAMELLIA128_CTS_CMAC,
.name = "enc 30 plain",
.plain = "'30 bytes bytes bytes bytes byt",
.conf = "CA7A7AB4BE192DABD603506DB19C39E2",
.K0 = "7824F8C16F83FF354C6BF7515B973F43",
.usage = 4,
.ct = "A26A3905A4FFD5816B7B1E27380D08090C8EC1F304496E1ABDCD2BDCD1DFFC660989E117A713DDBB57A4146C1587CBA4356665591D2240282F5842B105A5",
}, {
.etype = KRB5_ENCTYPE_CAMELLIA256_CTS_CMAC,
.name = "enc no plain",
.plain = "",
.conf = "3CBBD2B45917941067F96599BB98926C",
.K0 = "B61C86CC4E5D2757545AD423399FB7031ECAB913CBB900BD7A3C6DD8BF92015B",
.usage = 0,
.ct = "03886D03310B47A6D8F06D7B94D1DD837ECCE315EF652AFF620859D94A259266",
}, {
.etype = KRB5_ENCTYPE_CAMELLIA256_CTS_CMAC,
.name = "enc 1 plain",
.plain = "'1",
.conf = "DEF487FCEBE6DE6346D4DA4521BBA2D2",
.K0 = "1B97FE0A190E2021EB30753E1B6E1E77B0754B1D684610355864104963463833",
.usage = 1,
.ct = "2C9C1570133C99BF6A34BC1B0212002FD194338749DB4135497A347CFCD9D18A12",
}, {
.etype = KRB5_ENCTYPE_CAMELLIA256_CTS_CMAC,
.name = "enc 9 plain",
.plain = "'9 bytesss",
.conf = "AD4FF904D34E555384B14100FC465F88",
.K0 = "32164C5B434D1D1538E4CFD9BE8040FE8C4AC7ACC4B93D3314D2133668147A05",
.usage = 2,
.ct = "9C6DE75F812DE7ED0D28B2963557A115640998275B0AF5152709913FF52A2A9C8E63B872F92E64C839",
}, {
.etype = KRB5_ENCTYPE_CAMELLIA256_CTS_CMAC,
.name = "enc 13 plain",
.plain = "'13 bytes byte",
.conf = "CF9BCA6DF1144E0C0AF9B8F34C90D514",
.K0 = "B038B132CD8E06612267FAB7170066D88AECCBA0B744BFC60DC89BCA182D0715",
.usage = 3,
.ct = "EEEC85A9813CDC536772AB9B42DEFC5706F726E975DDE05A87EB5406EA324CA185C9986B42AABE794B84821BEE",
}, {
.etype = KRB5_ENCTYPE_CAMELLIA256_CTS_CMAC,
.name = "enc 30 plain",
.plain = "'30 bytes bytes bytes bytes byt",
.conf = "644DEF38DA35007275878D216855E228",
.K0 = "CCFCD349BF4C6677E86E4B02B8EAB924A546AC731CF9BF6989B996E7D6BFBBA7",
.usage = 4,
.ct = "0E44680985855F2D1F1812529CA83BFD8E349DE6FD9ADA0BAAA048D68E265FEBF34AD1255A344999AD37146887A6C6845731AC7F46376A0504CD06571474",
},
{/* END */}
};
/*
* Checksum generation tests.
*/
const struct krb5_mic_test krb5_mic_tests[] = {
/* rfc8009 Appendix A */
{
.etype = KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128,
.name = "mic",
.plain = "000102030405060708090A0B0C0D0E0F1011121314",
.Kc = "B31A018A48F54776F403E9A396325DC3",
.mic = "D78367186643D67B411CBA9139FC1DEE",
}, {
.etype = KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192,
.name = "mic",
.plain = "000102030405060708090A0B0C0D0E0F1011121314",
.Kc = "EF5718BE86CC84963D8BBB5031E9F5C4BA41F28FAF69E73D",
.mic = "45EE791567EEFCA37F4AC1E0222DE80D43C3BFA06699672A",
},
/* rfc6803 sec 10 */
{
.etype = KRB5_ENCTYPE_CAMELLIA128_CTS_CMAC,
.name = "mic abc",
.plain = "'abcdefghijk",
.K0 = "1DC46A8D763F4F93742BCBA3387576C3",
.usage = 7,
.mic = "1178E6C5C47A8C1AE0C4B9C7D4EB7B6B",
}, {
.etype = KRB5_ENCTYPE_CAMELLIA128_CTS_CMAC,
.name = "mic ABC",
.plain = "'ABCDEFGHIJKLMNOPQRSTUVWXYZ",
.K0 = "5027BC231D0F3A9D23333F1CA6FDBE7C",
.usage = 8,
.mic = "D1B34F7004A731F23A0C00BF6C3F753A",
}, {
.etype = KRB5_ENCTYPE_CAMELLIA256_CTS_CMAC,
.name = "mic 123",
.plain = "'123456789",
.K0 = "B61C86CC4E5D2757545AD423399FB7031ECAB913CBB900BD7A3C6DD8BF92015B",
.usage = 9,
.mic = "87A12CFD2B96214810F01C826E7744B1",
}, {
.etype = KRB5_ENCTYPE_CAMELLIA256_CTS_CMAC,
.name = "mic !@#",
.plain = "'!@#$%^&*()!@#$%^&*()!@#$%^&*()",
.K0 = "32164C5B434D1D1538E4CFD9BE8040FE8C4AC7ACC4B93D3314D2133668147A05",
.usage = 10,
.mic = "3FA0B42355E52B189187294AA252AB64",
},
{/* END */}
};

504
crypto/krb5enc.c Normal file
View File

@ -0,0 +1,504 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* AEAD wrapper for Kerberos 5 RFC3961 simplified profile.
*
* Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* Derived from authenc:
* Copyright (c) 2007-2015 Herbert Xu <herbert@gondor.apana.org.au>
*/
#include <crypto/internal/aead.h>
#include <crypto/internal/hash.h>
#include <crypto/internal/skcipher.h>
#include <crypto/authenc.h>
#include <crypto/scatterwalk.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/rtnetlink.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
struct krb5enc_instance_ctx {
struct crypto_ahash_spawn auth;
struct crypto_skcipher_spawn enc;
unsigned int reqoff;
};
struct krb5enc_ctx {
struct crypto_ahash *auth;
struct crypto_skcipher *enc;
};
struct krb5enc_request_ctx {
struct scatterlist src[2];
struct scatterlist dst[2];
char tail[];
};
static void krb5enc_request_complete(struct aead_request *req, int err)
{
if (err != -EINPROGRESS)
aead_request_complete(req, err);
}
/**
* crypto_krb5enc_extractkeys - Extract Ke and Ki keys from the key blob.
* @keys: Where to put the key sizes and pointers
* @key: Encoded key material
* @keylen: Amount of key material
*
* Decode the key blob we're given. It starts with an rtattr that indicates
* the format and the length. Format CRYPTO_AUTHENC_KEYA_PARAM is:
*
* rtattr || __be32 enckeylen || authkey || enckey
*
* Note that the rtattr is in cpu-endian form, unlike enckeylen. This must be
* handled correctly in static testmgr data.
*/
int crypto_krb5enc_extractkeys(struct crypto_authenc_keys *keys, const u8 *key,
unsigned int keylen)
{
struct rtattr *rta = (struct rtattr *)key;
struct crypto_authenc_key_param *param;
if (!RTA_OK(rta, keylen))
return -EINVAL;
if (rta->rta_type != CRYPTO_AUTHENC_KEYA_PARAM)
return -EINVAL;
/*
* RTA_OK() didn't align the rtattr's payload when validating that it
* fits in the buffer. Yet, the keys should start on the next 4-byte
* aligned boundary. To avoid confusion, require that the rtattr
* payload be exactly the param struct, which has a 4-byte aligned size.
*/
if (RTA_PAYLOAD(rta) != sizeof(*param))
return -EINVAL;
BUILD_BUG_ON(sizeof(*param) % RTA_ALIGNTO);
param = RTA_DATA(rta);
keys->enckeylen = be32_to_cpu(param->enckeylen);
key += rta->rta_len;
keylen -= rta->rta_len;
if (keylen < keys->enckeylen)
return -EINVAL;
keys->authkeylen = keylen - keys->enckeylen;
keys->authkey = key;
keys->enckey = key + keys->authkeylen;
return 0;
}
EXPORT_SYMBOL(crypto_krb5enc_extractkeys);
static int krb5enc_setkey(struct crypto_aead *krb5enc, const u8 *key,
unsigned int keylen)
{
struct crypto_authenc_keys keys;
struct krb5enc_ctx *ctx = crypto_aead_ctx(krb5enc);
struct crypto_skcipher *enc = ctx->enc;
struct crypto_ahash *auth = ctx->auth;
unsigned int flags = crypto_aead_get_flags(krb5enc);
int err = -EINVAL;
if (crypto_krb5enc_extractkeys(&keys, key, keylen) != 0)
goto out;
crypto_ahash_clear_flags(auth, CRYPTO_TFM_REQ_MASK);
crypto_ahash_set_flags(auth, flags & CRYPTO_TFM_REQ_MASK);
err = crypto_ahash_setkey(auth, keys.authkey, keys.authkeylen);
if (err)
goto out;
crypto_skcipher_clear_flags(enc, CRYPTO_TFM_REQ_MASK);
crypto_skcipher_set_flags(enc, flags & CRYPTO_TFM_REQ_MASK);
err = crypto_skcipher_setkey(enc, keys.enckey, keys.enckeylen);
out:
memzero_explicit(&keys, sizeof(keys));
return err;
}
static void krb5enc_encrypt_done(void *data, int err)
{
struct aead_request *req = data;
krb5enc_request_complete(req, err);
}
/*
* Start the encryption of the plaintext. We skip over the associated data as
* that only gets included in the hash.
*/
static int krb5enc_dispatch_encrypt(struct aead_request *req,
unsigned int flags)
{
struct crypto_aead *krb5enc = crypto_aead_reqtfm(req);
struct aead_instance *inst = aead_alg_instance(krb5enc);
struct krb5enc_ctx *ctx = crypto_aead_ctx(krb5enc);
struct krb5enc_instance_ctx *ictx = aead_instance_ctx(inst);
struct krb5enc_request_ctx *areq_ctx = aead_request_ctx(req);
struct crypto_skcipher *enc = ctx->enc;
struct skcipher_request *skreq = (void *)(areq_ctx->tail +
ictx->reqoff);
struct scatterlist *src, *dst;
src = scatterwalk_ffwd(areq_ctx->src, req->src, req->assoclen);
if (req->src == req->dst)
dst = src;
else
dst = scatterwalk_ffwd(areq_ctx->dst, req->dst, req->assoclen);
skcipher_request_set_tfm(skreq, enc);
skcipher_request_set_callback(skreq, aead_request_flags(req),
krb5enc_encrypt_done, req);
skcipher_request_set_crypt(skreq, src, dst, req->cryptlen, req->iv);
return crypto_skcipher_encrypt(skreq);
}
/*
* Insert the hash into the checksum field in the destination buffer directly
* after the encrypted region.
*/
static void krb5enc_insert_checksum(struct aead_request *req, u8 *hash)
{
struct crypto_aead *krb5enc = crypto_aead_reqtfm(req);
scatterwalk_map_and_copy(hash, req->dst,
req->assoclen + req->cryptlen,
crypto_aead_authsize(krb5enc), 1);
}
/*
* Upon completion of an asynchronous digest, transfer the hash to the checksum
* field.
*/
static void krb5enc_encrypt_ahash_done(void *data, int err)
{
struct aead_request *req = data;
struct crypto_aead *krb5enc = crypto_aead_reqtfm(req);
struct aead_instance *inst = aead_alg_instance(krb5enc);
struct krb5enc_instance_ctx *ictx = aead_instance_ctx(inst);
struct krb5enc_request_ctx *areq_ctx = aead_request_ctx(req);
struct ahash_request *ahreq = (void *)(areq_ctx->tail + ictx->reqoff);
if (err)
return krb5enc_request_complete(req, err);
krb5enc_insert_checksum(req, ahreq->result);
err = krb5enc_dispatch_encrypt(req, 0);
if (err != -EINPROGRESS)
aead_request_complete(req, err);
}
/*
* Start the digest of the plaintext for encryption. In theory, this could be
* run in parallel with the encryption, provided the src and dst buffers don't
* overlap.
*/
static int krb5enc_dispatch_encrypt_hash(struct aead_request *req)
{
struct crypto_aead *krb5enc = crypto_aead_reqtfm(req);
struct aead_instance *inst = aead_alg_instance(krb5enc);
struct krb5enc_ctx *ctx = crypto_aead_ctx(krb5enc);
struct krb5enc_instance_ctx *ictx = aead_instance_ctx(inst);
struct crypto_ahash *auth = ctx->auth;
struct krb5enc_request_ctx *areq_ctx = aead_request_ctx(req);
struct ahash_request *ahreq = (void *)(areq_ctx->tail + ictx->reqoff);
u8 *hash = areq_ctx->tail;
int err;
ahash_request_set_callback(ahreq, aead_request_flags(req),
krb5enc_encrypt_ahash_done, req);
ahash_request_set_tfm(ahreq, auth);
ahash_request_set_crypt(ahreq, req->src, hash, req->assoclen + req->cryptlen);
err = crypto_ahash_digest(ahreq);
if (err)
return err;
krb5enc_insert_checksum(req, hash);
return 0;
}
/*
* Process an encryption operation. We can perform the cipher and the hash in
* parallel, provided the src and dst buffers are separate.
*/
static int krb5enc_encrypt(struct aead_request *req)
{
int err;
err = krb5enc_dispatch_encrypt_hash(req);
if (err < 0)
return err;
return krb5enc_dispatch_encrypt(req, aead_request_flags(req));
}
static int krb5enc_verify_hash(struct aead_request *req)
{
struct crypto_aead *krb5enc = crypto_aead_reqtfm(req);
struct aead_instance *inst = aead_alg_instance(krb5enc);
struct krb5enc_instance_ctx *ictx = aead_instance_ctx(inst);
struct krb5enc_request_ctx *areq_ctx = aead_request_ctx(req);
struct ahash_request *ahreq = (void *)(areq_ctx->tail + ictx->reqoff);
unsigned int authsize = crypto_aead_authsize(krb5enc);
u8 *calc_hash = areq_ctx->tail;
u8 *msg_hash = areq_ctx->tail + authsize;
scatterwalk_map_and_copy(msg_hash, req->src, ahreq->nbytes, authsize, 0);
if (crypto_memneq(msg_hash, calc_hash, authsize))
return -EBADMSG;
return 0;
}
static void krb5enc_decrypt_hash_done(void *data, int err)
{
struct aead_request *req = data;
if (err)
return krb5enc_request_complete(req, err);
err = krb5enc_verify_hash(req);
krb5enc_request_complete(req, err);
}
/*
* Dispatch the hashing of the plaintext after we've done the decryption.
*/
static int krb5enc_dispatch_decrypt_hash(struct aead_request *req)
{
struct crypto_aead *krb5enc = crypto_aead_reqtfm(req);
struct aead_instance *inst = aead_alg_instance(krb5enc);
struct krb5enc_ctx *ctx = crypto_aead_ctx(krb5enc);
struct krb5enc_instance_ctx *ictx = aead_instance_ctx(inst);
struct krb5enc_request_ctx *areq_ctx = aead_request_ctx(req);
struct ahash_request *ahreq = (void *)(areq_ctx->tail + ictx->reqoff);
struct crypto_ahash *auth = ctx->auth;
unsigned int authsize = crypto_aead_authsize(krb5enc);
u8 *hash = areq_ctx->tail;
int err;
ahash_request_set_tfm(ahreq, auth);
ahash_request_set_crypt(ahreq, req->dst, hash,
req->assoclen + req->cryptlen - authsize);
ahash_request_set_callback(ahreq, aead_request_flags(req),
krb5enc_decrypt_hash_done, req);
err = crypto_ahash_digest(ahreq);
if (err < 0)
return err;
return krb5enc_verify_hash(req);
}
/*
* Dispatch the decryption of the ciphertext.
*/
static int krb5enc_dispatch_decrypt(struct aead_request *req)
{
struct crypto_aead *krb5enc = crypto_aead_reqtfm(req);
struct aead_instance *inst = aead_alg_instance(krb5enc);
struct krb5enc_ctx *ctx = crypto_aead_ctx(krb5enc);
struct krb5enc_instance_ctx *ictx = aead_instance_ctx(inst);
struct krb5enc_request_ctx *areq_ctx = aead_request_ctx(req);
struct skcipher_request *skreq = (void *)(areq_ctx->tail +
ictx->reqoff);
unsigned int authsize = crypto_aead_authsize(krb5enc);
struct scatterlist *src, *dst;
src = scatterwalk_ffwd(areq_ctx->src, req->src, req->assoclen);
dst = src;
if (req->src != req->dst)
dst = scatterwalk_ffwd(areq_ctx->dst, req->dst, req->assoclen);
skcipher_request_set_tfm(skreq, ctx->enc);
skcipher_request_set_callback(skreq, aead_request_flags(req),
req->base.complete, req->base.data);
skcipher_request_set_crypt(skreq, src, dst,
req->cryptlen - authsize, req->iv);
return crypto_skcipher_decrypt(skreq);
}
static int krb5enc_decrypt(struct aead_request *req)
{
int err;
err = krb5enc_dispatch_decrypt(req);
if (err < 0)
return err;
return krb5enc_dispatch_decrypt_hash(req);
}
static int krb5enc_init_tfm(struct crypto_aead *tfm)
{
struct aead_instance *inst = aead_alg_instance(tfm);
struct krb5enc_instance_ctx *ictx = aead_instance_ctx(inst);
struct krb5enc_ctx *ctx = crypto_aead_ctx(tfm);
struct crypto_ahash *auth;
struct crypto_skcipher *enc;
int err;
auth = crypto_spawn_ahash(&ictx->auth);
if (IS_ERR(auth))
return PTR_ERR(auth);
enc = crypto_spawn_skcipher(&ictx->enc);
err = PTR_ERR(enc);
if (IS_ERR(enc))
goto err_free_ahash;
ctx->auth = auth;
ctx->enc = enc;
crypto_aead_set_reqsize(
tfm,
sizeof(struct krb5enc_request_ctx) +
ictx->reqoff + /* Space for two checksums */
umax(sizeof(struct ahash_request) + crypto_ahash_reqsize(auth),
sizeof(struct skcipher_request) + crypto_skcipher_reqsize(enc)));
return 0;
err_free_ahash:
crypto_free_ahash(auth);
return err;
}
static void krb5enc_exit_tfm(struct crypto_aead *tfm)
{
struct krb5enc_ctx *ctx = crypto_aead_ctx(tfm);
crypto_free_ahash(ctx->auth);
crypto_free_skcipher(ctx->enc);
}
static void krb5enc_free(struct aead_instance *inst)
{
struct krb5enc_instance_ctx *ctx = aead_instance_ctx(inst);
crypto_drop_skcipher(&ctx->enc);
crypto_drop_ahash(&ctx->auth);
kfree(inst);
}
/*
* Create an instance of a template for a specific hash and cipher pair.
*/
static int krb5enc_create(struct crypto_template *tmpl, struct rtattr **tb)
{
struct krb5enc_instance_ctx *ictx;
struct skcipher_alg_common *enc;
struct hash_alg_common *auth;
struct aead_instance *inst;
struct crypto_alg *auth_base;
u32 mask;
int err;
err = crypto_check_attr_type(tb, CRYPTO_ALG_TYPE_AEAD, &mask);
if (err) {
pr_err("attr_type failed\n");
return err;
}
inst = kzalloc(sizeof(*inst) + sizeof(*ictx), GFP_KERNEL);
if (!inst)
return -ENOMEM;
ictx = aead_instance_ctx(inst);
err = crypto_grab_ahash(&ictx->auth, aead_crypto_instance(inst),
crypto_attr_alg_name(tb[1]), 0, mask);
if (err) {
pr_err("grab ahash failed\n");
goto err_free_inst;
}
auth = crypto_spawn_ahash_alg(&ictx->auth);
auth_base = &auth->base;
err = crypto_grab_skcipher(&ictx->enc, aead_crypto_instance(inst),
crypto_attr_alg_name(tb[2]), 0, mask);
if (err) {
pr_err("grab skcipher failed\n");
goto err_free_inst;
}
enc = crypto_spawn_skcipher_alg_common(&ictx->enc);
ictx->reqoff = 2 * auth->digestsize;
err = -ENAMETOOLONG;
if (snprintf(inst->alg.base.cra_name, CRYPTO_MAX_ALG_NAME,
"krb5enc(%s,%s)", auth_base->cra_name,
enc->base.cra_name) >=
CRYPTO_MAX_ALG_NAME)
goto err_free_inst;
if (snprintf(inst->alg.base.cra_driver_name, CRYPTO_MAX_ALG_NAME,
"krb5enc(%s,%s)", auth_base->cra_driver_name,
enc->base.cra_driver_name) >= CRYPTO_MAX_ALG_NAME)
goto err_free_inst;
inst->alg.base.cra_priority = enc->base.cra_priority * 10 +
auth_base->cra_priority;
inst->alg.base.cra_blocksize = enc->base.cra_blocksize;
inst->alg.base.cra_alignmask = enc->base.cra_alignmask;
inst->alg.base.cra_ctxsize = sizeof(struct krb5enc_ctx);
inst->alg.ivsize = enc->ivsize;
inst->alg.chunksize = enc->chunksize;
inst->alg.maxauthsize = auth->digestsize;
inst->alg.init = krb5enc_init_tfm;
inst->alg.exit = krb5enc_exit_tfm;
inst->alg.setkey = krb5enc_setkey;
inst->alg.encrypt = krb5enc_encrypt;
inst->alg.decrypt = krb5enc_decrypt;
inst->free = krb5enc_free;
err = aead_register_instance(tmpl, inst);
if (err) {
pr_err("ref failed\n");
goto err_free_inst;
}
return 0;
err_free_inst:
krb5enc_free(inst);
return err;
}
static struct crypto_template crypto_krb5enc_tmpl = {
.name = "krb5enc",
.create = krb5enc_create,
.module = THIS_MODULE,
};
static int __init crypto_krb5enc_module_init(void)
{
return crypto_register_template(&crypto_krb5enc_tmpl);
}
static void __exit crypto_krb5enc_module_exit(void)
{
crypto_unregister_template(&crypto_krb5enc_tmpl);
}
subsys_initcall(crypto_krb5enc_module_init);
module_exit(crypto_krb5enc_module_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Simple AEAD wrapper for Kerberos 5 RFC3961");
MODULE_ALIAS_CRYPTO("krb5enc");

View File

@ -4505,6 +4505,12 @@ static const struct alg_test_desc alg_test_descs[] = {
.alg = "authenc(hmac(sha256),ctr(aes))",
.test = alg_test_null,
.fips_allowed = 1,
}, {
.alg = "authenc(hmac(sha256),cts(cbc(aes)))",
.test = alg_test_aead,
.suite = {
.aead = __VECS(krb5_test_aes128_cts_hmac_sha256_128)
}
}, {
.alg = "authenc(hmac(sha256),rfc3686(ctr(aes)))",
.test = alg_test_null,
@ -4525,6 +4531,12 @@ static const struct alg_test_desc alg_test_descs[] = {
.alg = "authenc(hmac(sha384),ctr(aes))",
.test = alg_test_null,
.fips_allowed = 1,
}, {
.alg = "authenc(hmac(sha384),cts(cbc(aes)))",
.test = alg_test_aead,
.suite = {
.aead = __VECS(krb5_test_aes256_cts_hmac_sha384_192)
}
}, {
.alg = "authenc(hmac(sha384),rfc3686(ctr(aes)))",
.test = alg_test_null,
@ -5398,6 +5410,10 @@ static const struct alg_test_desc alg_test_descs[] = {
.alg = "jitterentropy_rng",
.fips_allowed = 1,
.test = alg_test_null,
}, {
.alg = "krb5enc(cmac(camellia),cts(cbc(camellia)))",
.test = alg_test_aead,
.suite.aead = __VECS(krb5_test_camellia_cts_cmac)
}, {
.alg = "lrw(aes)",
.generic_driver = "lrw(ecb(aes-generic))",

View File

@ -38894,4 +38894,355 @@ static const struct cipher_testvec aes_hctr2_tv_template[] = {
};
#ifdef __LITTLE_ENDIAN
#define AUTHENC_KEY_HEADER(enckeylen) \
"\x08\x00\x01\x00" /* LE rtattr */ \
enckeylen /* crypto_authenc_key_param */
#else
#define AUTHENC_KEY_HEADER(enckeylen) \
"\x00\x08\x00\x01" /* BE rtattr */ \
enckeylen /* crypto_authenc_key_param */
#endif
static const struct aead_testvec krb5_test_aes128_cts_hmac_sha256_128[] = {
/* rfc8009 Appendix A */
{
/* "enc no plain" */
.key =
AUTHENC_KEY_HEADER("\x00\x00\x00\x10")
"\x9F\xDA\x0E\x56\xAB\x2D\x85\xE1\x56\x9A\x68\x86\x96\xC2\x6A\x6C" // Ki
"\x9B\x19\x7D\xD1\xE8\xC5\x60\x9D\x6E\x67\xC3\xE3\x7C\x62\xC7\x2E", // Ke
.klen = 4 + 4 + 16 + 16,
.ptext =
"\x7E\x58\x95\xEA\xF2\x67\x24\x35\xBA\xD8\x17\xF5\x45\xA3\x71\x48" // Confounder
"", // Plain
.plen = 16 + 0,
.ctext =
"\xEF\x85\xFB\x89\x0B\xB8\x47\x2F\x4D\xAB\x20\x39\x4D\xCA\x78\x1D"
"\xAD\x87\x7E\xDA\x39\xD5\x0C\x87\x0C\x0D\x5A\x0A\x8E\x48\xC7\x18",
.clen = 16 + 0 + 16,
.assoc = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // IV
.alen = 16,
}, {
/* "enc plain<block" */
.key =
AUTHENC_KEY_HEADER("\x00\x00\x00\x10")
"\x9F\xDA\x0E\x56\xAB\x2D\x85\xE1\x56\x9A\x68\x86\x96\xC2\x6A\x6C" // Ki
"\x9B\x19\x7D\xD1\xE8\xC5\x60\x9D\x6E\x67\xC3\xE3\x7C\x62\xC7\x2E", // Ke
.klen = 4 + 4 + 16 + 16,
.ptext =
"\x7B\xCA\x28\x5E\x2F\xD4\x13\x0F\xB5\x5B\x1A\x5C\x83\xBC\x5B\x24" // Confounder
"\x00\x01\x02\x03\x04\x05", // Plain
.plen = 16 + 6,
.ctext =
"\x84\xD7\xF3\x07\x54\xED\x98\x7B\xAB\x0B\xF3\x50\x6B\xEB\x09\xCF"
"\xB5\x54\x02\xCE\xF7\xE6\x87\x7C\xE9\x9E\x24\x7E\x52\xD1\x6E\xD4"
"\x42\x1D\xFD\xF8\x97\x6C",
.clen = 16 + 6 + 16,
.assoc = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // IV
.alen = 16,
}, {
/* "enc plain==block" */
.key =
AUTHENC_KEY_HEADER("\x00\x00\x00\x10")
"\x9F\xDA\x0E\x56\xAB\x2D\x85\xE1\x56\x9A\x68\x86\x96\xC2\x6A\x6C" // Ki
"\x9B\x19\x7D\xD1\xE8\xC5\x60\x9D\x6E\x67\xC3\xE3\x7C\x62\xC7\x2E", // Ke
.klen = 4 + 4 + 16 + 16,
.ptext =
"\x56\xAB\x21\x71\x3F\xF6\x2C\x0A\x14\x57\x20\x0F\x6F\xA9\x94\x8F" // Confounder
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", // Plain
.plen = 16 + 16,
.ctext =
"\x35\x17\xD6\x40\xF5\x0D\xDC\x8A\xD3\x62\x87\x22\xB3\x56\x9D\x2A"
"\xE0\x74\x93\xFA\x82\x63\x25\x40\x80\xEA\x65\xC1\x00\x8E\x8F\xC2"
"\x95\xFB\x48\x52\xE7\xD8\x3E\x1E\x7C\x48\xC3\x7E\xEB\xE6\xB0\xD3",
.clen = 16 + 16 + 16,
.assoc = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // IV
.alen = 16,
}, {
/* "enc plain>block" */
.key =
AUTHENC_KEY_HEADER("\x00\x00\x00\x10")
"\x9F\xDA\x0E\x56\xAB\x2D\x85\xE1\x56\x9A\x68\x86\x96\xC2\x6A\x6C" // Ki
"\x9B\x19\x7D\xD1\xE8\xC5\x60\x9D\x6E\x67\xC3\xE3\x7C\x62\xC7\x2E", // Ke
.klen = 4 + 4 + 16 + 16,
.ptext =
"\xA7\xA4\xE2\x9A\x47\x28\xCE\x10\x66\x4F\xB6\x4E\x49\xAD\x3F\xAC" // Confounder
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
"\x10\x11\x12\x13\x14", // Plain
.plen = 16 + 21,
.ctext =
"\x72\x0F\x73\xB1\x8D\x98\x59\xCD\x6C\xCB\x43\x46\x11\x5C\xD3\x36"
"\xC7\x0F\x58\xED\xC0\xC4\x43\x7C\x55\x73\x54\x4C\x31\xC8\x13\xBC"
"\xE1\xE6\xD0\x72\xC1\x86\xB3\x9A\x41\x3C\x2F\x92\xCA\x9B\x83\x34"
"\xA2\x87\xFF\xCB\xFC",
.clen = 16 + 21 + 16,
.assoc = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // IV
.alen = 16,
},
};
static const struct aead_testvec krb5_test_aes256_cts_hmac_sha384_192[] = {
/* rfc8009 Appendix A */
{
/* "enc no plain" */
.key =
AUTHENC_KEY_HEADER("\x00\x00\x00\x20")
"\x69\xB1\x65\x14\xE3\xCD\x8E\x56\xB8\x20\x10\xD5\xC7\x30\x12\xB6"
"\x22\xC4\xD0\x0F\xFC\x23\xED\x1F" // Ki
"\x56\xAB\x22\xBE\xE6\x3D\x82\xD7\xBC\x52\x27\xF6\x77\x3F\x8E\xA7"
"\xA5\xEB\x1C\x82\x51\x60\xC3\x83\x12\x98\x0C\x44\x2E\x5C\x7E\x49", // Ke
.klen = 4 + 4 + 32 + 24,
.ptext =
"\xF7\x64\xE9\xFA\x15\xC2\x76\x47\x8B\x2C\x7D\x0C\x4E\x5F\x58\xE4" // Confounder
"", // Plain
.plen = 16 + 0,
.ctext =
"\x41\xF5\x3F\xA5\xBF\xE7\x02\x6D\x91\xFA\xF9\xBE\x95\x91\x95\xA0"
"\x58\x70\x72\x73\xA9\x6A\x40\xF0\xA0\x19\x60\x62\x1A\xC6\x12\x74"
"\x8B\x9B\xBF\xBE\x7E\xB4\xCE\x3C",
.clen = 16 + 0 + 24,
.assoc = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // IV
.alen = 16,
}, {
/* "enc plain<block" */
.key =
AUTHENC_KEY_HEADER("\x00\x00\x00\x20")
"\x69\xB1\x65\x14\xE3\xCD\x8E\x56\xB8\x20\x10\xD5\xC7\x30\x12\xB6"
"\x22\xC4\xD0\x0F\xFC\x23\xED\x1F" // Ki
"\x56\xAB\x22\xBE\xE6\x3D\x82\xD7\xBC\x52\x27\xF6\x77\x3F\x8E\xA7"
"\xA5\xEB\x1C\x82\x51\x60\xC3\x83\x12\x98\x0C\x44\x2E\x5C\x7E\x49", // Ke
.klen = 4 + 4 + 32 + 24,
.ptext =
"\xB8\x0D\x32\x51\xC1\xF6\x47\x14\x94\x25\x6F\xFE\x71\x2D\x0B\x9A" // Confounder
"\x00\x01\x02\x03\x04\x05", // Plain
.plen = 16 + 6,
.ctext =
"\x4E\xD7\xB3\x7C\x2B\xCA\xC8\xF7\x4F\x23\xC1\xCF\x07\xE6\x2B\xC7"
"\xB7\x5F\xB3\xF6\x37\xB9\xF5\x59\xC7\xF6\x64\xF6\x9E\xAB\x7B\x60"
"\x92\x23\x75\x26\xEA\x0D\x1F\x61\xCB\x20\xD6\x9D\x10\xF2",
.clen = 16 + 6 + 24,
.assoc = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // IV
.alen = 16,
}, {
/* "enc plain==block" */
.key =
AUTHENC_KEY_HEADER("\x00\x00\x00\x20")
"\x69\xB1\x65\x14\xE3\xCD\x8E\x56\xB8\x20\x10\xD5\xC7\x30\x12\xB6"
"\x22\xC4\xD0\x0F\xFC\x23\xED\x1F" // Ki
"\x56\xAB\x22\xBE\xE6\x3D\x82\xD7\xBC\x52\x27\xF6\x77\x3F\x8E\xA7"
"\xA5\xEB\x1C\x82\x51\x60\xC3\x83\x12\x98\x0C\x44\x2E\x5C\x7E\x49", // Ke
.klen = 4 + 4 + 32 + 24,
.ptext =
"\x53\xBF\x8A\x0D\x10\x52\x65\xD4\xE2\x76\x42\x86\x24\xCE\x5E\x63" // Confounder
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", // Plain
.plen = 16 + 16,
.ctext =
"\xBC\x47\xFF\xEC\x79\x98\xEB\x91\xE8\x11\x5C\xF8\xD1\x9D\xAC\x4B"
"\xBB\xE2\xE1\x63\xE8\x7D\xD3\x7F\x49\xBE\xCA\x92\x02\x77\x64\xF6"
"\x8C\xF5\x1F\x14\xD7\x98\xC2\x27\x3F\x35\xDF\x57\x4D\x1F\x93\x2E"
"\x40\xC4\xFF\x25\x5B\x36\xA2\x66",
.clen = 16 + 16 + 24,
.assoc = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // IV
.alen = 16,
}, {
/* "enc plain>block" */
.key =
AUTHENC_KEY_HEADER("\x00\x00\x00\x20")
"\x69\xB1\x65\x14\xE3\xCD\x8E\x56\xB8\x20\x10\xD5\xC7\x30\x12\xB6"
"\x22\xC4\xD0\x0F\xFC\x23\xED\x1F" // Ki
"\x56\xAB\x22\xBE\xE6\x3D\x82\xD7\xBC\x52\x27\xF6\x77\x3F\x8E\xA7"
"\xA5\xEB\x1C\x82\x51\x60\xC3\x83\x12\x98\x0C\x44\x2E\x5C\x7E\x49", // Ke
.klen = 4 + 4 + 32 + 24,
.ptext =
"\x76\x3E\x65\x36\x7E\x86\x4F\x02\xF5\x51\x53\xC7\xE3\xB5\x8A\xF1" // Confounder
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
"\x10\x11\x12\x13\x14", // Plain
.plen = 16 + 21,
.ctext =
"\x40\x01\x3E\x2D\xF5\x8E\x87\x51\x95\x7D\x28\x78\xBC\xD2\xD6\xFE"
"\x10\x1C\xCF\xD5\x56\xCB\x1E\xAE\x79\xDB\x3C\x3E\xE8\x64\x29\xF2"
"\xB2\xA6\x02\xAC\x86\xFE\xF6\xEC\xB6\x47\xD6\x29\x5F\xAE\x07\x7A"
"\x1F\xEB\x51\x75\x08\xD2\xC1\x6B\x41\x92\xE0\x1F\x62",
.clen = 16 + 21 + 24,
.assoc = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // IV
.alen = 16,
},
};
static const struct aead_testvec krb5_test_camellia_cts_cmac[] = {
/* rfc6803 sec 10 */
{
// "enc no plain"
.key =
AUTHENC_KEY_HEADER("\x00\x00\x00\x10")
"\x45\xeb\x66\xe2\xef\xa8\x77\x8f\x7d\xf1\x46\x54\x53\x05\x98\x06" // Ki
"\xe9\x9b\x82\xb3\x6c\x4a\xe8\xea\x19\xe9\x5d\xfa\x9e\xde\x88\x2c", // Ke
.klen = 4 + 4 + 16 * 2,
.ptext =
"\xB6\x98\x22\xA1\x9A\x6B\x09\xC0\xEB\xC8\x55\x7D\x1F\x1B\x6C\x0A" // Confounder
"", // Plain
.plen = 16 + 0,
.ctext =
"\xC4\x66\xF1\x87\x10\x69\x92\x1E\xDB\x7C\x6F\xDE\x24\x4A\x52\xDB"
"\x0B\xA1\x0E\xDC\x19\x7B\xDB\x80\x06\x65\x8C\xA3\xCC\xCE\x6E\xB8",
.clen = 16 + 0 + 16,
}, {
// "enc 1 plain",
.key =
AUTHENC_KEY_HEADER("\x00\x00\x00\x10")
"\x13\x5f\xe7\x11\x6f\x53\xc2\xaa\x36\x12\xb7\xea\xe0\xf2\x84\xaa" // Ki
"\xa7\xed\xcd\x53\x97\xea\x6d\x12\xb0\xaf\xf4\xcb\x8d\xaa\x57\xad", // Ke
.klen = 4 + 4 + 16 * 2,
.ptext =
"\x6F\x2F\xC3\xC2\xA1\x66\xFD\x88\x98\x96\x7A\x83\xDE\x95\x96\xD9" // Confounder
"1", // Plain
.plen = 16 + 1,
.ctext =
"\x84\x2D\x21\xFD\x95\x03\x11\xC0\xDD\x46\x4A\x3F\x4B\xE8\xD6\xDA"
"\x88\xA5\x6D\x55\x9C\x9B\x47\xD3\xF9\xA8\x50\x67\xAF\x66\x15\x59"
"\xB8",
.clen = 16 + 1 + 16,
}, {
// "enc 9 plain",
.key =
AUTHENC_KEY_HEADER("\x00\x00\x00\x10")
"\x10\x2c\x34\xd0\x75\x74\x9f\x77\x8a\x15\xca\xd1\xe9\x7d\xa9\x86" // Ki
"\xdd\xe4\x2e\xca\x7c\xd9\x86\x3f\xc3\xce\x89\xcb\xc9\x43\x62\xd7", // Ke
.klen = 4 + 4 + 16 * 2,
.ptext =
"\xA5\xB4\xA7\x1E\x07\x7A\xEE\xF9\x3C\x87\x63\xC1\x8F\xDB\x1F\x10" // Confounder
"9 bytesss", // Plain
.plen = 16 + 9,
.ctext =
"\x61\x9F\xF0\x72\xE3\x62\x86\xFF\x0A\x28\xDE\xB3\xA3\x52\xEC\x0D"
"\x0E\xDF\x5C\x51\x60\xD6\x63\xC9\x01\x75\x8C\xCF\x9D\x1E\xD3\x3D"
"\x71\xDB\x8F\x23\xAA\xBF\x83\x48\xA0",
.clen = 16 + 9 + 16,
}, {
// "enc 13 plain",
.key =
AUTHENC_KEY_HEADER("\x00\x00\x00\x10")
"\xb8\xc4\x38\xcc\x1a\x00\x60\xfc\x91\x3a\x8e\x07\x16\x96\xbd\x08" // Ki
"\xc3\x11\x3a\x25\x85\x90\xb9\xae\xbf\x72\x1b\x1a\xf6\xb0\xcb\xf8", // Ke
.klen = 4 + 4 + 16 * 2,
.ptext =
"\x19\xFE\xE4\x0D\x81\x0C\x52\x4B\x5B\x22\xF0\x18\x74\xC6\x93\xDA" // Confounder
"13 bytes byte", // Plain
.plen = 16 + 13,
.ctext =
"\xB8\xEC\xA3\x16\x7A\xE6\x31\x55\x12\xE5\x9F\x98\xA7\xC5\x00\x20"
"\x5E\x5F\x63\xFF\x3B\xB3\x89\xAF\x1C\x41\xA2\x1D\x64\x0D\x86\x15"
"\xC9\xED\x3F\xBE\xB0\x5A\xB6\xAC\xB6\x76\x89\xB5\xEA",
.clen = 16 + 13 + 16,
}, {
// "enc 30 plain",
.key =
AUTHENC_KEY_HEADER("\x00\x00\x00\x10")
"\x18\xaf\x19\xb0\x23\x74\x44\xfd\x75\x04\xad\x7d\xbd\x48\xad\xd3" // Ki
"\x8b\x07\xee\xd3\x01\x49\x91\x6a\xa2\x0d\xb3\xf5\xce\xd8\xaf\xad", // Ke
.klen = 4 + 4 + 16 * 2,
.ptext =
"\xCA\x7A\x7A\xB4\xBE\x19\x2D\xAB\xD6\x03\x50\x6D\xB1\x9C\x39\xE2" // Confounder
"30 bytes bytes bytes bytes byt", // Plain
.plen = 16 + 30,
.ctext =
"\xA2\x6A\x39\x05\xA4\xFF\xD5\x81\x6B\x7B\x1E\x27\x38\x0D\x08\x09"
"\x0C\x8E\xC1\xF3\x04\x49\x6E\x1A\xBD\xCD\x2B\xDC\xD1\xDF\xFC\x66"
"\x09\x89\xE1\x17\xA7\x13\xDD\xBB\x57\xA4\x14\x6C\x15\x87\xCB\xA4"
"\x35\x66\x65\x59\x1D\x22\x40\x28\x2F\x58\x42\xB1\x05\xA5",
.clen = 16 + 30 + 16,
}, {
// "enc no plain",
.key =
AUTHENC_KEY_HEADER("\x00\x00\x00\x20")
"\xa2\xb8\x33\xe9\x43\xbb\x10\xee\x53\xb4\xa1\x9b\xc2\xbb\xc7\xe1"
"\x9b\x87\xad\x5d\xe9\x21\x22\xa4\x33\x8b\xe6\xf7\x32\xfd\x8a\x0e" // Ki
"\x6c\xcb\x3f\x25\xd8\xae\x57\xf4\xe8\xf6\xca\x47\x4b\xdd\xef\xf1"
"\x16\xce\x13\x1b\x3f\x71\x01\x2e\x75\x6d\x6b\x1e\x3f\x70\xa7\xf1", // Ke
.klen = 4 + 4 + 32 * 2,
.ptext =
"\x3C\xBB\xD2\xB4\x59\x17\x94\x10\x67\xF9\x65\x99\xBB\x98\x92\x6C" // Confounder
"", // Plain
.plen = 16 + 0,
.ctext =
"\x03\x88\x6D\x03\x31\x0B\x47\xA6\xD8\xF0\x6D\x7B\x94\xD1\xDD\x83"
"\x7E\xCC\xE3\x15\xEF\x65\x2A\xFF\x62\x08\x59\xD9\x4A\x25\x92\x66",
.clen = 16 + 0 + 16,
}, {
// "enc 1 plain",
.key =
AUTHENC_KEY_HEADER("\x00\x00\x00\x20")
"\x84\x61\x4b\xfa\x98\xf1\x74\x8a\xa4\xaf\x99\x2b\x8c\x26\x28\x0d"
"\xc8\x98\x73\x29\xdf\x77\x5c\x1d\xb0\x4a\x43\xf1\x21\xaa\x86\x65" // Ki
"\xe9\x31\x73\xaa\x01\xeb\x3c\x24\x62\x31\xda\xfc\x78\x02\xee\x32"
"\xaf\x24\x85\x1d\x8c\x73\x87\xd1\x8c\xb9\xb2\xc5\xb7\xf5\x70\xb8", // Ke
.klen = 4 + 4 + 32 * 2,
.ptext =
"\xDE\xF4\x87\xFC\xEB\xE6\xDE\x63\x46\xD4\xDA\x45\x21\xBB\xA2\xD2" // Confounder
"1", // Plain
.plen = 16 + 1,
.ctext =
"\x2C\x9C\x15\x70\x13\x3C\x99\xBF\x6A\x34\xBC\x1B\x02\x12\x00\x2F"
"\xD1\x94\x33\x87\x49\xDB\x41\x35\x49\x7A\x34\x7C\xFC\xD9\xD1\x8A"
"\x12",
.clen = 16 + 1 + 16,
}, {
// "enc 9 plain",
.key =
AUTHENC_KEY_HEADER("\x00\x00\x00\x20")
"\x47\xb9\xf5\xba\xd7\x63\x00\x58\x2a\x54\x45\xfa\x0c\x1b\x29\xc3"
"\xaa\x83\xec\x63\xb9\x0b\x4a\xb0\x08\x48\xc1\x85\x67\x4f\x44\xa7" // Ki
"\xcd\xa2\xd3\x9a\x9b\x24\x3f\xfe\xb5\x6e\x8d\x5f\x4b\xd5\x28\x74"
"\x1e\xcb\x52\x0c\x62\x12\x3f\xb0\x40\xb8\x41\x8b\x15\xc7\xd7\x0c", // Ke
.klen = 4 + 4 + 32 * 2,
.ptext =
"\xAD\x4F\xF9\x04\xD3\x4E\x55\x53\x84\xB1\x41\x00\xFC\x46\x5F\x88" // Confounder
"9 bytesss", // Plain
.plen = 16 + 9,
.ctext =
"\x9C\x6D\xE7\x5F\x81\x2D\xE7\xED\x0D\x28\xB2\x96\x35\x57\xA1\x15"
"\x64\x09\x98\x27\x5B\x0A\xF5\x15\x27\x09\x91\x3F\xF5\x2A\x2A\x9C"
"\x8E\x63\xB8\x72\xF9\x2E\x64\xC8\x39",
.clen = 16 + 9 + 16,
}, {
// "enc 13 plain",
.key =
AUTHENC_KEY_HEADER("\x00\x00\x00\x20")
"\x15\x2f\x8c\x9d\xc9\x85\x79\x6e\xb1\x94\xed\x14\xc5\x9e\xac\xdd"
"\x41\x8a\x33\x32\x36\xb7\x8f\xaf\xa7\xc7\x9b\x04\xe0\xac\xe7\xbf" // Ki
"\xcd\x8a\x10\xe2\x79\xda\xdd\xb6\x90\x1e\xc3\x0b\xdf\x98\x73\x25"
"\x0f\x6e\xfc\x6a\x77\x36\x7d\x74\xdc\x3e\xe7\xf7\x4b\xc7\x77\x4e", // Ke
.klen = 4 + 4 + 32 * 2,
.ptext =
"\xCF\x9B\xCA\x6D\xF1\x14\x4E\x0C\x0A\xF9\xB8\xF3\x4C\x90\xD5\x14" // Confounder
"13 bytes byte",
.plen = 16 + 13,
.ctext =
"\xEE\xEC\x85\xA9\x81\x3C\xDC\x53\x67\x72\xAB\x9B\x42\xDE\xFC\x57"
"\x06\xF7\x26\xE9\x75\xDD\xE0\x5A\x87\xEB\x54\x06\xEA\x32\x4C\xA1"
"\x85\xC9\x98\x6B\x42\xAA\xBE\x79\x4B\x84\x82\x1B\xEE",
.clen = 16 + 13 + 16,
}, {
// "enc 30 plain",
.key =
AUTHENC_KEY_HEADER("\x00\x00\x00\x20")
"\x04\x8d\xeb\xf7\xb1\x2c\x09\x32\xe8\xb2\x96\x99\x6c\x23\xf8\xb7"
"\x9d\x59\xb9\x7e\xa1\x19\xfc\x0c\x15\x6b\xf7\x88\xdc\x8c\x85\xe8" // Ki
"\x1d\x51\x47\xf3\x4b\xb0\x01\xa0\x4a\x68\xa7\x13\x46\xe7\x65\x4e"
"\x02\x23\xa6\x0d\x90\xbc\x2b\x79\xb4\xd8\x79\x56\xd4\x7c\xd4\x2a", // Ke
.klen = 4 + 4 + 32 * 2,
.ptext =
"\x64\x4D\xEF\x38\xDA\x35\x00\x72\x75\x87\x8D\x21\x68\x55\xE2\x28" // Confounder
"30 bytes bytes bytes bytes byt", // Plain
.plen = 16 + 30,
.ctext =
"\x0E\x44\x68\x09\x85\x85\x5F\x2D\x1F\x18\x12\x52\x9C\xA8\x3B\xFD"
"\x8E\x34\x9D\xE6\xFD\x9A\xDA\x0B\xAA\xA0\x48\xD6\x8E\x26\x5F\xEB"
"\xF3\x4A\xD1\x25\x5A\x34\x49\x99\xAD\x37\x14\x68\x87\xA6\xC6\x84"
"\x57\x31\xAC\x7F\x46\x37\x6A\x05\x04\xCD\x06\x57\x14\x74",
.clen = 16 + 30 + 16,
},
};
#endif /* _CRYPTO_TESTMGR_H */

View File

@ -28,5 +28,7 @@ struct crypto_authenc_keys {
int crypto_authenc_extractkeys(struct crypto_authenc_keys *keys, const u8 *key,
unsigned int keylen);
int crypto_krb5enc_extractkeys(struct crypto_authenc_keys *keys, const u8 *key,
unsigned int keylen);
#endif /* _CRYPTO_AUTHENC_H */

160
include/crypto/krb5.h Normal file
View File

@ -0,0 +1,160 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/* Kerberos 5 crypto
*
* Copyright (C) 2025 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*/
#ifndef _CRYPTO_KRB5_H
#define _CRYPTO_KRB5_H
#include <linux/crypto.h>
#include <crypto/aead.h>
#include <crypto/hash.h>
struct crypto_shash;
struct scatterlist;
/*
* Per Kerberos v5 protocol spec crypto types from the wire. These get mapped
* to linux kernel crypto routines.
*/
#define KRB5_ENCTYPE_NULL 0x0000
#define KRB5_ENCTYPE_DES_CBC_CRC 0x0001 /* DES cbc mode with CRC-32 */
#define KRB5_ENCTYPE_DES_CBC_MD4 0x0002 /* DES cbc mode with RSA-MD4 */
#define KRB5_ENCTYPE_DES_CBC_MD5 0x0003 /* DES cbc mode with RSA-MD5 */
#define KRB5_ENCTYPE_DES_CBC_RAW 0x0004 /* DES cbc mode raw */
/* XXX deprecated? */
#define KRB5_ENCTYPE_DES3_CBC_SHA 0x0005 /* DES-3 cbc mode with NIST-SHA */
#define KRB5_ENCTYPE_DES3_CBC_RAW 0x0006 /* DES-3 cbc mode raw */
#define KRB5_ENCTYPE_DES_HMAC_SHA1 0x0008
#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_CAMELLIA128_CTS_CMAC 0x0019
#define KRB5_ENCTYPE_CAMELLIA256_CTS_CMAC 0x001a
#define KRB5_ENCTYPE_UNKNOWN 0x01ff
#define KRB5_CKSUMTYPE_CRC32 0x0001
#define KRB5_CKSUMTYPE_RSA_MD4 0x0002
#define KRB5_CKSUMTYPE_RSA_MD4_DES 0x0003
#define KRB5_CKSUMTYPE_DESCBC 0x0004
#define KRB5_CKSUMTYPE_RSA_MD5 0x0007
#define KRB5_CKSUMTYPE_RSA_MD5_DES 0x0008
#define KRB5_CKSUMTYPE_NIST_SHA 0x0009
#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_CMAC_CAMELLIA128 0x0011
#define KRB5_CKSUMTYPE_CMAC_CAMELLIA256 0x0012
#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 */
/*
* Constants used for key derivation
*/
/* from rfc3961 */
#define KEY_USAGE_SEED_CHECKSUM (0x99)
#define KEY_USAGE_SEED_ENCRYPTION (0xAA)
#define KEY_USAGE_SEED_INTEGRITY (0x55)
/*
* Mode of operation.
*/
enum krb5_crypto_mode {
KRB5_CHECKSUM_MODE, /* Checksum only */
KRB5_ENCRYPT_MODE, /* Fully encrypted, possibly with integrity checksum */
};
struct krb5_buffer {
unsigned int len;
void *data;
};
/*
* 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+checksum name */
const char *cksum_name; /* Crypto checksum name */
const char *hash_name; /* Crypto hash name */
const char *derivation_enc; /* Cipher used in key derivation */
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 */
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 */
};
/*
* krb5_api.c
*/
const struct krb5_enctype *crypto_krb5_find_enctype(u32 enctype);
size_t crypto_krb5_how_much_buffer(const struct krb5_enctype *krb5,
enum krb5_crypto_mode mode,
size_t data_size, size_t *_offset);
size_t crypto_krb5_how_much_data(const struct krb5_enctype *krb5,
enum krb5_crypto_mode mode,
size_t *_buffer_size, size_t *_offset);
void crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5,
enum krb5_crypto_mode mode,
size_t *_offset, size_t *_len);
struct crypto_aead *crypto_krb5_prepare_encryption(const struct krb5_enctype *krb5,
const struct krb5_buffer *TK,
u32 usage, gfp_t gfp);
struct crypto_shash *crypto_krb5_prepare_checksum(const struct krb5_enctype *krb5,
const struct krb5_buffer *TK,
u32 usage, gfp_t gfp);
ssize_t crypto_krb5_encrypt(const struct krb5_enctype *krb5,
struct crypto_aead *aead,
struct scatterlist *sg, unsigned int nr_sg,
size_t sg_len,
size_t data_offset, size_t data_len,
bool preconfounded);
int crypto_krb5_decrypt(const struct krb5_enctype *krb5,
struct crypto_aead *aead,
struct scatterlist *sg, unsigned int nr_sg,
size_t *_offset, size_t *_len);
ssize_t crypto_krb5_get_mic(const struct krb5_enctype *krb5,
struct crypto_shash *shash,
const struct krb5_buffer *metadata,
struct scatterlist *sg, unsigned int nr_sg,
size_t sg_len,
size_t data_offset, size_t data_len);
int crypto_krb5_verify_mic(const struct krb5_enctype *krb5,
struct crypto_shash *shash,
const struct krb5_buffer *metadata,
struct scatterlist *sg, unsigned int nr_sg,
size_t *_offset, size_t *_len);
/*
* krb5_kdf.c
*/
int crypto_krb5_calc_PRFplus(const struct krb5_enctype *krb5,
const struct krb5_buffer *K,
unsigned int L,
const struct krb5_buffer *S,
struct krb5_buffer *result,
gfp_t gfp);
#endif /* _CRYPTO_KRB5_H */