svn rev #21616: branches/aes-ccm/src/lib/crypto/ dk/ enc_provider/
lhoward@MIT.EDU
lhoward at MIT.EDU
Sun Dec 28 07:40:14 EST 2008
http://src.mit.edu/fisheye/changelog/krb5/?cs=21616
Commit By: lhoward
Log Message:
At the possible expense of some performance, completely parameterized
CCM implementation so different values of n and q can be chosen by
changing the crypto_length() implementation.
Also, HEADER now only contains the nonce rather than the B0 (which can
be reasembled on decryption from Flags | HEADER | Payload length)
Changed Files:
U branches/aes-ccm/src/lib/crypto/dk/dk_ccm.c
U branches/aes-ccm/src/lib/crypto/enc_provider/aes_ctr.c
Modified: branches/aes-ccm/src/lib/crypto/dk/dk_ccm.c
===================================================================
--- branches/aes-ccm/src/lib/crypto/dk/dk_ccm.c 2008-12-28 01:06:10 UTC (rev 21615)
+++ branches/aes-ccm/src/lib/crypto/dk/dk_ccm.c 2008-12-28 12:40:13 UTC (rev 21616)
@@ -71,9 +71,6 @@
#define CCM_FLAG_ADATA 0x40
#define CCM_FLAG_RESERVED 0x80
-#define CCM_NONCE_LENGTH 12
-#define CCM_COUNTER_LENGTH 3
-
static krb5_error_code
krb5int_ccm_crypto_length(const struct krb5_aead_provider *aead,
const struct krb5_enc_provider *enc,
@@ -81,11 +78,9 @@
krb5_cryptotype type,
unsigned int *length)
{
- assert(enc->block_size >= 16);
-
switch (type) {
case KRB5_CRYPTO_TYPE_HEADER:
- *length = 16;
+ *length = 12; /* RFC 5116 5.3 */
break;
case KRB5_CRYPTO_TYPE_PADDING:
*length = 0; /* CTR mode requires no padding */
@@ -154,38 +149,118 @@
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 *iv,
+ const krb5_data *ivec,
krb5_crypto_iov *data,
size_t num_data)
{
krb5_error_code ret;
- unsigned char constantdata[K5CLENGTH], *headerdata;
+ 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;
- unsigned int payload_len = 0;
- krb5_ui_8 adata_len = 0;
- unsigned char flags = 0;
- krb5_data nonce, cksum, ivec;
+ 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;
- ivec.data = NULL;
- ivec.length = 0;
-
cksum.data = NULL;
cksum.length = 0;
@@ -194,7 +269,7 @@
return ret;
header = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_HEADER);
- if (header == NULL)
+ 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);
@@ -223,56 +298,32 @@
}
}
- if (header->data.length < enc->block_size)
- return KRB5_BAD_MSIZE;
-
- /* RFC 5116 5.3, format flags octet */
- flags = CCM_COUNTER_LENGTH - 1; /* q=3 */
- flags |= (((trailer_len - 2) / 2) << 3);
- if (adata_len != 0)
- flags |= CCM_FLAG_ADATA;
+ header->data.length = header_len;
- headerdata = (unsigned char *)header->data.data;
- headerdata[0] = flags;
-
- nonce.data = (char *)&headerdata[1];
- nonce.length = CCM_NONCE_LENGTH;
-
- if (iv != NULL) {
- /* iv should be NONCE | COUNTER */
- if (iv->length != nonce.length) {
- ret = KRB5_BAD_MSIZE;
- goto cleanup;
- }
- memcpy(nonce.data, iv->data, iv->length);
- } else {
- ret = krb5_c_random_make_octets(/* XXX */ NULL, &nonce);
- if (ret != 0)
- goto cleanup;
- }
-
- if (payload_len > 0xFFFFFF) {
- ret = KRB5_BAD_MSIZE;
+ ret = krb5_c_random_make_octets(/* XXX */ NULL, &header->data);
+ if (ret != 0)
goto cleanup;
- }
- headerdata[13] = (payload_len >> 16) & 0xFF;
- headerdata[14] = (payload_len >> 8 ) & 0xFF;
- headerdata[15] = (payload_len ) & 0xFF;
-
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++] = *header;
+ 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, adata_len);
+ ret = encode_a_len(&sign_data[num_sign_data].data, (krb5_ui_8)adata_len);
if (ret != 0)
goto cleanup;
num_sign_data++;
@@ -331,27 +382,27 @@
if (ret != 0)
goto cleanup;
- /* Setup counter */
- ivec.data = malloc(enc->block_size);
- if (ivec.data == NULL) {
- ret = ENOMEM;
- 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;
}
- ivec.length = enc->block_size;
- ivec.data[0] = flags;
- memcpy(&ivec.data[1], nonce.data, nonce.length); /* Copy in nonce */
- memset(&ivec.data[1 + nonce.length], 0, CCM_COUNTER_LENGTH); /* Set counter to zero */
-
trailer->data.length = trailer_len;
/* Encrypt checksum and place in trailer */
- ret = enc->encrypt(&kc, &ivec, &cksum, &trailer->data);
+ 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);
+ ret = enc->encrypt_iov(&kc, ivec, data, num_data);
if (ret != 0)
goto cleanup;
@@ -363,10 +414,6 @@
if (cksum.data != NULL) {
free(cksum.data);
}
- if (ivec.data != NULL) {
- zap(ivec.data, ivec.length);
- free(ivec.data);
- }
if (sign_data != NULL) {
free(sign_data);
}
@@ -380,33 +427,29 @@
const struct krb5_hash_provider *hash,
const krb5_keyblock *key,
krb5_keyusage usage,
- const krb5_data *iv,
+ const krb5_data *ivec,
krb5_crypto_iov *data,
size_t num_data)
{
krb5_error_code ret;
- unsigned char constantdata[K5CLENGTH], *headerdata;
+ 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;
- krb5_ui_8 actual_adata_len = 0;
- unsigned int actual_payload_len = 0;
- unsigned int payload_len = 0;
- unsigned char flags = 0;
- krb5_data cksum, ivec;
+ 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;
- ivec.data = NULL;
- ivec.length = 0;
-
cksum.data = NULL;
cksum.length = 0;
@@ -415,7 +458,7 @@
return ret;
header = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_HEADER);
- if (header == NULL || header != &data[0])
+ 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);
@@ -431,10 +474,10 @@
switch (iov->flags) {
case KRB5_CRYPTO_TYPE_DATA:
- actual_payload_len += iov->data.length;
+ payload_len += iov->data.length;
break;
case KRB5_CRYPTO_TYPE_SIGN_ONLY:
- actual_adata_len += iov->data.length;
+ adata_len += iov->data.length;
break;
case KRB5_CRYPTO_TYPE_PADDING:
if (iov->data.length != 0)
@@ -445,51 +488,26 @@
}
}
- if (actual_payload_len > 0xFFFFFF)
- return KRB5_BAD_MSIZE;
-
- if (header->data.length < enc->block_size)
- return KRB5_BAD_MSIZE;
-
- headerdata = (unsigned char *)header->data.data;
-
- flags = headerdata[0];
- if ((flags & CCM_FLAG_RESERVED) != 0) {
- return KRB5_BAD_MSIZE;
- }
- if ((flags & CCM_FLAG_MASK_Q) != CCM_COUNTER_LENGTH - 1) {
- /* Check q=3 */
- return KRB5_BAD_MSIZE;
- }
- if ((unsigned int)(flags & CCM_FLAG_MASK_T) >> 3 != (trailer->data.length - 2) / 2) {
- /* Check bits 3-5 contain (trailer_len-2)/2 */
- return KRB5_BAD_MSIZE;
- }
- if ((flags & CCM_FLAG_ADATA) != (actual_adata_len ? CCM_FLAG_ADATA : 0)) {
- /* Check that AData flag matches presence of associated data */
- return KRB5_BAD_MSIZE;
- }
-
- payload_len = (headerdata[13] << 16);
- payload_len |= (headerdata[14] << 8 );
- payload_len |= (headerdata[15] );
-
- if (payload_len > actual_payload_len)
- return KRB5_BAD_MSIZE;
-
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++] = *header;
+ 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, actual_adata_len);
+ ret = encode_a_len(&sign_data[num_sign_data].data, (krb5_ui_8)adata_len);
if (ret != 0)
goto cleanup;
num_sign_data++;
@@ -524,32 +542,25 @@
}
cksum.length = trailer_len;
- /* Setup counter */
- ivec.data = malloc(enc->block_size);
- if (ivec.data == NULL) {
- ret = ENOMEM;
- goto cleanup;
- }
- ivec.length = enc->block_size;
+ /* Initialize first counter block */
+ if (ivec == NULL) {
+ counter.length = sizeof(Ctr);
+ counter.data = (char *)Ctr;
- ivec.data[0] = flags;
- if (iv != NULL) {
- if (iv->length != CCM_NONCE_LENGTH) {
- ret = KRB5_BAD_MSIZE;
+ ret = format_Ctr0(&counter, &sign_data[0].data);
+ if (ret != 0)
goto cleanup;
- }
- memcpy(&ivec.data[1], iv->data, iv->length);
- } else
- memcpy(&ivec.data[1], &headerdata[1], CCM_NONCE_LENGTH); /* Copy in nonce */
- memset(&ivec.data[1 + CCM_NONCE_LENGTH], 0, CCM_COUNTER_LENGTH); /* Set counter to zero */
+ ivec = &counter;
+ }
+
/* Decrypt checksum from trailer */
- ret = enc->decrypt(&kc, &ivec, &trailer->data, &trailer->data);
+ 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);
+ ret = enc->decrypt_iov(&kc, ivec, data, num_data);
if (ret != 0)
goto cleanup;
@@ -592,10 +603,6 @@
if (cksum.data != NULL) {
free(cksum.data);
}
- if (ivec.data != NULL) {
- zap(ivec.data, ivec.length);
- free(ivec.data);
- }
if (sign_data != NULL) {
free(sign_data);
}
Modified: branches/aes-ccm/src/lib/crypto/enc_provider/aes_ctr.c
===================================================================
--- branches/aes-ccm/src/lib/crypto/enc_provider/aes_ctr.c 2008-12-28 01:06:10 UTC (rev 21615)
+++ branches/aes-ccm/src/lib/crypto/enc_provider/aes_ctr.c 2008-12-28 12:40:13 UTC (rev 21616)
@@ -29,6 +29,8 @@
#include "aes.h"
#include "../aead.h"
+#define CCM_FLAG_MASK_Q 0x07
+
#define CCM_COUNTER_LENGTH 3
static void xorblock(unsigned char *out, const unsigned char *in)
@@ -38,6 +40,9 @@
out[z] ^= in[z];
}
+/*
+ * 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,
@@ -46,7 +51,8 @@
{
aes_ctx ctx;
unsigned char ctr[BLOCK_SIZE];
- size_t blockno;
+ register krb5_octet q, i;
+ krb5_ui_8 blockno;
struct iov_block_state input_pos, output_pos;
if (aes_enc_key(key->contents, key->length, &ctx) != aes_good)
@@ -59,17 +65,25 @@
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 != NULL) {
+ if (ivec->length != BLOCK_SIZE || (ivec->data[0] & ~(CCM_FLAG_MASK_Q)))
+ return KRB5_BAD_MSIZE;
+
memcpy(ctr, ivec->data, BLOCK_SIZE);
- else
+ } else {
memset(ctr, 0, BLOCK_SIZE);
+ ctr[0] = CCM_COUNTER_LENGTH - 1; /* default q=3 from RFC 5116 5.3 */
+ }
+ q = ctr[0] + 1;
- ctr[0] = CCM_COUNTER_LENGTH - 1; /* q=3 */
+ assert(q >= 2 && q <= 8);
- blockno = (ctr[13] << 16);
- blockno |= (ctr[14] << 8 );
- blockno |= (ctr[15] );
+ for (i = 0, blockno = 0; i < q; i++) {
+ register int s = (q - i - 1) * 8;
+ blockno |= ctr[16 - q + i] << s;
+ }
+
for (;;) {
unsigned char plain[BLOCK_SIZE];
unsigned char ectr[BLOCK_SIZE];
@@ -85,9 +99,11 @@
blockno++;
- ctr[13] = (blockno >> 16) & 0xFF;
- ctr[14] = (blockno >> 8 ) & 0xFF;
- ctr[15] = (blockno ) & 0xFF;
+ for (i = 0; i < q; i++) {
+ register int s = (q - i - 1) * 8;
+
+ ctr[16 - q + i] = (blockno >> s) & 0xFF;
+ }
}
if (ivec != NULL)
@@ -104,7 +120,8 @@
{
aes_ctx ctx;
unsigned char ctr[BLOCK_SIZE];
- size_t blockno = 0;
+ register krb5_octet q, i;
+ krb5_ui_8 blockno;
struct iov_block_state input_pos, output_pos;
if (aes_enc_key(key->contents, key->length, &ctx) != aes_good)
@@ -117,17 +134,25 @@
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 != NULL) {
+ if (ivec->length != BLOCK_SIZE || (ivec->data[0] & ~(CCM_FLAG_MASK_Q)))
+ return KRB5_BAD_MSIZE;
+
memcpy(ctr, ivec->data, BLOCK_SIZE);
- else
+ } else {
memset(ctr, 0, BLOCK_SIZE);
+ ctr[0] = CCM_COUNTER_LENGTH - 1; /* default q=3 from RFC 5116 5.3 */
+ }
+ q = ctr[0] + 1;
- ctr[0] = CCM_COUNTER_LENGTH - 1; /* q=3 */
+ assert(q >= 2 && q <= 8);
- blockno = (ctr[13] << 16);
- blockno |= (ctr[14] << 8 );
- blockno |= (ctr[15] );
+ for (i = 0, blockno = 0; i < q; i++) {
+ register krb5_octet s = (q - i - 1) * 8;
+ blockno |= ctr[16 - q + i] << s;
+ }
+
for (;;) {
unsigned char ectr[BLOCK_SIZE];
unsigned char cipher[BLOCK_SIZE];
@@ -143,9 +168,11 @@
blockno++;
- ctr[13] = (blockno >> 16) & 0xFF;
- ctr[14] = (blockno >> 8 ) & 0xFF;
- ctr[15] = (blockno ) & 0xFF;
+ for (i = 0; i < q; i++) {
+ register krb5_octet s = (q - i - 1) * 8;
+
+ ctr[16 - q + i] = (blockno >> s) & 0xFF;
+ }
}
if (ivec != NULL)
More information about the cvs-krb5
mailing list