krb5 commit: Simplify crypto IOV helpers

Greg Hudson ghudson at MIT.EDU
Fri May 24 14:26:12 EDT 2013


https://github.com/krb5/krb5/commit/3b142e5746e7db1939a9cf8095faecfed6222054
commit 3b142e5746e7db1939a9cf8095faecfed6222054
Author: Greg Hudson <ghudson at mit.edu>
Date:   Mon May 6 00:43:27 2013 -0400

    Simplify crypto IOV helpers
    
    Expand the concept of an IOV block state into a cursor which remembers
    the IOV set being iterated over, the block size, and both input and
    output positions.  Eliminate the no-copy inline block getter for now,
    but provide helpers to grab contiguous chains of blocks from a cursor.
    Also provide an inline helper to sum the total length of an iov chain.

 src/lib/crypto/builtin/des/d3_aead.c           |   88 +++------
 src/lib/crypto/builtin/des/f_aead.c            |  118 ++++--------
 src/lib/crypto/builtin/enc_provider/aes.c      |   83 +++-----
 src/lib/crypto/builtin/enc_provider/camellia.c |   97 +++------
 src/lib/crypto/builtin/enc_provider/des.c      |   13 +-
 src/lib/crypto/builtin/enc_provider/des3.c     |   11 +-
 src/lib/crypto/krb/aead.c                      |   86 ++++++++
 src/lib/crypto/krb/cmac.c                      |   18 +-
 src/lib/crypto/krb/crypto_int.h                |  260 ++++--------------------
 src/lib/crypto/krb/enc_old.c                   |   10 +-
 src/lib/crypto/nss/enc_provider/enc_gen.c      |   93 +++------
 src/lib/crypto/openssl/enc_provider/aes.c      |   77 +++-----
 src/lib/crypto/openssl/enc_provider/camellia.c |   93 +++------
 src/lib/crypto/openssl/enc_provider/des.c      |   54 ++----
 src/lib/crypto/openssl/enc_provider/des3.c     |   42 +---
 15 files changed, 375 insertions(+), 768 deletions(-)

diff --git a/src/lib/crypto/builtin/des/d3_aead.c b/src/lib/crypto/builtin/des/d3_aead.c
index af34fe9..bddf75a 100644
--- a/src/lib/crypto/builtin/des/d3_aead.c
+++ b/src/lib/crypto/builtin/des/d3_aead.c
@@ -36,11 +36,8 @@ krb5int_des3_cbc_encrypt(krb5_crypto_iov *data, unsigned long num_data,
     unsigned DES_INT32 left, right;
     const unsigned DES_INT32 *kp1, *kp2, *kp3;
     const unsigned char *ip;
-    struct iov_block_state input_pos, output_pos;
-    unsigned char storage[MIT_DES_BLOCK_LENGTH], *block = NULL, *ptr;
-
-    IOV_BLOCK_STATE_INIT(&input_pos);
-    IOV_BLOCK_STATE_INIT(&output_pos);
+    struct iov_cursor cursor;
+    unsigned char block[MIT_DES_BLOCK_LENGTH];
 
     /* Get key pointers here.  These won't need to be reinitialized. */
     kp1 = (const unsigned DES_INT32 *)ks1;
@@ -49,41 +46,28 @@ krb5int_des3_cbc_encrypt(krb5_crypto_iov *data, unsigned long num_data,
 
     /* Initialize left and right with the contents of the initial vector. */
     ip = (ivec != NULL) ? ivec : mit_des_zeroblock;
-    GET_HALF_BLOCK(left, ip);
-    GET_HALF_BLOCK(right, ip);
-
-    /* Work the length down 8 bytes at a time. */
-    for (;;) {
-        unsigned DES_INT32 temp;
-
-        if (!krb5int_c_iov_get_block_nocopy(storage, MIT_DES_BLOCK_LENGTH,
-                                            data, num_data, &input_pos, &ptr))
-            break;
-        block = ptr;
+    left = load_32_be(ip);
+    right = load_32_be(ip + 4);
 
-        /* Decompose this block and xor it with the previous ciphertext. */
-        GET_HALF_BLOCK(temp, ptr);
-        left  ^= temp;
-        GET_HALF_BLOCK(temp, ptr);
-        right ^= temp;
+    k5_iov_cursor_init(&cursor, data, num_data, MIT_DES_BLOCK_LENGTH, FALSE);
+    while (k5_iov_cursor_get(&cursor, block)) {
+        /* xor this block with the previous ciphertext. */
+        left ^= load_32_be(block);
+        right ^= load_32_be(block + 4);
 
         /* Encrypt what we have and store it back into block. */
         DES_DO_ENCRYPT(left, right, kp1);
         DES_DO_DECRYPT(left, right, kp2);
         DES_DO_ENCRYPT(left, right, kp3);
-        ptr = block;
-        PUT_HALF_BLOCK(left, ptr);
-        PUT_HALF_BLOCK(right, ptr);
+        store_32_be(left, block);
+        store_32_be(right, block + 4);
 
-        krb5int_c_iov_put_block_nocopy(data, num_data, storage,
-                                       MIT_DES_BLOCK_LENGTH, &output_pos,
-                                       block);
+        k5_iov_cursor_put(&cursor, block);
     }
 
-    if (ivec != NULL && block != NULL) {
-        ptr = ivec;
-        PUT_HALF_BLOCK(left, ptr);
-        PUT_HALF_BLOCK(right, ptr);
+    if (ivec != NULL) {
+        store_32_be(left, ivec);
+        store_32_be(right, ivec + 4);
     }
 }
 
@@ -99,11 +83,8 @@ krb5int_des3_cbc_decrypt(krb5_crypto_iov *data, unsigned long num_data,
     const unsigned char *ip;
     unsigned DES_INT32 ocipherl, ocipherr;
     unsigned DES_INT32 cipherl, cipherr;
-    struct iov_block_state input_pos, output_pos;
-    unsigned char storage[MIT_DES_BLOCK_LENGTH], *block = NULL, *ptr;
-
-    IOV_BLOCK_STATE_INIT(&input_pos);
-    IOV_BLOCK_STATE_INIT(&output_pos);
+    struct iov_cursor cursor;
+    unsigned char block[MIT_DES_BLOCK_LENGTH];
 
     /* Get key pointers here.  These won't need to be reinitialized. */
     kp1 = (const unsigned DES_INT32 *)ks1;
@@ -118,21 +99,14 @@ krb5int_des3_cbc_decrypt(krb5_crypto_iov *data, unsigned long num_data,
 
     /* Prime the old cipher with ivec.*/
     ip = (ivec != NULL) ? ivec : mit_des_zeroblock;
-    GET_HALF_BLOCK(ocipherl, ip);
-    GET_HALF_BLOCK(ocipherr, ip);
-
-    /* Work the length down 8 bytes at a time. */
-    for (;;) {
-        if (!krb5int_c_iov_get_block_nocopy(storage, MIT_DES_BLOCK_LENGTH,
-                                            data, num_data, &input_pos, &ptr))
-            break;
-        block = ptr;
+    ocipherl = load_32_be(ip);
+    ocipherr = load_32_be(ip + 4);
 
+    k5_iov_cursor_init(&cursor, data, num_data, MIT_DES_BLOCK_LENGTH, FALSE);
+    while (k5_iov_cursor_get(&cursor, block)) {
         /* Split this block into left and right. */
-        GET_HALF_BLOCK(left, ptr);
-        GET_HALF_BLOCK(right, ptr);
-        cipherl = left;
-        cipherr = right;
+        cipherl = left = load_32_be(block);
+        cipherr = right = load_32_be(block + 4);
 
         /* Decrypt and xor with the old cipher to get plain text. */
         DES_DO_DECRYPT(left, right, kp3);
@@ -142,22 +116,18 @@ krb5int_des3_cbc_decrypt(krb5_crypto_iov *data, unsigned long num_data,
         right ^= ocipherr;
 
         /* Store the encrypted halves back into block. */
-        ptr = block;
-        PUT_HALF_BLOCK(left, ptr);
-        PUT_HALF_BLOCK(right, ptr);
+        store_32_be(left, block);
+        store_32_be(right, block + 4);
 
         /* Save current cipher block halves. */
         ocipherl = cipherl;
         ocipherr = cipherr;
 
-        krb5int_c_iov_put_block_nocopy(data, num_data, storage,
-                                       MIT_DES_BLOCK_LENGTH, &output_pos,
-                                       block);
+        k5_iov_cursor_put(&cursor, block);
     }
 
-    if (ivec != NULL && block != NULL) {
-        ptr = ivec;
-        PUT_HALF_BLOCK(ocipherl, ptr);
-        PUT_HALF_BLOCK(ocipherr, ptr);
+    if (ivec != NULL) {
+        store_32_be(ocipherl, ivec);
+        store_32_be(ocipherr, ivec + 4);
     }
 }
diff --git a/src/lib/crypto/builtin/des/f_aead.c b/src/lib/crypto/builtin/des/f_aead.c
index 5d80286..71b8dff 100644
--- a/src/lib/crypto/builtin/des/f_aead.c
+++ b/src/lib/crypto/builtin/des/f_aead.c
@@ -36,50 +36,34 @@ krb5int_des_cbc_encrypt(krb5_crypto_iov *data, unsigned long num_data,
     unsigned DES_INT32 left, right;
     const unsigned DES_INT32 *kp;
     const unsigned char *ip;
-    struct iov_block_state input_pos, output_pos;
-    unsigned char storage[MIT_DES_BLOCK_LENGTH], *block = NULL, *ptr;
-
-    IOV_BLOCK_STATE_INIT(&input_pos);
-    IOV_BLOCK_STATE_INIT(&output_pos);
+    struct iov_cursor cursor;
+    unsigned char block[MIT_DES_BLOCK_LENGTH];
 
     /* Get key pointer here.  This won't need to be reinitialized. */
     kp = (const unsigned DES_INT32 *)schedule;
 
     /* Initialize left and right with the contents of the initial vector. */
     ip = (ivec != NULL) ? ivec : mit_des_zeroblock;
-    GET_HALF_BLOCK(left, ip);
-    GET_HALF_BLOCK(right, ip);
-
-    /* Work the length down 8 bytes at a time. */
-    for (;;) {
-        unsigned DES_INT32 temp;
-
-        if (!krb5int_c_iov_get_block_nocopy(storage, MIT_DES_BLOCK_LENGTH,
-                                            data, num_data, &input_pos, &ptr))
-            break;
-        block = ptr;
+    left = load_32_be(ip);
+    right = load_32_be(ip + 4);
 
+    k5_iov_cursor_init(&cursor, data, num_data, MIT_DES_BLOCK_LENGTH, FALSE);
+    while (k5_iov_cursor_get(&cursor, block)) {
         /* Decompose this block and xor it with the previous ciphertext. */
-        GET_HALF_BLOCK(temp, ptr);
-        left  ^= temp;
-        GET_HALF_BLOCK(temp, ptr);
-        right ^= temp;
+        left ^= load_32_be(block);
+        right ^= load_32_be(block + 4);
 
         /* Encrypt what we have and put back into block. */
         DES_DO_ENCRYPT(left, right, kp);
-        ptr = block;
-        PUT_HALF_BLOCK(left, ptr);
-        PUT_HALF_BLOCK(right, ptr);
+        store_32_be(left, block);
+        store_32_be(right, block + 4);
 
-        krb5int_c_iov_put_block_nocopy(data, num_data, storage,
-                                       MIT_DES_BLOCK_LENGTH, &output_pos,
-                                       block);
+        k5_iov_cursor_put(&cursor, block);
     }
 
-    if (ivec != NULL && block != NULL) {
-        ptr = ivec;
-        PUT_HALF_BLOCK(left, ptr);
-        PUT_HALF_BLOCK(right, ptr);
+    if (ivec != NULL) {
+        store_32_be(left, ivec);
+        store_32_be(right, ivec + 4);
     }
 }
 
@@ -93,11 +77,8 @@ krb5int_des_cbc_decrypt(krb5_crypto_iov *data, unsigned long num_data,
     const unsigned char *ip;
     unsigned DES_INT32 ocipherl, ocipherr;
     unsigned DES_INT32 cipherl, cipherr;
-    struct iov_block_state input_pos, output_pos;
-    unsigned char storage[MIT_DES_BLOCK_LENGTH], *block = NULL, *ptr;
-
-    IOV_BLOCK_STATE_INIT(&input_pos);
-    IOV_BLOCK_STATE_INIT(&output_pos);
+    struct iov_cursor cursor;
+    unsigned char block[MIT_DES_BLOCK_LENGTH];
 
     /* Get key pointer here.  This won't need to be reinitialized. */
     kp = (const unsigned DES_INT32 *)schedule;
@@ -110,21 +91,14 @@ krb5int_des_cbc_decrypt(krb5_crypto_iov *data, unsigned long num_data,
 
     /* Prime the old cipher with ivec. */
     ip = (ivec != NULL) ? ivec : mit_des_zeroblock;
-    GET_HALF_BLOCK(ocipherl, ip);
-    GET_HALF_BLOCK(ocipherr, ip);
-
-    /* Work the length down 8 bytes at a time. */
-    for (;;) {
-        if (!krb5int_c_iov_get_block_nocopy(storage, MIT_DES_BLOCK_LENGTH,
-                                            data, num_data, &input_pos, &ptr))
-            break;
-        block = ptr;
+    ocipherl = load_32_be(ip);
+    ocipherr = load_32_be(ip + 4);
 
+    k5_iov_cursor_init(&cursor, data, num_data, MIT_DES_BLOCK_LENGTH, FALSE);
+    while (k5_iov_cursor_get(&cursor, block)) {
         /* Split this block into left and right. */
-        GET_HALF_BLOCK(left, ptr);
-        GET_HALF_BLOCK(right, ptr);
-        cipherl = left;
-        cipherr = right;
+        cipherl = left = load_32_be(block);
+        cipherr = right = load_32_be(block + 4);
 
         /* Decrypt and xor with the old cipher to get plain text. */
         DES_DO_DECRYPT(left, right, kp);
@@ -132,23 +106,19 @@ krb5int_des_cbc_decrypt(krb5_crypto_iov *data, unsigned long num_data,
         right ^= ocipherr;
 
         /* Store the encrypted halves back into block. */
-        ptr = block;
-        PUT_HALF_BLOCK(left, ptr);
-        PUT_HALF_BLOCK(right, ptr);
+        store_32_be(left, block);
+        store_32_be(right, block + 4);
 
         /* Save current cipher block halves. */
         ocipherl = cipherl;
         ocipherr = cipherr;
 
-        krb5int_c_iov_put_block_nocopy(data, num_data, storage,
-                                       MIT_DES_BLOCK_LENGTH, &output_pos,
-                                       block);
+        k5_iov_cursor_put(&cursor, block);
     }
 
-    if (ivec != NULL && block != NULL) {
-        ptr = ivec;
-        PUT_HALF_BLOCK(ocipherl, ptr);
-        PUT_HALF_BLOCK(ocipherr, ptr);
+    if (ivec != NULL) {
+        store_32_be(ocipherl, ivec);
+        store_32_be(ocipherr, ivec + 4);
     }
 }
 
@@ -160,42 +130,30 @@ krb5int_des_cbc_mac(const krb5_crypto_iov *data, unsigned long num_data,
     unsigned DES_INT32 left, right;
     const unsigned DES_INT32 *kp;
     const unsigned char *ip;
-    struct iov_block_state input_pos;
-    unsigned char storage[MIT_DES_BLOCK_LENGTH], *ptr;
-
-    IOV_BLOCK_STATE_INIT(&input_pos);
-    input_pos.include_sign_only = 1;
+    struct iov_cursor cursor;
+    unsigned char block[MIT_DES_BLOCK_LENGTH];
 
     /* Get key pointer here.  This won't need to be reinitialized. */
     kp = (const unsigned DES_INT32 *)schedule;
 
     /* Initialize left and right with the contents of the initial vector. */
     ip = (ivec != NULL) ? ivec : mit_des_zeroblock;
-    GET_HALF_BLOCK(left, ip);
-    GET_HALF_BLOCK(right, ip);
-
-    /* Work the length down 8 bytes at a time. */
-    for (;;) {
-        unsigned DES_INT32 temp;
-
-        if (!krb5int_c_iov_get_block_nocopy(storage, MIT_DES_BLOCK_LENGTH,
-                                            data, num_data, &input_pos, &ptr))
-            break;
+    left = load_32_be(ip);
+    right = load_32_be(ip + 4);
 
+    k5_iov_cursor_init(&cursor, data, num_data, MIT_DES_BLOCK_LENGTH, TRUE);
+    while (k5_iov_cursor_get(&cursor, block)) {
         /* Decompose this block and xor it with the previous ciphertext. */
-        GET_HALF_BLOCK(temp, ptr);
-        left  ^= temp;
-        GET_HALF_BLOCK(temp, ptr);
-        right ^= temp;
+        left ^= load_32_be(block);
+        right ^= load_32_be(block + 4);
 
         /* Encrypt what we have. */
         DES_DO_ENCRYPT(left, right, kp);
     }
 
     /* Output the final ciphertext block. */
-    ptr = out;
-    PUT_HALF_BLOCK(left, ptr);
-    PUT_HALF_BLOCK(right, ptr);
+    store_32_be(left, out);
+    store_32_be(right, out + 4);
 }
 
 #if defined(CONFIG_SMALL) && !defined(CONFIG_SMALL_NO_CRYPTO)
diff --git a/src/lib/crypto/builtin/enc_provider/aes.c b/src/lib/crypto/builtin/enc_provider/aes.c
index b46680a..280119a 100644
--- a/src/lib/crypto/builtin/enc_provider/aes.c
+++ b/src/lib/crypto/builtin/enc_provider/aes.c
@@ -85,9 +85,8 @@ krb5int_aes_encrypt(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data,
                     size_t num_data)
 {
     unsigned char tmp[BLOCK_SIZE], tmp2[BLOCK_SIZE];
-    int nblocks = 0, blockno;
-    size_t input_length, i;
-    struct iov_block_state input_pos, output_pos;
+    size_t input_length, nblocks, blockno;
+    struct iov_cursor cursor;
 
     if (key->cache == NULL) {
         key->cache = malloc(sizeof(struct aes_key_info_cache));
@@ -106,34 +105,25 @@ krb5int_aes_encrypt(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data,
     else
         memset(tmp, 0, BLOCK_SIZE);
 
-    for (i = 0, input_length = 0; i < num_data; i++) {
-        krb5_crypto_iov *iov = &data[i];
-
-        if (ENCRYPT_IOV(iov))
-            input_length += iov->data.length;
-    }
-
-    IOV_BLOCK_STATE_INIT(&input_pos);
-    IOV_BLOCK_STATE_INIT(&output_pos);
+    k5_iov_cursor_init(&cursor, data, num_data, BLOCK_SIZE, FALSE);
 
+    input_length = iov_total_length(data, num_data, FALSE);
     nblocks = (input_length + BLOCK_SIZE - 1) / BLOCK_SIZE;
     if (nblocks == 1) {
-        krb5int_c_iov_get_block(tmp, BLOCK_SIZE, data, num_data, &input_pos);
+        k5_iov_cursor_get(&cursor, tmp);
         enc(tmp2, tmp, &CACHE(key)->enc_ctx);
-        krb5int_c_iov_put_block(data, num_data, tmp2, BLOCK_SIZE, &output_pos);
+        k5_iov_cursor_put(&cursor, tmp2);
     } else if (nblocks > 1) {
         unsigned char blockN2[BLOCK_SIZE];   /* second last */
         unsigned char blockN1[BLOCK_SIZE];   /* last block */
 
         for (blockno = 0; blockno < nblocks - 2; blockno++) {
-            unsigned char blockN[BLOCK_SIZE], *block;
+            unsigned char block[BLOCK_SIZE];
 
-            krb5int_c_iov_get_block_nocopy(blockN, BLOCK_SIZE,
-                                           data, num_data, &input_pos, &block);
+            k5_iov_cursor_get(&cursor, block);
             xorblock(tmp, block);
             enc(block, tmp, &CACHE(key)->enc_ctx);
-            krb5int_c_iov_put_block_nocopy(data, num_data, blockN, BLOCK_SIZE,
-                                           &output_pos, block);
+            k5_iov_cursor_put(&cursor, block);
 
             /* Set up for next block.  */
             memcpy(tmp, block, BLOCK_SIZE);
@@ -143,11 +133,8 @@ krb5int_aes_encrypt(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data,
            may or may not be incomplete).  */
 
         /* First, get the last two blocks */
-        memset(blockN1, 0, sizeof(blockN1)); /* pad last block with zeros */
-        krb5int_c_iov_get_block(blockN2, BLOCK_SIZE, data, num_data,
-                                &input_pos);
-        krb5int_c_iov_get_block(blockN1, BLOCK_SIZE, data, num_data,
-                                &input_pos);
+        k5_iov_cursor_get(&cursor, blockN2);
+        k5_iov_cursor_get(&cursor, blockN1);
 
         /* Encrypt second last block */
         xorblock(tmp, blockN2);
@@ -161,10 +148,8 @@ krb5int_aes_encrypt(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data,
         memcpy(blockN1, tmp2, BLOCK_SIZE);
 
         /* Put the last two blocks back into the iovec (reverse order) */
-        krb5int_c_iov_put_block(data, num_data, blockN1, BLOCK_SIZE,
-                                &output_pos);
-        krb5int_c_iov_put_block(data, num_data, blockN2, BLOCK_SIZE,
-                                &output_pos);
+        k5_iov_cursor_put(&cursor, blockN1);
+        k5_iov_cursor_put(&cursor, blockN2);
 
         if (ivec != NULL)
             memcpy(ivec->data, blockN1, BLOCK_SIZE);
@@ -178,10 +163,8 @@ krb5int_aes_decrypt(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data,
                     size_t num_data)
 {
     unsigned char tmp[BLOCK_SIZE], tmp2[BLOCK_SIZE], tmp3[BLOCK_SIZE];
-    int nblocks = 0, blockno;
-    unsigned int i;
-    size_t input_length;
-    struct iov_block_state input_pos, output_pos;
+    size_t input_length, nblocks, blockno;
+    struct iov_cursor cursor;
 
     CHECK_SIZES;
 
@@ -202,47 +185,35 @@ krb5int_aes_decrypt(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data,
     else
         memset(tmp, 0, BLOCK_SIZE);
 
-    for (i = 0, input_length = 0; i < num_data; i++) {
-        krb5_crypto_iov *iov = &data[i];
-
-        if (ENCRYPT_IOV(iov))
-            input_length += iov->data.length;
-    }
-
-    IOV_BLOCK_STATE_INIT(&input_pos);
-    IOV_BLOCK_STATE_INIT(&output_pos);
+    k5_iov_cursor_init(&cursor, data, num_data, BLOCK_SIZE, FALSE);
 
+    input_length = iov_total_length(data, num_data, FALSE);
     nblocks = (input_length + BLOCK_SIZE - 1) / BLOCK_SIZE;
     if (nblocks == 1) {
-        krb5int_c_iov_get_block(tmp, BLOCK_SIZE, data, num_data, &input_pos);
+        k5_iov_cursor_get(&cursor, tmp);
         dec(tmp2, tmp, &CACHE(key)->dec_ctx);
-        krb5int_c_iov_put_block(data, num_data, tmp2, BLOCK_SIZE, &output_pos);
+        k5_iov_cursor_put(&cursor, tmp2);
     } else if (nblocks > 1) {
         unsigned char blockN2[BLOCK_SIZE];   /* second last */
         unsigned char blockN1[BLOCK_SIZE];   /* last block */
 
         for (blockno = 0; blockno < nblocks - 2; blockno++) {
-            unsigned char blockN[BLOCK_SIZE], *block;
+            unsigned char block[BLOCK_SIZE];
 
-            krb5int_c_iov_get_block_nocopy(blockN, BLOCK_SIZE,
-                                           data, num_data, &input_pos, &block);
+            k5_iov_cursor_get(&cursor, block);
             memcpy(tmp2, block, BLOCK_SIZE);
             dec(block, block, &CACHE(key)->dec_ctx);
             xorblock(block, tmp);
             memcpy(tmp, tmp2, BLOCK_SIZE);
-            krb5int_c_iov_put_block_nocopy(data, num_data, blockN, BLOCK_SIZE,
-                                           &output_pos, block);
+            k5_iov_cursor_put(&cursor, block);
         }
 
         /* Do last two blocks, the second of which (next-to-last block
            of plaintext) may be incomplete.  */
 
         /* First, get the last two encrypted blocks */
-        memset(blockN1, 0, sizeof(blockN1)); /* pad last block with zeros */
-        krb5int_c_iov_get_block(blockN2, BLOCK_SIZE, data, num_data,
-                                &input_pos);
-        krb5int_c_iov_get_block(blockN1, BLOCK_SIZE, data, num_data,
-                                &input_pos);
+        k5_iov_cursor_get(&cursor, blockN2);
+        k5_iov_cursor_get(&cursor, blockN1);
 
         if (ivec != NULL)
             memcpy(ivec->data, blockN2, BLOCK_SIZE);
@@ -263,10 +234,8 @@ krb5int_aes_decrypt(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data,
         memcpy(blockN1, tmp3, BLOCK_SIZE);
 
         /* Put the last two blocks back into the iovec */
-        krb5int_c_iov_put_block(data, num_data, blockN1, BLOCK_SIZE,
-                                &output_pos);
-        krb5int_c_iov_put_block(data, num_data, blockN2, BLOCK_SIZE,
-                                &output_pos);
+        k5_iov_cursor_put(&cursor, blockN1);
+        k5_iov_cursor_put(&cursor, blockN2);
     }
 
     return 0;
diff --git a/src/lib/crypto/builtin/enc_provider/camellia.c b/src/lib/crypto/builtin/enc_provider/camellia.c
index 7a0fbd9..efba629 100644
--- a/src/lib/crypto/builtin/enc_provider/camellia.c
+++ b/src/lib/crypto/builtin/enc_provider/camellia.c
@@ -82,9 +82,8 @@ krb5int_camellia_encrypt(krb5_key key, const krb5_data *ivec,
                          krb5_crypto_iov *data, size_t num_data)
 {
     unsigned char tmp[BLOCK_SIZE], tmp2[BLOCK_SIZE];
-    int nblocks = 0, blockno;
-    size_t input_length, i;
-    struct iov_block_state input_pos, output_pos;
+    size_t input_length, nblocks, blockno;
+    struct iov_cursor cursor;
 
     if (key->cache == NULL) {
         key->cache = malloc(sizeof(struct camellia_key_info_cache));
@@ -102,34 +101,25 @@ krb5int_camellia_encrypt(krb5_key key, const krb5_data *ivec,
     else
         memset(tmp, 0, BLOCK_SIZE);
 
-    for (i = 0, input_length = 0; i < num_data; i++) {
-        krb5_crypto_iov *iov = &data[i];
-
-        if (ENCRYPT_IOV(iov))
-            input_length += iov->data.length;
-    }
-
-    IOV_BLOCK_STATE_INIT(&input_pos);
-    IOV_BLOCK_STATE_INIT(&output_pos);
+    k5_iov_cursor_init(&cursor, data, num_data, BLOCK_SIZE, FALSE);
 
+    input_length = iov_total_length(data, num_data, FALSE);
     nblocks = (input_length + BLOCK_SIZE - 1) / BLOCK_SIZE;
     if (nblocks == 1) {
-        krb5int_c_iov_get_block(tmp, BLOCK_SIZE, data, num_data, &input_pos);
+        k5_iov_cursor_get(&cursor, tmp);
         enc(tmp2, tmp, &CACHE(key)->enc_ctx);
-        krb5int_c_iov_put_block(data, num_data, tmp2, BLOCK_SIZE, &output_pos);
+        k5_iov_cursor_put(&cursor, tmp2);
     } else if (nblocks > 1) {
         unsigned char blockN2[BLOCK_SIZE];   /* second last */
         unsigned char blockN1[BLOCK_SIZE];   /* last block */
 
         for (blockno = 0; blockno < nblocks - 2; blockno++) {
-            unsigned char blockN[BLOCK_SIZE], *block;
+            unsigned char block[BLOCK_SIZE];
 
-            krb5int_c_iov_get_block_nocopy(blockN, BLOCK_SIZE,
-                                           data, num_data, &input_pos, &block);
+            k5_iov_cursor_get(&cursor, block);
             xorblock(tmp, block);
             enc(block, tmp, &CACHE(key)->enc_ctx);
-            krb5int_c_iov_put_block_nocopy(data, num_data, blockN, BLOCK_SIZE,
-                                           &output_pos, block);
+            k5_iov_cursor_put(&cursor, block);
 
             /* Set up for next block.  */
             memcpy(tmp, block, BLOCK_SIZE);
@@ -139,11 +129,8 @@ krb5int_camellia_encrypt(krb5_key key, const krb5_data *ivec,
            may or may not be incomplete).  */
 
         /* First, get the last two blocks */
-        memset(blockN1, 0, sizeof(blockN1)); /* pad last block with zeros */
-        krb5int_c_iov_get_block(blockN2, BLOCK_SIZE, data, num_data,
-                                &input_pos);
-        krb5int_c_iov_get_block(blockN1, BLOCK_SIZE, data, num_data,
-                                &input_pos);
+        k5_iov_cursor_get(&cursor, blockN2);
+        k5_iov_cursor_get(&cursor, blockN1);
 
         /* Encrypt second last block */
         xorblock(tmp, blockN2);
@@ -157,10 +144,8 @@ krb5int_camellia_encrypt(krb5_key key, const krb5_data *ivec,
         memcpy(blockN1, tmp2, BLOCK_SIZE);
 
         /* Put the last two blocks back into the iovec (reverse order) */
-        krb5int_c_iov_put_block(data, num_data, blockN1, BLOCK_SIZE,
-                                &output_pos);
-        krb5int_c_iov_put_block(data, num_data, blockN2, BLOCK_SIZE,
-                                &output_pos);
+        k5_iov_cursor_put(&cursor, blockN1);
+        k5_iov_cursor_put(&cursor, blockN2);
 
         if (ivec != NULL)
             memcpy(ivec->data, blockN1, BLOCK_SIZE);
@@ -174,10 +159,8 @@ krb5int_camellia_decrypt(krb5_key key, const krb5_data *ivec,
                          krb5_crypto_iov *data, size_t num_data)
 {
     unsigned char tmp[BLOCK_SIZE], tmp2[BLOCK_SIZE], tmp3[BLOCK_SIZE];
-    int nblocks = 0, blockno;
-    unsigned int i;
-    size_t input_length;
-    struct iov_block_state input_pos, output_pos;
+    size_t input_length, nblocks, blockno;
+    struct iov_cursor cursor;
 
     if (key->cache == NULL) {
         key->cache = malloc(sizeof(struct camellia_key_info_cache));
@@ -196,47 +179,35 @@ krb5int_camellia_decrypt(krb5_key key, const krb5_data *ivec,
     else
         memset(tmp, 0, BLOCK_SIZE);
 
-    for (i = 0, input_length = 0; i < num_data; i++) {
-        krb5_crypto_iov *iov = &data[i];
-
-        if (ENCRYPT_IOV(iov))
-            input_length += iov->data.length;
-    }
-
-    IOV_BLOCK_STATE_INIT(&input_pos);
-    IOV_BLOCK_STATE_INIT(&output_pos);
+    k5_iov_cursor_init(&cursor, data, num_data, BLOCK_SIZE, FALSE);
 
+    input_length = iov_total_length(data, num_data, FALSE);
     nblocks = (input_length + BLOCK_SIZE - 1) / BLOCK_SIZE;
     if (nblocks == 1) {
-        krb5int_c_iov_get_block(tmp, BLOCK_SIZE, data, num_data, &input_pos);
+        k5_iov_cursor_get(&cursor, tmp);
         dec(tmp2, tmp, &CACHE(key)->dec_ctx);
-        krb5int_c_iov_put_block(data, num_data, tmp2, BLOCK_SIZE, &output_pos);
+        k5_iov_cursor_put(&cursor, tmp2);
     } else if (nblocks > 1) {
         unsigned char blockN2[BLOCK_SIZE];   /* second last */
         unsigned char blockN1[BLOCK_SIZE];   /* last block */
 
         for (blockno = 0; blockno < nblocks - 2; blockno++) {
-            unsigned char blockN[BLOCK_SIZE], *block;
+            unsigned char block[BLOCK_SIZE];
 
-            krb5int_c_iov_get_block_nocopy(blockN, BLOCK_SIZE,
-                                           data, num_data, &input_pos, &block);
+            k5_iov_cursor_get(&cursor, block);
             memcpy(tmp2, block, BLOCK_SIZE);
             dec(block, block, &CACHE(key)->dec_ctx);
             xorblock(block, tmp);
             memcpy(tmp, tmp2, BLOCK_SIZE);
-            krb5int_c_iov_put_block_nocopy(data, num_data, blockN, BLOCK_SIZE,
-                                           &output_pos, block);
+            k5_iov_cursor_put(&cursor, block);
         }
 
         /* Do last two blocks, the second of which (next-to-last block
            of plaintext) may be incomplete.  */
 
         /* First, get the last two encrypted blocks */
-        memset(blockN1, 0, sizeof(blockN1)); /* pad last block with zeros */
-        krb5int_c_iov_get_block(blockN2, BLOCK_SIZE, data, num_data,
-                                &input_pos);
-        krb5int_c_iov_get_block(blockN1, BLOCK_SIZE, data, num_data,
-                                &input_pos);
+        k5_iov_cursor_get(&cursor, blockN2);
+        k5_iov_cursor_get(&cursor, blockN1);
 
         if (ivec != NULL)
             memcpy(ivec->data, blockN2, BLOCK_SIZE);
@@ -257,10 +228,8 @@ krb5int_camellia_decrypt(krb5_key key, const krb5_data *ivec,
         memcpy(blockN1, tmp3, BLOCK_SIZE);
 
         /* Put the last two blocks back into the iovec */
-        krb5int_c_iov_put_block(data, num_data, blockN1, BLOCK_SIZE,
-                                &output_pos);
-        krb5int_c_iov_put_block(data, num_data, blockN2, BLOCK_SIZE,
-                                &output_pos);
+        k5_iov_cursor_put(&cursor, blockN1);
+        k5_iov_cursor_put(&cursor, blockN2);
     }
 
     return 0;
@@ -272,8 +241,8 @@ krb5int_camellia_cbc_mac(krb5_key key, const krb5_crypto_iov *data,
                          krb5_data *output)
 {
     camellia_ctx ctx;
-    unsigned char blockY[BLOCK_SIZE];
-    struct iov_block_state iov_state;
+    unsigned char blockY[BLOCK_SIZE], blockB[BLOCK_SIZE];
+    struct iov_cursor cursor;
 
     if (output->length < BLOCK_SIZE)
         return KRB5_BAD_MSIZE;
@@ -287,14 +256,8 @@ krb5int_camellia_cbc_mac(krb5_key key, const krb5_crypto_iov *data,
     else
         memset(blockY, 0, BLOCK_SIZE);
 
-    IOV_BLOCK_STATE_INIT(&iov_state);
-    for (;;) {
-        unsigned char blockB[BLOCK_SIZE];
-
-        if (!krb5int_c_iov_get_block(blockB, BLOCK_SIZE, data, num_data,
-                                     &iov_state))
-            break;
-
+    k5_iov_cursor_init(&cursor, data, num_data, BLOCK_SIZE, FALSE);
+    while (k5_iov_cursor_get(&cursor, blockB)) {
         xorblock(blockB, blockY);
         if (camellia_enc_blk(blockB, blockY, &ctx) != camellia_good)
             abort();
diff --git a/src/lib/crypto/builtin/enc_provider/des.c b/src/lib/crypto/builtin/enc_provider/des.c
index 72a3ffc..30b8229 100644
--- a/src/lib/crypto/builtin/enc_provider/des.c
+++ b/src/lib/crypto/builtin/enc_provider/des.c
@@ -33,18 +33,11 @@ validate_and_schedule(krb5_key key, const krb5_data *ivec,
                       const krb5_crypto_iov *data, size_t num_data,
                       mit_des_key_schedule schedule)
 {
-    size_t i, input_length;
-
-    for (i = 0, input_length = 0; i < num_data; i++) {
-        const krb5_crypto_iov *iov = &data[i];
-
-        if (ENCRYPT_IOV(iov))
-            input_length += iov->data.length;
-    }
-
     if (key->keyblock.length != 8)
         return KRB5_BAD_KEYSIZE;
-    if (input_length % 8 != 0 || (ivec != NULL && ivec->length != 8))
+    if (iov_total_length(data, num_data, FALSE) % 8 != 0)
+        return KRB5_BAD_MSIZE;
+    if (ivec != NULL && ivec->length != 8)
         return KRB5_BAD_MSIZE;
 
     switch (mit_des_key_sched(key->keyblock.contents, schedule)) {
diff --git a/src/lib/crypto/builtin/enc_provider/des3.c b/src/lib/crypto/builtin/enc_provider/des3.c
index 793db5e..9b82442 100644
--- a/src/lib/crypto/builtin/enc_provider/des3.c
+++ b/src/lib/crypto/builtin/enc_provider/des3.c
@@ -33,18 +33,9 @@ validate_and_schedule(krb5_key key, const krb5_data *ivec,
                       const krb5_crypto_iov *data, size_t num_data,
                       mit_des3_key_schedule *schedule)
 {
-    size_t i, input_length;
-
-    for (i = 0, input_length = 0; i < num_data; i++) {
-        const krb5_crypto_iov *iov = &data[i];
-
-        if (ENCRYPT_IOV(iov))
-            input_length += iov->data.length;
-    }
-
     if (key->keyblock.length != 24)
         return(KRB5_BAD_KEYSIZE);
-    if ((input_length%8) != 0)
+    if (iov_total_length(data, num_data, FALSE) % 8 != 0)
         return(KRB5_BAD_MSIZE);
     if (ivec && (ivec->length != 8))
         return(KRB5_BAD_MSIZE);
diff --git a/src/lib/crypto/krb/aead.c b/src/lib/crypto/krb/aead.c
index 7bfed4b..935125d 100644
--- a/src/lib/crypto/krb/aead.c
+++ b/src/lib/crypto/krb/aead.c
@@ -135,3 +135,89 @@ krb5int_c_padding_length(const struct krb5_keytypes *ktp, size_t data_length)
     else
         return padding - (data_length % padding);
 }
+
+/* Return the next iov (starting from ind) which cursor should process, or
+ * cursor->iov_count if there are none remaining. */
+static size_t
+next_iov_to_process(struct iov_cursor *cursor, size_t ind)
+{
+    krb5_crypto_iov *iov;
+
+    for (; ind < cursor->iov_count; ind++) {
+        iov = &cursor->iov[ind];
+        if (cursor->signing ? SIGN_IOV(iov) : ENCRYPT_IOV(iov))
+            break;
+    }
+    return ind;
+}
+
+void
+k5_iov_cursor_init(struct iov_cursor *cursor, const krb5_crypto_iov *iov,
+                   size_t count, size_t block_size, krb5_boolean signing)
+{
+    cursor->iov = iov;
+    cursor->iov_count = count;
+    cursor->block_size = block_size;
+    cursor->signing = signing;
+    cursor->in_iov = next_iov_to_process(cursor, 0);
+    cursor->out_iov = cursor->in_iov;
+    cursor->in_pos = cursor->out_pos = 0;
+}
+
+/* Fetch one block from cursor's input position. */
+krb5_boolean
+k5_iov_cursor_get(struct iov_cursor *cursor, unsigned char *block)
+{
+    size_t nbytes, bsz = cursor->block_size, remain = cursor->block_size;
+    const krb5_crypto_iov *iov;
+
+    remain = cursor->block_size;
+    while (remain > 0 && cursor->in_iov < cursor->iov_count) {
+        iov = &cursor->iov[cursor->in_iov];
+
+        nbytes = iov->data.length - cursor->in_pos;
+        if (nbytes > remain)
+            nbytes = remain;
+
+        memcpy(block + bsz - remain, iov->data.data + cursor->in_pos, nbytes);
+        cursor->in_pos += nbytes;
+        remain -= nbytes;
+
+        if (cursor->in_pos == iov->data.length) {
+            cursor->in_iov = next_iov_to_process(cursor, cursor->in_iov + 1);
+            cursor->in_pos = 0;
+        }
+    }
+
+    if (remain == bsz)
+        return FALSE;
+    if (remain > 0)
+        memset(block + bsz - remain, 0, remain);
+    return TRUE;
+}
+
+/* Write a block to a cursor's output position. */
+void
+k5_iov_cursor_put(struct iov_cursor *cursor, unsigned char *block)
+{
+    size_t nbytes, bsz = cursor->block_size, remain = cursor->block_size;
+    const krb5_crypto_iov *iov;
+
+    remain = cursor->block_size;
+    while (remain > 0 && cursor->out_iov < cursor->iov_count) {
+        iov = &cursor->iov[cursor->out_iov];
+
+        nbytes = iov->data.length - cursor->out_pos;
+        if (nbytes > remain)
+            nbytes = remain;
+
+        memcpy(iov->data.data + cursor->out_pos, block + bsz - remain, nbytes);
+        cursor->out_pos += nbytes;
+        remain -= nbytes;
+
+        if (cursor->out_pos == iov->data.length) {
+            cursor->out_iov = next_iov_to_process(cursor, cursor->out_iov + 1);
+            cursor->out_pos = 0;
+        }
+    }
+}
diff --git a/src/lib/crypto/krb/cmac.c b/src/lib/crypto/krb/cmac.c
index 2e220c5..066b534 100644
--- a/src/lib/crypto/krb/cmac.c
+++ b/src/lib/crypto/krb/cmac.c
@@ -145,8 +145,8 @@ krb5int_cmac_checksum(const struct krb5_enc_provider *enc, krb5_key key,
     unsigned char input[BLOCK_SIZE];
     unsigned int n, i, flag;
     krb5_error_code ret;
-    struct iov_block_state iov_state;
-    unsigned int length;
+    struct iov_cursor cursor;
+    size_t length;
     krb5_crypto_iov iov[1];
     krb5_data d;
 
@@ -155,12 +155,7 @@ krb5int_cmac_checksum(const struct krb5_enc_provider *enc, krb5_key key,
     if (enc->block_size != BLOCK_SIZE)
         return KRB5_BAD_MSIZE;
 
-    for (i = 0, length = 0; i < num_data; i++) {
-        const krb5_crypto_iov *piov = &data[i];
-
-        if (SIGN_IOV(piov))
-            length += piov->data.length;
-    }
+    length = iov_total_length(data, num_data, TRUE);
 
     /* Step 1. */
     ret = generate_subkey(enc, key, K1, K2);
@@ -186,10 +181,9 @@ krb5int_cmac_checksum(const struct krb5_enc_provider *enc, krb5_key key,
     d = make_data(Y, BLOCK_SIZE);
 
     /* Step 6 (all but last block). */
-    IOV_BLOCK_STATE_INIT(&iov_state);
-    iov_state.include_sign_only = 1;
+    k5_iov_cursor_init(&cursor, data, num_data, BLOCK_SIZE, TRUE);
     for (i = 0; i < n - 1; i++) {
-        krb5int_c_iov_get_block(input, BLOCK_SIZE, data, num_data, &iov_state);
+        k5_iov_cursor_get(&cursor, input);
 
         ret = enc->cbc_mac(key, iov, 1, &d, &d);
         if (ret != 0)
@@ -197,7 +191,7 @@ krb5int_cmac_checksum(const struct krb5_enc_provider *enc, krb5_key key,
     }
 
     /* Step 4. */
-    krb5int_c_iov_get_block(input, BLOCK_SIZE, data, num_data, &iov_state);
+    k5_iov_cursor_get(&cursor, input);
     if (flag) {
         /* last block is complete block */
         xor_128(input, K1, M_last);
diff --git a/src/lib/crypto/krb/crypto_int.h b/src/lib/crypto/krb/crypto_int.h
index 01090d0..c2c6344 100644
--- a/src/lib/crypto/krb/crypto_int.h
+++ b/src/lib/crypto/krb/crypto_int.h
@@ -381,20 +381,17 @@ void krb5int_default_free_state(krb5_data *state);
 #define SIGN_IOV(_iov)          (ENCRYPT_IOV(_iov) ||                   \
                                  (_iov)->flags == KRB5_CRYPTO_TYPE_SIGN_ONLY )
 
-struct iov_block_state {
-    size_t iov_pos;                     /* index into iov array */
-    size_t data_pos;                    /* index into iov contents */
-    unsigned int ignore_header : 1;     /* have/should we process HEADER */
-    unsigned int include_sign_only : 1; /* should we process SIGN_ONLY blocks */
-    unsigned int pad_to_boundary : 1;   /* should we zero fill blocks until next buffer */
+struct iov_cursor {
+    const krb5_crypto_iov *iov; /* iov array we are iterating over */
+    size_t iov_count;           /* size of iov array */
+    size_t block_size;          /* size of blocks we will be obtaining */
+    krb5_boolean signing;       /* should we process SIGN_ONLY blocks */
+    size_t in_iov;              /* read index into iov array */
+    size_t in_pos;              /* read index into iov contents */
+    size_t out_iov;             /* write index into iov array */
+    size_t out_pos;             /* write index into iov contents */
 };
 
-#define IOV_BLOCK_STATE_INIT(_state)    ((_state)->iov_pos =            \
-                                         (_state)->data_pos =           \
-                                         (_state)->ignore_header =      \
-                                         (_state)->include_sign_only =  \
-                                         (_state)->pad_to_boundary = 0)
-
 krb5_crypto_iov *krb5int_c_locate_iov(krb5_crypto_iov *data, size_t num_data,
                                       krb5_cryptotype type);
 
@@ -408,6 +405,14 @@ krb5_error_code krb5int_c_iov_decrypt_stream(const struct krb5_keytypes *ktp,
 unsigned int krb5int_c_padding_length(const struct krb5_keytypes *ktp,
                                       size_t data_length);
 
+void k5_iov_cursor_init(struct iov_cursor *cursor, const krb5_crypto_iov *iov,
+                        size_t count, size_t block_size, krb5_boolean signing);
+
+krb5_boolean k5_iov_cursor_get(struct iov_cursor *cursor,
+                               unsigned char *block);
+
+void k5_iov_cursor_put(struct iov_cursor *cursor, unsigned char *block);
+
 /*** Crypto module declarations ***/
 
 /* Modules must implement the following enc_providers and hash_providers: */
@@ -580,225 +585,50 @@ encrypt_block(const struct krb5_enc_provider *enc, krb5_key key,
         return enc->encrypt(key, 0, &iov, 1);
 }
 
-/* Decide whether to process an IOV block. */
-static inline int
-process_block_p(const krb5_crypto_iov *data, size_t num_data,
-                struct iov_block_state *iov_state, size_t i)
-{
-    const krb5_crypto_iov *iov = &data[i];
-    int process_block;
-
-    switch (iov->flags) {
-    case KRB5_CRYPTO_TYPE_SIGN_ONLY:
-        process_block = iov_state->include_sign_only;
-        break;
-    case KRB5_CRYPTO_TYPE_PADDING:
-        process_block = (iov_state->pad_to_boundary == 0);
-        break;
-    case KRB5_CRYPTO_TYPE_HEADER:
-        process_block = (iov_state->ignore_header == 0);
-        break;
-    case KRB5_CRYPTO_TYPE_DATA:
-        process_block = 1;
-        break;
-    default:
-        process_block = 0;
-        break;
-    }
-
-    return process_block;
-}
-
-/*
- * Returns TRUE if, having reached the end of the current buffer,
- * we should pad the rest of the block with zeros.
- */
-static inline int
-pad_to_boundary_p(const krb5_crypto_iov *data,
-                  size_t num_data,
-                  struct iov_block_state *iov_state,
-                  size_t i,
-                  size_t j)
-{
-    /* If the pad_to_boundary flag is unset, return FALSE */
-    if (iov_state->pad_to_boundary == 0)
-        return 0;
-
-    /* If we haven't got any data, we need to get some */
-    if (j == 0)
-        return 0;
-
-    /* No boundary between adjacent buffers marked for processing */
-    if (data[iov_state->iov_pos].flags == data[i].flags)
-        return 0;
-
-    return 1;
-}
-
-/*
- * Retrieve a block from the IOV. If p is non-NULL and the next block is
- * completely contained within the current buffer, then *p will contain an
- * alias into the buffer; otherwise, a copy will be made into storage.
- *
- * After calling this function, encrypt the returned block and then call
- * krb5int_c_iov_put_block_nocopy() (with a separate output cursor). If
- * p was non-NULL on the call to get_block(), then pass that pointer in.
- */
-static inline krb5_boolean
-krb5int_c_iov_get_block_nocopy(unsigned char *storage,
-                               size_t block_size,
-                               const krb5_crypto_iov *data,
-                               size_t num_data,
-                               struct iov_block_state *iov_state,
-                               unsigned char **p)
+/* Return the total length of the to-be-signed or to-be-encrypted buffers in an
+ * iov chain. */
+static inline size_t
+iov_total_length(const krb5_crypto_iov *data, size_t num_data,
+                 krb5_boolean signing)
 {
-    size_t i, j = 0;
-
-    if (p != NULL)
-        *p = storage;
-
-    for (i = iov_state->iov_pos; i < num_data; i++) {
-        const krb5_crypto_iov *iov = &data[i];
-        size_t nbytes;
-
-        if (!process_block_p(data, num_data, iov_state, i))
-            continue;
-
-        if (pad_to_boundary_p(data, num_data, iov_state, i, j))
-            break;
-
-        iov_state->iov_pos = i;
-
-        nbytes = iov->data.length - iov_state->data_pos;
-        if (nbytes > block_size - j)
-            nbytes = block_size - j;
-
-        /*
-         * If we can return a pointer into a complete block, then do so.
-         */
-        if (p != NULL && j == 0 && nbytes == block_size) {
-            *p = (unsigned char *)iov->data.data + iov_state->data_pos;
-        } else {
-            memcpy(storage + j, iov->data.data + iov_state->data_pos, nbytes);
-        }
+    size_t i, total = 0;
 
-        iov_state->data_pos += nbytes;
-        j += nbytes;
-
-        assert(j <= block_size);
-
-        if (j == block_size)
-            break;
-
-        assert(iov_state->data_pos == iov->data.length);
-
-        iov_state->data_pos = 0;
+    for (i = 0; i < num_data; i++) {
+        if (signing ? SIGN_IOV(&data[i]) : ENCRYPT_IOV(&data[i]))
+            total += data[i].data.length;
     }
-
-    iov_state->iov_pos = i;
-
-    if (j == 0)
-        return FALSE;
-    else if (j != block_size)
-        memset(storage + j, 0, block_size - j);
-
-    return TRUE;
+    return total;
 }
 
 /*
- * Store a block retrieved with krb5int_c_iov_get_block_no_copy if
- * necessary, and advance the output cursor.
+ * Return the number of contiguous blocks available within the current input
+ * IOV of the cursor c, so that the caller can do in-place encryption.
+ * Do not call if c might be exhausted.
  */
-static inline krb5_boolean
-krb5int_c_iov_put_block_nocopy(const krb5_crypto_iov *data,
-                               size_t num_data,
-                               unsigned char *storage,
-                               size_t block_size,
-                               struct iov_block_state *iov_state,
-                               unsigned char *p)
+static inline size_t
+iov_cursor_contig_blocks(struct iov_cursor *c)
 {
-    size_t i, j = 0;
-
-    assert(p != NULL);
-
-    for (i = iov_state->iov_pos; i < num_data; i++) {
-        const krb5_crypto_iov *iov = &data[i];
-        size_t nbytes;
-
-        if (!process_block_p(data, num_data, iov_state, i))
-            continue;
-
-        if (pad_to_boundary_p(data, num_data, iov_state, i, j))
-            break;
-
-        iov_state->iov_pos = i;
-
-        nbytes = iov->data.length - iov_state->data_pos;
-        if (nbytes > block_size - j)
-            nbytes = block_size - j;
-
-        /*
-         * If we had previously returned a pointer into a complete block,
-         * then no action is required.
-         */
-        if (p == storage) {
-            memcpy(iov->data.data + iov_state->data_pos, storage + j, nbytes);
-        } else {
-            /* Ensure correctly paired with a call to get_block_nocopy(). */
-            assert(j == 0);
-            assert(nbytes == 0 || nbytes == block_size);
-        }
-
-        iov_state->data_pos += nbytes;
-        j += nbytes;
-
-        assert(j <= block_size);
-
-        if (j == block_size)
-            break;
-
-        assert(iov_state->data_pos == iov->data.length);
-
-        iov_state->data_pos = 0;
-    }
-
-    iov_state->iov_pos = i;
-
-#ifdef DEBUG_IOV
-    dump_block("put_block", i, j, p, block_size);
-#endif
-
-    return (iov_state->iov_pos < num_data);
+    return (c->iov[c->in_iov].data.length - c->in_pos) / c->block_size;
 }
 
-/*
- * A wrapper for krb5int_c_iov_get_block_nocopy() that always makes
- * a copy.
- */
-static inline krb5_boolean
-krb5int_c_iov_get_block(unsigned char *block,
-                        size_t block_size,
-                        const krb5_crypto_iov *data,
-                        size_t num_data,
-                        struct iov_block_state *iov_state)
+/* Return the current input pointer within the cursor c.  Do not call if c
+ * might be exhausted. */
+static inline unsigned char *
+iov_cursor_ptr(struct iov_cursor *c)
 {
-    return krb5int_c_iov_get_block_nocopy(block, block_size, data, num_data,
-                                          iov_state, NULL);
+    return (unsigned char *)&c->iov[c->in_iov].data.data[c->in_pos];
 }
 
 /*
- * A wrapper for krb5int_c_iov_put_block_nocopy() that always copies
- * the block.
+ * Advance the input and output pointers of c by nblocks blocks.  nblocks must
+ * not be greater than the return value of iov_cursor_contig_blocks, and the
+ * input and output positions must be identical.
  */
-static inline krb5_boolean
-krb5int_c_iov_put_block(const krb5_crypto_iov *data,
-                        size_t num_data,
-                        unsigned char *block,
-                        size_t block_size,
-                        struct iov_block_state *iov_state)
+static inline void
+iov_cursor_advance(struct iov_cursor *c, size_t nblocks)
 {
-    return krb5int_c_iov_put_block_nocopy(data, num_data, block, block_size,
-                                          iov_state, block);
+    c->in_pos += nblocks * c->block_size;
+    c->out_pos += nblocks * c->block_size;
 }
 
 #endif /* CRYPTO_INT_H */
diff --git a/src/lib/crypto/krb/enc_old.c b/src/lib/crypto/krb/enc_old.c
index b44a399..a40f709 100644
--- a/src/lib/crypto/krb/enc_old.c
+++ b/src/lib/crypto/krb/enc_old.c
@@ -130,17 +130,9 @@ krb5int_old_decrypt(const struct krb5_keytypes *ktp, krb5_key key,
     krb5_crypto_iov *header, *trailer;
     krb5_data checksum, crcivec = empty_data();
     char *saved_checksum = NULL;
-    size_t i;
-    unsigned int cipherlen = 0;
 
     /* Check that the input data is correctly padded. */
-    for (i = 0; i < num_data; i++) {
-        const krb5_crypto_iov *iov = &data[i];
-
-        if (ENCRYPT_IOV(iov))
-            cipherlen += iov->data.length;
-    }
-    if (cipherlen % enc->block_size != 0)
+    if (iov_total_length(data, num_data, FALSE) % enc->block_size != 0)
         return KRB5_BAD_MSIZE;
 
     header = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_HEADER);
diff --git a/src/lib/crypto/nss/enc_provider/enc_gen.c b/src/lib/crypto/nss/enc_provider/enc_gen.c
index dea22f8..7022a78 100644
--- a/src/lib/crypto/nss/enc_provider/enc_gen.c
+++ b/src/lib/crypto/nss/enc_provider/enc_gen.c
@@ -172,35 +172,25 @@ k5_nss_gen_block_iov(krb5_key krb_key, CK_MECHANISM_TYPE mech,
     PK11Context *ctx = NULL;
     SECStatus rv;
     SECItem *param = NULL;
-    struct iov_block_state input_pos, output_pos;
-    unsigned char storage[MAX_BLOCK_SIZE];
+    struct iov_cursor cursor;
+    unsigned char block[MAX_BLOCK_SIZE];
     unsigned char iv0[MAX_BLOCK_SIZE];
-    unsigned char *ptr = NULL,*lastptr = NULL;
+    unsigned char *lastptr = NULL;
     SECItem iv;
     size_t blocksize;
     int length = 0;
     int lastblock = -1;
     int currentblock;
 
-    IOV_BLOCK_STATE_INIT(&input_pos);
-    IOV_BLOCK_STATE_INIT(&output_pos);
-
     blocksize = PK11_GetBlockSize(mech, NULL);
-    assert(blocksize <= sizeof(storage));
+    assert(blocksize <= sizeof(block));
 
     if (ivec && ivec->data) {
         iv.data = (unsigned char *)ivec->data;
         iv.len = ivec->length;
         if (operation == CKA_DECRYPT) {
-            int i, inputlength;
-
             /* Count the blocks so we know which block is last. */
-            for (i = 0, inputlength = 0; i < (int)num_data; i++) {
-                krb5_crypto_iov *iov = &data[i];
-
-                if (ENCRYPT_IOV(iov))
-                    inputlength += iov->data.length;
-            }
+            int inputlength = iov_total_length(data, num_data, FALSE);
             lastblock = (inputlength/blocksize) -1;
         }
     } else {
@@ -216,26 +206,25 @@ k5_nss_gen_block_iov(krb5_key krb_key, CK_MECHANISM_TYPE mech,
         goto done;
     }
 
+    k5_iov_cursor_init(&cursor, data, num_data, blocksize, FALSE);
     for (currentblock = 0;;currentblock++) {
-        if (!krb5int_c_iov_get_block_nocopy(storage, blocksize, data, num_data,
-                                            &input_pos, &ptr))
+        if (!k5_iov_cursor_get(&cursor, block))
             break;
 
         lastptr = NULL;
 
         /* only set if we are decrypting */
         if (lastblock == currentblock)
-            memcpy(ivec->data, ptr, blocksize);
+            memcpy(ivec->data, block, blocksize);
 
-        rv = PK11_CipherOp(ctx, ptr, &length, blocksize, ptr, blocksize);
+        rv = PK11_CipherOp(ctx, block, &length, blocksize, block, blocksize);
         if (rv != SECSuccess) {
             ret = k5_nss_map_last_error();
             break;
         }
 
-        lastptr = ptr;
-        krb5int_c_iov_put_block_nocopy(data, num_data, storage, blocksize,
-                                       &output_pos, ptr);
+        lastptr = block;
+        k5_iov_cursor_put(&cursor, block);
     }
 
     if (lastptr && ivec && ivec->data && operation == CKA_ENCRYPT) {
@@ -349,26 +338,22 @@ k5_nss_gen_cts_iov(krb5_key krb_key, CK_MECHANISM_TYPE mech,
     PK11Context *ctx = NULL;
     SECStatus rv;
     SECItem *param = NULL;
-    struct iov_block_state input_pos, output_pos;
-    unsigned char storage[MAX_BLOCK_SIZE];
+    struct iov_cursor cursor;
+    unsigned char block[MAX_BLOCK_SIZE];
     unsigned char recover1[MAX_BLOCK_SIZE];
     unsigned char recover2[MAX_BLOCK_SIZE];
     unsigned char block1[MAX_BLOCK_SIZE];
     unsigned char block2[MAX_BLOCK_SIZE];
     unsigned char iv0[MAX_BLOCK_SIZE];
-    unsigned char *ptr = NULL;
     SECItem iv;
     size_t blocksize;
     size_t bulk_length, remainder;
     size_t input_length, lastblock;
     size_t length;
-    int i, len;
-
-    IOV_BLOCK_STATE_INIT(&input_pos);
-    IOV_BLOCK_STATE_INIT(&output_pos);
+    int len;
 
     blocksize = PK11_GetBlockSize(mech, NULL);
-    assert(blocksize <= sizeof(storage));
+    assert(blocksize <= sizeof(block));
 
     if (ivec) {
         iv.data = (unsigned char *)ivec->data;
@@ -380,12 +365,7 @@ k5_nss_gen_cts_iov(krb5_key krb_key, CK_MECHANISM_TYPE mech,
     }
     param = PK11_ParamFromIV(mech, &iv);
 
-    for (i = 0, input_length = 0; i < (int)num_data; i++) {
-        krb5_crypto_iov *iov = &data[i];
-
-        if (ENCRYPT_IOV(iov))
-            input_length += iov->data.length;
-    }
+    input_length = iov_total_length(data, num_data, FALSE);
     /* Must be at least a block or we fail. */
     if (input_length < blocksize) {
         ret = EINVAL;
@@ -429,48 +409,44 @@ k5_nss_gen_cts_iov(krb5_key krb_key, CK_MECHANISM_TYPE mech,
             }
         }
     }
+    k5_iov_cursor_init(&cursor, data, num_data, blocksize, FALSE);
     for (length = 0; length < lastblock; length += blocksize) {
-        if (!krb5int_c_iov_get_block_nocopy(storage, blocksize, data, num_data,
-                                            &input_pos, &ptr))
+        if (!k5_iov_cursor_get(&cursor, block))
             break;
 
-        rv = PK11_CipherOp(ctx, ptr, &len, blocksize, ptr, blocksize);
+        rv = PK11_CipherOp(ctx, block, &len, blocksize, block, blocksize);
         if (rv != SECSuccess) {
             ret = k5_nss_map_last_error();
             break;
         }
 
-        krb5int_c_iov_put_block_nocopy(data, num_data, storage, blocksize,
-                                       &output_pos, ptr);
+        k5_iov_cursor_put(&cursor, block);
     }
     if (remainder) {
         if (operation == CKA_DECRYPT) {
             if (bulk_length > blocksize) {
                 /* we need to save cn-2 */
-                if (!krb5int_c_iov_get_block_nocopy(storage, blocksize, data,
-                                                    num_data, &input_pos,
-                                                    &ptr))
+                if (!k5_iov_cursor_get(&cursor, block))
                     goto done; /* shouldn't happen */
 
                 /* save cn-2 */
-                memcpy(recover1, ptr, blocksize);
-                memcpy(recover2, ptr, blocksize);
+                memcpy(recover1, block, blocksize);
+                memcpy(recover2, block, blocksize);
 
                 /* now process it as normal */
-                rv = PK11_CipherOp(ctx, ptr, &len, blocksize, ptr, blocksize);
+                rv = PK11_CipherOp(ctx, block, &len, blocksize, block,
+                                   blocksize);
                 if (rv != SECSuccess) {
                     ret = k5_nss_map_last_error();
                     goto done;
                 }
 
-                krb5int_c_iov_put_block_nocopy(data, num_data, storage,
-                                               blocksize, &output_pos, ptr);
+                k5_iov_cursor_put(&cursor, block);
             }
         }
         /* fetch the last 2 blocks */
-        memset(block1, 0, blocksize); /* last block, could be partial */
-        krb5int_c_iov_get_block(block2, blocksize, data, num_data, &input_pos);
-        krb5int_c_iov_get_block(block1, remainder, data, num_data, &input_pos);
+        k5_iov_cursor_get(&cursor, block2);
+        k5_iov_cursor_get(&cursor, block1);
         if (operation == CKA_DECRYPT) {
             /* recover1 and recover2 are xor values to recover the true
              * underlying data of the last 2 decrypts. This keeps us from
@@ -518,8 +494,8 @@ k5_nss_gen_cts_iov(krb5_key krb_key, CK_MECHANISM_TYPE mech,
                 memcpy(ivec->data, block1, blocksize);
             }
         }
-        krb5int_c_iov_put_block(data,num_data, block1, blocksize, &output_pos);
-        krb5int_c_iov_put_block(data,num_data, block2, remainder, &output_pos);
+        k5_iov_cursor_put(&cursor, block1);
+        k5_iov_cursor_put(&cursor, block2);
     }
 
 done:
@@ -541,7 +517,7 @@ k5_nss_gen_cbcmac_iov(krb5_key krb_key, CK_MECHANISM_TYPE mech,
     PK11Context *ctx = NULL;
     SECStatus rv;
     SECItem *param = NULL;
-    struct iov_block_state input_pos, output_pos;
+    struct iov_cursor cursor;
     unsigned char block[MAX_BLOCK_SIZE], *lastblock;
     unsigned char iv0[MAX_BLOCK_SIZE];
     SECItem iv;
@@ -549,9 +525,6 @@ k5_nss_gen_cbcmac_iov(krb5_key krb_key, CK_MECHANISM_TYPE mech,
     int length = 0;
     int currentblock;
 
-    IOV_BLOCK_STATE_INIT(&input_pos);
-    IOV_BLOCK_STATE_INIT(&output_pos);
-
     blocksize = PK11_GetBlockSize(mech, NULL);
     assert(blocksize <= sizeof(block));
     if (output->length < blocksize)
@@ -574,9 +547,9 @@ k5_nss_gen_cbcmac_iov(krb5_key krb_key, CK_MECHANISM_TYPE mech,
     }
 
     lastblock = iv.data;
+    k5_iov_cursor_init(&cursor, data, num_data, blocksize, FALSE);
     for (currentblock = 0;;currentblock++) {
-        if (!krb5int_c_iov_get_block(block, blocksize, data, num_data,
-                                     &input_pos))
+        if (!k5_iov_cursor_get(&cursor, block))
             break;
         rv = PK11_CipherOp(ctx, block, &length, blocksize, block, blocksize);
         if (rv != SECSuccess) {
diff --git a/src/lib/crypto/openssl/enc_provider/aes.c b/src/lib/crypto/openssl/enc_provider/aes.c
index ced93f7..8afa1a7 100644
--- a/src/lib/crypto/openssl/enc_provider/aes.c
+++ b/src/lib/crypto/openssl/enc_provider/aes.c
@@ -66,7 +66,7 @@ cbc_enc(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data,
     int             ret, olen = BLOCK_SIZE;
     unsigned char   iblock[BLOCK_SIZE], oblock[BLOCK_SIZE];
     EVP_CIPHER_CTX  ciph_ctx;
-    struct iov_block_state input_pos, output_pos;
+    struct iov_cursor cursor;
 
     EVP_CIPHER_CTX_init(&ciph_ctx);
     ret = EVP_EncryptInit_ex(&ciph_ctx, map_mode(key->keyblock.length),
@@ -74,15 +74,12 @@ cbc_enc(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data,
     if (ret == 0)
         return KRB5_CRYPTO_INTERNAL;
 
-    IOV_BLOCK_STATE_INIT(&input_pos);
-    IOV_BLOCK_STATE_INIT(&output_pos);
-    krb5int_c_iov_get_block(iblock, BLOCK_SIZE, data, num_data, &input_pos);
+    k5_iov_cursor_init(&cursor, data, num_data, BLOCK_SIZE, FALSE);
+    k5_iov_cursor_get(&cursor, iblock);
     EVP_CIPHER_CTX_set_padding(&ciph_ctx,0);
     ret = EVP_EncryptUpdate(&ciph_ctx, oblock, &olen, iblock, BLOCK_SIZE);
-    if (ret == 1) {
-        krb5int_c_iov_put_block(data, num_data, oblock, BLOCK_SIZE,
-                                &output_pos);
-    }
+    if (ret == 1)
+        k5_iov_cursor_put(&cursor, oblock);
     EVP_CIPHER_CTX_cleanup(&ciph_ctx);
 
     zap(iblock, BLOCK_SIZE);
@@ -98,7 +95,7 @@ cbc_decr(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data,
     int              ret = 0, olen = BLOCK_SIZE;
     unsigned char    iblock[BLOCK_SIZE], oblock[BLOCK_SIZE];
     EVP_CIPHER_CTX   ciph_ctx;
-    struct iov_block_state input_pos, output_pos;
+    struct iov_cursor cursor;
 
     EVP_CIPHER_CTX_init(&ciph_ctx);
     ret = EVP_DecryptInit_ex(&ciph_ctx, map_mode(key->keyblock.length),
@@ -106,15 +103,12 @@ cbc_decr(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data,
     if (ret == 0)
         return KRB5_CRYPTO_INTERNAL;
 
-    IOV_BLOCK_STATE_INIT(&input_pos);
-    IOV_BLOCK_STATE_INIT(&output_pos);
-    krb5int_c_iov_get_block(iblock, BLOCK_SIZE, data, num_data, &input_pos);
+    k5_iov_cursor_init(&cursor, data, num_data, BLOCK_SIZE, FALSE);
+    k5_iov_cursor_get(&cursor, iblock);
     EVP_CIPHER_CTX_set_padding(&ciph_ctx,0);
     ret = EVP_DecryptUpdate(&ciph_ctx, oblock, &olen, iblock, BLOCK_SIZE);
-    if (ret == 1) {
-        krb5int_c_iov_put_block(data, num_data, oblock, BLOCK_SIZE,
-                                &output_pos);
-    }
+    if (ret == 1)
+        k5_iov_cursor_put(&cursor, oblock);
     EVP_CIPHER_CTX_cleanup(&ciph_ctx);
 
     zap(iblock, BLOCK_SIZE);
@@ -130,7 +124,7 @@ cts_encr(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data,
     size_t                 size = 0;
     unsigned char         *oblock = NULL, *dbuf = NULL;
     unsigned char          iv_cts[IV_CTS_BUF_SIZE];
-    struct iov_block_state input_pos, output_pos;
+    struct iov_cursor      cursor;
     AES_KEY                enck;
 
     memset(iv_cts,0,sizeof(iv_cts));
@@ -150,22 +144,18 @@ cts_encr(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data,
         return ENOMEM;
     }
 
-    IOV_BLOCK_STATE_INIT(&input_pos);
-    IOV_BLOCK_STATE_INIT(&output_pos);
-
-    krb5int_c_iov_get_block(dbuf, dlen, data, num_data, &input_pos);
+    k5_iov_cursor_init(&cursor, data, num_data, dlen, FALSE);
+    k5_iov_cursor_get(&cursor, dbuf);
 
     AES_set_encrypt_key(key->keyblock.contents,
                         NUM_BITS * key->keyblock.length, &enck);
 
     size = CRYPTO_cts128_encrypt((unsigned char *)dbuf, oblock, dlen, &enck,
                                  iv_cts, (cbc128_f)AES_cbc_encrypt);
-    if (size <= 0) {
+    if (size <= 0)
         ret = KRB5_CRYPTO_INTERNAL;
-    } else {
-        krb5int_c_iov_put_block(data, num_data,
-                                oblock, dlen, &output_pos);
-    }
+    else
+        k5_iov_cursor_put(&cursor, oblock);
 
     if (!ret && ivec && ivec->data)
         memcpy(ivec->data, iv_cts, sizeof(iv_cts));
@@ -187,7 +177,7 @@ cts_decr(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data,
     unsigned char         *oblock = NULL;
     unsigned char         *dbuf = NULL;
     unsigned char          iv_cts[IV_CTS_BUF_SIZE];
-    struct iov_block_state input_pos, output_pos;
+    struct iov_cursor      cursor;
     AES_KEY                deck;
 
     memset(iv_cts,0,sizeof(iv_cts));
@@ -197,9 +187,6 @@ cts_decr(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data,
         memcpy(iv_cts, ivec->data,ivec->length);
     }
 
-    IOV_BLOCK_STATE_INIT(&input_pos);
-    IOV_BLOCK_STATE_INIT(&output_pos);
-
     oblock = OPENSSL_malloc(dlen);
     if (!oblock)
         return ENOMEM;
@@ -212,16 +199,16 @@ cts_decr(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data,
     AES_set_decrypt_key(key->keyblock.contents,
                         NUM_BITS * key->keyblock.length, &deck);
 
-    krb5int_c_iov_get_block(dbuf, dlen, data, num_data, &input_pos);
+    k5_iov_cursor_init(&cursor, data, num_data, dlen, FALSE);
+    k5_iov_cursor_get(&cursor, dbuf);
 
     size = CRYPTO_cts128_decrypt((unsigned char *)dbuf, oblock,
                                  dlen, &deck,
                                  iv_cts, (cbc128_f)AES_cbc_encrypt);
     if (size <= 0)
         ret = KRB5_CRYPTO_INTERNAL;
-    else {
-        krb5int_c_iov_put_block(data, num_data, oblock, dlen, &output_pos);
-    }
+    else
+        k5_iov_cursor_put(&cursor, oblock);
 
     if (!ret && ivec && ivec->data)
         memcpy(ivec->data, iv_cts, sizeof(iv_cts));
@@ -239,16 +226,9 @@ krb5int_aes_encrypt(krb5_key key, const krb5_data *ivec,
                     krb5_crypto_iov *data, size_t num_data)
 {
     int    ret = 0;
-    int    nblocks = 0;
-    size_t input_length, i;
-
-    for (i = 0, input_length = 0; i < num_data; i++){
-        krb5_crypto_iov *iov = &data[i];
-
-        if (ENCRYPT_IOV(iov))
-            input_length += iov->data.length;
-    }
+    size_t input_length, nblocks;
 
+    input_length = iov_total_length(data, num_data, FALSE);
     nblocks = (input_length + BLOCK_SIZE - 1) / BLOCK_SIZE;
     if (nblocks == 1) {
         if (input_length != BLOCK_SIZE)
@@ -266,16 +246,9 @@ krb5int_aes_decrypt(krb5_key key, const krb5_data *ivec,
                     krb5_crypto_iov *data, size_t num_data)
 {
     int    ret = 0;
-    int    nblocks = 0;
-    size_t input_length, i;
-
-    for (i = 0, input_length = 0; i < num_data; i++) {
-        krb5_crypto_iov *iov = &data[i];
-
-        if (ENCRYPT_IOV(iov))
-            input_length += iov->data.length;
-    }
+    size_t input_length, nblocks;
 
+    input_length = iov_total_length(data, num_data, FALSE);
     nblocks = (input_length + BLOCK_SIZE - 1) / BLOCK_SIZE;
     if (nblocks == 1) {
         if (input_length != BLOCK_SIZE)
diff --git a/src/lib/crypto/openssl/enc_provider/camellia.c b/src/lib/crypto/openssl/enc_provider/camellia.c
index 2173db6..3ac3fd9 100644
--- a/src/lib/crypto/openssl/enc_provider/camellia.c
+++ b/src/lib/crypto/openssl/enc_provider/camellia.c
@@ -90,7 +90,7 @@ cbc_enc(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data,
     int             ret, olen = BLOCK_SIZE;
     unsigned char   iblock[BLOCK_SIZE], oblock[BLOCK_SIZE];
     EVP_CIPHER_CTX  ciph_ctx;
-    struct iov_block_state input_pos, output_pos;
+    struct iov_cursor cursor;
 
     EVP_CIPHER_CTX_init(&ciph_ctx);
     ret = EVP_EncryptInit_ex(&ciph_ctx, map_mode(key->keyblock.length),
@@ -98,15 +98,12 @@ cbc_enc(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data,
     if (ret == 0)
         return KRB5_CRYPTO_INTERNAL;
 
-    IOV_BLOCK_STATE_INIT(&input_pos);
-    IOV_BLOCK_STATE_INIT(&output_pos);
-    krb5int_c_iov_get_block(iblock, BLOCK_SIZE, data, num_data, &input_pos);
+    k5_iov_cursor_init(&cursor, data, num_data, BLOCK_SIZE, FALSE);
+    k5_iov_cursor_get(&cursor, iblock);
     EVP_CIPHER_CTX_set_padding(&ciph_ctx,0);
     ret = EVP_EncryptUpdate(&ciph_ctx, oblock, &olen, iblock, BLOCK_SIZE);
-    if (ret == 1) {
-        krb5int_c_iov_put_block(data, num_data, oblock, BLOCK_SIZE,
-                                &output_pos);
-    }
+    if (ret == 1)
+        k5_iov_cursor_put(&cursor, oblock);
     EVP_CIPHER_CTX_cleanup(&ciph_ctx);
 
     zap(iblock, BLOCK_SIZE);
@@ -122,7 +119,7 @@ cbc_decr(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data,
     int              ret = 0, olen = BLOCK_SIZE;
     unsigned char    iblock[BLOCK_SIZE], oblock[BLOCK_SIZE];
     EVP_CIPHER_CTX   ciph_ctx;
-    struct iov_block_state input_pos, output_pos;
+    struct iov_cursor cursor;
 
     EVP_CIPHER_CTX_init(&ciph_ctx);
     ret = EVP_DecryptInit_ex(&ciph_ctx, map_mode(key->keyblock.length),
@@ -130,15 +127,12 @@ cbc_decr(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data,
     if (ret == 0)
         return KRB5_CRYPTO_INTERNAL;
 
-    IOV_BLOCK_STATE_INIT(&input_pos);
-    IOV_BLOCK_STATE_INIT(&output_pos);
-    krb5int_c_iov_get_block(iblock, BLOCK_SIZE, data, num_data, &input_pos);
+    k5_iov_cursor_init(&cursor, data, num_data, BLOCK_SIZE, FALSE);
+    k5_iov_cursor_get(&cursor, iblock);
     EVP_CIPHER_CTX_set_padding(&ciph_ctx,0);
     ret = EVP_DecryptUpdate(&ciph_ctx, oblock, &olen, iblock, BLOCK_SIZE);
-    if (ret == 1) {
-        krb5int_c_iov_put_block(data, num_data, oblock, BLOCK_SIZE,
-                                &output_pos);
-    }
+    if (ret == 1)
+        k5_iov_cursor_put(&cursor, oblock);
     EVP_CIPHER_CTX_cleanup(&ciph_ctx);
 
     zap(iblock, BLOCK_SIZE);
@@ -154,7 +148,7 @@ cts_encr(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data,
     size_t                 size = 0;
     unsigned char         *oblock = NULL, *dbuf = NULL;
     unsigned char          iv_cts[IV_CTS_BUF_SIZE];
-    struct iov_block_state input_pos, output_pos;
+    struct iov_cursor      cursor;
     CAMELLIA_KEY           enck;
 
     memset(iv_cts,0,sizeof(iv_cts));
@@ -174,22 +168,18 @@ cts_encr(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data,
         return ENOMEM;
     }
 
-    IOV_BLOCK_STATE_INIT(&input_pos);
-    IOV_BLOCK_STATE_INIT(&output_pos);
-
-    krb5int_c_iov_get_block(dbuf, dlen, data, num_data, &input_pos);
+    k5_iov_cursor_init(&cursor, data, num_data, dlen, FALSE);
+    k5_iov_cursor_get(&cursor, dbuf);
 
     Camellia_set_key(key->keyblock.contents, NUM_BITS * key->keyblock.length,
                      &enck);
 
     size = CRYPTO_cts128_encrypt((unsigned char *)dbuf, oblock, dlen, &enck,
                                  iv_cts, (cbc128_f)Camellia_cbc_encrypt);
-    if (size <= 0) {
+    if (size <= 0)
         ret = KRB5_CRYPTO_INTERNAL;
-    } else {
-        krb5int_c_iov_put_block(data, num_data,
-                                oblock, dlen, &output_pos);
-    }
+    else
+        k5_iov_cursor_put(&cursor, oblock);
 
     if (!ret && ivec && ivec->data)
         memcpy(ivec->data, iv_cts, sizeof(iv_cts));
@@ -211,7 +201,7 @@ cts_decr(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data,
     unsigned char         *oblock = NULL;
     unsigned char         *dbuf = NULL;
     unsigned char          iv_cts[IV_CTS_BUF_SIZE];
-    struct iov_block_state input_pos, output_pos;
+    struct iov_cursor      cursor;
     CAMELLIA_KEY           deck;
 
     memset(iv_cts,0,sizeof(iv_cts));
@@ -221,9 +211,6 @@ cts_decr(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data,
         memcpy(iv_cts, ivec->data,ivec->length);
     }
 
-    IOV_BLOCK_STATE_INIT(&input_pos);
-    IOV_BLOCK_STATE_INIT(&output_pos);
-
     oblock = OPENSSL_malloc(dlen);
     if (!oblock)
         return ENOMEM;
@@ -236,16 +223,16 @@ cts_decr(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data,
     Camellia_set_key(key->keyblock.contents, NUM_BITS * key->keyblock.length,
                      &deck);
 
-    krb5int_c_iov_get_block(dbuf, dlen, data, num_data, &input_pos);
+    k5_iov_cursor_init(&cursor, data, num_data, dlen, FALSE);
+    k5_iov_cursor_get(&cursor, dbuf);
 
     size = CRYPTO_cts128_decrypt((unsigned char *)dbuf, oblock,
                                  dlen, &deck,
                                  iv_cts, (cbc128_f)Camellia_cbc_encrypt);
     if (size <= 0)
         ret = KRB5_CRYPTO_INTERNAL;
-    else {
-        krb5int_c_iov_put_block(data, num_data, oblock, dlen, &output_pos);
-    }
+    else
+        k5_iov_cursor_put(&cursor, oblock);
 
     if (!ret && ivec && ivec->data)
         memcpy(ivec->data, iv_cts, sizeof(iv_cts));
@@ -263,16 +250,9 @@ krb5int_camellia_encrypt(krb5_key key, const krb5_data *ivec,
                          krb5_crypto_iov *data, size_t num_data)
 {
     int    ret = 0;
-    int    nblocks = 0;
-    size_t input_length, i;
-
-    for (i = 0, input_length = 0; i < num_data; i++){
-        krb5_crypto_iov *iov = &data[i];
-
-        if (ENCRYPT_IOV(iov))
-            input_length += iov->data.length;
-    }
+    size_t input_length, nblocks;
 
+    input_length = iov_total_length(data, num_data, FALSE);
     nblocks = (input_length + BLOCK_SIZE - 1) / BLOCK_SIZE;
     if (nblocks == 1) {
         if (input_length != BLOCK_SIZE)
@@ -290,16 +270,9 @@ krb5int_camellia_decrypt(krb5_key key, const krb5_data *ivec,
                          krb5_crypto_iov *data, size_t num_data)
 {
     int    ret = 0;
-    int    nblocks = 0;
-    size_t input_length, i;
-
-    for (i = 0, input_length = 0; i < num_data; i++) {
-        krb5_crypto_iov *iov = &data[i];
-
-        if (ENCRYPT_IOV(iov))
-            input_length += iov->data.length;
-    }
+    size_t input_length, nblocks;
 
+    input_length = iov_total_length(data, num_data, FALSE);
     nblocks = (input_length + BLOCK_SIZE - 1) / BLOCK_SIZE;
     if (nblocks == 1) {
         if (input_length != BLOCK_SIZE)
@@ -318,8 +291,8 @@ krb5int_camellia_cbc_mac(krb5_key key, const krb5_crypto_iov *data,
                          krb5_data *output)
 {
     CAMELLIA_KEY enck;
-    unsigned char blockY[CAMELLIA_BLOCK_SIZE];
-    struct iov_block_state iov_state;
+    unsigned char blockY[CAMELLIA_BLOCK_SIZE], blockB[CAMELLIA_BLOCK_SIZE];
+    struct iov_cursor cursor;
 
     if (output->length < CAMELLIA_BLOCK_SIZE)
         return KRB5_BAD_MSIZE;
@@ -332,17 +305,9 @@ krb5int_camellia_cbc_mac(krb5_key key, const krb5_crypto_iov *data,
     else
         memset(blockY, 0, CAMELLIA_BLOCK_SIZE);
 
-    IOV_BLOCK_STATE_INIT(&iov_state);
-
-    for (;;) {
-        unsigned char blockB[CAMELLIA_BLOCK_SIZE];
-
-        if (!krb5int_c_iov_get_block(blockB, CAMELLIA_BLOCK_SIZE, data,
-                                     num_data, &iov_state))
-            break;
-
+    k5_iov_cursor_init(&cursor, data, num_data, CAMELLIA_BLOCK_SIZE, FALSE);
+    while (k5_iov_cursor_get(&cursor, blockB)) {
         xorblock(blockB, blockY);
-
         Camellia_ecb_encrypt(blockB, blockY, &enck, 1);
     }
 
diff --git a/src/lib/crypto/openssl/enc_provider/des.c b/src/lib/crypto/openssl/enc_provider/des.c
index 644e266..1db5ef0 100644
--- a/src/lib/crypto/openssl/enc_provider/des.c
+++ b/src/lib/crypto/openssl/enc_provider/des.c
@@ -62,13 +62,7 @@ static krb5_error_code
 validate(krb5_key key, const krb5_data *ivec, const krb5_crypto_iov *data,
          size_t num_data, krb5_boolean *empty)
 {
-    size_t i, input_length;
-
-    for (i = 0, input_length = 0; i < num_data; i++) {
-        const krb5_crypto_iov *iov = &data[i];
-        if (ENCRYPT_IOV(iov))
-            input_length += iov->data.length;
-    }
+    size_t input_length = iov_total_length(data, num_data, FALSE);
 
     if (key->keyblock.length != DES_KEY_SIZE)
         return(KRB5_BAD_KEYSIZE);
@@ -87,13 +81,10 @@ k5_des_encrypt(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data,
 {
     int ret, olen = DES_BLOCK_SIZE;
     unsigned char iblock[DES_BLOCK_SIZE], oblock[DES_BLOCK_SIZE];
-    struct iov_block_state input_pos, output_pos;
+    struct iov_cursor cursor;
     EVP_CIPHER_CTX ciph_ctx;
     krb5_boolean empty;
 
-    IOV_BLOCK_STATE_INIT(&input_pos);
-    IOV_BLOCK_STATE_INIT(&output_pos);
-
     ret = validate(key, ivec, data, num_data, &empty);
     if (ret != 0 || empty)
         return ret;
@@ -107,19 +98,13 @@ k5_des_encrypt(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data,
 
     EVP_CIPHER_CTX_set_padding(&ciph_ctx,0);
 
-    for (;;) {
-
-        if (!krb5int_c_iov_get_block(iblock, DES_BLOCK_SIZE, data,
-                                     num_data, &input_pos))
-            break;
-
+    k5_iov_cursor_init(&cursor, data, num_data, DES_BLOCK_SIZE, FALSE);
+    while (k5_iov_cursor_get(&cursor, iblock)) {
         ret = EVP_EncryptUpdate(&ciph_ctx, oblock, &olen,
                                 (unsigned char *)iblock, DES_BLOCK_SIZE);
         if (!ret)
             break;
-
-        krb5int_c_iov_put_block(data, num_data, oblock, DES_BLOCK_SIZE,
-                                &output_pos);
+        k5_iov_cursor_put(&cursor, oblock);
     }
 
     if (ivec != NULL)
@@ -141,13 +126,10 @@ k5_des_decrypt(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data,
 {
     int ret, olen = DES_BLOCK_SIZE;
     unsigned char iblock[DES_BLOCK_SIZE], oblock[DES_BLOCK_SIZE];
-    struct iov_block_state input_pos, output_pos;
+    struct iov_cursor cursor;
     EVP_CIPHER_CTX ciph_ctx;
     krb5_boolean empty;
 
-    IOV_BLOCK_STATE_INIT(&input_pos);
-    IOV_BLOCK_STATE_INIT(&output_pos);
-
     ret = validate(key, ivec, data, num_data, &empty);
     if (ret != 0 || empty)
         return ret;
@@ -162,18 +144,13 @@ k5_des_decrypt(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data,
 
     EVP_CIPHER_CTX_set_padding(&ciph_ctx,0);
 
-    for (;;) {
-
-        if (!krb5int_c_iov_get_block(iblock, DES_BLOCK_SIZE,
-                                     data, num_data, &input_pos))
-            break;
-
+    k5_iov_cursor_init(&cursor, data, num_data, DES_BLOCK_SIZE, FALSE);
+    while (k5_iov_cursor_get(&cursor, iblock)) {
         ret = EVP_DecryptUpdate(&ciph_ctx, oblock, &olen,
                                 iblock, DES_BLOCK_SIZE);
-        if (!ret) break;
-
-        krb5int_c_iov_put_block(data, num_data, oblock,
-                                DES_BLOCK_SIZE, &output_pos);
+        if (!ret)
+            break;
+        k5_iov_cursor_put(&cursor, oblock);
     }
 
     if (ivec != NULL)
@@ -194,7 +171,7 @@ k5_des_cbc_mac(krb5_key key, const krb5_crypto_iov *data, size_t num_data,
                const krb5_data *ivec, krb5_data *output)
 {
     int ret;
-    struct iov_block_state iov_state;
+    struct iov_cursor cursor;
     DES_cblock blockY, blockB;
     DES_key_schedule sched;
     krb5_boolean empty;
@@ -214,11 +191,8 @@ k5_des_cbc_mac(krb5_key key, const krb5_crypto_iov *data, size_t num_data,
     else
         memset(blockY, 0, DES_BLOCK_SIZE);
 
-    IOV_BLOCK_STATE_INIT(&iov_state);
-    for (;;) {
-        if (!krb5int_c_iov_get_block(blockB, DES_BLOCK_SIZE, data, num_data,
-                                     &iov_state))
-            break;
+    k5_iov_cursor_init(&cursor, data, num_data, DES_BLOCK_SIZE, FALSE);
+    while (k5_iov_cursor_get(&cursor, blockB)) {
         store_64_n(load_64_n(blockB) ^ load_64_n(blockY), blockB);
         DES_ecb_encrypt(&blockB, &blockY, &sched, 1);
     }
diff --git a/src/lib/crypto/openssl/enc_provider/des3.c b/src/lib/crypto/openssl/enc_provider/des3.c
index ca843f9..d531f10 100644
--- a/src/lib/crypto/openssl/enc_provider/des3.c
+++ b/src/lib/crypto/openssl/enc_provider/des3.c
@@ -61,13 +61,7 @@ static krb5_error_code
 validate(krb5_key key, const krb5_data *ivec, const krb5_crypto_iov *data,
          size_t num_data, krb5_boolean *empty)
 {
-    size_t i, input_length;
-
-    for (i = 0, input_length = 0; i < num_data; i++) {
-        const krb5_crypto_iov *iov = &data[i];
-        if (ENCRYPT_IOV(iov))
-            input_length += iov->data.length;
-    }
+    size_t input_length = iov_total_length(data, num_data, FALSE);
 
     if (key->keyblock.length != DES3_KEY_SIZE)
         return(KRB5_BAD_KEYSIZE);
@@ -86,7 +80,7 @@ k5_des3_encrypt(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data,
 {
     int ret, olen = DES3_BLOCK_SIZE;
     unsigned char iblock[DES3_BLOCK_SIZE], oblock[DES3_BLOCK_SIZE];
-    struct iov_block_state input_pos, output_pos;
+    struct iov_cursor cursor;
     EVP_CIPHER_CTX ciph_ctx;
     krb5_boolean empty;
 
@@ -94,9 +88,6 @@ k5_des3_encrypt(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data,
     if (ret != 0 || empty)
         return ret;
 
-    IOV_BLOCK_STATE_INIT(&input_pos);
-    IOV_BLOCK_STATE_INIT(&output_pos);
-
     EVP_CIPHER_CTX_init(&ciph_ctx);
 
     ret = EVP_EncryptInit_ex(&ciph_ctx, EVP_des_ede3_cbc(), NULL,
@@ -107,19 +98,13 @@ k5_des3_encrypt(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data,
 
     EVP_CIPHER_CTX_set_padding(&ciph_ctx,0);
 
-    for (;;) {
-
-        if (!krb5int_c_iov_get_block(iblock, DES3_BLOCK_SIZE,
-                                     data, num_data, &input_pos))
-            break;
-
+    k5_iov_cursor_init(&cursor, data, num_data, DES3_BLOCK_SIZE, FALSE);
+    while (k5_iov_cursor_get(&cursor, iblock)) {
         ret = EVP_EncryptUpdate(&ciph_ctx, oblock, &olen,
                                 (unsigned char *)iblock, DES3_BLOCK_SIZE);
         if (!ret)
             break;
-
-        krb5int_c_iov_put_block(data, num_data,
-                                oblock, DES3_BLOCK_SIZE, &output_pos);
+        k5_iov_cursor_put(&cursor, oblock);
     }
 
     if (ivec != NULL)
@@ -141,7 +126,7 @@ k5_des3_decrypt(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data,
 {
     int ret, olen = DES3_BLOCK_SIZE;
     unsigned char iblock[DES3_BLOCK_SIZE], oblock[DES3_BLOCK_SIZE];
-    struct iov_block_state input_pos, output_pos;
+    struct iov_cursor cursor;
     EVP_CIPHER_CTX ciph_ctx;
     krb5_boolean empty;
 
@@ -149,9 +134,6 @@ k5_des3_decrypt(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data,
     if (ret != 0 || empty)
         return ret;
 
-    IOV_BLOCK_STATE_INIT(&input_pos);
-    IOV_BLOCK_STATE_INIT(&output_pos);
-
     EVP_CIPHER_CTX_init(&ciph_ctx);
 
     ret = EVP_DecryptInit_ex(&ciph_ctx, EVP_des_ede3_cbc(), NULL,
@@ -162,19 +144,13 @@ k5_des3_decrypt(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data,
 
     EVP_CIPHER_CTX_set_padding(&ciph_ctx,0);
 
-    for (;;) {
-
-        if (!krb5int_c_iov_get_block(iblock, DES3_BLOCK_SIZE,
-                                     data, num_data, &input_pos))
-            break;
-
+    k5_iov_cursor_init(&cursor, data, num_data, DES3_BLOCK_SIZE, FALSE);
+    while (k5_iov_cursor_get(&cursor, iblock)) {
         ret = EVP_DecryptUpdate(&ciph_ctx, oblock, &olen,
                                 (unsigned char *)iblock, DES3_BLOCK_SIZE);
         if (!ret)
             break;
-
-        krb5int_c_iov_put_block(data, num_data, oblock, DES3_BLOCK_SIZE,
-                                &output_pos);
+        k5_iov_cursor_put(&cursor, oblock);
     }
 
     if (ivec != NULL)


More information about the cvs-krb5 mailing list