diff --git a/lib/crypto-api.c b/lib/crypto-api.c index c035fee..473f120 100644 --- a/lib/crypto-api.c +++ b/lib/crypto-api.c @@ -927,9 +927,9 @@ gnutls_aead_cipher_encryptv(gnutls_aead_cipher_hd_t handle, uint8_t *dst; size_t dst_size, total = 0; uint8_t *p; + size_t len; size_t blocksize = handle->ctx_enc.e->blocksize; struct iov_iter_st iter; - size_t blocks; /* Limitation: this function provides an optimization under the internally registered * AEAD ciphers. When an AEAD cipher is used registered with gnutls_crypto_register_aead_cipher(), @@ -980,15 +980,7 @@ gnutls_aead_cipher_encryptv(gnutls_aead_cipher_hd_t handle, return gnutls_assert_val(ret); if (ret == 0) break; - blocks = ret; - ret = _gnutls_cipher_auth(&handle->ctx_enc, p, - blocksize * blocks); - if (unlikely(ret < 0)) - return gnutls_assert_val(ret); - } - if (iter.block_offset > 0) { - ret = _gnutls_cipher_auth(&handle->ctx_enc, - iter.block, iter.block_offset); + ret = _gnutls_cipher_auth(&handle->ctx_enc, p, ret); if (unlikely(ret < 0)) return gnutls_assert_val(ret); } @@ -1005,29 +997,15 @@ gnutls_aead_cipher_encryptv(gnutls_aead_cipher_hd_t handle, return gnutls_assert_val(ret); if (ret == 0) break; - blocks = ret; - if (unlikely(dst_size < blocksize * blocks)) - return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER); - ret = _gnutls_cipher_encrypt2(&handle->ctx_enc, p, - blocksize * blocks, - dst, dst_size); - if (unlikely(ret < 0)) - return gnutls_assert_val(ret); - DECR_LEN(dst_size, blocksize * blocks); - dst += blocksize * blocks; - total += blocksize * blocks; - } - if (iter.block_offset > 0) { - if (unlikely(dst_size < iter.block_offset)) - return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER); + len = ret; ret = _gnutls_cipher_encrypt2(&handle->ctx_enc, - iter.block, iter.block_offset, + p, len, dst, dst_size); if (unlikely(ret < 0)) return gnutls_assert_val(ret); - DECR_LEN(dst_size, iter.block_offset); - dst += iter.block_offset; - total += iter.block_offset; + DECR_LEN(dst_size, len); + dst += len; + total += len; } if (dst_size < tag_size) @@ -1070,9 +1048,9 @@ gnutls_aead_cipher_encryptv2(gnutls_aead_cipher_hd_t handle, api_aead_cipher_hd_st *h = handle; ssize_t ret; uint8_t *p; + size_t len; ssize_t blocksize = handle->ctx_enc.e->blocksize; struct iov_iter_st iter; - size_t blocks; size_t _tag_size; if (tag_size == NULL || *tag_size == 0) @@ -1155,15 +1133,7 @@ gnutls_aead_cipher_encryptv2(gnutls_aead_cipher_hd_t handle, return gnutls_assert_val(ret); if (ret == 0) break; - blocks = ret; - ret = _gnutls_cipher_auth(&handle->ctx_enc, p, - blocksize * blocks); - if (unlikely(ret < 0)) - return gnutls_assert_val(ret); - } - if (iter.block_offset > 0) { - ret = _gnutls_cipher_auth(&handle->ctx_enc, - iter.block, iter.block_offset); + ret = _gnutls_cipher_auth(&handle->ctx_enc, p, ret); if (unlikely(ret < 0)) return gnutls_assert_val(ret); } @@ -1177,17 +1147,13 @@ gnutls_aead_cipher_encryptv2(gnutls_aead_cipher_hd_t handle, return gnutls_assert_val(ret); if (ret == 0) break; - blocks = ret; - ret = _gnutls_cipher_encrypt2(&handle->ctx_enc, - p, blocksize * blocks, - p, blocksize * blocks); + + len = ret; + ret = _gnutls_cipher_encrypt2(&handle->ctx_enc, p, len, p, len); if (unlikely(ret < 0)) return gnutls_assert_val(ret); - } - if (iter.block_offset > 0) { - ret = _gnutls_cipher_encrypt2(&handle->ctx_enc, - iter.block, iter.block_offset, - iter.block, iter.block_offset); + + ret = _gnutls_iov_iter_sync(&iter, p, len); if (unlikely(ret < 0)) return gnutls_assert_val(ret); } @@ -1229,9 +1195,9 @@ gnutls_aead_cipher_decryptv2(gnutls_aead_cipher_hd_t handle, api_aead_cipher_hd_st *h = handle; ssize_t ret; uint8_t *p; + size_t len; ssize_t blocksize = handle->ctx_enc.e->blocksize; struct iov_iter_st iter; - size_t blocks; uint8_t _tag[MAX_HASH_SIZE]; if (tag_size == 0) @@ -1305,15 +1271,7 @@ gnutls_aead_cipher_decryptv2(gnutls_aead_cipher_hd_t handle, return gnutls_assert_val(ret); if (ret == 0) break; - blocks = ret; - ret = _gnutls_cipher_auth(&handle->ctx_enc, p, - blocksize * blocks); - if (unlikely(ret < 0)) - return gnutls_assert_val(ret); - } - if (iter.block_offset > 0) { - ret = _gnutls_cipher_auth(&handle->ctx_enc, - iter.block, iter.block_offset); + ret = _gnutls_cipher_auth(&handle->ctx_enc, p, ret); if (unlikely(ret < 0)) return gnutls_assert_val(ret); } @@ -1327,17 +1285,13 @@ gnutls_aead_cipher_decryptv2(gnutls_aead_cipher_hd_t handle, return gnutls_assert_val(ret); if (ret == 0) break; - blocks = ret; - ret = _gnutls_cipher_decrypt2(&handle->ctx_enc, - p, blocksize * blocks, - p, blocksize * blocks); + + len = ret; + ret = _gnutls_cipher_decrypt2(&handle->ctx_enc, p, len, p, len); if (unlikely(ret < 0)) return gnutls_assert_val(ret); - } - if (iter.block_offset > 0) { - ret = _gnutls_cipher_decrypt2(&handle->ctx_enc, - iter.block, iter.block_offset, - iter.block, iter.block_offset); + + ret = _gnutls_iov_iter_sync(&iter, p, len); if (unlikely(ret < 0)) return gnutls_assert_val(ret); } diff --git a/lib/iov.c b/lib/iov.c index 5dc29c5..1cd8d46 100644 --- a/lib/iov.c +++ b/lib/iov.c @@ -58,8 +58,8 @@ _gnutls_iov_iter_init(struct iov_iter_st *iter, * @data: the return location of extracted data * * Retrieve block(s) pointed by @iter and advance it to the next - * position. It returns the number of consecutive blocks in @data. - * At the end of iteration, 0 is returned. + * position. It returns the number of bytes in @data. At the end of + * iteration, 0 is returned. * * If the data stored in @iter is not multiple of the block size, the * remaining data is stored in the "block" field of @iter with the @@ -88,25 +88,30 @@ _gnutls_iov_iter_next(struct iov_iter_st *iter, uint8_t **data) if ((len % iter->block_size) == 0) { iter->iov_index++; iter->iov_offset = 0; - } else - iter->iov_offset += - len - (len % iter->block_size); + } else { + len -= (len % iter->block_size); + iter->iov_offset += len; + } /* Return the blocks. */ *data = p; - return len / iter->block_size; + return len; } /* We can complete one full block to return. */ block_left = iter->block_size - iter->block_offset; if (len >= block_left) { memcpy(iter->block + iter->block_offset, p, block_left); - iter->iov_offset += block_left; + if (len == block_left) { + iter->iov_index++; + iter->iov_offset = 0; + } else + iter->iov_offset += block_left; iter->block_offset = 0; /* Return the filled block. */ *data = iter->block; - return 1; + return iter->block_size; } /* Not enough data for a full block, store in temp @@ -116,5 +121,74 @@ _gnutls_iov_iter_next(struct iov_iter_st *iter, uint8_t **data) iter->iov_index++; iter->iov_offset = 0; } + + if (iter->block_offset > 0) { + size_t len = iter->block_offset; + + /* Return the incomplete block. */ + *data = iter->block; + iter->block_offset = 0; + return len; + } + + return 0; +} + +/** + * _gnutls_iov_iter_sync: + * @iter: the iterator + * @data: data returned by _gnutls_iov_iter_next + * @data_size: size of @data + * + * Flush the content of temp buffer (if any) to the data buffer. + */ +int +_gnutls_iov_iter_sync(struct iov_iter_st *iter, const uint8_t *data, + size_t data_size) +{ + size_t iov_index; + size_t iov_offset; + + /* We didn't return the cached block. */ + if (data != iter->block) + return 0; + + iov_index = iter->iov_index; + iov_offset = iter->iov_offset; + + /* When syncing a cache block we walk backwards because we only have a + * pointer to were the block ends in the iovec, walking backwards is + * fine as we are always writing a full block, so the whole content + * is written in the right places: + * iovec: |--0--|---1---|--2--|-3-| + * block: |-----------------------| + * 1st write |---| + * 2nd write |----- + * 3rd write |------- + * last write |----- + */ + while (data_size > 0) { + const giovec_t *iov; + uint8_t *p; + size_t to_write; + + while (iov_offset == 0) { + if (unlikely(iov_index == 0)) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + + iov_index--; + iov_offset = iter->iov[iov_index].iov_len; + } + + iov = &iter->iov[iov_index]; + p = iov->iov_base; + to_write = MIN(data_size, iov_offset); + + iov_offset -= to_write; + data_size -= to_write; + + memcpy(p + iov_offset, &iter->block[data_size], to_write); + } + return 0; } diff --git a/lib/iov.h b/lib/iov.h index 47fba55..5b99034 100644 --- a/lib/iov.h +++ b/lib/iov.h @@ -34,7 +34,6 @@ struct iov_iter_st { uint8_t block[MAX_CIPHER_BLOCK_SIZE]; /* incomplete block for reading */ size_t block_size; /* actual block size of the cipher */ size_t block_offset; /* offset in block */ - }; int _gnutls_iov_iter_init(struct iov_iter_st *iter, @@ -43,4 +42,7 @@ int _gnutls_iov_iter_init(struct iov_iter_st *iter, ssize_t _gnutls_iov_iter_next(struct iov_iter_st *iter, uint8_t **data); +int _gnutls_iov_iter_sync(struct iov_iter_st *iter, const uint8_t *data, + size_t data_size); + #endif /* GNUTLS_LIB_IOV_H */ diff --git a/lib/libgnutls.map b/lib/libgnutls.map index 54b178c..988bc41 100644 --- a/lib/libgnutls.map +++ b/lib/libgnutls.map @@ -1384,4 +1384,5 @@ GNUTLS_PRIVATE_3_4 { # needed by tests/iov: _gnutls_iov_iter_init; _gnutls_iov_iter_next; + _gnutls_iov_iter_sync; } GNUTLS_3_4; diff --git a/tests/aead-cipher-vec.c b/tests/aead-cipher-vec.c index 6c2542c..10e3db8 100644 --- a/tests/aead-cipher-vec.c +++ b/tests/aead-cipher-vec.c @@ -43,9 +43,9 @@ static void start(const char *name, int algo) uint8_t key16[64]; uint8_t iv16[32]; uint8_t auth[128]; - uint8_t data[128+64]; + uint8_t data[64+56+36]; gnutls_datum_t key, iv; - giovec_t iov[2]; + giovec_t iov[3]; giovec_t auth_iov[2]; uint8_t tag[64]; size_t tag_size = 0; @@ -60,13 +60,15 @@ static void start(const char *name, int algo) memset(iv.data, 0xff, iv.size); memset(key.data, 0xfe, key.size); - memset(data, 0xfa, 128); + memset(data, 0xfa, sizeof(data)); memset(auth, 0xaa, sizeof(auth)); iov[0].iov_base = data; iov[0].iov_len = 64; iov[1].iov_base = data + 64; - iov[1].iov_len = 64; + iov[1].iov_len = 56; + iov[2].iov_base = data + 64 + 56; + iov[2].iov_len = 36; auth_iov[0].iov_base = auth; auth_iov[0].iov_len = 64; @@ -83,7 +85,7 @@ static void start(const char *name, int algo) ret = gnutls_aead_cipher_encryptv2(ch, iv.data, iv.size, auth_iov, 2, - iov, 2, + iov, 3, tag, &tag_size); if (ret < 0) fail("could not encrypt data: %s\n", gnutls_strerror(ret)); @@ -91,7 +93,7 @@ static void start(const char *name, int algo) ret = gnutls_aead_cipher_decryptv2(ch, iv.data, iv.size, auth_iov, 2, - iov, 2, + iov, 3, tag, tag_size); if (ret < 0) fail("could not decrypt data: %s\n", gnutls_strerror(ret)); diff --git a/tests/iov.c b/tests/iov.c index eda5583..2acd2b5 100644 --- a/tests/iov.c +++ b/tests/iov.c @@ -32,7 +32,6 @@ struct exp_st { ssize_t ret; size_t iov_index; size_t iov_offset; - size_t block_offset; }; struct test_st { @@ -42,51 +41,51 @@ struct test_st { size_t block_size; const struct exp_st *exp; size_t expcnt; - size_t remaining; }; static const giovec_t iov16[] = { - {(void *) "0123456789abcdef", 16}, - {(void *) "0123456789abcdef", 16}, - {(void *) "0123456789abcdef", 16}, - {(void *) "0123456789abcdef", 16} + {(void *) "0123456789012345", 16}, + {(void *) "0123456789012345", 16}, + {(void *) "0123456789012345", 16}, + {(void *) "0123456789012345", 16} }; static const struct exp_st exp16_64[] = { - {1, 3, 16, 0}, - {0, 0, 0, 0} + {64, 4, 0}, + {0, 0, 0} }; static const struct exp_st exp16_32[] = { - {1, 1, 16, 0}, - {1, 3, 16, 0}, - {0, 0, 0, 0} + {32, 2, 0}, + {32, 4, 0}, + {0, 0, 0} }; static const struct exp_st exp16_16[] = { - {1, 1, 0, 0}, - {1, 2, 0, 0}, - {1, 3, 0, 0}, - {1, 4, 0, 0}, - {0, 0, 0, 0} + {16, 1, 0}, + {16, 2, 0}, + {16, 3, 0}, + {16, 4, 0}, + {0, 0, 0} }; static const struct exp_st exp16_4[] = { - {4, 1, 0, 0}, - {4, 2, 0, 0}, - {4, 3, 0, 0}, - {4, 4, 0, 0}, - {0, 0, 0, 0} + {16, 1, 0}, + {16, 2, 0}, + {16, 3, 0}, + {16, 4, 0}, + {0, 0, 0} }; static const struct exp_st exp16_3[] = { - {5, 0, 15, 0}, - {1, 1, 2, 0}, - {4, 1, 14, 0}, - {1, 2, 1, 0}, - {5, 3, 0, 0}, - {5, 3, 15, 0}, - {0, 0, 0, 1} + {15, 0, 15}, + {3, 1, 2}, + {12, 1, 14}, + {3, 2, 1}, + {15, 3, 0}, + {15, 3, 15}, + {1, 4, 0}, + {0, 0, 0} }; static const giovec_t iov8[] = { @@ -97,38 +96,123 @@ static const giovec_t iov8[] = { }; static const struct exp_st exp8_64[] = { - {0, 0, 0, 32} + {32, 4, 0}, + {0, 0, 0} +}; + +static const giovec_t iov_odd[] = { + {(void *) "0", 1}, + {(void *) "012", 3}, + {(void *) "01234", 5}, + {(void *) "0123456", 7}, + {(void *) "012345678", 9}, + {(void *) "01234567890", 11}, + {(void *) "0123456789012", 13}, + {(void *) "012345678901234", 15} +}; + +static const struct exp_st exp_odd_16[] = { + {16, 4, 0}, + {16, 5, 7}, + {16, 6, 12}, + {16, 8, 0}, + {0, 0, 0} +}; + +static const giovec_t iov_skip[] = { + {(void *) "0123456789012345", 16}, + {(void *) "01234567", 8}, + {(void *) "", 0}, + {(void *) "", 0}, + {(void *) "0123456789012345", 16} +}; + +static const struct exp_st exp_skip_16[] = { + {16, 1, 0}, + {16, 4, 8}, + {8, 5, 0}, + {0, 0, 0} +}; + +static const giovec_t iov_empty[] = { + {(void *) "", 0}, + {(void *) "", 0}, + {(void *) "", 0}, + {(void *) "", 0} +}; + +static const struct exp_st exp_empty_16[] = { + {0, 0, 0} }; static const struct test_st tests[] = { { "16/64", iov16, sizeof(iov16)/sizeof(iov16[0]), 64, - exp16_64, sizeof(exp16_64)/sizeof(exp16_64[0]), 0 }, + exp16_64, sizeof(exp16_64)/sizeof(exp16_64[0]) }, { "16/32", iov16, sizeof(iov16)/sizeof(iov16[0]), 32, - exp16_32, sizeof(exp16_32)/sizeof(exp16_32[0]), 0 }, + exp16_32, sizeof(exp16_32)/sizeof(exp16_32[0]) }, { "16/16", iov16, sizeof(iov16)/sizeof(iov16[0]), 16, - exp16_16, sizeof(exp16_16)/sizeof(exp16_16[0]), 0 }, + exp16_16, sizeof(exp16_16)/sizeof(exp16_16[0]) }, { "16/4", iov16, sizeof(iov16)/sizeof(iov16[0]), 4, - exp16_4, sizeof(exp16_4)/sizeof(exp16_4[0]), 0 }, + exp16_4, sizeof(exp16_4)/sizeof(exp16_4[0]) }, { "16/3", iov16, sizeof(iov16)/sizeof(iov16[0]), 3, - exp16_3, sizeof(exp16_3)/sizeof(exp16_3[0]), 1 }, + exp16_3, sizeof(exp16_3)/sizeof(exp16_3[0]) }, { "8/64", iov8, sizeof(iov8)/sizeof(iov8[0]), 64, - exp8_64, sizeof(exp8_64)/sizeof(exp8_64[0]), 32 } + exp8_64, sizeof(exp8_64)/sizeof(exp8_64[0]) }, + { "odd/16", iov_odd, sizeof(iov_odd)/sizeof(iov_odd[0]), 16, + exp_odd_16, sizeof(exp_odd_16)/sizeof(exp_odd_16[0]) }, + { "skip/16", iov_skip, sizeof(iov_skip)/sizeof(iov_skip[0]), 16, + exp_skip_16, sizeof(exp_skip_16)/sizeof(exp_skip_16[0]) }, + { "empty/16", iov_empty, sizeof(iov_empty)/sizeof(iov_empty[0]), 16, + exp_empty_16, sizeof(exp_empty_16)/sizeof(exp_empty_16[0]) }, }; +static void +copy(giovec_t *dst, uint8_t *buffer, const giovec_t *src, size_t iovcnt) +{ + uint8_t *p = buffer; + size_t i; + + for (i = 0; i < iovcnt; i++) { + dst[i].iov_base = p; + dst[i].iov_len = src[i].iov_len; + memcpy(dst[i].iov_base, src[i].iov_base, src[i].iov_len); + p += src[i].iov_len; + } +} + +static void +translate(uint8_t *data, size_t len) +{ + for (; len > 0; len--) { + uint8_t *p = &data[len - 1]; + if (*p >= '0' && *p <= '9') + *p = 'A' + *p - '0'; + else if (*p >= 'A' && *p <= 'Z') + *p = '0' + *p - 'A'; + } +} + +#define MAX_BUF 1024 +#define MAX_IOV 16 + void doit (void) { + uint8_t buffer[MAX_BUF]; size_t i; for (i = 0; i < sizeof(tests)/sizeof(tests[0]); i++) { + giovec_t iov[MAX_IOV]; struct iov_iter_st iter; const struct exp_st *exp = tests[i].exp; uint8_t *data; size_t j; + copy(iov, buffer, tests[i].iov, tests[i].iovcnt); + success("%s\n", tests[i].name); assert(_gnutls_iov_iter_init(&iter, - tests[i].iov, tests[i].iovcnt, + iov, tests[i].iovcnt, tests[i].block_size) == 0); for (j = 0; j < tests[i].expcnt; j++) { ssize_t ret; @@ -155,16 +239,31 @@ doit (void) else if (debug) success("iter.iov_offset: %u == %u\n", (unsigned) iter.iov_offset, (unsigned) exp[j].iov_offset); - if (iter.block_offset != exp[j].block_offset) - fail("iter.block_offset: %u != %u\n", - (unsigned) iter.block_offset, (unsigned) exp[j].block_offset); + if (iter.block_offset != 0) + fail("iter.block_offset: %u != 0\n", + (unsigned) iter.block_offset); else if (debug) - success("iter.block_offset: %u == %u\n", - (unsigned) iter.block_offset, (unsigned) exp[j].block_offset); + success("iter.block_offset: %u == 0\n", + (unsigned) iter.block_offset); + + translate(data, ret); + + ret = _gnutls_iov_iter_sync(&iter, data, ret); + if (ret < 0) + fail("sync failed\n"); } } - if (iter.block_offset != tests[i].remaining) - fail("remaining: %u != %u\n", - (unsigned) iter.block_offset, (unsigned) tests[i].remaining); + + for (j = 0; j < tests[i].iovcnt; j++) { + translate(iov[j].iov_base, iov[j].iov_len); + + if (memcmp(iov[j].iov_base, tests[i].iov[j].iov_base, + iov[j].iov_len) != 0) + fail("iov doesn't match: %*s != %*s\n", + (int)iov[j].iov_len, + (char *)iov[j].iov_base, + (int)tests[i].iov[j].iov_len, + (char *)tests[i].iov[j].iov_len); + } } }