diff --git a/src/chacha20poly1305.c b/src/chacha20poly1305.c index 4bdc43b..ce309cd 100644 --- a/src/chacha20poly1305.c +++ b/src/chacha20poly1305.c @@ -328,10 +328,9 @@ int poly1305_tagcmp_scalar(const uint8_t tag1[16], const uint8_t tag2[16]) static int chacha20_counter_wraps(uint32_t counter, size_t n) { - /* The data call starts at counter+1 and uses ceil(n/64) blocks */ - uint64_t start = (uint32_t)(counter + 1); + /* A call starting at block counter 'counter' uses ceil(n/64) blocks */ uint64_t blocks = ((uint64_t)n + 63) / 64; - return (start + blocks) > ((uint64_t)1 << 32); + return ((uint64_t)counter + blocks) > ((uint64_t)1 << 32); } /* diff --git a/src/cipher_chacha20.c b/src/cipher_chacha20.c index a1d9248..82073fd 100644 --- a/src/cipher_chacha20.c +++ b/src/cipher_chacha20.c @@ -220,7 +220,7 @@ EncryptPageChaCha20Cipher(void* cipher, int page, unsigned char* data, int len, chacha20_rng(data + n, PAGE_NONCE_LEN_CHACHA20); counter = LOAD32_LE(data + n + PAGE_NONCE_LEN_CHACHA20 - 4) ^ (uint32_t)page; } - while (chacha20_counter_wraps(counter, n - offset)); + while (chacha20_counter_wraps(counter + 1, n - offset)); chacha20_xor(otk, OTK_LEN_CHACHA20, chacha20Cipher->m_key, data + n, counter); chacha20_xor(data + offset, n - offset, otk + 32, data + n, counter + 1); diff --git a/src/sqlite3mc_vle.c b/src/sqlite3mc_vle.c index a26fb95..7a59b1a 100644 --- a/src/sqlite3mc_vle.c +++ b/src/sqlite3mc_vle.c @@ -402,7 +402,7 @@ vle_aeadEncrypt(const VleContext* ctx, chacha20_rng(nonce, nonceLen); counter = vle_readU32BE(nonce + nonceLen - 4) ^ (uint32_t) VLE_MAGIC_NUM; } - while (chacha20_counter_wraps(counter, ctextLen)); + while (chacha20_counter_wraps(counter + 1, ctextLen)); /* otk is all zero, so this yields pure keystream under the subkey */ chacha20_xor(otk, VLE_OTK_LEN, subkey, nonce, counter); diff --git a/test/cryptotest.c b/test/cryptotest.c index ecd6837..800a372 100644 --- a/test/cryptotest.c +++ b/test/cryptotest.c @@ -391,7 +391,10 @@ static void testAesCbc(void) /* ** ChaCha20 and Poly1305: every implementation has to reproduce the RFC 8439 ** test vectors and give the same result as the first implementation in its -** list (the reference) for pseudo-random input. +** list (the reference) for pseudo-random input. In addition, chacha20_xor has +** to wrap the 32-bit block counter like sqleet when it overflows within a call, +** because a page may be read with a different implementation than the one +** that wrote it. */ typedef void (*ChaCha20Function)(unsigned char* output, const unsigned char* input, size_t length, @@ -482,6 +485,10 @@ static const Poly1305Implementation poly1305List[] = static void testChaCha20(void) { static const uint32_t counters[] = { 0, 1, 0x12345, 0xfffff000u }; +#ifdef HAVE_SELECTABLE_IMPLEMENTATIONS + static const size_t overflowLengths[] = { 1024, 2048, 4064, 8000 }; + const int defaultIndex = ARRAY_COUNT(chacha20List) - 1; +#endif const ChaCha20Implementation* reference = &chacha20List[0]; int failures[ARRAY_COUNT(chacha20List)] = { 0 }; int checks[ARRAY_COUNT(chacha20List)] = { 0 }; @@ -531,6 +538,30 @@ static void testChaCha20(void) } } +#ifdef HAVE_SELECTABLE_IMPLEMENTATIONS + /* Counter overflow within the call: every start counter from just below the overflow up to 2^32-1 */ + for (i = 0; i < ARRAY_COUNT(overflowLengths); i++) + { + size_t length = overflowLengths[i]; + uint64_t blocks = (length + 63) / 64; + uint64_t start; + pseudoRandomBytes(testKey, sizeof(testKey)); + pseudoRandomBytes(testNonce, sizeof(testNonce)); + pseudoRandomBytes(inputBuffer, length); + for (start = 0x100000000ull - blocks - 1; start <= 0xffffffffull; start++) + { + chacha20Sqleet(expectedBuffer, inputBuffer, length, testKey, testNonce, (uint32_t) start); + snprintf(description, sizeof(description), "counter overflow, length %d, counter 0x%08x", + (int) length, (unsigned int) start); + memset(outputBuffer, 0, length); + chacha20Default(outputBuffer, inputBuffer, length, testKey, testNonce, (uint32_t) start); + failures[defaultIndex] += checkResult(memcmp(outputBuffer, expectedBuffer, length) == 0, + chacha20List[defaultIndex].name, description); + checks[defaultIndex]++; + } + } +#endif + for (j = 0; j < ARRAY_COUNT(chacha20List); j++) { if (cpuSupports(chacha20List[j].requiredCpuFeatures))