From 69be36cb13a5aa74f22f37aaa0b79d0274bf5cdd Mon Sep 17 00:00:00 2001 From: Luke Howard Date: Mon, 19 Oct 2009 19:14:39 +0000 Subject: quick port of AES-CCM code to crypto modularity, no OpenSSL support yet git-svn-id: svn://anonsvn.mit.edu/krb5/users/lhoward/aes-ccm@22939 dc483132-0cff-0310-8789-dd5450dbe970 --- src/lib/crypto/krb/dk/Makefile.in | 3 + src/lib/crypto/krb/dk/dk.h | 23 + src/lib/crypto/krb/dk/dk_ccm.c | 628 ++++++++++++++++++++++++ src/lib/crypto/krb/enc_provider/Makefile.in | 4 +- src/lib/crypto/krb/enc_provider/aes_ctr.c | 320 ++++++++++++ src/lib/crypto/krb/etypes.c | 22 + src/lib/crypto/krb/keyhash_provider/Makefile.in | 6 +- src/lib/crypto/krb/keyhash_provider/aescbc.c | 118 +++++ 8 files changed, 1120 insertions(+), 4 deletions(-) create mode 100644 src/lib/crypto/krb/dk/dk_ccm.c create mode 100644 src/lib/crypto/krb/enc_provider/aes_ctr.c create mode 100644 src/lib/crypto/krb/keyhash_provider/aescbc.c (limited to 'src/lib/crypto/krb') diff --git a/src/lib/crypto/krb/dk/Makefile.in b/src/lib/crypto/krb/dk/Makefile.in index a731cbc..fe26298 100644 --- a/src/lib/crypto/krb/dk/Makefile.in +++ b/src/lib/crypto/krb/dk/Makefile.in @@ -15,6 +15,7 @@ PROG_RPATH=$(KRB5_LIBDIR) STLIBOBJS=\ checksum.o \ dk_aead.o \ + dk_ccm.o \ dk_decrypt.o \ dk_encrypt.o \ derive.o \ @@ -23,6 +24,7 @@ STLIBOBJS=\ OBJS=\ $(OUTPRE)checksum.$(OBJEXT) \ $(OUTPRE)dk_aead.$(OBJEXT) \ + $(OUTPRE)dk_ccm.$(OBJEXT) \ $(OUTPRE)dk_decrypt.$(OBJEXT) \ $(OUTPRE)dk_encrypt.$(OBJEXT) \ $(OUTPRE)derive.$(OBJEXT) \ @@ -31,6 +33,7 @@ OBJS=\ SRCS=\ $(srcdir)/checksum.c \ $(srcdir)/dk_aead.c \ + $(srcdir)/dk_ccm.c \ $(srcdir)/dk_decrypt.c \ $(srcdir)/dk_encrypt.c \ $(srcdir)/derive.c \ diff --git a/src/lib/crypto/krb/dk/dk.h b/src/lib/crypto/krb/dk/dk.h index 9ddeb40..fc1ef4e 100644 --- a/src/lib/crypto/krb/dk/dk.h +++ b/src/lib/crypto/krb/dk/dk.h @@ -94,3 +94,26 @@ krb5_derive_random(const struct krb5_enc_provider *enc, extern const struct krb5_aead_provider krb5int_aead_dk; extern const struct krb5_aead_provider krb5int_aead_aes; + +/* CCM */ + +void +krb5int_ccm_encrypt_length(const struct krb5_enc_provider *enc, + const struct krb5_hash_provider *hash, + size_t inputlen, size_t *length); + +extern const struct krb5_aead_provider krb5int_aead_ccm; + +krb5_error_code krb5int_ccm_encrypt +(const struct krb5_enc_provider *enc, + const struct krb5_hash_provider *hash, + const krb5_keyblock *key, krb5_keyusage usage, + const krb5_data *ivec, const krb5_data *input, + krb5_data *arg_output); + +krb5_error_code krb5int_ccm_decrypt +(const struct krb5_enc_provider *enc, + const struct krb5_hash_provider *hash, + const krb5_keyblock *key, krb5_keyusage usage, + const krb5_data *ivec, const krb5_data *input, + krb5_data *arg_output); diff --git a/src/lib/crypto/krb/dk/dk_ccm.c b/src/lib/crypto/krb/dk/dk_ccm.c new file mode 100644 index 0000000..41f6022 --- /dev/null +++ b/src/lib/crypto/krb/dk/dk_ccm.c @@ -0,0 +1,628 @@ +/* + * lib/crypto/dk/dk_ccm.c + * + * Copyright 2008 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}_CCM as described in section 5.3 of RFC 5116. + * + * This is the CCM mode as described in NIST 800-38C, with a 12 byte nonce + * 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 | 0xCC) + * + * Again as required by the CCM 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_CCM_128 and ENCTYPE_AES256_CCM_128) + * are most useful in conjunction with RFC 4537. + */ + +#define K5CLENGTH 5 /* 32 bit net byte order integer + one byte seed */ + +#define CCM_FLAG_MASK_Q 0x07 +#define CCM_FLAG_MASK_T 0x38 +#define CCM_FLAG_ADATA 0x40 +#define CCM_FLAG_RESERVED 0x80 + +static krb5_error_code +krb5int_ccm_crypto_length(const struct krb5_aead_provider *aead, + const struct krb5_enc_provider *enc, + const struct krb5_hash_provider *hash, + krb5_cryptotype type, + unsigned int *length) +{ + switch (type) { + case KRB5_CRYPTO_TYPE_HEADER: + *length = 12; /* RFC 5116 5.3 */ + 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 = enc->block_size; + break; + default: + assert(0 && "invalid cryptotype passed to krb5int_ccm_crypto_length"); + break; + } + + return 0; +} + +static krb5_error_code +encode_a_len(krb5_data *a, krb5_ui_8 adata_len) +{ + size_t len; + unsigned char *p; + + if (adata_len > (1LL << 32)) + len = 10; + else if (adata_len > (1LL << 16) - (1LL << 8)) + len = 6; + else if (adata_len) + len = 2; + else + len = 0; + + if (a->length < len) + return KRB5_BAD_MSIZE; + + p = (unsigned char *)a->data; + + switch (len) { + case 2: + p[0] = (adata_len >> 8) & 0xFF; + p[1] = (adata_len ) & 0xFF; + break; + case 6: + p[0] = 0xFF; + p[1] = 0xFE; + p[2] = (adata_len >> 24) & 0xFF; + p[3] = (adata_len >> 16) & 0xFF; + p[4] = (adata_len >> 8 ) & 0xFF; + p[5] = (adata_len ) & 0xFF; + break; + case 10: + p[0] = 0xFF; + p[1] = 0xFF; + p[2] = (adata_len >> 56) & 0xFF; + p[3] = (adata_len >> 48) & 0xFF; + p[4] = (adata_len >> 40) & 0xFF; + p[5] = (adata_len >> 32) & 0xFF; + p[6] = (adata_len >> 24) & 0xFF; + p[7] = (adata_len >> 16) & 0xFF; + p[8] = (adata_len >> 8 ) & 0xFF; + p[9] = (adata_len ) & 0xFF; + break; + } + + a->length = len; + + return 0; +} + +/* + * format_B0() allows the tradeoff between nonce and payload length to + * be parameterized by replacing the crypto_length() callback + */ +static krb5_error_code +format_B0(krb5_data *B0, /* B0 */ + krb5_data *nonce, /* N */ + size_t trailer_len, /* t */ + krb5_ui_8 adata_len, /* a */ + krb5_ui_8 payload_len) /* Q */ +{ + unsigned char flags; + unsigned char *p; + krb5_octet q, i = 0; + + if (B0->length != 16) + return KRB5_BAD_MSIZE; + + /* SP800-38C A.1: Length Requirements */ + + /* t is an elements of {4, 6, 8, 10, 12, 14, 16} */ + if (trailer_len % 2 || + (trailer_len < 4 || trailer_len > 16)) + return KRB5_BAD_MSIZE; + + /* n is an element of {7, 8, 9, 10, 11, 12, 13} */ + if (nonce->length < 7 || nonce->length > 13) + return KRB5_BAD_MSIZE; + + q = 15 - nonce->length; + + /* P consists of fewer than 2^(8q) octets */ + if (payload_len >= (1UL << (8 * q))) + return KRB5_BAD_MSIZE; + + /* SP800-38C A.1: Formatting of the Control Information and the Nonce */ + flags = q - 1; + flags |= (((trailer_len - 2) / 2) << 3); + if (adata_len != 0) + flags |= CCM_FLAG_ADATA; + + p = (unsigned char *)B0->data; + p[i++] = flags; + + memcpy(&p[i], nonce->data, nonce->length); + i += nonce->length; + + for (; i < B0->length; i++) { + register krb5_octet s; + + s = (q - (i - nonce->length)) * 8; + + p[i] = (payload_len >> s) & 0xFF; + } + + return 0; +} + +/* + * format_Ctr0 is parameterized by extracting the flags octet from B0 + */ +static krb5_error_code +format_Ctr0(krb5_data *Ctr0, krb5_data *B0) +{ + krb5_octet n; /* nonce length */ + krb5_octet q; /* counter length */ + + assert(B0->length == 16); + + q = (B0->data[0] & CCM_FLAG_MASK_Q) + 1; + + Ctr0->data[0] = q - 1; + + n = 15 - q; + + assert(n >= 7 && n <= 13); + + memcpy(&Ctr0->data[1], &B0->data[1], n); + memset(&Ctr0->data[1 + n], 0, q); + + return 0; +} + +static krb5_error_code +krb5int_ccm_encrypt_iov(const struct krb5_aead_provider *aead, + const struct krb5_enc_provider *enc, + const struct krb5_hash_provider *hash, + const krb5_keyblock *key, + krb5_keyusage usage, + const krb5_data *ivec, + krb5_crypto_iov *data, + size_t num_data) +{ + krb5_error_code ret; + unsigned char constantdata[K5CLENGTH]; + krb5_data d1; + krb5_crypto_iov *header, *trailer, *sign_data = NULL; + krb5_keyblock kc; + size_t i, num_sign_data = 0; + unsigned int header_len = 0; + unsigned int trailer_len = 0; + size_t payload_len = 0; + size_t adata_len = 0; + krb5_data cksum, counter; + krb5_cksumtype cksumtype; + const struct krb5_cksumtypes *keyhash; + char adata_len_buf[6]; + unsigned char B0[16], Ctr[16]; + + kc.contents = NULL; + kc.length = 0; + + cksum.data = NULL; + cksum.length = 0; + + ret = aead->crypto_length(aead, enc, hash, KRB5_CRYPTO_TYPE_HEADER, &header_len); + if (ret != 0) + return ret; + + header = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_HEADER); + if (header == NULL || header->data.length < header_len) + return KRB5_BAD_MSIZE; + + ret = aead->crypto_length(aead, enc, hash, KRB5_CRYPTO_TYPE_TRAILER, &trailer_len); + if (ret != 0) + return ret; + + trailer = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_TRAILER); + if (trailer == NULL || trailer->data.length < trailer_len) + return KRB5_BAD_MSIZE; + + for (i = 0; i < num_data; i++) { + krb5_crypto_iov *iov = &data[i]; + + switch (iov->flags) { + case KRB5_CRYPTO_TYPE_DATA: + payload_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; + } + } + + header->data.length = header_len; + + if (ivec != NULL) { + if (ivec->length != 16 || + ivec->data[0] & ~(CCM_FLAG_MASK_Q) || + 15 - (unsigned)ivec->data[0] != header_len) { + ret = KRB5_BAD_MSIZE; + goto cleanup; + } + memcpy(header->data.data, &ivec->data[1], header_len); + } else { + ret = krb5_c_random_make_octets(/* XXX */ NULL, &header->data); + if (ret != 0) + goto cleanup; + } + + sign_data = (krb5_crypto_iov *)calloc(num_data + 1, sizeof(krb5_crypto_iov)); + if (sign_data == NULL) { + ret = ENOMEM; + goto cleanup; + } + + sign_data[num_sign_data].flags = KRB5_CRYPTO_TYPE_HEADER; + sign_data[num_sign_data].data.data = (char *)B0; + sign_data[num_sign_data].data.length = sizeof(B0); + ret = format_B0(&sign_data[num_sign_data].data, &header->data, trailer_len, + (krb5_ui_8)adata_len, (krb5_ui_8)payload_len); + if (ret != 0) + goto cleanup; + num_sign_data++; + + /* Include length of associated data in CBC-MAC */ + sign_data[num_sign_data].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY; + sign_data[num_sign_data].data.data = adata_len_buf; + sign_data[num_sign_data].data.length = sizeof(adata_len_buf); + ret = encode_a_len(&sign_data[num_sign_data].data, (krb5_ui_8)adata_len); + if (ret != 0) + goto cleanup; + num_sign_data++; + + /* 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]; + } + + 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] = 0xCC; + + kc.length = enc->keylength; + kc.contents = malloc(kc.length); + if (kc.contents == NULL) { + ret = ENOMEM; + goto cleanup; + } + + ret = krb5_derive_key(enc, key, &kc, &d1); + if (ret != 0) + goto cleanup; + + assert(enc->encrypt_iov != NULL); + + cksum.data = malloc(trailer_len); + if (cksum.data == NULL) { + ret = ENOMEM; + goto cleanup; + } + cksum.length = trailer_len; + + ret = krb5int_c_mandatory_cksumtype(NULL, key->enctype, &cksumtype); + if (ret != 0) + goto cleanup; + + keyhash = krb5int_c_find_checksum_type(cksumtype); + if (keyhash == NULL) { + ret = KRB5_BAD_ENCTYPE; + goto cleanup; + } + + ret = krb5int_c_make_checksum_iov(keyhash, &kc, usage, sign_data, num_sign_data, &cksum); + if (ret != 0) + goto cleanup; + + /* Initialize first counter block */ + if (ivec == NULL) { + counter.length = sizeof(Ctr); + counter.data = (char *)Ctr; + + ret = format_Ctr0(&counter, &sign_data[0].data); + if (ret != 0) + goto cleanup; + + ivec = &counter; + } + + trailer->data.length = trailer_len; + + /* Encrypt checksum and place in trailer */ + ret = enc->encrypt(&kc, ivec, &cksum, &trailer->data); + if (ret != 0) + goto cleanup; + + /* Don't encrypt B0 (header), but encrypt everything else */ + ret = enc->encrypt_iov(&kc, ivec, data, num_data); + if (ret != 0) + goto cleanup; + +cleanup: + if (kc.contents != NULL) { + zap(kc.contents, kc.length); + free(kc.contents); + } + if (cksum.data != NULL) { + free(cksum.data); + } + if (sign_data != NULL) { + free(sign_data); + } + + return ret; +} + +static krb5_error_code +krb5int_ccm_decrypt_iov(const struct krb5_aead_provider *aead, + const struct krb5_enc_provider *enc, + const struct krb5_hash_provider *hash, + const krb5_keyblock *key, + krb5_keyusage usage, + const krb5_data *ivec, + krb5_crypto_iov *data, + size_t num_data) +{ + krb5_error_code ret; + unsigned char constantdata[K5CLENGTH]; + krb5_data d1; + krb5_crypto_iov *header, *trailer, *sign_data = NULL; + krb5_keyblock kc; + size_t i, num_sign_data = 0; + unsigned int header_len = 0; + unsigned int trailer_len = 0; + size_t adata_len = 0; + size_t payload_len = 0; + krb5_data cksum, counter; + krb5_cksumtype cksumtype; + const struct krb5_cksumtypes *keyhash; + char adata_len_buf[6]; + unsigned char B0[16], Ctr[16]; + + kc.contents = NULL; + kc.length = 0; + + cksum.data = NULL; + cksum.length = 0; + + ret = aead->crypto_length(aead, enc, hash, KRB5_CRYPTO_TYPE_HEADER, &header_len); + if (ret != 0) + return ret; + + header = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_HEADER); + if (header == NULL || header->data.length != header_len) + return KRB5_BAD_MSIZE; + + ret = aead->crypto_length(aead, enc, hash, KRB5_CRYPTO_TYPE_TRAILER, &trailer_len); + if (ret != 0) + return ret; + + trailer = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_TRAILER); + if (trailer == NULL || trailer->data.length != trailer_len) + return KRB5_BAD_MSIZE; + + for (i = 0; i < num_data; i++) { + krb5_crypto_iov *iov = &data[i]; + + switch (iov->flags) { + case KRB5_CRYPTO_TYPE_DATA: + payload_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) + return KRB5_BAD_MSIZE; + break; + default: + break; + } + } + + sign_data = (krb5_crypto_iov *)calloc(num_data + 1, sizeof(krb5_crypto_iov)); + if (sign_data == NULL) { + ret = ENOMEM; + goto cleanup; + } + + sign_data[num_sign_data].flags = KRB5_CRYPTO_TYPE_HEADER; + sign_data[num_sign_data].data.data = (char *)B0; + sign_data[num_sign_data].data.length = sizeof(B0); + ret = format_B0(&sign_data[num_sign_data].data, &header->data, trailer_len, + (krb5_ui_8)adata_len, (krb5_ui_8)payload_len); + if (ret != 0) + goto cleanup; + num_sign_data++; + + /* Include length of associated data in CBC-MAC */ + sign_data[num_sign_data].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY; + sign_data[num_sign_data].data.data = adata_len_buf; + sign_data[num_sign_data].data.length = sizeof(adata_len_buf); + ret = encode_a_len(&sign_data[num_sign_data].data, (krb5_ui_8)adata_len); + if (ret != 0) + goto cleanup; + num_sign_data++; + + 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] = 0xCC; + + kc.length = enc->keylength; + kc.contents = malloc(kc.length); + if (kc.contents == NULL) { + ret = ENOMEM; + goto cleanup; + } + + ret = krb5_derive_key(enc, key, &kc, &d1); + if (ret != 0) + goto cleanup; + + assert(enc->decrypt_iov != NULL); + + cksum.data = malloc(trailer_len); + if (cksum.data == NULL) { + ret = ENOMEM; + goto cleanup; + } + cksum.length = trailer_len; + + /* Initialize first counter block */ + if (ivec == NULL) { + counter.length = sizeof(Ctr); + counter.data = (char *)Ctr; + + ret = format_Ctr0(&counter, &sign_data[0].data); + if (ret != 0) + goto cleanup; + + ivec = &counter; + } + + /* Decrypt checksum from trailer */ + ret = enc->decrypt(&kc, ivec, &trailer->data, &trailer->data); + if (ret != 0) + goto cleanup; + + /* Don't decrypt B0 (header), but decrypt everything else */ + ret = enc->decrypt_iov(&kc, ivec, data, num_data); + if (ret != 0) + goto cleanup; + + /* Now, calculate hash for comparison (including B0) */ + ret = krb5int_c_mandatory_cksumtype(NULL, key->enctype, &cksumtype); + if (ret != 0) + goto cleanup; + + keyhash = krb5int_c_find_checksum_type(cksumtype); + if (keyhash == NULL) { + ret = KRB5_BAD_ENCTYPE; + 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]; + } + + ret = krb5int_c_make_checksum_iov(keyhash, &kc, usage, sign_data, num_sign_data, &cksum); + if (ret != 0) + goto cleanup; + + if (cksum.length != trailer->data.length || + memcmp(cksum.data, trailer->data.data, trailer->data.length) != 0) { + ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; + goto cleanup; + } + +cleanup: + if (kc.contents != NULL) { + zap(kc.contents, kc.length); + free(kc.contents); + } + if (cksum.data != NULL) { + free(cksum.data); + } + if (sign_data != NULL) { + free(sign_data); + } + + return ret; +} + +const struct krb5_aead_provider krb5int_aead_ccm = { + krb5int_ccm_crypto_length, + krb5int_ccm_encrypt_iov, + krb5int_ccm_decrypt_iov +}; + diff --git a/src/lib/crypto/krb/enc_provider/Makefile.in b/src/lib/crypto/krb/enc_provider/Makefile.in index 2eedf1d..4bcfc82 100644 --- a/src/lib/crypto/krb/enc_provider/Makefile.in +++ b/src/lib/crypto/krb/enc_provider/Makefile.in @@ -13,18 +13,20 @@ DEFS= PROG_LIBPATH=-L$(TOPLIBD) PROG_RPATH=$(KRB5_LIBDIR) -STLIBOBJS= des.o des3.o rc4.o aes.o +STLIBOBJS= des.o des3.o rc4.o aes.o aes_ctr.o OBJS= \ $(OUTPRE)des.$(OBJEXT) \ $(OUTPRE)des3.$(OBJEXT) \ $(OUTPRE)aes.$(OBJEXT) \ + $(OUTPRE)aes_ctr.$(OBJEXT) \ $(OUTPRE)rc4.$(OBJEXT) SRCS= \ $(srcdir)/des.c \ $(srcdir)/des3.c \ $(srcdir)/aes.c \ + $(srcdir)/aes_ctr.c \ $(srcdir)/rc4.c ##DOS##LIBOBJS = $(OBJS) diff --git a/src/lib/crypto/krb/enc_provider/aes_ctr.c b/src/lib/crypto/krb/enc_provider/aes_ctr.c new file mode 100644 index 0000000..a6e3634 --- /dev/null +++ b/src/lib/crypto/krb/enc_provider/aes_ctr.c @@ -0,0 +1,320 @@ +/* + * lib/crypto/enc_provider/aes_ctr.c + * + * Copyright (C) 2003, 2007, 2008 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 "enc_provider.h" +#include "aes.h" +#include "../aead.h" + +#define CCM_FLAG_MASK_Q 0x07 + +#define CCM_DEFAULT_COUNTER_LEN 3 /* default q=3 from RFC 5116 5.3 */ + +static inline void xorblock(unsigned char *out, const unsigned char *in) +{ + int z; + for (z = 0; z < BLOCK_SIZE; z++) + out[z] ^= in[z]; +} + +/* Get the current counter block number from the IV */ +static inline void getctrblockno(krb5_ui_8 *pblockno, + const unsigned char ctr[BLOCK_SIZE]) +{ + register krb5_octet q, i; + krb5_ui_8 blockno; + + q = ctr[0] + 1; + + assert(q >= 2 && q <= 8); + + for (i = 0, blockno = 0; i < q; i++) { + register krb5_octet s = (q - i - 1) * 8; + + blockno |= ctr[16 - q + i] << s; + } + + *pblockno = blockno; +} + +/* Store the current counter block number in the IV */ +static inline void putctrblockno(krb5_ui_8 blockno, + unsigned char ctr[BLOCK_SIZE]) +{ + register krb5_octet q, i; + + q = ctr[0] + 1; + + for (i = 0; i < q; i++) { + register krb5_octet s = (q - i - 1) * 8; + + ctr[16 - q + i] = (blockno >> s) & 0xFF; + } +} + +/* Maximum number of invocations with a given nonce and key */ +#define maxblocks(q) (1UL << (8 * (q))) + +/* + * ivec must be a correctly formatted counter block per SP800-38C A.3 + */ +static krb5_error_code +krb5int_aes_encrypt_ctr_iov(const krb5_keyblock *key, + const krb5_data *ivec, + krb5_crypto_iov *data, + size_t num_data) +{ + aes_ctx ctx; + unsigned char ctr[BLOCK_SIZE]; + krb5_ui_8 blockno; + struct iov_block_state input_pos, output_pos; + + if (aes_enc_key(key->contents, key->length, &ctx) != aes_good) + abort(); + + IOV_BLOCK_STATE_INIT(&input_pos); + IOV_BLOCK_STATE_INIT(&output_pos); + + /* Don't encrypt the header (B0), and use zero instead of IOV padding */ + input_pos.ignore_header = output_pos.ignore_header = 1; + input_pos.pad_to_boundary = output_pos.pad_to_boundary = 1; + + if (ivec != NULL) { + if (ivec->length != BLOCK_SIZE || (ivec->data[0] & ~(CCM_FLAG_MASK_Q))) + return KRB5_BAD_MSIZE; + + memcpy(ctr, ivec->data, BLOCK_SIZE); + } else { + memset(ctr, 0, BLOCK_SIZE); + ctr[0] = CCM_DEFAULT_COUNTER_LEN - 1; + } + + getctrblockno(&blockno, ctr); + + for (;;) { + unsigned char plain[BLOCK_SIZE]; + unsigned char ectr[BLOCK_SIZE]; + + if (blockno >= maxblocks(ctr[0] + 1)) + return KRB5_CRYPTO_INTERNAL; + + if (!krb5int_c_iov_get_block((unsigned char *)plain, BLOCK_SIZE, data, num_data, &input_pos)) + break; + + if (aes_enc_blk(ctr, ectr, &ctx) != aes_good) + abort(); + + xorblock(plain, ectr); + krb5int_c_iov_put_block(data, num_data, (unsigned char *)plain, BLOCK_SIZE, &output_pos); + + putctrblockno(++blockno, ctr); + } + + if (ivec != NULL) + memcpy(ivec->data, ctr, sizeof(ctr)); + + return 0; +} + +static krb5_error_code +krb5int_aes_decrypt_ctr_iov(const krb5_keyblock *key, + const krb5_data *ivec, + krb5_crypto_iov *data, + size_t num_data) +{ + aes_ctx ctx; + unsigned char ctr[BLOCK_SIZE]; + krb5_ui_8 blockno; + struct iov_block_state input_pos, output_pos; + + if (aes_enc_key(key->contents, key->length, &ctx) != aes_good) + abort(); + + IOV_BLOCK_STATE_INIT(&input_pos); + IOV_BLOCK_STATE_INIT(&output_pos); + + /* Don't encrypt the header (B0), and use zero instead of IOV padding */ + input_pos.ignore_header = output_pos.ignore_header = 1; + input_pos.pad_to_boundary = output_pos.pad_to_boundary = 1; + + if (ivec != NULL) { + if (ivec->length != BLOCK_SIZE || (ivec->data[0] & ~(CCM_FLAG_MASK_Q))) + return KRB5_BAD_MSIZE; + + memcpy(ctr, ivec->data, BLOCK_SIZE); + } else { + memset(ctr, 0, BLOCK_SIZE); + ctr[0] = CCM_DEFAULT_COUNTER_LEN - 1; + } + + getctrblockno(&blockno, ctr); + + for (;;) { + unsigned char ectr[BLOCK_SIZE]; + unsigned char cipher[BLOCK_SIZE]; + + if (blockno >= maxblocks(ctr[0] + 1)) + return KRB5_CRYPTO_INTERNAL; + + if (!krb5int_c_iov_get_block((unsigned char *)cipher, BLOCK_SIZE, data, num_data, &input_pos)) + break; + + if (aes_enc_blk(ctr, ectr, &ctx) != aes_good) + abort(); + + xorblock(cipher, ectr); + krb5int_c_iov_put_block(data, num_data, (unsigned char *)cipher, BLOCK_SIZE, &output_pos); + + putctrblockno(++blockno, ctr); + } + + if (ivec != NULL) + memcpy(ivec->data, ctr, sizeof(ctr)); + + return 0; +} + +static krb5_error_code +krb5int_aes_encrypt_ctr(const krb5_keyblock *key, const krb5_data *ivec, + const krb5_data *input, krb5_data *output) +{ + krb5_crypto_iov iov[1]; + krb5_error_code ret; + + assert(output->data != NULL); + + memcpy(output->data, input->data, input->length); + output->length = input->length; + + iov[0].flags = KRB5_CRYPTO_TYPE_DATA; + iov[0].data = *output; + + ret = krb5int_aes_encrypt_ctr_iov(key, ivec, iov, sizeof(iov)/sizeof(iov[0])); + if (ret != 0) { + zap(output->data, output->length); + } + + return ret; +} + +static krb5_error_code +krb5int_aes_decrypt_ctr(const krb5_keyblock *key, const krb5_data *ivec, + const krb5_data *input, krb5_data *output) +{ + krb5_crypto_iov iov[1]; + krb5_error_code ret; + + assert(output->data != NULL); + + memcpy(output->data, input->data, input->length); + output->length = input->length; + + iov[0].flags = KRB5_CRYPTO_TYPE_DATA; + iov[0].data = *output; + + ret = krb5int_aes_decrypt_ctr_iov(key, ivec, iov, sizeof(iov)/sizeof(iov[0])); + if (ret != 0) { + zap(output->data, output->length); + } + + return ret; +} + +static krb5_error_code +k5_aes_make_key_ctr(const krb5_data *randombits, krb5_keyblock *key) +{ + if (key->length != 16 && key->length != 32) + return(KRB5_BAD_KEYSIZE); + if (randombits->length != key->length) + return(KRB5_CRYPTO_INTERNAL); + + key->magic = KV5M_KEYBLOCK; + + memcpy(key->contents, randombits->data, randombits->length); + return(0); +} + +static krb5_error_code +krb5int_aes_init_state_ctr (const krb5_keyblock *key, krb5_keyusage usage, + krb5_data *state) +{ + krb5_data nonce; + unsigned int n, q; + krb5_error_code code; + + code = krb5_c_crypto_length(NULL, key->enctype, KRB5_CRYPTO_TYPE_HEADER, &n); + if (code != 0) + return code; + + assert(n >= 7 && n <= 13); + + state->length = 16; + state->data = malloc(state->length); + if (state->data == NULL) + return ENOMEM; + + q = 15 - n; + state->data[0] = q - 1; + + nonce.data = &state->data[1]; + nonce.length = n; + + code = krb5_c_random_make_octets(NULL, &nonce); + if (code != 0) { + free(state->data); + state->data = NULL; + return code; + } + + memset(&state->data[1 + n], 0, q); + + return 0; +} + +const struct krb5_enc_provider krb5int_enc_aes128_ctr = { + 16, + 16, 16, + krb5int_aes_encrypt_ctr, + krb5int_aes_decrypt_ctr, + k5_aes_make_key_ctr, + krb5int_aes_init_state_ctr, + krb5int_default_free_state, + krb5int_aes_encrypt_ctr_iov, + krb5int_aes_decrypt_ctr_iov +}; + +const struct krb5_enc_provider krb5int_enc_aes256_ctr = { + 16, + 32, 32, + krb5int_aes_encrypt_ctr, + krb5int_aes_decrypt_ctr, + k5_aes_make_key_ctr, + krb5int_aes_init_state_ctr, + krb5int_default_free_state, + krb5int_aes_encrypt_ctr_iov, + krb5int_aes_decrypt_ctr_iov +}; + diff --git a/src/lib/crypto/krb/etypes.c b/src/lib/crypto/krb/etypes.c index b3abd18..6cc514e 100644 --- a/src/lib/crypto/krb/etypes.c +++ b/src/lib/crypto/krb/etypes.c @@ -164,6 +164,28 @@ const struct krb5_keytypes krb5_enctypes_list[] = { CKSUMTYPE_HMAC_SHA1_96_AES256, &krb5int_aead_aes, 0 /*flags*/ }, + { ENCTYPE_AES128_CCM_128, + "aes128-ccm-128", { "aes128-ccm" }, + "AES-128 CTR mode with 128-bit CBC MAC", + &krb5int_enc_aes128_ctr, NULL, + 16, + NULL, NULL, NULL, + krb5int_aes_string_to_key, + krb5int_dk_prf, + CKSUMTYPE_AES128_CBC, + &krb5int_aead_ccm + }, + { ENCTYPE_AES256_CCM_128, + "aes256-ccm-128", { "aes256-ccm" }, + "AES-256 CTR mode with 128-bit CBC MAC", + &krb5int_enc_aes256_ctr, NULL, + 16, + NULL, NULL, NULL, + krb5int_aes_string_to_key, + krb5int_dk_prf, + CKSUMTYPE_AES128_CBC, + &krb5int_aead_ccm + }, }; const int krb5_enctypes_length = diff --git a/src/lib/crypto/krb/keyhash_provider/Makefile.in b/src/lib/crypto/krb/keyhash_provider/Makefile.in index d30f6c4..905bb2b 100644 --- a/src/lib/crypto/krb/keyhash_provider/Makefile.in +++ b/src/lib/crypto/krb/keyhash_provider/Makefile.in @@ -14,11 +14,11 @@ DEFS= PROG_LIBPATH=-L$(TOPLIBD) PROG_RPATH=$(KRB5_LIBDIR) -STLIBOBJS= descbc.o k5_md4des.o k5_md5des.o hmac_md5.o md5_hmac.o +STLIBOBJS= aescbc.o descbc.o k5_md4des.o k5_md5des.o hmac_md5.o md5_hmac.o -OBJS= $(OUTPRE)descbc.$(OBJEXT) $(OUTPRE)k5_md4des.$(OBJEXT) $(OUTPRE)k5_md5des.$(OBJEXT) $(OUTPRE)hmac_md5.$(OBJEXT) $(OUTPRE)md5_hmac.$(OBJEXT) +OBJS= $(OUTPRE)aescbc.$(OBJEXT) $(OUTPRE)descbc.$(OBJEXT) $(OUTPRE)k5_md4des.$(OBJEXT) $(OUTPRE)k5_md5des.$(OBJEXT) $(OUTPRE)hmac_md5.$(OBJEXT) $(OUTPRE)md5_hmac.$(OBJEXT) -SRCS= $(srcdir)/descbc.c $(srcdir)/k5_md4des.c $(srcdir)/k5_md5des.c $(srcdir)/hmac_md5.c $(srcdir)/md5_hmac.c +SRCS= $(srcdir)/aescbc.c $(srcdir)/descbc.c $(srcdir)/k5_md4des.c $(srcdir)/k5_md5des.c $(srcdir)/hmac_md5.c $(srcdir)/md5_hmac.c ##DOS##LIBOBJS = $(OBJS) diff --git a/src/lib/crypto/krb/keyhash_provider/aescbc.c b/src/lib/crypto/krb/keyhash_provider/aescbc.c new file mode 100644 index 0000000..70c5060 --- /dev/null +++ b/src/lib/crypto/krb/keyhash_provider/aescbc.c @@ -0,0 +1,118 @@ +/* + * lib/crypto/keyhash_provider/aescbc.c + * + * Copyright 2008 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 "keyhash_provider.h" +#include "hash_provider.h" +#include "../aes/aes.h" +#include "../aead.h" + +static void xorblock(unsigned char *out, unsigned const char *in) +{ + int z; + for (z = 0; z < BLOCK_SIZE; z++) + out[z] ^= in[z]; +} + +static krb5_error_code +k5_aescbc_hash_iov (const krb5_keyblock *key, krb5_keyusage usage, + const krb5_data *iv, + const krb5_crypto_iov *data, size_t num_data, + krb5_data *output) +{ + aes_ctx ctx; + unsigned char blockY[BLOCK_SIZE]; + struct iov_block_state iov_state; + + if (output->length < BLOCK_SIZE) + return KRB5_BAD_MSIZE; + + if (aes_enc_key(key->contents, key->length, &ctx) != aes_good) + abort(); + + if (iv != NULL) + memcpy(blockY, iv->data, BLOCK_SIZE); + else + memset(blockY, 0, BLOCK_SIZE); + + IOV_BLOCK_STATE_INIT(&iov_state); + + /* + * The CCM header may not fit in a block, because it includes a variable + * length encoding of the associated data length. This encoding plus the + * associated data itself is padded to the block size. + */ + iov_state.include_sign_only = 1; + iov_state.pad_to_boundary = 1; + + for (;;) { + unsigned char blockB[BLOCK_SIZE]; + + if (!krb5int_c_iov_get_block(blockB, BLOCK_SIZE, data, num_data, &iov_state)) + break; + + xorblock(blockB, blockY); + + if (aes_enc_blk(blockB, blockY, &ctx) != aes_good) + abort(); + } + + output->length = BLOCK_SIZE; + memcpy(output->data, blockY, BLOCK_SIZE); + + return 0; +} + +static krb5_error_code +k5_aescbc_hash (const krb5_keyblock *key, krb5_keyusage usage, + const krb5_data *iv, + const krb5_data *input, krb5_data *output) +{ + krb5_crypto_iov iov[1]; + + iov[0].flags = KRB5_CRYPTO_TYPE_DATA; + iov[0].data = *input; + + return k5_aescbc_hash_iov(key, usage, iv, iov, 1, output); +} + +const struct krb5_keyhash_provider krb5int_keyhash_aescbc_128 = { + 16, + k5_aescbc_hash, + NULL, /*checksum again*/ + k5_aescbc_hash_iov, + NULL /*checksum again */ +}; + +const struct krb5_keyhash_provider krb5int_keyhash_aescbc_256 = { + 16, + k5_aescbc_hash, + NULL, /*checksum again*/ + k5_aescbc_hash_iov, + NULL /*checksum again */ +}; + -- cgit v1.1