From: Greg Hudson Date: Sun, 6 Dec 2015 00:36:57 +0000 (-0500) Subject: Add aes-sha2 enctype support X-Git-Tag: krb5-1.15-beta1~14 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=135a9ac3c58b444998361a3b13f5decfdece2105;p=thirdparty%2Fkrb5.git Add aes-sha2 enctype support Add support to libk5crypto for the aes128-cts-hmac-sha256-128 and aes256-cts-hmac-sha384-192 encryption types, and the hmac-sha256-128-aes128 and hmac-sha384-192-aes256 checksum types. Key derivation for the new encryption types uses a hash, so we need to add a hash parameter to the krb5int_derive_ functions, which can be null except when DERIVE_SP800_108_HMAC is given. Rename the helper function derive_random_sp800_108_cmac() to derive_random_sp800_108_feedback_cmac() to make it clear that feedback mode is used, since the new enctype uses counter mode. ticket: 8490 --- diff --git a/src/include/krb5/krb5.hin b/src/include/krb5/krb5.hin index 44c8bfa956..64b0d0fb74 100644 --- a/src/include/krb5/krb5.hin +++ b/src/include/krb5/krb5.hin @@ -423,14 +423,16 @@ typedef struct _krb5_crypto_iov { #define ENCTYPE_RSA_ES_OAEP_ENV 0x000e /**< RSA w/OEAP encryption, CMS enveloped data */ #define ENCTYPE_DES3_CBC_ENV 0x000f /**< DES-3 cbc mode, CMS enveloped data */ -#define ENCTYPE_DES3_CBC_SHA1 0x0010 -#define ENCTYPE_AES128_CTS_HMAC_SHA1_96 0x0011 /**< RFC 3962 */ -#define ENCTYPE_AES256_CTS_HMAC_SHA1_96 0x0012 /**< RFC 3962 */ -#define ENCTYPE_ARCFOUR_HMAC 0x0017 -#define ENCTYPE_ARCFOUR_HMAC_EXP 0x0018 -#define ENCTYPE_CAMELLIA128_CTS_CMAC 0x0019 /**< RFC 6803 */ -#define ENCTYPE_CAMELLIA256_CTS_CMAC 0x001a /**< RFC 6803 */ -#define ENCTYPE_UNKNOWN 0x01ff +#define ENCTYPE_DES3_CBC_SHA1 0x0010 +#define ENCTYPE_AES128_CTS_HMAC_SHA1_96 0x0011 /**< RFC 3962 */ +#define ENCTYPE_AES256_CTS_HMAC_SHA1_96 0x0012 /**< RFC 3962 */ +#define ENCTYPE_AES128_CTS_HMAC_SHA256_128 0x0013 +#define ENCTYPE_AES256_CTS_HMAC_SHA384_192 0x0014 +#define ENCTYPE_ARCFOUR_HMAC 0x0017 +#define ENCTYPE_ARCFOUR_HMAC_EXP 0x0018 +#define ENCTYPE_CAMELLIA128_CTS_CMAC 0x0019 /**< RFC 6803 */ +#define ENCTYPE_CAMELLIA256_CTS_CMAC 0x001a /**< RFC 6803 */ +#define ENCTYPE_UNKNOWN 0x01ff #define CKSUMTYPE_CRC32 0x0001 #define CKSUMTYPE_RSA_MD4 0x0002 @@ -446,6 +448,8 @@ typedef struct _krb5_crypto_iov { ENCTYPE_AES128_CTS_HMAC_SHA1_96 */ #define CKSUMTYPE_HMAC_SHA1_96_AES256 0x0010 /**< RFC 3962. Used with ENCTYPE_AES256_CTS_HMAC_SHA1_96 */ +#define CKSUMTYPE_HMAC_SHA256_128_AES128 0x0013 +#define CKSUMTYPE_HMAC_SHA384_192_AES256 0x0014 #define CKSUMTYPE_CMAC_CAMELLIA128 0x0011 /**< RFC 6803 */ #define CKSUMTYPE_CMAC_CAMELLIA256 0x0012 /**< RFC 6803 */ #define CKSUMTYPE_MD5_HMAC_ARCFOUR -137 /*Microsoft netlogon cksumtype*/ diff --git a/src/lib/crypto/crypto_tests/t_derive.c b/src/lib/crypto/crypto_tests/t_derive.c index fe287513e5..f8c32917af 100644 --- a/src/lib/crypto/crypto_tests/t_derive.c +++ b/src/lib/crypto/crypto_tests/t_derive.c @@ -259,7 +259,7 @@ main(int argc, char **argv) ret = krb5_k_create_key(context, &kb, &inkey); assert(!ret); enc = get_enc_provider(test->enctype); - ret = krb5int_derive_key(enc, inkey, &outkey, &test->constant, + ret = krb5int_derive_key(enc, NULL, inkey, &outkey, &test->constant, test->alg); assert(!ret); if (verbose) { diff --git a/src/lib/crypto/krb/Makefile.in b/src/lib/crypto/krb/Makefile.in index 7b59839b0f..c5660c5fe1 100644 --- a/src/lib/crypto/krb/Makefile.in +++ b/src/lib/crypto/krb/Makefile.in @@ -14,6 +14,7 @@ STLIBOBJS=\ checksum_confounder.o \ checksum_dk_cmac.o \ checksum_dk_hmac.o \ + checksum_etm.o \ checksum_hmac_md5.o \ checksum_unkeyed.o \ checksum_length.o \ @@ -35,6 +36,7 @@ STLIBOBJS=\ enctype_util.o \ enc_dk_cmac.o \ enc_dk_hmac.o \ + enc_etm.o \ enc_old.o \ enc_raw.o \ enc_rc4.o \ @@ -51,6 +53,7 @@ STLIBOBJS=\ nfold.o \ old_api_glue.o \ prf.o \ + prf_aes2.o \ prf_cmac.o \ prf_des.o \ prf_dk.o \ @@ -76,6 +79,7 @@ OBJS=\ $(OUTPRE)checksum_confounder.$(OBJEXT) \ $(OUTPRE)checksum_dk_cmac.$(OBJEXT) \ $(OUTPRE)checksum_dk_hmac.$(OBJEXT) \ + $(OUTPRE)checksum_etm.$(OBJEXT) \ $(OUTPRE)checksum_hmac_md5.$(OBJEXT) \ $(OUTPRE)checksum_unkeyed.$(OBJEXT) \ $(OUTPRE)checksum_length.$(OBJEXT) \ @@ -97,6 +101,7 @@ OBJS=\ $(OUTPRE)enctype_util.$(OBJEXT) \ $(OUTPRE)enc_dk_cmac.$(OBJEXT) \ $(OUTPRE)enc_dk_hmac.$(OBJEXT) \ + $(OUTPRE)enc_etm.$(OBJEXT) \ $(OUTPRE)enc_old.$(OBJEXT) \ $(OUTPRE)enc_raw.$(OBJEXT) \ $(OUTPRE)enc_rc4.$(OBJEXT) \ @@ -113,6 +118,7 @@ OBJS=\ $(OUTPRE)nfold.$(OBJEXT) \ $(OUTPRE)old_api_glue.$(OBJEXT) \ $(OUTPRE)prf.$(OBJEXT) \ + $(OUTPRE)prf_aes2.$(OBJEXT) \ $(OUTPRE)prf_cmac.$(OBJEXT) \ $(OUTPRE)prf_des.$(OBJEXT) \ $(OUTPRE)prf_dk.$(OBJEXT) \ @@ -138,6 +144,7 @@ SRCS=\ $(srcdir)/checksum_confounder.c \ $(srcdir)/checksum_dk_cmac.c \ $(srcdir)/checksum_dk_hmac.c \ + $(srcdir)/checksum_etm.c \ $(srcdir)/checksum_hmac_md5.c \ $(srcdir)/checksum_unkeyed.c \ $(srcdir)/checksum_length.c \ @@ -159,6 +166,7 @@ SRCS=\ $(srcdir)/enctype_util.c \ $(srcdir)/enc_dk_cmac.c \ $(srcdir)/enc_dk_hmac.c \ + $(srcdir)/enc_etm.c \ $(srcdir)/enc_old.c \ $(srcdir)/enc_raw.c \ $(srcdir)/enc_rc4.c \ @@ -175,6 +183,7 @@ SRCS=\ $(srcdir)/nfold.c \ $(srcdir)/old_api_glue.c \ $(srcdir)/prf.c \ + $(srcdir)/prf_aes2.c \ $(srcdir)/prf_cmac.c \ $(srcdir)/prf_des.c \ $(srcdir)/prf_dk.c \ diff --git a/src/lib/crypto/krb/checksum_dk_cmac.c b/src/lib/crypto/krb/checksum_dk_cmac.c index 59d5c5a523..809f9d729b 100644 --- a/src/lib/crypto/krb/checksum_dk_cmac.c +++ b/src/lib/crypto/krb/checksum_dk_cmac.c @@ -44,7 +44,8 @@ krb5int_dk_cmac_checksum(const struct krb5_cksumtypes *ctp, datain = make_data(constantdata, K5CLENGTH); store_32_be(usage, constantdata); constantdata[4] = (char) 0x99; - ret = krb5int_derive_key(enc, key, &kc, &datain, DERIVE_SP800_108_CMAC); + ret = krb5int_derive_key(enc, NULL, key, &kc, &datain, + DERIVE_SP800_108_CMAC); if (ret != 0) return ret; diff --git a/src/lib/crypto/krb/checksum_dk_hmac.c b/src/lib/crypto/krb/checksum_dk_hmac.c index 517a5f32c8..64ab8993e8 100644 --- a/src/lib/crypto/krb/checksum_dk_hmac.c +++ b/src/lib/crypto/krb/checksum_dk_hmac.c @@ -45,7 +45,7 @@ krb5int_dk_checksum(const struct krb5_cksumtypes *ctp, datain = make_data(constantdata, K5CLENGTH); store_32_be(usage, constantdata); constantdata[4] = (char) 0x99; - ret = krb5int_derive_key(enc, key, &kc, &datain, DERIVE_RFC3961); + ret = krb5int_derive_key(enc, NULL, key, &kc, &datain, DERIVE_RFC3961); if (ret) return ret; diff --git a/src/lib/crypto/krb/checksum_etm.c b/src/lib/crypto/krb/checksum_etm.c new file mode 100644 index 0000000000..eaa85b2d50 --- /dev/null +++ b/src/lib/crypto/krb/checksum_etm.c @@ -0,0 +1,65 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/crypto/krb/checksum_etm.c - checksum for encrypt-then-mac 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_etm_checksum(const struct krb5_cksumtypes *ctp, krb5_key key, + krb5_keyusage usage, const krb5_crypto_iov *data, + size_t num_data, krb5_data *output) +{ + krb5_error_code ret; + uint8_t label[5]; + krb5_data label_data = make_data(label, 5), kc = empty_data(); + krb5_keyblock kb = { 0 }; + + /* Derive the checksum key. */ + store_32_be(usage, label); + label[4] = 0x99; + label_data = make_data(label, 5); + ret = alloc_data(&kc, ctp->hash->hashsize / 2); + if (ret) + goto cleanup; + ret = krb5int_derive_random(ctp->enc, ctp->hash, key, &kc, &label_data, + DERIVE_SP800_108_HMAC); + if (ret) + goto cleanup; + + /* Compute an HMAC with kc over the data. */ + kb.length = kc.length; + kb.contents = (uint8_t *)kc.data; + ret = krb5int_hmac_keyblock(ctp->hash, &kb, data, num_data, output); + +cleanup: + zapfree(kc.data, kc.length); + return ret; +} diff --git a/src/lib/crypto/krb/cksumtypes.c b/src/lib/crypto/krb/cksumtypes.c index a1ced981fb..85967f9aa0 100644 --- a/src/lib/crypto/krb/cksumtypes.c +++ b/src/lib/crypto/krb/cksumtypes.c @@ -112,6 +112,18 @@ const struct krb5_cksumtypes krb5int_cksumtypes_list[] = { &krb5int_enc_camellia256, NULL, krb5int_dk_cmac_checksum, NULL, 16, 16, 0 }, + + { CKSUMTYPE_HMAC_SHA256_128_AES128, + "hmac-sha256-128-aes128", { 0 }, "HMAC-SHA256 AES128 key", + &krb5int_enc_aes128, &krb5int_hash_sha256, + krb5int_etm_checksum, NULL, + 32, 16, 0 }, + + { CKSUMTYPE_HMAC_SHA384_192_AES256, + "hmac-sha384-192-aes256", { 0 }, "HMAC-SHA384 AES256 key", + &krb5int_enc_aes256, &krb5int_hash_sha384, + krb5int_etm_checksum, NULL, + 48, 24, 0 }, }; const size_t krb5int_cksumtypes_length = diff --git a/src/lib/crypto/krb/combine_keys.c b/src/lib/crypto/krb/combine_keys.c index 9ab0ac4f94..90905c5ae2 100644 --- a/src/lib/crypto/krb/combine_keys.c +++ b/src/lib/crypto/krb/combine_keys.c @@ -191,7 +191,8 @@ krb5int_c_combine_keys(krb5_context context, krb5_keyblock *key1, myalloc = TRUE; } - ret = krb5int_derive_keyblock(enc, tkey, outkey, &input, DERIVE_RFC3961); + ret = krb5int_derive_keyblock(enc, NULL, tkey, outkey, &input, + DERIVE_RFC3961); if (ret) { if (myalloc) { free(outkey->contents); @@ -222,7 +223,7 @@ dr(const struct krb5_enc_provider *enc, const krb5_keyblock *inkey, ret = krb5_k_create_key(NULL, inkey, &key); if (ret != 0) return ret; - ret = krb5int_derive_random(enc, key, &outdata, in_constant, + ret = krb5int_derive_random(enc, NULL, key, &outdata, in_constant, DERIVE_RFC3961); krb5_k_free_key(NULL, key); return ret; diff --git a/src/lib/crypto/krb/crypto_int.h b/src/lib/crypto/krb/crypto_int.h index fec0eab777..d75b49c693 100644 --- a/src/lib/crypto/krb/crypto_int.h +++ b/src/lib/crypto/krb/crypto_int.h @@ -184,6 +184,8 @@ unsigned int krb5int_aes_crypto_length(const struct krb5_keytypes *ktp, krb5_cryptotype type); unsigned int krb5int_camellia_crypto_length(const struct krb5_keytypes *ktp, krb5_cryptotype type); +unsigned int krb5int_aes2_crypto_length(const struct krb5_keytypes *ktp, + krb5_cryptotype type); /* Encrypt */ krb5_error_code krb5int_old_encrypt(const struct krb5_keytypes *ktp, @@ -208,6 +210,10 @@ krb5_error_code krb5int_dk_cmac_encrypt(const struct krb5_keytypes *ktp, const krb5_data *ivec, krb5_crypto_iov *data, size_t num_data); +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); /* Decrypt */ krb5_error_code krb5int_old_decrypt(const struct krb5_keytypes *ktp, @@ -232,6 +238,10 @@ krb5_error_code krb5int_dk_cmac_decrypt(const struct krb5_keytypes *ktp, const krb5_data *ivec, krb5_crypto_iov *data, size_t num_data); +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); /* String to key */ krb5_error_code krb5int_des_string_to_key(const struct krb5_keytypes *ktp, @@ -259,6 +269,11 @@ krb5_error_code krb5int_camellia_string_to_key(const struct krb5_keytypes *enc, const krb5_data *salt, const krb5_data *params, krb5_keyblock *key); +krb5_error_code krb5int_aes2_string_to_key(const struct krb5_keytypes *enc, + const krb5_data *string, + const krb5_data *salt, + const krb5_data *params, + krb5_keyblock *key); /* Random to key */ krb5_error_code k5_rand2key_direct(const krb5_data *randombits, @@ -280,6 +295,8 @@ krb5_error_code krb5int_dk_prf(const struct krb5_keytypes *ktp, krb5_key key, krb5_error_code krb5int_dk_cmac_prf(const struct krb5_keytypes *ktp, krb5_key key, const krb5_data *in, krb5_data *out); +krb5_error_code krb5int_aes2_prf(const struct krb5_keytypes *ktp, krb5_key key, + const krb5_data *in, krb5_data *out); /*** Prototypes for cksumtype handler functions ***/ @@ -317,26 +334,38 @@ krb5_error_code krb5int_confounder_verify(const struct krb5_cksumtypes *ctp, size_t num_data, const krb5_data *input, krb5_boolean *valid); +krb5_error_code krb5int_etm_checksum(const struct krb5_cksumtypes *ctp, + krb5_key key, krb5_keyusage usage, + const krb5_crypto_iov *data, + size_t num_data, krb5_data *output); /*** Key derivation functions ***/ enum deriv_alg { DERIVE_RFC3961, /* RFC 3961 section 5.1 */ - DERIVE_SP800_108_CMAC /* NIST SP 800-108 with CMAC as PRF */ + DERIVE_SP800_108_CMAC, /* NIST SP 800-108 with CMAC as PRF */ + DERIVE_SP800_108_HMAC /* NIST SP 800-108 with HMAC as PRF */ }; 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); 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); 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_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); /*** Miscellaneous prototypes ***/ diff --git a/src/lib/crypto/krb/derive.c b/src/lib/crypto/krb/derive.c index f15fec1a29..6707a73082 100644 --- a/src/lib/crypto/krb/derive.c +++ b/src/lib/crypto/krb/derive.c @@ -139,9 +139,9 @@ cleanup: * - 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]; @@ -204,16 +204,75 @@ cleanup: 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; } @@ -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) { @@ -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; @@ -253,6 +313,7 @@ cleanup: 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) { @@ -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; diff --git a/src/lib/crypto/krb/enc_dk_cmac.c b/src/lib/crypto/krb/enc_dk_cmac.c index 9bb3dbaecd..b65b9b70eb 100644 --- a/src/lib/crypto/krb/enc_dk_cmac.c +++ b/src/lib/crypto/krb/enc_dk_cmac.c @@ -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; diff --git a/src/lib/crypto/krb/enc_dk_hmac.c b/src/lib/crypto/krb/enc_dk_hmac.c index f16459ec21..713044c67d 100644 --- a/src/lib/crypto/krb/enc_dk_hmac.c +++ b/src/lib/crypto/krb/enc_dk_hmac.c @@ -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; @@ -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; diff --git a/src/lib/crypto/krb/enc_etm.c b/src/lib/crypto/krb/enc_etm.c new file mode 100644 index 0000000000..3135dd5bb8 --- /dev/null +++ b/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; +} diff --git a/src/lib/crypto/krb/etypes.c b/src/lib/crypto/krb/etypes.c index 53be1d54df..112ae08aa5 100644 --- a/src/lib/crypto/krb/etypes.c +++ b/src/lib/crypto/krb/etypes.c @@ -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 = diff --git a/src/lib/crypto/krb/prf_aes2.c b/src/lib/crypto/krb/prf_aes2.c new file mode 100644 index 0000000000..9a5cffc56c --- /dev/null +++ b/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); +} diff --git a/src/lib/crypto/krb/prf_cmac.c b/src/lib/crypto/krb/prf_cmac.c index 131c36d123..1d4cc4c395 100644 --- a/src/lib/crypto/krb/prf_cmac.c +++ b/src/lib/crypto/krb/prf_cmac.c @@ -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; diff --git a/src/lib/crypto/krb/prf_dk.c b/src/lib/crypto/krb/prf_dk.c index bf8d5223be..544418bab7 100644 --- a/src/lib/crypto/krb/prf_dk.c +++ b/src/lib/crypto/krb/prf_dk.c @@ -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; diff --git a/src/lib/crypto/krb/s2k_pbkdf2.c b/src/lib/crypto/krb/s2k_pbkdf2.c index 316f59a905..ec5856c2be 100644 --- a/src/lib/crypto/krb/s2k_pbkdf2.c +++ b/src/lib/crypto/krb/s2k_pbkdf2.c @@ -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); @@ -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) @@ -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); +}