mirror of https://github.com/torvalds/linux.git
238 lines
5.7 KiB
C
238 lines
5.7 KiB
C
// 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,
|
|
};
|