74 changes: 68 additions & 6 deletions src/lib/crypto/krb/derive.c
Expand Up @@ -139,9 +139,9 @@ derive_random_rfc3961(const struct krb5_enc_provider *enc,
* - Four bytes are used to encode the output length in the PRF input.
*/
static krb5_error_code
derive_random_sp800_108_cmac(const struct krb5_enc_provider *enc,
krb5_key inkey, krb5_data *outrnd,
const krb5_data *in_constant)
derive_random_sp800_108_feedback_cmac(const struct krb5_enc_provider *enc,
krb5_key inkey, krb5_data *outrnd,
const krb5_data *in_constant)
{
size_t blocksize, keybytes, n;
krb5_crypto_iov iov[6];
Expand Down Expand Up @@ -204,16 +204,75 @@ derive_random_sp800_108_cmac(const struct krb5_enc_provider *enc,
return ret;
}

/*
* NIST SP800-108 KDF in counter mode (section 5.1).
* Parameters:
* - HMAC (with hash as the hash provider) is the PRF.
* - A block counter of four bytes is used.
* - Four bytes are used to encode the output length in the PRF input.
*
* There are no uses requiring more than a single PRF invocation.
*/
krb5_error_code
k5_sp800_108_counter_hmac(const struct krb5_hash_provider *hash,
krb5_key inkey, krb5_data *outrnd,
const krb5_data *label, const krb5_data *context)
{
krb5_crypto_iov iov[5];
krb5_error_code ret;
krb5_data prf;
unsigned char ibuf[4], lbuf[4];

if (hash == NULL || outrnd->length > hash->hashsize)
return KRB5_CRYPTO_INTERNAL;

/* Allocate encryption data buffer. */
ret = alloc_data(&prf, hash->hashsize);
if (ret)
return ret;

/* [i]2: four-byte big-endian binary string giving the block counter (1) */
iov[0].flags = KRB5_CRYPTO_TYPE_DATA;
iov[0].data = make_data(ibuf, sizeof(ibuf));
store_32_be(1, ibuf);
/* Label */
iov[1].flags = KRB5_CRYPTO_TYPE_DATA;
iov[1].data = *label;
/* 0x00: separator byte */
iov[2].flags = KRB5_CRYPTO_TYPE_DATA;
iov[2].data = make_data("", 1);
/* Context */
iov[3].flags = KRB5_CRYPTO_TYPE_DATA;
iov[3].data = *context;
/* [L]2: four-byte big-endian binary string giving the output length */
iov[4].flags = KRB5_CRYPTO_TYPE_DATA;
iov[4].data = make_data(lbuf, sizeof(lbuf));
store_32_be(outrnd->length * 8, lbuf);

ret = krb5int_hmac(hash, inkey, iov, 5, &prf);
if (!ret)
memcpy(outrnd->data, prf.data, outrnd->length);
zapfree(prf.data, prf.length);
return ret;
}

krb5_error_code
krb5int_derive_random(const struct krb5_enc_provider *enc,
const struct krb5_hash_provider *hash,
krb5_key inkey, krb5_data *outrnd,
const krb5_data *in_constant, enum deriv_alg alg)
{
krb5_data empty = empty_data();

switch (alg) {
case DERIVE_RFC3961:
return derive_random_rfc3961(enc, inkey, outrnd, in_constant);
case DERIVE_SP800_108_CMAC:
return derive_random_sp800_108_cmac(enc, inkey, outrnd, in_constant);
return derive_random_sp800_108_feedback_cmac(enc, inkey, outrnd,
in_constant);
case DERIVE_SP800_108_HMAC:
return k5_sp800_108_counter_hmac(hash, inkey, outrnd, in_constant,
&empty);
default:
return EINVAL;
}
Expand All @@ -227,6 +286,7 @@ krb5int_derive_random(const struct krb5_enc_provider *enc,
*/
krb5_error_code
krb5int_derive_keyblock(const struct krb5_enc_provider *enc,
const struct krb5_hash_provider *hash,
krb5_key inkey, krb5_keyblock *outkey,
const krb5_data *in_constant, enum deriv_alg alg)
{
Expand All @@ -239,7 +299,7 @@ krb5int_derive_keyblock(const struct krb5_enc_provider *enc,
goto cleanup;

/* Derive pseudo-random data for the key bytes. */
ret = krb5int_derive_random(enc, inkey, &rawkey, in_constant, alg);
ret = krb5int_derive_random(enc, hash, inkey, &rawkey, in_constant, alg);
if (ret)
goto cleanup;

Expand All @@ -253,6 +313,7 @@ krb5int_derive_keyblock(const struct krb5_enc_provider *enc,

krb5_error_code
krb5int_derive_key(const struct krb5_enc_provider *enc,
const struct krb5_hash_provider *hash,
krb5_key inkey, krb5_key *outkey,
const krb5_data *in_constant, enum deriv_alg alg)
{
Expand All @@ -275,7 +336,8 @@ krb5int_derive_key(const struct krb5_enc_provider *enc,
keyblock.enctype = inkey->keyblock.enctype;
if (keyblock.contents == NULL)
return ENOMEM;
ret = krb5int_derive_keyblock(enc, inkey, &keyblock, in_constant, alg);
ret = krb5int_derive_keyblock(enc, hash, inkey, &keyblock, in_constant,
alg);
if (ret)
goto cleanup;

Expand Down
6 changes: 4 additions & 2 deletions src/lib/crypto/krb/enc_dk_cmac.c
Expand Up @@ -64,13 +64,15 @@ derive_keys(const struct krb5_enc_provider *enc, krb5_key key,
/* Derive the encryption key. */
store_32_be(usage, buf);
buf[4] = 0xAA;
ret = krb5int_derive_key(enc, key, &ke, &constant, DERIVE_SP800_108_CMAC);
ret = krb5int_derive_key(enc, NULL, key, &ke, &constant,
DERIVE_SP800_108_CMAC);
if (ret != 0)
return ret;

/* Derive the integrity key. */
buf[4] = 0x55;
ret = krb5int_derive_key(enc, key, &ki, &constant, DERIVE_SP800_108_CMAC);
ret = krb5int_derive_key(enc, NULL, key, &ki, &constant,
DERIVE_SP800_108_CMAC);
if (ret != 0) {
krb5_k_free_key(NULL, ke);
return ret;
Expand Down
8 changes: 4 additions & 4 deletions src/lib/crypto/krb/enc_dk_hmac.c
Expand Up @@ -131,13 +131,13 @@ krb5int_dk_encrypt(const struct krb5_keytypes *ktp, krb5_key key,

d1.data[4] = 0xAA;

ret = krb5int_derive_key(enc, key, &ke, &d1, DERIVE_RFC3961);
ret = krb5int_derive_key(enc, NULL, key, &ke, &d1, DERIVE_RFC3961);
if (ret != 0)
goto cleanup;

d1.data[4] = 0x55;

ret = krb5int_derive_key(enc, key, &ki, &d1, DERIVE_RFC3961);
ret = krb5int_derive_key(enc, NULL, key, &ki, &d1, DERIVE_RFC3961);
if (ret != 0)
goto cleanup;

Expand Down Expand Up @@ -232,13 +232,13 @@ krb5int_dk_decrypt(const struct krb5_keytypes *ktp, krb5_key key,

d1.data[4] = 0xAA;

ret = krb5int_derive_key(enc, key, &ke, &d1, DERIVE_RFC3961);
ret = krb5int_derive_key(enc, NULL, key, &ke, &d1, DERIVE_RFC3961);
if (ret != 0)
goto cleanup;

d1.data[4] = 0x55;

ret = krb5int_derive_key(enc, key, &ki, &d1, DERIVE_RFC3961);
ret = krb5int_derive_key(enc, NULL, key, &ki, &d1, DERIVE_RFC3961);
if (ret != 0)
goto cleanup;

Expand Down
257 changes: 257 additions & 0 deletions src/lib/crypto/krb/enc_etm.c
@@ -0,0 +1,257 @@
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/* lib/crypto/krb/enc_etm.c - encrypt-then-mac construction for aes-sha2 */
/*
* Copyright (C) 2015 by the Massachusetts Institute of Technology.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "crypto_int.h"

unsigned int
krb5int_aes2_crypto_length(const struct krb5_keytypes *ktp,
krb5_cryptotype type)
{
switch (type) {
case KRB5_CRYPTO_TYPE_HEADER:
return ktp->enc->block_size;
case KRB5_CRYPTO_TYPE_PADDING:
return 0;
case KRB5_CRYPTO_TYPE_TRAILER:
case KRB5_CRYPTO_TYPE_CHECKSUM:
return ktp->hash->hashsize / 2;
default:
assert(0 && "invalid cryptotype passed to krb5int_aes2_crypto_length");
return 0;
}
}

/* Derive encryption and integrity keys for CMAC-using enctypes. */
static krb5_error_code
derive_keys(const struct krb5_keytypes *ktp, krb5_key key,
krb5_keyusage usage, krb5_key *ke_out, krb5_data *ki_out)
{
krb5_error_code ret;
uint8_t label[5];
krb5_data label_data = make_data(label, 5), ki = empty_data();
krb5_key ke = NULL;

*ke_out = NULL;
*ki_out = empty_data();

/* Derive the encryption key. */
store_32_be(usage, label);
label[4] = 0xAA;
ret = krb5int_derive_key(ktp->enc, ktp->hash, key, &ke, &label_data,
DERIVE_SP800_108_HMAC);
if (ret)
goto cleanup;

/* Derive the integrity key. */
label[4] = 0x55;
ret = alloc_data(&ki, ktp->hash->hashsize / 2);
if (ret)
goto cleanup;
ret = krb5int_derive_random(NULL, ktp->hash, key, &ki, &label_data,
DERIVE_SP800_108_HMAC);
if (ret)
goto cleanup;

*ke_out = ke;
ke = NULL;
*ki_out = ki;
ki = empty_data();

cleanup:
krb5_k_free_key(NULL, ke);
zapfree(ki.data, ki.length);
return ret;
}

/* Compute an HMAC checksum over the cipher state and data. Allocate enough
* space in *out for the checksum. */
static krb5_error_code
hmac_ivec_data(const struct krb5_keytypes *ktp, const krb5_data *ki,
const krb5_data *ivec, krb5_crypto_iov *data, size_t num_data,
krb5_data *out)
{
krb5_error_code ret;
krb5_data zeroivec = empty_data();
krb5_crypto_iov *iovs = NULL;
krb5_keyblock kb = { 0 };

if (ivec == NULL) {
ret = ktp->enc->init_state(NULL, 0, &zeroivec);
if (ret)
goto cleanup;
ivec = &zeroivec;
}

/* Make a copy of data with an extra iov at the beginning for the ivec. */
iovs = k5calloc(num_data + 1, sizeof(*iovs), &ret);
if (iovs == NULL)
goto cleanup;
iovs[0].flags = KRB5_CRYPTO_TYPE_DATA;
iovs[0].data = *ivec;
memcpy(iovs + 1, data, num_data * sizeof(*iovs));

ret = alloc_data(out, ktp->hash->hashsize);
if (ret)
goto cleanup;
kb.length = ki->length;
kb.contents = (uint8_t *)ki->data;
ret = krb5int_hmac_keyblock(ktp->hash, &kb, iovs, num_data + 1, out);

cleanup:
if (zeroivec.data != NULL)
ktp->enc->free_state(&zeroivec);
free(iovs);
return ret;
}

krb5_error_code
krb5int_etm_encrypt(const struct krb5_keytypes *ktp, krb5_key key,
krb5_keyusage usage, const krb5_data *ivec,
krb5_crypto_iov *data, size_t num_data)
{
const struct krb5_enc_provider *enc = ktp->enc;
krb5_error_code ret;
krb5_data ivcopy = empty_data(), cksum = empty_data();
krb5_crypto_iov *header, *trailer, *padding;
krb5_key ke = NULL;
krb5_data ki = empty_data();
unsigned int trailer_len;

/* E(Confounder | Plaintext) | Checksum(IV | ciphertext) */

trailer_len = ktp->crypto_length(ktp, KRB5_CRYPTO_TYPE_TRAILER);

/* Validate header and trailer lengths, and zero out padding length. */
header = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_HEADER);
if (header == NULL || header->data.length < enc->block_size)
return KRB5_BAD_MSIZE;
trailer = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_TRAILER);
if (trailer == NULL || trailer->data.length < trailer_len)
return KRB5_BAD_MSIZE;
padding = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_PADDING);
if (padding != NULL)
padding->data.length = 0;

if (ivec != NULL) {
ret = alloc_data(&ivcopy, ivec->length);
if (ret)
goto cleanup;
memcpy(ivcopy.data, ivec->data, ivec->length);
}

/* Derive the encryption and integrity keys. */
ret = derive_keys(ktp, key, usage, &ke, &ki);
if (ret)
goto cleanup;

/* Generate confounder. */
header->data.length = enc->block_size;
ret = krb5_c_random_make_octets(NULL, &header->data);
if (ret)
goto cleanup;

/* Encrypt the plaintext (header | data). */
ret = enc->encrypt(ke, (ivec == NULL) ? NULL : &ivcopy, data, num_data);
if (ret)
goto cleanup;

/* HMAC the IV, confounder, and ciphertext with sign-only data. */
ret = hmac_ivec_data(ktp, &ki, ivec, data, num_data, &cksum);
if (ret)
goto cleanup;

/* Truncate the HMAC checksum to the trailer length. */
assert(trailer_len <= cksum.length);
memcpy(trailer->data.data, cksum.data, trailer_len);
trailer->data.length = trailer_len;

/* Copy out the updated ivec if desired. */
if (ivec != NULL)
memcpy(ivec->data, ivcopy.data, ivcopy.length);

cleanup:
krb5_k_free_key(NULL, ke);
zapfree(ki.data, ki.length);
free(cksum.data);
zapfree(ivcopy.data, ivcopy.length);
return ret;
}

krb5_error_code
krb5int_etm_decrypt(const struct krb5_keytypes *ktp, krb5_key key,
krb5_keyusage usage, const krb5_data *ivec,
krb5_crypto_iov *data, size_t num_data)
{
const struct krb5_enc_provider *enc = ktp->enc;
krb5_error_code ret;
krb5_data cksum = empty_data();
krb5_crypto_iov *header, *trailer;
krb5_key ke = NULL;
krb5_data ki = empty_data();
unsigned int trailer_len;

trailer_len = ktp->crypto_length(ktp, KRB5_CRYPTO_TYPE_TRAILER);

/* Validate header and trailer lengths. */
header = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_HEADER);
if (header == NULL || header->data.length != enc->block_size)
return KRB5_BAD_MSIZE;
trailer = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_TRAILER);
if (trailer == NULL || trailer->data.length != trailer_len)
return KRB5_BAD_MSIZE;

/* Derive the encryption and integrity keys. */
ret = derive_keys(ktp, key, usage, &ke, &ki);
if (ret)
goto cleanup;

/* HMAC the IV, confounder, and ciphertext with sign-only data. */
ret = hmac_ivec_data(ktp, &ki, ivec, data, num_data, &cksum);
if (ret)
goto cleanup;

/* Compare only the possibly truncated length. */
assert(trailer_len <= cksum.length);
if (k5_bcmp(cksum.data, trailer->data.data, trailer_len) != 0) {
ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
goto cleanup;
}

/* Decrypt the ciphertext (header | data | padding). */
ret = enc->decrypt(ke, ivec, data, num_data);

cleanup:
krb5_k_free_key(NULL, ke);
zapfree(ki.data, ki.length);
zapfree(cksum.data, cksum.length);
return ret;
}
21 changes: 21 additions & 0 deletions src/lib/crypto/krb/etypes.c
Expand Up @@ -167,6 +167,27 @@ const struct krb5_keytypes krb5int_enctypes_list[] = {
krb5int_dk_cmac_prf,
CKSUMTYPE_CMAC_CAMELLIA256,
0 /*flags */ },

{ ENCTYPE_AES128_CTS_HMAC_SHA256_128,
"aes128-cts-hmac-sha256-128", { "aes128-sha2" },
"AES-128 CTS mode with 128-bit SHA-256 HMAC",
&krb5int_enc_aes128, &krb5int_hash_sha256,
32,
krb5int_aes2_crypto_length, krb5int_etm_encrypt, krb5int_etm_decrypt,
krb5int_aes2_string_to_key, k5_rand2key_direct,
krb5int_aes2_prf,
CKSUMTYPE_HMAC_SHA256_128_AES128,
0 /*flags*/ },
{ ENCTYPE_AES256_CTS_HMAC_SHA384_192,
"aes256-cts-hmac-sha384-192", { "aes256-sha2" },
"AES-256 CTS mode with 192-bit SHA-384 HMAC",
&krb5int_enc_aes256, &krb5int_hash_sha384,
48,
krb5int_aes2_crypto_length, krb5int_etm_encrypt, krb5int_etm_decrypt,
krb5int_aes2_string_to_key, k5_rand2key_direct,
krb5int_aes2_prf,
CKSUMTYPE_HMAC_SHA384_192_AES256,
0 /*flags*/ },
};

const int krb5int_enctypes_length =
Expand Down
42 changes: 42 additions & 0 deletions src/lib/crypto/krb/prf_aes2.c
@@ -0,0 +1,42 @@
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/* lib/crypto/krb/prf_aes2.c - PRF for aes-sha2 enctypes */
/*
* Copyright (C) 2015 by the Massachusetts Institute of Technology.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "crypto_int.h"

krb5_error_code
krb5int_aes2_prf(const struct krb5_keytypes *ktp, krb5_key key,
const krb5_data *in, krb5_data *out)
{
krb5_data label = string2data("prf");

return k5_sp800_108_counter_hmac(ktp->hash, key, out, &label, in);
}
2 changes: 1 addition & 1 deletion src/lib/crypto/krb/prf_cmac.c
Expand Up @@ -42,7 +42,7 @@ krb5int_dk_cmac_prf(const struct krb5_keytypes *ktp, krb5_key key,
iov.data = *in;

/* Derive a key using the PRF constant. */
ret = krb5int_derive_key(ktp->enc, key, &kp, &prfconst,
ret = krb5int_derive_key(ktp->enc, NULL, key, &kp, &prfconst,
DERIVE_SP800_108_CMAC);
if (ret != 0)
goto cleanup;
Expand Down
3 changes: 2 additions & 1 deletion src/lib/crypto/krb/prf_dk.c
Expand Up @@ -48,7 +48,8 @@ krb5int_dk_prf(const struct krb5_keytypes *ktp, krb5_key key,
goto cleanup;

/* Derive a key using the PRF constant. */
ret = krb5int_derive_key(ktp->enc, key, &kp, &prfconst, DERIVE_RFC3961);
ret = krb5int_derive_key(ktp->enc, NULL, key, &kp, &prfconst,
DERIVE_RFC3961);
if (ret != 0)
goto cleanup;

Expand Down
16 changes: 14 additions & 2 deletions src/lib/crypto/krb/s2k_pbkdf2.c
Expand Up @@ -87,7 +87,7 @@ krb5int_dk_string_to_key(const struct krb5_keytypes *ktp,
indata.length = kerberos_len;
indata.data = (char *) kerberos;

ret = krb5int_derive_keyblock(ktp->enc, foldkey, keyblock, &indata,
ret = krb5int_derive_keyblock(ktp->enc, NULL, foldkey, keyblock, &indata,
DERIVE_RFC3961);
if (ret != 0)
memset(keyblock->contents, 0, keyblock->length);
Expand Down Expand Up @@ -168,7 +168,8 @@ pbkdf2_string_to_key(const struct krb5_keytypes *ktp, const krb5_data *string,
if (err)
goto cleanup;

err = krb5int_derive_keyblock(ktp->enc, tempkey, key, &usage, deriv_alg);
err = krb5int_derive_keyblock(ktp->enc, ktp->hash, tempkey, key, &usage,
deriv_alg);

cleanup:
if (sandp.data)
Expand Down Expand Up @@ -202,3 +203,14 @@ krb5int_camellia_string_to_key(const struct krb5_keytypes *ktp,
return pbkdf2_string_to_key(ktp, string, salt, &pepper, params, key,
DERIVE_SP800_108_CMAC, 32768);
}

krb5_error_code
krb5int_aes2_string_to_key(const struct krb5_keytypes *ktp,
const krb5_data *string, const krb5_data *salt,
const krb5_data *params, krb5_keyblock *key)
{
krb5_data pepper = string2data(ktp->name);

return pbkdf2_string_to_key(ktp, string, salt, &pepper, params, key,
DERIVE_SP800_108_HMAC, 32768);
}