diff options
author | Luke Howard <lukeh@padl.com> | 2010-05-22 19:11:24 +0000 |
---|---|---|
committer | Luke Howard <lukeh@padl.com> | 2010-05-22 19:11:24 +0000 |
commit | f1dd1977c775b63f92af88bd60c3c92e2a8fb43a (patch) | |
tree | b4ee78c91fd3d759b1a0e4cfce50977e7cad42c5 | |
parent | 16e37d18f13cfe3ab850a662fe5d1f630f752319 (diff) | |
download | krb5-f1dd1977c775b63f92af88bd60c3c92e2a8fb43a.zip krb5-f1dd1977c775b63f92af88bd60c3c92e2a8fb43a.tar.gz krb5-f1dd1977c775b63f92af88bd60c3c92e2a8fb43a.tar.bz2 |
Initial implementation of GCM
git-svn-id: svn://anonsvn.mit.edu/krb5/users/lhoward/camellia-ccm@24087 dc483132-0cff-0310-8789-dd5450dbe970
-rw-r--r-- | src/lib/crypto/crypto_tests/t_encrypt.c | 6 | ||||
-rw-r--r-- | src/lib/crypto/crypto_tests/t_short.c | 4 | ||||
-rw-r--r-- | src/lib/crypto/krb/cksumtypes.c | 24 | ||||
-rw-r--r-- | src/lib/crypto/krb/dk/Makefile.in | 6 | ||||
-rw-r--r-- | src/lib/crypto/krb/dk/checksum_gmac.c | 194 | ||||
-rw-r--r-- | src/lib/crypto/krb/dk/dk.h | 51 | ||||
-rw-r--r-- | src/lib/crypto/krb/dk/dk_ccm.c | 12 | ||||
-rw-r--r-- | src/lib/crypto/krb/dk/dk_gcm.c | 618 | ||||
-rw-r--r-- | src/lib/crypto/krb/etypes.c | 48 | ||||
-rw-r--r-- | src/lib/crypto/krb/prf/Makefile.in | 6 | ||||
-rw-r--r-- | src/lib/crypto/krb/prf/cmac_prf.c (renamed from src/lib/crypto/krb/prf/ccm_prf.c) | 6 | ||||
-rw-r--r-- | src/lib/crypto/krb/prf/prf_int.h | 4 | ||||
-rw-r--r-- | src/util/k5test.py | 10 |
13 files changed, 967 insertions, 22 deletions
diff --git a/src/lib/crypto/crypto_tests/t_encrypt.c b/src/lib/crypto/crypto_tests/t_encrypt.c index b0b8233..bb3af2d 100644 --- a/src/lib/crypto/crypto_tests/t_encrypt.c +++ b/src/lib/crypto/crypto_tests/t_encrypt.c @@ -48,11 +48,13 @@ krb5_enctype interesting_enctypes[] = { #ifdef AES_CCM ENCTYPE_AES128_CCM_128, ENCTYPE_AES256_CCM_128, + ENCTYPE_AES128_GCM_128, + ENCTYPE_AES256_GCM_128, #endif ENCTYPE_CAMELLIA128_CTS_HMAC_SHA1_96, ENCTYPE_CAMELLIA256_CTS_HMAC_SHA1_96, - ENCTYPE_CAMELLIA128_CCM_128, - ENCTYPE_CAMELLIA256_CCM_128, + ENCTYPE_CAMELLIA128_GCM_128, + ENCTYPE_CAMELLIA256_GCM_128, 0 }; diff --git a/src/lib/crypto/crypto_tests/t_short.c b/src/lib/crypto/crypto_tests/t_short.c index a149da7..68cde6e 100644 --- a/src/lib/crypto/crypto_tests/t_short.c +++ b/src/lib/crypto/crypto_tests/t_short.c @@ -44,11 +44,15 @@ krb5_enctype interesting_enctypes[] = { #ifdef AES_CCM ENCTYPE_AES128_CCM_128, ENCTYPE_AES256_CCM_128, + ENCTYPE_AES128_GCM_128, + ENCTYPE_AES256_GCM_128, #endif ENCTYPE_CAMELLIA128_CTS_HMAC_SHA1_96, ENCTYPE_CAMELLIA256_CTS_HMAC_SHA1_96, ENCTYPE_CAMELLIA128_CCM_128, ENCTYPE_CAMELLIA256_CCM_128, + ENCTYPE_CAMELLIA128_GCM_128, + ENCTYPE_CAMELLIA256_GCM_128, 0 }; diff --git a/src/lib/crypto/krb/cksumtypes.c b/src/lib/crypto/krb/cksumtypes.c index d757889..d501689 100644 --- a/src/lib/crypto/krb/cksumtypes.c +++ b/src/lib/crypto/krb/cksumtypes.c @@ -111,6 +111,18 @@ const struct krb5_cksumtypes krb5int_cksumtypes_list[] = { &krb5int_enc_aes256_ctr, NULL, krb5int_dk_cmac_checksum, NULL, 16, 16, 0 }, + + { CKSUMTYPE_GMAC_128_AES128, + "gmac-128-aes128", { 0 }, "GMAC AES128 key", + &krb5int_enc_aes128_ctr, NULL, + krb5int_dk_gmac_checksum, krb5int_dk_gmac_verify, + 28, 28, 0 }, + + { CKSUMTYPE_GMAC_128_AES256, + "gmac-128-aes256", { 0 }, "GMAC AES256 key", + &krb5int_enc_aes256_ctr, NULL, + krb5int_dk_gmac_checksum, krb5int_dk_gmac_verify, + 28, 28, 0 }, #endif /* AES_CCM */ { CKSUMTYPE_MD5_HMAC_ARCFOUR, @@ -142,6 +154,18 @@ const struct krb5_cksumtypes krb5int_cksumtypes_list[] = { &krb5int_enc_camellia256_ctr, NULL, krb5int_dk_cmac_checksum, NULL, 16, 16, 0 }, + + { CKSUMTYPE_GMAC_128_CAMELLIA128, + "gmac-128-camellia128", { 0 }, "GMAC Camellia128 key", + &krb5int_enc_camellia128_ctr, NULL, + krb5int_dk_gmac_checksum, krb5int_dk_gmac_verify, + 28, 28, 0 }, + + { CKSUMTYPE_GMAC_128_CAMELLIA256, + "gmac-128-camellia256", { 0 }, "GMAC Camellia256 key", + &krb5int_enc_camellia256_ctr, NULL, + krb5int_dk_gmac_checksum, krb5int_dk_gmac_verify, + 28, 28, 0 }, }; const size_t krb5int_cksumtypes_length = diff --git a/src/lib/crypto/krb/dk/Makefile.in b/src/lib/crypto/krb/dk/Makefile.in index 5c54601..4a2d2f7 100644 --- a/src/lib/crypto/krb/dk/Makefile.in +++ b/src/lib/crypto/krb/dk/Makefile.in @@ -13,24 +13,30 @@ PROG_RPATH=$(KRB5_LIBDIR) STLIBOBJS=\ checksum.o \ checksum_cmac.o \ + checksum_gmac.o \ dk_aead.o \ dk_ccm.o \ + dk_gcm.o \ derive.o \ stringtokey.o OBJS=\ $(OUTPRE)checksum.$(OBJEXT) \ $(OUTPRE)checksum_cmac.$(OBJEXT)\ + $(OUTPRE)checksum_gmac.$(OBJEXT)\ $(OUTPRE)dk_aead.$(OBJEXT) \ $(OUTPRE)dk_ccm.$(OBJEXT) \ + $(OUTPRE)dk_gcm.$(OBJEXT) \ $(OUTPRE)derive.$(OBJEXT) \ $(OUTPRE)stringtokey.$(OBJEXT) SRCS=\ $(srcdir)/checksum.c \ $(srcdir)/checksum_cmac.c \ + $(srcdir)/checksum_gmac.c \ $(srcdir)/dk_aead.c \ $(srcdir)/dk_ccm.c \ + $(srcdir)/dk_gcm.c \ $(srcdir)/derive.c \ $(srcdir)/stringtokey.c diff --git a/src/lib/crypto/krb/dk/checksum_gmac.c b/src/lib/crypto/krb/dk/checksum_gmac.c new file mode 100644 index 0000000..1fa162a --- /dev/null +++ b/src/lib/crypto/krb/dk/checksum_gmac.c @@ -0,0 +1,194 @@ +/* + * lib/crypto/krb/dk/checksum_gmac.c + * + * Copyright 2010 by the Massachusetts Institute of Technology. + * 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 M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#include "k5-int.h" +#include "etypes.h" +#include "dk.h" +#include "aead.h" +#include "cksumtypes.h" + +#define K5CLENGTH 5 /* 32 bit net byte order integer + one byte seed */ + +krb5_error_code +krb5int_dk_gmac_checksum(const struct krb5_cksumtypes *ctp, + krb5_key key, krb5_keyusage usage, + const krb5_crypto_iov *data, size_t num_data, + krb5_data *output) +{ + const struct krb5_keytypes *ktp; + const struct krb5_enc_provider *enc; + krb5_error_code ret; + unsigned char constantdata[K5CLENGTH]; + krb5_data datain; + krb5_key kc; + unsigned int header_len; + unsigned int trailer_len; + krb5_crypto_iov *sign_data; + size_t i; + + /* Use the key's enctype (more flexible than setting an enctype in ctp). */ + ktp = find_enctype(key->keyblock.enctype); + if (ktp == NULL) + return KRB5_BAD_ENCTYPE; + enc = ktp->enc; + if (key->keyblock.length != enc->keylength) + return KRB5_BAD_KEYSIZE; + + header_len = ktp->crypto_length(ktp, KRB5_CRYPTO_TYPE_HEADER); + trailer_len = ktp->crypto_length(ktp, KRB5_CRYPTO_TYPE_TRAILER); + + if (ctp->compute_size != header_len + trailer_len) + return KRB5_BAD_MSIZE; + + if (output->length < ctp->compute_size) + return KRB5_BAD_MSIZE; + + sign_data = k5alloc((num_data + 2) * sizeof(krb5_crypto_iov), &ret); + if (sign_data == NULL) + return ret; + + for (i = 0; i < num_data; i++) { + sign_data[i].data = data[i].data; + + if (SIGN_IOV(&data[i])) { + sign_data[i].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY; + } else { + sign_data[i].flags = KRB5_CRYPTO_TYPE_EMPTY; + } + } + + sign_data[i].flags = KRB5_CRYPTO_TYPE_HEADER; + sign_data[i].data = make_data(output->data, header_len); + i++; + + sign_data[i].flags = KRB5_CRYPTO_TYPE_TRAILER; + sign_data[i].data = make_data(&output->data[header_len], trailer_len); + i++; + + /* Derive the key. */ + datain = make_data(constantdata, K5CLENGTH); + store_32_be(usage, constantdata); + constantdata[4] = (char) 0x99; + ret = krb5int_derive_key(enc, key, &kc, &datain); + if (ret != 0) { + free(sign_data); + return ret; + } + + /* Hash the data. */ + ret = krb5int_gcm_encrypt(ktp, kc, usage, NULL, sign_data, i); + if (ret != 0) + memset(output->data, 0, output->length); + + output->length = header_len + trailer_len; + + krb5_k_free_key(NULL, kc); + free(sign_data); + return ret; +} + +krb5_error_code +krb5int_dk_gmac_verify(const struct krb5_cksumtypes *ctp, + krb5_key key, krb5_keyusage usage, + const krb5_crypto_iov *data, size_t num_data, + const krb5_data *input, krb5_boolean *valid) +{ + const struct krb5_keytypes *ktp; + const struct krb5_enc_provider *enc; + krb5_error_code ret; + unsigned char constantdata[K5CLENGTH]; + krb5_data datain; + krb5_key kc; + unsigned int header_len; + unsigned int trailer_len; + krb5_crypto_iov *sign_data; + size_t i; + + *valid = FALSE; + + /* Use the key's enctype (more flexible than setting an enctype in ctp). */ + ktp = find_enctype(key->keyblock.enctype); + if (ktp == NULL) + return KRB5_BAD_ENCTYPE; + enc = ktp->enc; + if (key->keyblock.length != enc->keylength) + return KRB5_BAD_KEYSIZE; + + header_len = ktp->crypto_length(ktp, KRB5_CRYPTO_TYPE_HEADER); + trailer_len = ktp->crypto_length(ktp, KRB5_CRYPTO_TYPE_TRAILER); + + if (ctp->compute_size != header_len + trailer_len) + return KRB5_BAD_MSIZE; + + if (input->length != ctp->compute_size) + return KRB5_BAD_MSIZE; + + sign_data = k5alloc((num_data + 2) * sizeof(krb5_crypto_iov), &ret); + if (sign_data == NULL) + return ret; + + for (i = 0; i < num_data; i++) { + sign_data[i].data = data[i].data; + + if (SIGN_IOV(&data[i])) { + sign_data[i].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY; + } else { + sign_data[i].flags = KRB5_CRYPTO_TYPE_EMPTY; + } + } + + sign_data[i].flags = KRB5_CRYPTO_TYPE_HEADER; + sign_data[i].data = make_data(input->data, header_len); + i++; + + sign_data[i].flags = KRB5_CRYPTO_TYPE_TRAILER; + sign_data[i].data = make_data(&input->data[header_len], trailer_len); + i++; + + /* Derive the key. */ + datain = make_data(constantdata, K5CLENGTH); + store_32_be(usage, constantdata); + constantdata[4] = (char) 0x99; + ret = krb5int_derive_key(enc, key, &kc, &datain); + if (ret != 0) { + free(sign_data); + return ret; + } + + /* Verify the data. */ + ret = krb5int_gcm_decrypt(ktp, kc, usage, NULL, sign_data, i); + if (ret == KRB5KRB_AP_ERR_BAD_INTEGRITY) { + ret = 0; + } else if (ret == 0) { + *valid = TRUE; + } + + krb5_k_free_key(NULL, kc); + free(sign_data); + + return ret; +} + diff --git a/src/lib/crypto/krb/dk/dk.h b/src/lib/crypto/krb/dk/dk.h index 5d07332..9b5f898 100644 --- a/src/lib/crypto/krb/dk/dk.h +++ b/src/lib/crypto/krb/dk/dk.h @@ -67,8 +67,11 @@ krb5int_peppered_string_to_key(const struct krb5_keytypes *enc, const krb5_data *params, krb5_keyblock *key); #define krb5int_aes_ccm_string_to_key krb5int_peppered_string_to_key +#define krb5int_aes_gcm_string_to_key krb5int_peppered_string_to_key + #define krb5int_camellia_string_to_key krb5int_peppered_string_to_key #define krb5int_camellia_ccm_string_to_key krb5int_peppered_string_to_key +#define krb5int_camellia_gcm_string_to_key krb5int_peppered_string_to_key krb5_error_code krb5int_derive_keyblock(const struct krb5_enc_provider *enc, @@ -118,3 +121,51 @@ krb5int_dk_cmac_checksum(const struct krb5_cksumtypes *ctp, krb5_key key, krb5_keyusage usage, const krb5_crypto_iov *data, size_t num_data, krb5_data *output); + +unsigned int +krb5int_dk_gcm_crypto_length(const struct krb5_keytypes *ktp, + krb5_cryptotype type); + +krb5_error_code +krb5int_gcm_encrypt(const struct krb5_keytypes *ktp, + krb5_key key, + krb5_keyusage usage, + const krb5_data *ivec, + krb5_crypto_iov *data, + size_t num_data); + +krb5_error_code +krb5int_dk_gcm_encrypt(const struct krb5_keytypes *ktp, + krb5_key key, + krb5_keyusage usage, + const krb5_data *ivec, + krb5_crypto_iov *data, + size_t num_data); + +krb5_error_code +krb5int_gcm_decrypt(const struct krb5_keytypes *ktp, + krb5_key key, + krb5_keyusage usage, + const krb5_data *ivec, + krb5_crypto_iov *data, + size_t num_data); + +krb5_error_code +krb5int_dk_gcm_decrypt(const struct krb5_keytypes *ktp, + krb5_key key, + krb5_keyusage usage, + const krb5_data *ivec, + krb5_crypto_iov *data, + size_t num_data); + +krb5_error_code +krb5int_dk_gmac_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 +krb5int_dk_gmac_verify(const struct krb5_cksumtypes *ctp, + krb5_key key, krb5_keyusage usage, + const krb5_crypto_iov *data, size_t num_data, + const krb5_data *input, krb5_boolean *valid); diff --git a/src/lib/crypto/krb/dk/dk_ccm.c b/src/lib/crypto/krb/dk/dk_ccm.c index 5209bba..75a9cd5 100644 --- a/src/lib/crypto/krb/dk/dk_ccm.c +++ b/src/lib/crypto/krb/dk/dk_ccm.c @@ -323,11 +323,9 @@ ccm_encrypt(const struct krb5_keytypes *ktp, if (ret != 0) goto cleanup; - sign_data = (krb5_crypto_iov *)calloc(num_data + 1, sizeof(krb5_crypto_iov)); - if (sign_data == NULL) { - ret = ENOMEM; + sign_data = k5alloc((num_data + 1) * sizeof(krb5_crypto_iov), &ret); + if (sign_data == NULL) goto cleanup; - } sign_data[num_sign_data].flags = KRB5_CRYPTO_TYPE_HEADER; sign_data[num_sign_data].data = make_data((char *)B0, sizeof(B0)); @@ -490,11 +488,9 @@ ccm_decrypt(const struct krb5_keytypes *ktp, if (ret != 0) goto cleanup; - sign_data = (krb5_crypto_iov *)calloc(num_data + 1, sizeof(krb5_crypto_iov)); - if (sign_data == NULL) { - ret = ENOMEM; + sign_data = k5alloc((num_data + 1) * sizeof(krb5_crypto_iov), &ret); + if (sign_data == NULL) goto cleanup; - } sign_data[num_sign_data].flags = KRB5_CRYPTO_TYPE_HEADER; sign_data[num_sign_data].data = make_data((char *)B0, sizeof(B0)); diff --git a/src/lib/crypto/krb/dk/dk_gcm.c b/src/lib/crypto/krb/dk/dk_gcm.c new file mode 100644 index 0000000..4c79841 --- /dev/null +++ b/src/lib/crypto/krb/dk/dk_gcm.c @@ -0,0 +1,618 @@ +/* + * lib/crypto/krb/dk/dk_gcm.c + * + * Copyright 2010 by the Massachusetts Institute of Technology. + * 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 M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#include "k5-int.h" +#include "dk.h" +#include "aead.h" + +/* + * Implement AEAD_AES_{128,256}_GCM as described in section 5.1 of RFC 5116. + * + * This is the GCM mode as described in NIST 800-38D, with a 12 byte IV + * and 16 byte checksum. Multiple buffers of the same type are logically + * concatenated. + * + * The IOV should be laid out as follows: + * + * HEADER | SIGN_DATA | DATA | PADDING | TRAILER + * + * SIGN_DATA and PADDING may be absent. + * + * Upon decryption, one can pass in explicit buffers as for encryption, or + * one can pass in STREAM, being the concatenation of HEADER | DATA | TRAILER. + * + * STREAM | SIGN_DATA | DATA + * + * Upon output, DATA will contain a pointer into the STREAM buffer with the + * decrypted payload. SIGN_DATA should be ordered relative to the output DATA + * buffer as it was upon encryption. + * + * For compatibility with RFC 5116, a single key is used both for encryption + * and checksumming. The key derivation function is as follows: + * + * Kc = DK(base-key, usage | 0x6C) + * + * Again as required by the GCM specification, SIGN_DATA is processed before + * DATA for the purpose of checksumming. + * + * Because the base keys are compatible with RFC 3962, the two encryption + * types defined here (ENCTYPE_AES128_GCM_128 and ENCTYPE_AES256_GCM_128) + * are most useful in conjunction with RFC 4537. + */ + +#define K5CLENGTH 5 /* 32 bit net byte order integer + one byte seed */ + +#define BLOCK_SIZE 16 + +static char zeros[BLOCK_SIZE]; + +unsigned int +krb5int_dk_gcm_crypto_length(const struct krb5_keytypes *ktp, + krb5_cryptotype type) +{ + unsigned int length; + + switch (type) { + case KRB5_CRYPTO_TYPE_HEADER: + length = 12; /* RFC 5116 5.1 */ + break; + case KRB5_CRYPTO_TYPE_PADDING: + length = 0; /* CTR mode requires no padding */ + break; + case KRB5_CRYPTO_TYPE_TRAILER: + case KRB5_CRYPTO_TYPE_CHECKSUM: + length = ktp->enc->block_size; + assert(length == BLOCK_SIZE); /* SP-800-38D requires this */ + break; + default: + assert(0 && "invalid cryptotype passed to gcm_crypto_length"); + length = ~0; + break; + } + + return length; +} + +static krb5_boolean +valid_payload_length_p(const struct krb5_keytypes *ktp, + unsigned int n, + unsigned int payload_len) +{ + unsigned int block_size = ktp->enc->block_size; + unsigned long nblocks, maxblocks; + + maxblocks = (1UL << 32); + + nblocks = 1; /* tag */ + nblocks += (payload_len + block_size - 1) / block_size; + + return (nblocks <= maxblocks); +} + +static void +xor_128(unsigned char a[BLOCK_SIZE], + unsigned char b[BLOCK_SIZE], + unsigned char out[BLOCK_SIZE]) +{ + unsigned char z; + + for (z = 0; z < 4; z++) { + unsigned char *aptr = &a[z * 4]; + unsigned char *bptr = &b[z * 4]; + unsigned char *outptr = &out[z * 4]; + + store_32_n(load_32_n(aptr) ^ load_32_n(bptr), outptr); + } +} + +static void +rightshift_onebit(unsigned char input[BLOCK_SIZE], + unsigned char output[BLOCK_SIZE]) +{ + unsigned char last_overflow = 0, overflow; + unsigned char i; + + for (i = 0; i < BLOCK_SIZE; i++) { + overflow = (input[i] & 1) ? 0x80 : 0; + output[i] = (input[i] >> 1) & 0x7F; + output[i] |= last_overflow; + last_overflow = overflow; + } +} + +static void +block_product(unsigned char X[BLOCK_SIZE], + unsigned char Y[BLOCK_SIZE], + unsigned char out[BLOCK_SIZE]) +{ + unsigned char V[BLOCK_SIZE]; + unsigned char Z[BLOCK_SIZE]; + unsigned char i; + + memset(Z, 0, BLOCK_SIZE); + memcpy(V, X, BLOCK_SIZE); + + for (i = 0; i < 128; i++) { + unsigned char j = 7 - (i % 8); + + if (Y[i / 8] & (1 << j)) + xor_128(Z, V, Z); + + if (V[15] & 1) { + rightshift_onebit(V, V); + V[0] ^= 0xE1; /* R */ + } else + rightshift_onebit(V, V); + } + + memcpy(out, Z, BLOCK_SIZE); +} + +static krb5_error_code +GHASH(unsigned char H[BLOCK_SIZE], + krb5_crypto_iov *data, + size_t num_data, + unsigned char Y[BLOCK_SIZE]) +{ + struct iov_block_state iov_state; + unsigned char X[BLOCK_SIZE]; + + memset(Y, 0, BLOCK_SIZE); + + IOV_BLOCK_STATE_INIT(&iov_state); + iov_state.include_sign_only = 1; + iov_state.pad_to_boundary = 1; + + while (krb5int_c_iov_get_block(X, sizeof(X), data, num_data, &iov_state)) { + xor_128(Y, X, Y); + block_product(Y, H, Y); + } + + return 0; +} + +/* + * Format initial counter block. Counter may be chained + * across invocations. + */ +static krb5_error_code +format_J0(unsigned char H[BLOCK_SIZE], + unsigned char J0[BLOCK_SIZE], + const krb5_data *iv, + const krb5_data *state) +{ + if (iv->length != 12) { + /* + * SP800-38D 7.1.2: if a non-96-bit nonce is specified, + * then the initial counter block is: + * + * GHASH(H, IV || pad || [0]64 || [len(IV)]64) + * + * Note that len(IV) is the bit length of the IV. + */ + krb5_crypto_iov ivdata[2]; + unsigned char ivlength[BLOCK_SIZE]; + + store_64_be((krb5_ui_8)0, &ivlength[0]); + store_64_be((krb5_ui_8)iv->length * 8, &ivlength[8]); + + ivdata[0].flags = KRB5_CRYPTO_TYPE_DATA; + ivdata[0].data = *iv; + ivdata[1].flags = KRB5_CRYPTO_TYPE_HEADER; + ivdata[1].data = make_data(ivlength, BLOCK_SIZE); + + GHASH(H, ivdata, 2, J0); + } else { + /* Otherwise J0 is IV || [1]32 */ + memcpy(J0, iv->data, iv->length); + store_32_be(1, &J0[12]); + } + + /* + * If non-initial cipher state, propagate counter value + * to support chaining. + */ + if (state != NULL) { + krb5_ui_4 i; + + if (state->length < 4) + return KRB5_BAD_MSIZE; + + i = load_32_be(&state->data[state->length - 4]); + if (i != 0) + store_32_be(i, &J0[12]); + } + + return 0; +} + +static void +format_ICB(unsigned char J0[BLOCK_SIZE], + unsigned char ICB[BLOCK_SIZE]) +{ + krb5_ui_4 i; + + i = load_32_be(&J0[12]); + memcpy(ICB, J0, 12); + store_32_be(i + 1, &ICB[12]); +} + +static krb5_error_code +derive_H(const struct krb5_keytypes *ktp, + krb5_key kg, + unsigned char H[BLOCK_SIZE]) +{ + krb5_crypto_iov data[1]; + krb5_data H_data; + + data[0].flags = KRB5_CRYPTO_TYPE_DATA; + data[0].data = make_data(zeros, sizeof(zeros)); + + H_data = make_data(H, BLOCK_SIZE); + + assert(ktp->enc->cbc_mac != NULL); + + return ktp->enc->cbc_mac(kg, data, 1, NULL, &H_data); +} + +krb5_error_code +krb5int_gcm_encrypt(const struct krb5_keytypes *ktp, + krb5_key kg, + krb5_keyusage usage, + const krb5_data *state, + krb5_crypto_iov *data, + size_t num_data) +{ + krb5_error_code ret; + krb5_crypto_iov *header, *trailer, *sign_data = NULL; + size_t i, num_sign_data = 0; + unsigned int header_len, trailer_len; + size_t plain_len = 0, adata_len = 0; + char len_buf[BLOCK_SIZE]; + unsigned char H[BLOCK_SIZE], J0[BLOCK_SIZE]; + unsigned char ICB[BLOCK_SIZE]; + krb5_data counter; + + header_len = ktp->crypto_length(ktp, KRB5_CRYPTO_TYPE_HEADER); + + header = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_HEADER); + if (header == NULL || header->data.length != header_len) { + ret = KRB5_BAD_MSIZE; + goto cleanup; + } + + trailer_len = ktp->crypto_length(ktp, KRB5_CRYPTO_TYPE_TRAILER); + + trailer = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_TRAILER); + if (trailer == NULL || trailer->data.length != trailer_len) { + ret = KRB5_BAD_MSIZE; + goto cleanup; + } + + for (i = 0; i < num_data; i++) { + krb5_crypto_iov *iov = &data[i]; + + switch (iov->flags) { + case KRB5_CRYPTO_TYPE_DATA: + plain_len += iov->data.length; + break; + case KRB5_CRYPTO_TYPE_SIGN_ONLY: + adata_len += iov->data.length; + break; + case KRB5_CRYPTO_TYPE_PADDING: + if (iov->data.length != 0) { + ret = KRB5_BAD_MSIZE; + goto cleanup; + } + break; + default: + break; + } + } + + if (!valid_payload_length_p(ktp, header_len, plain_len)) { + ret = KRB5_BAD_MSIZE; + goto cleanup; + } + + header->data.length = header_len; + + /* Initialize IV */ + ret = krb5_c_random_make_octets(NULL, &header->data); + if (ret != 0) + goto cleanup; + + /* Derive H subkey */ + ret = derive_H(ktp, kg, H); + if (ret != 0) + goto cleanup; + + /* Initialize counter block */ + ret = format_J0(H, J0, &header->data, state); + if (ret != 0) + goto cleanup; + + format_ICB(J0, ICB); + counter = make_data((char *)ICB, sizeof(ICB)); + + if (plain_len != 0) { + /* Encrypt plaintext */ + ret = ktp->enc->encrypt(kg, &counter, data, num_data); + if (ret != 0) + goto cleanup; + } + + sign_data = k5alloc((num_data + 1) * sizeof(krb5_crypto_iov), &ret); + if (sign_data == NULL) + goto cleanup; + + /* Reorder input IOV so SIGN_ONLY data is before DATA */ + for (i = 0; i < num_data; i++) { + if (data[i].flags == KRB5_CRYPTO_TYPE_SIGN_ONLY) + sign_data[num_sign_data++] = data[i]; + } + for (i = 0; i < num_data; i++) { + if (data[i].flags == KRB5_CRYPTO_TYPE_DATA) + sign_data[num_sign_data++] = data[i]; + } + + /* Append bit length of SIGN_ONLY and DATA */ + store_64_be((krb5_ui_8)adata_len * 8, &len_buf[0]); + store_64_be((krb5_ui_8)plain_len * 8, &len_buf[8]); + + sign_data[num_sign_data].flags = KRB5_CRYPTO_TYPE_HEADER; + sign_data[num_sign_data].data = make_data(len_buf, sizeof(len_buf)); + num_sign_data++; + + /* Make checksum */ + ret = GHASH(H, sign_data, num_sign_data, + (unsigned char *)trailer->data.data); + if (ret != 0) + goto cleanup; + + /* Encrypt checksum in trailer */ + { + krb5_data J0_data = make_data((char *)J0, sizeof(J0)); + krb5_crypto_iov cksum[1]; + + cksum[0].flags = KRB5_CRYPTO_TYPE_DATA; + cksum[0].data = trailer->data; + + ret = ktp->enc->encrypt(kg, &J0_data, cksum, 1); + if (ret != 0) + goto cleanup; + } + + if (state != NULL) + memcpy(state->data, counter.data, counter.length); + +cleanup: + free(sign_data); + zap(ICB, sizeof(ICB)); + zap(J0, sizeof(J0)); + zap(H, sizeof(H)); + + return ret; +} + +krb5_error_code +krb5int_dk_gcm_encrypt(const struct krb5_keytypes *ktp, + krb5_key key, + krb5_keyusage usage, + const krb5_data *state, + krb5_crypto_iov *data, + size_t num_data) +{ + unsigned char constantdata[K5CLENGTH]; + krb5_error_code ret; + krb5_key kc; + krb5_data d1; + + d1.data = (char *)constantdata; + d1.length = K5CLENGTH; + + d1.data[0] = (usage >> 24) & 0xFF; + d1.data[1] = (usage >> 16) & 0xFF; + d1.data[2] = (usage >> 8 ) & 0xFF; + d1.data[3] = (usage ) & 0xFF; + + d1.data[4] = 0x6C; + + ret = krb5int_derive_key(ktp->enc, key, &kc, &d1); + if (ret != 0) + return ret; + + ret = krb5int_gcm_encrypt(ktp, kc, usage, state, data, num_data); + + krb5_k_free_key(NULL, kc); + + return ret; +} + +krb5_error_code +krb5int_gcm_decrypt(const struct krb5_keytypes *ktp, + krb5_key kg, + krb5_keyusage usage, + const krb5_data *state, + krb5_crypto_iov *data, + size_t num_data) +{ + krb5_error_code ret; + krb5_crypto_iov *header, *trailer, *sign_data = NULL; + size_t i, num_sign_data = 0; + unsigned int header_len, trailer_len; + size_t plain_len = 0, adata_len = 0; + char len_buf[BLOCK_SIZE]; + unsigned char H[BLOCK_SIZE], J0[BLOCK_SIZE]; + krb5_data counter; + unsigned char made_cksum[BLOCK_SIZE]; + krb5_crypto_iov made_cksum_data[1]; + + header_len = ktp->crypto_length(ktp, KRB5_CRYPTO_TYPE_HEADER); + + header = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_HEADER); + if (header == NULL || header->data.length < header_len) { + ret = KRB5_BAD_MSIZE; + goto cleanup; + } + + trailer_len = ktp->crypto_length(ktp, KRB5_CRYPTO_TYPE_TRAILER); + assert(trailer_len <= sizeof(made_cksum)); + + trailer = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_TRAILER); + if (trailer == NULL || trailer->data.length < trailer_len) { + ret = KRB5_BAD_MSIZE; + goto cleanup; + } + + for (i = 0; i < num_data; i++) { + krb5_crypto_iov *iov = &data[i]; + + switch (iov->flags) { + case KRB5_CRYPTO_TYPE_DATA: + plain_len += iov->data.length; + break; + case KRB5_CRYPTO_TYPE_SIGN_ONLY: + adata_len += iov->data.length; + break; + case KRB5_CRYPTO_TYPE_PADDING: + iov->data.length = 0; + break; + default: + break; + } + } + + if (!valid_payload_length_p(ktp, header_len, plain_len)) { + ret = KRB5_BAD_MSIZE; + goto cleanup; + } + + /* Derive H subkey */ + ret = derive_H(ktp, kg, H); + if (ret != 0) + goto cleanup; + + /* Initialize counter block */ + ret = format_J0(H, J0, &header->data, state); + if (ret != 0) + goto cleanup; + + counter = make_data((char *)J0, sizeof(J0)); + + sign_data = k5alloc((num_data + 1) * sizeof(krb5_crypto_iov), &ret); + if (sign_data == NULL) + goto cleanup; + + /* Reorder input IOV so SIGN_ONLY data is before DATA */ + for (i = 0; i < num_data; i++) { + if (data[i].flags == KRB5_CRYPTO_TYPE_SIGN_ONLY) + sign_data[num_sign_data++] = data[i]; + } + for (i = 0; i < num_data; i++) { + if (data[i].flags == KRB5_CRYPTO_TYPE_DATA) + sign_data[num_sign_data++] = data[i]; + } + + /* Append bit length of SIGN_ONLY and DATA */ + store_64_be((krb5_ui_8)adata_len * 8, &len_buf[0]); + store_64_be((krb5_ui_8)plain_len * 8, &len_buf[8]); + + sign_data[num_sign_data].flags = KRB5_CRYPTO_TYPE_HEADER; + sign_data[num_sign_data].data = make_data(len_buf, sizeof(len_buf)); + num_sign_data++; + + /* Calculate hash for comparison */ + ret = GHASH(H, sign_data, num_sign_data, made_cksum); + if (ret != 0) + goto cleanup; + + /* Encrypt checksum for comparison */ + made_cksum_data[0].flags = KRB5_CRYPTO_TYPE_DATA; + made_cksum_data[0].data = make_data(made_cksum, sizeof(made_cksum)); + + ret = ktp->enc->encrypt(kg, &counter, made_cksum_data, 1); + if (ret != 0) + goto cleanup; + + /* Compare (possibly truncated) checksum */ + if (memcmp(made_cksum, trailer->data.data, trailer_len) != 0) { + ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; + goto cleanup; + } + + /* Decrypt plaintext */ + if (plain_len != 0) { + ret = ktp->enc->decrypt(kg, &counter, data, num_data); + if (ret != 0) + goto cleanup; + } + + if (state != NULL) + memcpy(state->data, counter.data, counter.length); + +cleanup: + free(sign_data); + zap(J0, sizeof(J0)); + zap(H, sizeof(H)); + zap(made_cksum, sizeof(made_cksum)); + + return ret; +} + +krb5_error_code +krb5int_dk_gcm_decrypt(const struct krb5_keytypes *ktp, + krb5_key key, + krb5_keyusage usage, + const krb5_data *state, + krb5_crypto_iov *data, + size_t num_data) +{ + unsigned char constantdata[K5CLENGTH]; + krb5_error_code ret; + krb5_key kc; + krb5_data d1; + + d1.data = (char *)constantdata; + d1.length = K5CLENGTH; + + d1.data[0] = (usage >> 24) & 0xFF; + d1.data[1] = (usage >> 16) & 0xFF; + d1.data[2] = (usage >> 8 ) & 0xFF; + d1.data[3] = (usage ) & 0xFF; + + d1.data[4] = 0x6C; + + ret = krb5int_derive_key(ktp->enc, key, &kc, &d1); + if (ret != 0) + return ret; + + ret = krb5int_gcm_decrypt(ktp, kc, usage, state, data, num_data); + + krb5_k_free_key(NULL, kc); + + return ret; +} diff --git a/src/lib/crypto/krb/etypes.c b/src/lib/crypto/krb/etypes.c index aa14dc6..0e6c2ac 100644 --- a/src/lib/crypto/krb/etypes.c +++ b/src/lib/crypto/krb/etypes.c @@ -161,7 +161,7 @@ const struct krb5_keytypes krb5int_enctypes_list[] = { 16, krb5int_dk_ccm_crypto_length, krb5int_dk_ccm_encrypt, krb5int_dk_ccm_decrypt, krb5int_aes_ccm_string_to_key, - krb5int_dk_ccm_prf, + krb5int_dk_cmac_prf, CKSUMTYPE_CMAC_128_AES128, 0 /*flags*/ }, { ENCTYPE_AES256_CCM_128, @@ -171,9 +171,29 @@ const struct krb5_keytypes krb5int_enctypes_list[] = { 16, krb5int_dk_ccm_crypto_length, krb5int_dk_ccm_encrypt, krb5int_dk_ccm_decrypt, krb5int_aes_ccm_string_to_key, - krb5int_dk_ccm_prf, + krb5int_dk_cmac_prf, CKSUMTYPE_CMAC_128_AES256, 0 /*flags */ }, + { ENCTYPE_AES128_GCM_128, + "aes128-gcm-128", { "aes128-gcm" }, + "AES-128 GCM mode with 128-bit MAC", + &krb5int_enc_aes128_ctr, NULL, + 16, + krb5int_dk_gcm_crypto_length, krb5int_dk_gcm_encrypt, krb5int_dk_gcm_decrypt, + krb5int_aes_gcm_string_to_key, + krb5int_dk_cmac_prf, + CKSUMTYPE_GMAC_128_AES128, + 0 /*flags*/ }, + { ENCTYPE_AES256_GCM_128, + "aes256-gcm-128", { "aes256-gcm" }, + "AES-256 GCM mode with 128-bit MAC", + &krb5int_enc_aes256_ctr, NULL, + 16, + krb5int_dk_gcm_crypto_length, krb5int_dk_gcm_encrypt, krb5int_dk_gcm_decrypt, + krb5int_aes_gcm_string_to_key, + krb5int_dk_cmac_prf, + CKSUMTYPE_GMAC_128_AES256, + 0 /*flags */ }, #endif /* AES_CCM */ { ENCTYPE_CAMELLIA128_CTS_HMAC_SHA1_96, "camellia128-cts-hmac-sha1-96", { "camellia128-cts" }, @@ -202,7 +222,7 @@ const struct krb5_keytypes krb5int_enctypes_list[] = { 16, krb5int_dk_ccm_crypto_length, krb5int_dk_ccm_encrypt, krb5int_dk_ccm_decrypt, krb5int_camellia_ccm_string_to_key, - krb5int_dk_ccm_prf, + krb5int_dk_cmac_prf, CKSUMTYPE_CMAC_128_CAMELLIA128, 0 /*flags*/ }, { ENCTYPE_CAMELLIA256_CCM_128, @@ -212,9 +232,29 @@ const struct krb5_keytypes krb5int_enctypes_list[] = { 16, krb5int_dk_ccm_crypto_length, krb5int_dk_ccm_encrypt, krb5int_dk_ccm_decrypt, krb5int_camellia_ccm_string_to_key, - krb5int_dk_ccm_prf, + krb5int_dk_cmac_prf, CKSUMTYPE_CMAC_128_CAMELLIA256, 0 /*flags */ }, + { ENCTYPE_CAMELLIA128_GCM_128, + "camellia128-gcm-128", { "camellia128-gcm" }, + "Camellia-128 GCM mode with 128-bit MAC", + &krb5int_enc_camellia128_ctr, NULL, + 16, + krb5int_dk_gcm_crypto_length, krb5int_dk_gcm_encrypt, krb5int_dk_gcm_decrypt, + krb5int_camellia_gcm_string_to_key, + krb5int_dk_cmac_prf, + CKSUMTYPE_GMAC_128_CAMELLIA128, + 0 /*flags*/ }, + { ENCTYPE_CAMELLIA256_GCM_128, + "camellia256-gcm-128", { "camellia256-gcm" }, + "Camellia-256 GCM mode with 128-bit MAC", + &krb5int_enc_camellia256_ctr, NULL, + 16, + krb5int_dk_gcm_crypto_length, krb5int_dk_gcm_encrypt, krb5int_dk_gcm_decrypt, + krb5int_camellia_gcm_string_to_key, + krb5int_dk_cmac_prf, + CKSUMTYPE_GMAC_128_CAMELLIA256, + 0 /*flags */ }, }; const int krb5int_enctypes_length = diff --git a/src/lib/crypto/krb/prf/Makefile.in b/src/lib/crypto/krb/prf/Makefile.in index 74a8979..a0e1ad6 100644 --- a/src/lib/crypto/krb/prf/Makefile.in +++ b/src/lib/crypto/krb/prf/Makefile.in @@ -14,13 +14,13 @@ DEFS= PROG_LIBPATH=-L$(TOPLIBD) PROG_RPATH=$(KRB5_LIBDIR) -STLIBOBJS= des_prf.o dk_prf.o rc4_prf.o ccm_prf.o +STLIBOBJS= des_prf.o dk_prf.o rc4_prf.o cmac_prf.o OBJS= $(OUTPRE)des_prf.$(OBJEXT) $(OUTPRE)dk_prf.$(OBJEXT) \ - $(OUTPRE)rc4_prf.$(OBJEXT) $(OUTPRE)ccm_prf.$(OBJEXT) + $(OUTPRE)rc4_prf.$(OBJEXT) $(OUTPRE)cmac_prf.$(OBJEXT) SRCS= $(srcdir)/des_prf.c $(srcdir)/dk_prf.c \ - $(srcdir)/rc4_prf.c $(srcdir)/ccm_prf.c + $(srcdir)/rc4_prf.c $(srcdir)/cmac_prf.c ##DOS##LIBOBJS = $(OBJS) diff --git a/src/lib/crypto/krb/prf/ccm_prf.c b/src/lib/crypto/krb/prf/cmac_prf.c index ba23be7..5b14a86 100644 --- a/src/lib/crypto/krb/prf/ccm_prf.c +++ b/src/lib/crypto/krb/prf/cmac_prf.c @@ -1,6 +1,6 @@ /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /* - * lib/crypto/krb/prf/ccm_prf.c + * lib/crypto/krb/prf/cmac_prf.c * * Copyright (C) 2010 by the Massachusetts Institute of Technology. * All rights reserved. @@ -34,8 +34,8 @@ #include <dk.h> krb5_error_code -krb5int_dk_ccm_prf(const struct krb5_keytypes *ktp, krb5_key key, - const krb5_data *in, krb5_data *out) +krb5int_dk_cmac_prf(const struct krb5_keytypes *ktp, krb5_key key, + const krb5_data *in, krb5_data *out) { krb5_crypto_iov iov; krb5_data prfconst = make_data("prf", 3); diff --git a/src/lib/crypto/krb/prf/prf_int.h b/src/lib/crypto/krb/prf/prf_int.h index 25c4da6..1c98720 100644 --- a/src/lib/crypto/krb/prf/prf_int.h +++ b/src/lib/crypto/krb/prf/prf_int.h @@ -44,7 +44,7 @@ krb5int_dk_prf(const struct krb5_keytypes *ktp, krb5_key key, const krb5_data *in, krb5_data *out); krb5_error_code -krb5int_dk_ccm_prf(const struct krb5_keytypes *ktp, krb5_key key, - const krb5_data *in, krb5_data *out); +krb5int_dk_cmac_prf(const struct krb5_keytypes *ktp, krb5_key key, + const krb5_data *in, krb5_data *out); #endif /*PRF_INTERNAL_DEFS*/ diff --git a/src/util/k5test.py b/src/util/k5test.py index 3e2f7a8..97269ac 100644 --- a/src/util/k5test.py +++ b/src/util/k5test.py @@ -1023,6 +1023,16 @@ _passes = [ 'supported_enctypes' : 'camellia256-ccm:normal', 'master_key_type' : 'camellia256-ccm'}}}}), + # Exercise the camellia256-gcm enctype. + ('camellia256', None, + {'all' : {'libdefaults' : { + 'default_tgs_enctypes' : 'camellia256-gcm', + 'default_tkt_enctypes' : 'camellia256-gcm', + 'permitted_enctypes' : 'camellia256-gcm'}}}, + {'master' : {'realms' : {'$realm' : { + 'supported_enctypes' : 'camellia256-gcm:normal', + 'master_key_type' : 'camellia256-gcm'}}}}), + # Test a setup with modern principal keys but an old TGT key. ('aes256.destgt', 'des-cbc-crc:normal', {'all' : {'libdefaults' : {'allow_weak_crypto' : 'true'}}}, |