Commit 45b4ef46 authored by Chuck Lever's avatar Chuck Lever

SUNRPC: Add KDF_FEEDBACK_CMAC

The Camellia enctypes use the KDF_FEEDBACK_CMAC Key Derivation
Function defined in RFC 6803 Section 3.
Tested-by: default avatarScott Mayhew <smayhew@redhat.com>
Signed-off-by: default avatarChuck Lever <chuck.lever@oracle.com>
parent 3394682f
...@@ -58,6 +58,12 @@ int krb5_kdf_hmac_sha2(const struct gss_krb5_enctype *gk5e, ...@@ -58,6 +58,12 @@ int krb5_kdf_hmac_sha2(const struct gss_krb5_enctype *gk5e,
const struct xdr_netobj *in_constant, const struct xdr_netobj *in_constant,
gfp_t gfp_mask); gfp_t gfp_mask);
int krb5_kdf_feedback_cmac(const struct gss_krb5_enctype *gk5e,
const struct xdr_netobj *inkey,
struct xdr_netobj *outkey,
const struct xdr_netobj *in_constant,
gfp_t gfp_mask);
/** /**
* krb5_derive_key - Derive a subkey from a protocol key * krb5_derive_key - Derive a subkey from a protocol key
* @kctx: Kerberos 5 context * @kctx: Kerberos 5 context
......
...@@ -363,6 +363,149 @@ int krb5_derive_key_v2(const struct gss_krb5_enctype *gk5e, ...@@ -363,6 +363,149 @@ int krb5_derive_key_v2(const struct gss_krb5_enctype *gk5e,
return ret; return ret;
} }
/*
* K(i) = CMAC(key, K(i-1) | i | constant | 0x00 | k)
*
* i: A block counter is used with a length of 4 bytes, represented
* in big-endian order.
*
* constant: The label input to the KDF is the usage constant supplied
* to the key derivation function
*
* k: The length of the output key in bits, represented as a 4-byte
* string in big-endian order.
*
* Caller fills in K(i-1) in @step, and receives the result K(i)
* in the same buffer.
*/
static int
krb5_cmac_Ki(struct crypto_shash *tfm, const struct xdr_netobj *constant,
u32 outlen, u32 count, struct xdr_netobj *step)
{
__be32 k = cpu_to_be32(outlen * 8);
SHASH_DESC_ON_STACK(desc, tfm);
__be32 i = cpu_to_be32(count);
u8 zero = 0;
int ret;
desc->tfm = tfm;
ret = crypto_shash_init(desc);
if (ret)
goto out_err;
ret = crypto_shash_update(desc, step->data, step->len);
if (ret)
goto out_err;
ret = crypto_shash_update(desc, (u8 *)&i, sizeof(i));
if (ret)
goto out_err;
ret = crypto_shash_update(desc, constant->data, constant->len);
if (ret)
goto out_err;
ret = crypto_shash_update(desc, &zero, sizeof(zero));
if (ret)
goto out_err;
ret = crypto_shash_update(desc, (u8 *)&k, sizeof(k));
if (ret)
goto out_err;
ret = crypto_shash_final(desc, step->data);
if (ret)
goto out_err;
out_err:
shash_desc_zero(desc);
return ret;
}
/**
* krb5_kdf_feedback_cmac - Derive a subkey for a Camellia/CMAC-based enctype
* @gk5e: Kerberos 5 enctype parameters
* @inkey: base protocol key
* @outkey: OUT: derived key
* @constant: subkey usage label
* @gfp_mask: memory allocation control flags
*
* RFC 6803 Section 3:
*
* "We use a key derivation function from the family specified in
* [SP800-108], Section 5.2, 'KDF in Feedback Mode'."
*
* 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))
*
* Caller sets @outkey->len to the desired length of the derived key (k).
*
* On success, returns 0 and fills in @outkey. A negative errno value
* is returned on failure.
*/
int
krb5_kdf_feedback_cmac(const struct gss_krb5_enctype *gk5e,
const struct xdr_netobj *inkey,
struct xdr_netobj *outkey,
const struct xdr_netobj *constant,
gfp_t gfp_mask)
{
struct xdr_netobj step = { .data = NULL };
struct xdr_netobj DR = { .data = NULL };
unsigned int blocksize, offset;
struct crypto_shash *tfm;
int n, count, ret;
/*
* This implementation assumes the CMAC used for an enctype's
* key derivation is the same as the CMAC used for its
* checksumming. This happens to be true for enctypes that
* are currently supported by this implementation.
*/
tfm = crypto_alloc_shash(gk5e->cksum_name, 0, 0);
if (IS_ERR(tfm)) {
ret = PTR_ERR(tfm);
goto out;
}
ret = crypto_shash_setkey(tfm, inkey->data, inkey->len);
if (ret)
goto out_free_tfm;
blocksize = crypto_shash_digestsize(tfm);
n = (outkey->len + blocksize - 1) / blocksize;
/* K(0) is all zeroes */
ret = -ENOMEM;
step.len = blocksize;
step.data = kzalloc(step.len, gfp_mask);
if (!step.data)
goto out_free_tfm;
DR.len = blocksize * n;
DR.data = kmalloc(DR.len, gfp_mask);
if (!DR.data)
goto out_free_tfm;
/* XXX: Does not handle partial-block key sizes */
for (offset = 0, count = 1; count <= n; count++) {
ret = krb5_cmac_Ki(tfm, constant, outkey->len, count, &step);
if (ret)
goto out_free_tfm;
memcpy(DR.data + offset, step.data, blocksize);
offset += blocksize;
}
/* k-truncate and random-to-key */
memcpy(outkey->data, DR.data, outkey->len);
ret = 0;
out_free_tfm:
crypto_free_shash(tfm);
out:
kfree_sensitive(step.data);
kfree_sensitive(DR.data);
return ret;
}
/* /*
* K1 = HMAC-SHA(key, 0x00000001 | label | 0x00 | k) * K1 = HMAC-SHA(key, 0x00000001 | label | 0x00 | k)
* *
......
...@@ -166,6 +166,7 @@ static const struct gss_krb5_enctype supported_gss_krb5_enctypes[] = { ...@@ -166,6 +166,7 @@ static const struct gss_krb5_enctype supported_gss_krb5_enctypes[] = {
.Ki_length = BITS2OCTETS(128), .Ki_length = BITS2OCTETS(128),
.import_ctx = gss_krb5_import_ctx_v2, .import_ctx = gss_krb5_import_ctx_v2,
.derive_key = krb5_kdf_feedback_cmac,
.encrypt = gss_krb5_aes_encrypt, .encrypt = gss_krb5_aes_encrypt,
.decrypt = gss_krb5_aes_decrypt, .decrypt = gss_krb5_aes_decrypt,
...@@ -192,6 +193,7 @@ static const struct gss_krb5_enctype supported_gss_krb5_enctypes[] = { ...@@ -192,6 +193,7 @@ static const struct gss_krb5_enctype supported_gss_krb5_enctypes[] = {
.Ki_length = BITS2OCTETS(256), .Ki_length = BITS2OCTETS(256),
.import_ctx = gss_krb5_import_ctx_v2, .import_ctx = gss_krb5_import_ctx_v2,
.derive_key = krb5_kdf_feedback_cmac,
.encrypt = gss_krb5_aes_encrypt, .encrypt = gss_krb5_aes_encrypt,
.decrypt = gss_krb5_aes_decrypt, .decrypt = gss_krb5_aes_decrypt,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment