Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+26
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
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
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[];
+494
View File
@@ -0,0 +1,494 @@
// 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().
*
* Return: 0 if successful, -EBADMSG if the message is too short or -EINVAL if
* the mode is unsupported.
*/
int 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:
if (*_len < krb5->cksum_len)
return -EBADMSG;
*_offset += krb5->cksum_len;
*_len -= krb5->cksum_len;
return 0;
case KRB5_ENCRYPT_MODE:
if (*_len < krb5->conf_len + krb5->cksum_len)
return -EBADMSG;
*_offset += krb5->conf_len;
*_len -= krb5->conf_len + krb5->cksum_len;
return 0;
default:
WARN_ON_ONCE(1);
return -EINVAL;
}
}
EXPORT_SYMBOL(crypto_krb5_where_is_the_data);
/**
* crypto_krb5_check_data_len - Check a message is big enough
* @krb5: The encoding to use.
* @mode: Mode of operation.
* @len: The length of the secure blob.
* @min_content: Minimum length of the content inside the blob.
*
* Check that a message is large enough to hold whatever bits the encryption
* type wants to glue on (nonce, checksum) plus a minimum amount of content.
*
* Return: 0 if successful, -EBADMSG if the message is too short or -EINVAL if
* the mode is unsupported.
*/
int crypto_krb5_check_data_len(const struct krb5_enctype *krb5,
enum krb5_crypto_mode mode,
size_t len, size_t min_content)
{
switch (mode) {
case KRB5_CHECKSUM_MODE:
if (len < krb5->cksum_len ||
len - krb5->cksum_len < min_content)
return -EBADMSG;
return 0;
case KRB5_ENCRYPT_MODE:
if (len < krb5->conf_len + krb5->cksum_len ||
len - (krb5->conf_len + krb5->cksum_len) < min_content)
return -EBADMSG;
return 0;
default:
WARN_ON_ONCE(1);
return -EINVAL;
}
}
EXPORT_SYMBOL(crypto_krb5_check_data_len);
/*
* 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, CRYPTO_ALG_ASYNC);
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_sensitive(keys.data);
return ci;
err:
kfree_sensitive(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_sensitive(keys.data);
return ci;
err:
kfree_sensitive(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
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);
}
@@ -0,0 +1,793 @@
// 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/scatterlist.h>
#include <linux/skbuff.h>
#include <linux/slab.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)
{
struct sg_mapping_iter miter;
size_t i, n;
int ret = 0;
sg_miter_start(&miter, sg, sg_nents(sg),
SG_MITER_FROM_SG | SG_MITER_LOCAL);
sg_miter_skip(&miter, offset);
for (i = 0; i < len; i += n) {
sg_miter_next(&miter);
n = min(miter.length, len - i);
ret = crypto_shash_update(desc, miter.addr, n);
if (ret < 0)
break;
}
sg_miter_stop(&miter);
return ret;
}
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
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,
};
+237
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
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,
};
+546
View File
@@ -0,0 +1,546 @@
// 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/hex.h>
#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(&prf);
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
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 */}
};