aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/lib/crypto/builtin/enc_provider/Makefile.in5
-rw-r--r--src/lib/crypto/builtin/enc_provider/aes_ctr.c320
-rw-r--r--src/lib/crypto/krb/dk/Makefile.in3
-rw-r--r--src/lib/crypto/krb/dk/dk.h23
-rw-r--r--src/lib/crypto/krb/dk/dk_ccm.c628
-rw-r--r--src/lib/crypto/krb/enc_provider/Makefile.in4
-rw-r--r--src/lib/crypto/krb/enc_provider/aes_ctr.c320
-rw-r--r--src/lib/crypto/krb/etypes.c22
-rw-r--r--src/lib/crypto/krb/keyhash_provider/Makefile.in6
-rw-r--r--src/lib/crypto/krb/keyhash_provider/aescbc.c118
-rw-r--r--src/lib/crypto/openssl/enc_provider/aes_ctr.c320
11 files changed, 1764 insertions, 5 deletions
diff --git a/src/lib/crypto/builtin/enc_provider/Makefile.in b/src/lib/crypto/builtin/enc_provider/Makefile.in
index 712a225..f541da0 100644
--- a/src/lib/crypto/builtin/enc_provider/Makefile.in
+++ b/src/lib/crypto/builtin/enc_provider/Makefile.in
@@ -21,18 +21,21 @@ STLIBOBJS= \
../../@CRYPTO_IMPL@/enc_provider/des.o \
../../@CRYPTO_IMPL@/enc_provider/des3.o \
../../@CRYPTO_IMPL@/enc_provider/rc4.o \
- ../../@CRYPTO_IMPL@/enc_provider/aes.o
+ ../../@CRYPTO_IMPL@/enc_provider/aes.o \
+ ../../@CRYPTO_IMPL@/enc_provider/aes_ctr.o
OBJS= \
$(OUTPRE)../../@CRYPTO_IMPL@/enc_provider/des.$(OBJEXT) \
$(OUTPRE)../../@CRYPTO_IMPL@/enc_provider/des3.$(OBJEXT) \
$(OUTPRE)../../@CRYPTO_IMPL@/enc_provider/aes.$(OBJEXT) \
+ $(OUTPRE)../../@CRYPTO_IMPL@/enc_provider/aes_ctr.$(OBJEXT) \
$(OUTPRE)../../@CRYPTO_IMPL@/enc_provider/rc4.$(OBJEXT)
SRCS= \
$(srcdir)/../../@CRYPTO_IMPL@/enc_provider/des.c \
$(srcdir)/../../@CRYPTO_IMPL@/enc_provider/des3.c \
$(srcdir)/../../@CRYPTO_IMPL@/enc_provider/aes.c \
+ $(srcdir)/../../@CRYPTO_IMPL@/enc_provider/aes_ctr.c \
$(srcdir)/../../@CRYPTO_IMPL@/enc_provider/rc4.c
##DOS##LIBOBJS = $(OBJS)
diff --git a/src/lib/crypto/builtin/enc_provider/aes_ctr.c b/src/lib/crypto/builtin/enc_provider/aes_ctr.c
new file mode 100644
index 0000000..4645bbd
--- /dev/null
+++ b/src/lib/crypto/builtin/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/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 */
+};
+
diff --git a/src/lib/crypto/openssl/enc_provider/aes_ctr.c b/src/lib/crypto/openssl/enc_provider/aes_ctr.c
new file mode 100644
index 0000000..a6e3634
--- /dev/null
+++ b/src/lib/crypto/openssl/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
+};
+