svn rev #24225: branches/camellia-ccm/src/lib/crypto/krb/dk/

ghudson@MIT.EDU ghudson at MIT.EDU
Tue Aug 3 05:01:46 EDT 2010


http://src.mit.edu/fisheye/changelog/krb5/?cs=24225
Commit By: ghudson
Log Message:
Massage the CCM encrypt/decrypt code.  Mostly minor comment and style
changes.  Remove the unused usage parameter from ccm_encrypt and
ccm_decrypt.



Changed Files:
U   branches/camellia-ccm/src/lib/crypto/krb/dk/dk_ccm.c
Modified: branches/camellia-ccm/src/lib/crypto/krb/dk/dk_ccm.c
===================================================================
--- branches/camellia-ccm/src/lib/crypto/krb/dk/dk_ccm.c	2010-08-02 19:44:38 UTC (rev 24224)
+++ branches/camellia-ccm/src/lib/crypto/krb/dk/dk_ccm.c	2010-08-03 09:01:46 UTC (rev 24225)
@@ -29,20 +29,20 @@
 #include "aead.h"
 
 /*
- * Implement AEAD_AES_{128,256}_CCM as described in section 5.3 of RFC 5116.
+ * Implement CCM-mode AEAD as described in section 5.3 and 5.4 of RFC 5116.
+ * This is the CCM mode as described in NIST SP800-38C, with a 12 byte nonce
+ * and 16 byte checksum.  Multiple buffers of the same type are logically
+ * concatenated.  The underlying enc provider must have a 16-byte block size,
+ * must have a counter-mode encrypt method, and must have a cbc_mac method.
  *
- * 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.
+ * 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
  *
@@ -57,19 +57,10 @@
  *
  * 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
-
 unsigned int
 krb5int_dk_ccm_crypto_length(const struct krb5_keytypes *ktp,
                              krb5_cryptotype type)
@@ -96,6 +87,11 @@
     return length;
 }
 
+/*
+ * Encode the length of the additional data according to NIST SP800-38C section
+ * A.2.2.  The size of the encoding will be 0, 2, 6, or 10 bytes depending on
+ * the length value.
+ */
 static krb5_error_code
 encode_a_len(krb5_data *a, krb5_ui_8 adata_len)
 {
@@ -118,10 +114,12 @@
 
     switch (len) {
     case 2:
+	/* Two raw bytes; first byte will not be 0xFF. */
         p[0] = (adata_len >> 8) & 0xFF;
         p[1] = (adata_len     ) & 0xFF;
         break;
     case 6:
+	/* FF FE followed by four bytes. */
         p[0] = 0xFF;
         p[1] = 0xFE;
         p[2] = (adata_len >> 24) & 0xFF;
@@ -130,6 +128,7 @@
         p[5] = (adata_len      ) & 0xFF;
         break;
     case 10:
+	/* FF FF followed by eight bytes. */
         p[0] = 0xFF;
         p[1] = 0xFF;
         p[2] = (adata_len >> 56) & 0xFF;
@@ -149,8 +148,8 @@
 }
 
 /*
- * format_B0() allows the tradeoff between nonce and payload length to
- * be parameterized by replacing the crypto_length() callback
+ * Encode the first 16-byte block of CBC-MAC input according to NIST SP800-38C
+ * section A.2.1.  n (the nonce length) is given by nonce->length.
  */
 static krb5_error_code
 format_B0(krb5_data *B0,            /* B0 */
@@ -166,35 +165,37 @@
     if (B0->length != 16)
         return KRB5_BAD_MSIZE;
 
-    /* SP800-38C A.1: Length Requirements */
+    /* Section A.1: Length Requirements */
 
-    /* t is an elements of {4, 6, 8, 10, 12, 14, 16} */
+    /* t is an element 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} */
+    /* 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 */
+    /* 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 */
+    /* Encode the flags octet. */
     flags = q - 1;
     flags |= (((trailer_len - 2) / 2) << 3);
     if (adata_len != 0)
-        flags |= CCM_FLAG_ADATA;
+        flags |= (1 << 6);
 
     p = (unsigned char *)B0->data;
     p[i++] = flags;
 
+    /* Next comes the nonce (n bytes). */
     memcpy(&p[i], nonce->data, nonce->length);
     i += nonce->length;
 
+    /* The final q bytes are the payload length. */
     for (; i < B0->length; i++) {
         register krb5_octet s;
 
@@ -207,23 +208,25 @@
 }
 
 /*
- * Format initial counter block. Counter may be chained
- * across invocations.
+ * Encode the initial counter block according to NIST SP800-38C section A.3.
+ * The counter value may be chained across krb5_k_encrypt invocations via the
+ * cipher_state parameter; otherwise it begins at 0.
  */
 static krb5_error_code
-format_Ctr0(krb5_data *counter,
-            const krb5_data *nonce,
-            const krb5_data *state,
+format_Ctr0(krb5_data *counter, const krb5_data *nonce, const krb5_data *state,
             unsigned int n)
 {
     krb5_octet q; /* counter length */
 
     assert(n >= 7 && n <= 13);
 
+    /* First byte is q-1 in the lowest three bits. */
     q = 15 - n;
     counter->data[0] = q - 1;
+    /* Next comes the nonce (n bytes). */
     memcpy(&counter->data[1], nonce->data, n);
 
+    /* Finally, the counter value. */
     if (state != NULL)
         memcpy(&counter->data[1 + n], &state->data[1 + n], q);
     else
@@ -232,9 +235,9 @@
     return 0;
 }
 
+/* Return true if the payload length is valid given the nonce length n. */
 static krb5_boolean
-valid_payload_length_p(const struct krb5_keytypes *ktp,
-                       unsigned int n,
+valid_payload_length_p(const struct krb5_keytypes *ktp, unsigned int n,
                        unsigned int payload_len)
 {
     unsigned int block_size = ktp->enc->block_size;
@@ -252,16 +255,13 @@
     return (nblocks <= maxblocks);
 }
 
+/* Encrypt and authenticate data according to NIST SP800-38C section 6.1. */
 static krb5_error_code
-ccm_encrypt(const struct krb5_keytypes *ktp,
-            krb5_key kc,
-            krb5_keyusage usage,
-            const krb5_data *state,
-            krb5_crypto_iov *data,
-            size_t num_data)
+ccm_encrypt(const struct krb5_keytypes *ktp, krb5_key kc,
+	    const krb5_data *state, krb5_crypto_iov *data, size_t num_data)
 {
     krb5_error_code ret;
-    krb5_crypto_iov *header, *trailer, *sign_data = NULL;
+    krb5_crypto_iov *header, *trailer, *sign_data = NULL, cksum;
     size_t i, num_sign_data = 0;
     unsigned int header_len;
     unsigned int trailer_len;
@@ -313,37 +313,38 @@
     header->data.length = header_len;
     trailer->data.length = trailer_len;
 
-    /* Initialize nonce */
+    /* Choose a random nonce. */
     ret = krb5_c_random_make_octets(NULL, &header->data);
     if (ret != 0)
         goto cleanup;
 
-    /* Initialize counter block */
+    /* Encode the first counter block. */
     ret = format_Ctr0(&counter, &header->data, state, header_len);
     if (ret != 0)
         goto cleanup;
 
+    /* Create a list of CBC-MAC input blocks. */
     sign_data = k5alloc((num_data + 1) * sizeof(krb5_crypto_iov), &ret);
     if (sign_data == NULL)
         goto cleanup;
 
-    sign_data[num_sign_data].flags = KRB5_CRYPTO_TYPE_HEADER;
-    sign_data[num_sign_data].data = make_data(B0, sizeof(B0));
-    ret = format_B0(&sign_data[num_sign_data].data, &header->data, trailer_len,
+    /* Format the initial control/nonce block. */
+    sign_data[0].flags = KRB5_CRYPTO_TYPE_HEADER;
+    sign_data[0].data = make_data(B0, sizeof(B0));
+    ret = format_B0(&sign_data[0].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 = make_data(adata_len_buf, sizeof(adata_len_buf));
-    ret = encode_a_len(&sign_data[num_sign_data].data, (krb5_ui_8)adata_len);
+    /* Format the length of associated data. */
+    sign_data[1].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY;
+    sign_data[1].data = make_data(adata_len_buf, sizeof(adata_len_buf));
+    ret = encode_a_len(&sign_data[1].data, (krb5_ui_8)adata_len);
     if (ret != 0)
         goto cleanup;
-    num_sign_data++;
+    num_sign_data = 2;
 
-    /* Reorder input IOV so SIGN_ONLY data is before 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];
@@ -356,44 +357,39 @@
     assert(ktp->enc->encrypt != NULL);
     assert(ktp->enc->cbc_mac != NULL);
 
-    /* Make checksum and place in trailer */
-    ret = ktp->enc->cbc_mac(kc, sign_data, num_sign_data, NULL, &trailer->data);
+    /* Make checksum and place in trailer. */
+    ret = ktp->enc->cbc_mac(kc, sign_data, num_sign_data, NULL,
+			    &trailer->data);
     if (ret != 0)
         goto cleanup;
 
-    /* Encrypt checksum in trailer */
-    {
-        krb5_crypto_iov cksum[1];
+    /* Encrypt checksum in trailer using the first counter block. */
+    cksum.flags = KRB5_CRYPTO_TYPE_DATA;
+    cksum.data = trailer->data;
+    ret = ktp->enc->encrypt(kc, &counter, &cksum, 1);
+    if (ret != 0)
+	goto cleanup;
 
-        cksum[0].flags = KRB5_CRYPTO_TYPE_DATA;
-        cksum[0].data = trailer->data;
-
-        ret = ktp->enc->encrypt(kc, &counter, cksum, 1);
-        if (ret != 0)
-            goto cleanup;
-    }
-
-    /* Don't encrypt B0 (header), but encrypt everything else */
+    /* Encrypt everything but B0 (header) in subsequent counter blocks. */
     ret = ktp->enc->encrypt(kc, &counter, data, num_data);
     if (ret != 0)
         goto cleanup;
 
+    /* Store the counter block as cipher state.  Subsequent encryptions will
+     * reuse the counter value but will generate a fresh nonce. */
     if (state != NULL)
         memcpy(state->data, counter.data, counter.length);
 
 cleanup:
     free(sign_data);
-
     return ret;
 }
 
+/* Derive an encryption key based on usage and CCM-encrypt data. */
 krb5_error_code
-krb5int_dk_ccm_encrypt(const struct krb5_keytypes *ktp,
-                       krb5_key key,
-                       krb5_keyusage usage,
-                       const krb5_data *state,
-                       krb5_crypto_iov *data,
-                       size_t num_data)
+krb5int_dk_ccm_encrypt(const struct krb5_keytypes *ktp, krb5_key key,
+                       krb5_keyusage usage, const krb5_data *state,
+                       krb5_crypto_iov *data, size_t num_data)
 {
     unsigned char constantdata[K5CLENGTH];
     krb5_error_code ret;
@@ -414,23 +410,20 @@
     if (ret != 0)
         return ret;
 
-    ret = ccm_encrypt(ktp, kc, usage, state, data, num_data);
+    ret = ccm_encrypt(ktp, kc, state, data, num_data);
 
     krb5_k_free_key(NULL, kc);
 
     return ret;
 }
 
+/* Decrypt and verify data according to NIST SP800-38C section 6.2. */
 static krb5_error_code
-ccm_decrypt(const struct krb5_keytypes *ktp,
-            krb5_key kc,
-            krb5_keyusage usage,
-            const krb5_data *state,
-            krb5_crypto_iov *data,
-            size_t num_data)
+ccm_decrypt(const struct krb5_keytypes *ktp, krb5_key kc,
+	    const krb5_data *state, krb5_crypto_iov *data, size_t num_data)
 {
     krb5_error_code ret;
-    krb5_crypto_iov *header, *trailer, *sign_data = NULL;
+    krb5_crypto_iov *header, *trailer, *sign_data = NULL, got_cksum;
     size_t i, num_sign_data = 0;
     unsigned int header_len;
     unsigned int trailer_len;
@@ -483,30 +476,31 @@
         goto cleanup;
     }
 
-    /* Initialize counter block */
+    /* Encode the first counter block. */
     ret = format_Ctr0(&counter, &header->data, state, header_len);
     if (ret != 0)
         goto cleanup;
 
+    /* Create a list of CBC-MAC input blocks. */
     sign_data = k5alloc((num_data + 1) * sizeof(krb5_crypto_iov), &ret);
     if (sign_data == NULL)
         goto cleanup;
 
-    sign_data[num_sign_data].flags = KRB5_CRYPTO_TYPE_HEADER;
-    sign_data[num_sign_data].data = make_data(B0, sizeof(B0));
-    ret = format_B0(&sign_data[num_sign_data].data, &header->data, trailer_len,
+    /* Format the initial control/nonce block. */
+    sign_data[0].flags = KRB5_CRYPTO_TYPE_HEADER;
+    sign_data[0].data = make_data(B0, sizeof(B0));
+    ret = format_B0(&sign_data[0].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 = make_data(adata_len_buf, sizeof(adata_len_buf));
-    ret = encode_a_len(&sign_data[num_sign_data].data, (krb5_ui_8)adata_len);
+    /* Format the length of associated data. */
+    sign_data[1].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY;
+    sign_data[1].data = make_data(adata_len_buf, sizeof(adata_len_buf));
+    ret = encode_a_len(&sign_data[1].data, (krb5_ui_8)adata_len);
     if (ret != 0)
         goto cleanup;
-    num_sign_data++;
+    num_sign_data = 2;
 
     assert(ktp->enc->decrypt != NULL);
     assert(ktp->enc->cbc_mac != NULL);
@@ -516,19 +510,14 @@
         goto cleanup;
     made_cksum.length = trailer_len;
 
-    /* Decrypt checksum from trailer */
-    {
-        krb5_crypto_iov got_cksum[1];
+    /* Decrypt checksum from trailer using the first counter block. */
+    got_cksum.flags = KRB5_CRYPTO_TYPE_DATA;
+    got_cksum.data = trailer->data;
+    ret = ktp->enc->decrypt(kc, &counter, &got_cksum, 1);
+    if (ret != 0)
+	goto cleanup;
 
-        got_cksum[0].flags = KRB5_CRYPTO_TYPE_DATA;
-        got_cksum[0].data = trailer->data;
-
-        ret = ktp->enc->decrypt(kc, &counter, got_cksum, 1);
-        if (ret != 0)
-            goto cleanup;
-    }
-
-    /* Don't decrypt B0 (header), but decrypt everything else */
+    /* Decrypt everything but B0 (header) in subsequent counter blocks. */
     ret = ktp->enc->decrypt(kc, &counter, data, num_data);
     if (ret != 0)
         goto cleanup;
@@ -543,17 +532,20 @@
             sign_data[num_sign_data++] = data[i];
     }
 
-    /* Now, calculate hash for comparison (including B0) */
+    /* Calculate CBC-MAC for comparison (including B0). */
     ret = ktp->enc->cbc_mac(kc, sign_data, num_sign_data, NULL, &made_cksum);
     if (ret != 0)
         goto cleanup;
 
     if (made_cksum.length != trailer->data.length ||
-        memcmp(made_cksum.data, trailer->data.data, trailer->data.length) != 0) {
+        memcmp(made_cksum.data, trailer->data.data,
+	       trailer->data.length) != 0) {
         ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
         goto cleanup;
     }
 
+    /* Store the counter block as cipher state.  Subsequent decryptions will
+     * reuse the counter value but will generate a fresh nonce. */
     if (state != NULL)
         memcpy(state->data, counter.data, counter.length);
 
@@ -564,13 +556,11 @@
     return ret;
 }
 
+/* Derive an encryption key based on usage and CCM-decrypt data. */
 krb5_error_code
-krb5int_dk_ccm_decrypt(const struct krb5_keytypes *ktp,
-                       krb5_key key,
-                       krb5_keyusage usage,
-                       const krb5_data *state,
-                       krb5_crypto_iov *data,
-                       size_t num_data)
+krb5int_dk_ccm_decrypt(const struct krb5_keytypes *ktp, krb5_key key,
+                       krb5_keyusage usage, const krb5_data *state,
+                       krb5_crypto_iov *data, size_t num_data)
 {
     unsigned char constantdata[K5CLENGTH];
     krb5_error_code ret;
@@ -591,7 +581,7 @@
     if (ret != 0)
         return ret;
 
-    ret = ccm_decrypt(ktp, kc, usage, state, data, num_data);
+    ret = ccm_decrypt(ktp, kc, state, data, num_data);
 
     krb5_k_free_key(NULL, kc);
 




More information about the cvs-krb5 mailing list