From: Daiki Ueno dueno@redhat.com
The ChaCha20 based header protection algorithm in QUIC requires a way to set the initial value of counter: https://quicwg.org/base-drafts/draft-ietf-quic-tls.html#name-chacha20-based-...
This will add a new function chacha_set_counter, which takes an 8-octet initial value of the block counter.
Signed-off-by: Daiki Ueno dueno@redhat.com --- chacha-set-nonce.c | 7 +++++++ chacha.h | 5 +++++ nettle.texinfo | 12 ++++++++++++ testsuite/chacha-test.c | 37 +++++++++++++++++++++++++++++++++++-- 4 files changed, 59 insertions(+), 2 deletions(-)
diff --git a/chacha-set-nonce.c b/chacha-set-nonce.c index 607f176b..2c34e498 100644 --- a/chacha-set-nonce.c +++ b/chacha-set-nonce.c @@ -68,3 +68,10 @@ chacha_set_nonce96(struct chacha_ctx *ctx, const uint8_t *nonce) ctx->state[14] = LE_READ_UINT32(nonce + 4); ctx->state[15] = LE_READ_UINT32(nonce + 8); } + +void +chacha_set_counter(struct chacha_ctx *ctx, const uint8_t *counter) +{ + ctx->state[12] = LE_READ_UINT32(counter + 0); + ctx->state[13] = LE_READ_UINT32(counter + 4); +} diff --git a/chacha.h b/chacha.h index 429a55b6..440fe968 100644 --- a/chacha.h +++ b/chacha.h @@ -46,6 +46,7 @@ extern "C" { #define chacha_set_key nettle_chacha_set_key #define chacha_set_nonce nettle_chacha_set_nonce #define chacha_set_nonce96 nettle_chacha_set_nonce96 +#define chacha_set_counter nettle_chacha_set_counter #define chacha_crypt nettle_chacha_crypt
/* Currently, only 256-bit keys are supported. */ @@ -53,6 +54,7 @@ extern "C" { #define CHACHA_BLOCK_SIZE 64 #define CHACHA_NONCE_SIZE 8 #define CHACHA_NONCE96_SIZE 12 +#define CHACHA_COUNTER_SIZE 8
#define _CHACHA_STATE_LENGTH 16
@@ -81,6 +83,9 @@ chacha_set_nonce(struct chacha_ctx *ctx, const uint8_t *nonce); void chacha_set_nonce96(struct chacha_ctx *ctx, const uint8_t *nonce);
+void +chacha_set_counter(struct chacha_ctx *ctx, const uint8_t *counter); + void chacha_crypt(struct chacha_ctx *ctx, size_t length, uint8_t *dst, const uint8_t *src); diff --git a/nettle.texinfo b/nettle.texinfo index 19eb6d34..0b339f51 100644 --- a/nettle.texinfo +++ b/nettle.texinfo @@ -1669,6 +1669,10 @@ ChaCha block size, 64. Size of the nonce, 8. @end defvr
+@defvr Constant CHACHA_COUNTER_SIZE +Size of the counter, 8. +@end defvr + @deftypefun void chacha_set_key (struct chacha_ctx *@var{ctx}, const uint8_t *@var{key}) Initialize the cipher. The same function is used for both encryption and decryption. Before using the cipher, @@ -1681,6 +1685,14 @@ octets. This function also initializes the block counter, setting it to zero. @end deftypefun
+@deftypefun void chacha_set_counter (struct chacha_ctx *@var{ctx}, const uint8_t *@var{counter}) +Sets the block counter. It is always of size @code{CHACHA_COUNTER_SIZE}, +8 octets. This is rarely needed since @code{chacha_set_nonce} +initializes the block counter to zero. When it is still necessary, this +function must be called after @code{chacha_set_nonce}. + +@end deftypefun + @deftypefun void chacha_crypt (struct chacha_ctx *@var{ctx}, size_t @var{length}, uint8_t *@var{dst}, const uint8_t *@var{src}) Encrypts or decrypts the data of a message, using ChaCha. When a message is encrypted using a sequence of calls to @code{chacha_crypt}, diff --git a/testsuite/chacha-test.c b/testsuite/chacha-test.c index d6489e9c..6875d4bb 100644 --- a/testsuite/chacha-test.c +++ b/testsuite/chacha-test.c @@ -38,8 +38,9 @@ #include "chacha-internal.h"
static void -test_chacha(const struct tstring *key, const struct tstring *nonce, - const struct tstring *expected, unsigned rounds) +_test_chacha(const struct tstring *key, const struct tstring *nonce, + const struct tstring *expected, unsigned rounds, + const struct tstring *counter) { struct chacha_ctx ctx;
@@ -69,6 +70,9 @@ test_chacha(const struct tstring *key, const struct tstring *nonce, else die ("Bad nonce size %u.\n", (unsigned) nonce->length);
+ if (counter) + chacha_set_counter(&ctx, counter->data); + chacha_crypt (&ctx, length, data, data);
ASSERT (data[-1] == 17); @@ -98,6 +102,8 @@ test_chacha(const struct tstring *key, const struct tstring *nonce, ASSERT (nonce->length == CHACHA_NONCE_SIZE);
chacha_set_nonce(&ctx, nonce->data); + if (counter) + chacha_set_counter(&ctx, counter->data); _chacha_core (out, ctx.state, rounds);
if (!MEMEQ(CHACHA_BLOCK_SIZE, out, expected->data)) @@ -117,6 +123,21 @@ test_chacha(const struct tstring *key, const struct tstring *nonce, } }
+static void +test_chacha(const struct tstring *key, const struct tstring *nonce, + const struct tstring *expected, unsigned rounds) +{ + _test_chacha(key, nonce, expected, rounds, NULL); +} + +static void +test_chacha_with_counter(const struct tstring *key, const struct tstring *nonce, + const struct tstring *expected, unsigned rounds, + const struct tstring *counter) +{ + _test_chacha(key, nonce, expected, rounds, counter); +} + void test_main(void) { @@ -644,4 +665,16 @@ test_main(void) "d2826446079faa09 14c2d705d98b02a2" "b5129cd1de164eb9 cbd083e8a2503c4e"), 20); + + /* This is identical to the 96-bit nonce test, but it manually sets + the counter value */ + test_chacha_with_counter(SHEX("0001020304050607 08090a0b0c0d0e0f" + "1011121314151617 18191a1b1c1d1e1f"), + SHEX("0000004a00000000"), + SHEX("10f1e7e4d13b5915 500fdd1fa32071c4" + "c7d1f4c733c06803 0422aa9ac3d46c4e" + "d2826446079faa09 14c2d705d98b02a2" + "b5129cd1de164eb9 cbd083e8a2503c4e"), + 20, + SHEX("0100000000000009")); }
From: Daiki Ueno dueno@redhat.com
The ChaCha-Poly1305 implementation previously used the chacha_crypt function that assumes the block counter is 64-bit long, while RFC 8439 defines that the counter is 32-bit long. Although this should be fine as long as up to 256 gigabytes of data is encrypted with the same key, it would be nice to use a separate functions (chacha_set_counter32 and chacha_crypt32) that assume the counter is 32-bit long.
Signed-off-by: Daiki Ueno dueno@redhat.com --- chacha-crypt.c | 32 ++++++++++++++++++++++++++++++++ chacha-poly1305.c | 4 ++-- chacha-set-nonce.c | 6 ++++++ chacha.h | 10 ++++++++++ nettle.texinfo | 31 +++++++++++++++++++++++++++++++ testsuite/chacha-test.c | 34 ++++++++++++++++++++++++++++++---- 6 files changed, 111 insertions(+), 6 deletions(-)
diff --git a/chacha-crypt.c b/chacha-crypt.c index 63d799ce..0bb44ed9 100644 --- a/chacha-crypt.c +++ b/chacha-crypt.c @@ -85,3 +85,35 @@ chacha_crypt(struct chacha_ctx *ctx, m += CHACHA_BLOCK_SIZE; } } + +void +chacha_crypt32(struct chacha_ctx *ctx, + size_t length, + uint8_t *c, + const uint8_t *m) +{ + if (!length) + return; + + for (;;) + { + uint32_t x[_CHACHA_STATE_LENGTH]; + + _chacha_core (x, ctx->state, CHACHA_ROUNDS); + + ++ctx->state[12]; + + /* stopping at 2^70 length per nonce is user's responsibility */ + + if (length <= CHACHA_BLOCK_SIZE) + { + memxor3 (c, m, x, length); + return; + } + memxor3 (c, m, x, CHACHA_BLOCK_SIZE); + + length -= CHACHA_BLOCK_SIZE; + c += CHACHA_BLOCK_SIZE; + m += CHACHA_BLOCK_SIZE; + } +} diff --git a/chacha-poly1305.c b/chacha-poly1305.c index 974a5022..a15fef0c 100644 --- a/chacha-poly1305.c +++ b/chacha-poly1305.c @@ -130,7 +130,7 @@ chacha_poly1305_encrypt (struct chacha_poly1305_ctx *ctx, assert (ctx->data_size % CHACHA_POLY1305_BLOCK_SIZE == 0); poly1305_pad (ctx);
- chacha_crypt (&ctx->chacha, length, dst, src); + chacha_crypt32 (&ctx->chacha, length, dst, src); poly1305_update (ctx, length, dst); ctx->data_size += length; } @@ -146,7 +146,7 @@ chacha_poly1305_decrypt (struct chacha_poly1305_ctx *ctx, poly1305_pad (ctx);
poly1305_update (ctx, length, src); - chacha_crypt (&ctx->chacha, length, dst, src); + chacha_crypt32 (&ctx->chacha, length, dst, src); ctx->data_size += length; } diff --git a/chacha-set-nonce.c b/chacha-set-nonce.c index 2c34e498..1547aea1 100644 --- a/chacha-set-nonce.c +++ b/chacha-set-nonce.c @@ -75,3 +75,9 @@ chacha_set_counter(struct chacha_ctx *ctx, const uint8_t *counter) ctx->state[12] = LE_READ_UINT32(counter + 0); ctx->state[13] = LE_READ_UINT32(counter + 4); } + +void +chacha_set_counter32(struct chacha_ctx *ctx, const uint8_t *counter) +{ + ctx->state[12] = LE_READ_UINT32(counter + 0); +} diff --git a/chacha.h b/chacha.h index 440fe968..fe28b835 100644 --- a/chacha.h +++ b/chacha.h @@ -47,7 +47,9 @@ extern "C" { #define chacha_set_nonce nettle_chacha_set_nonce #define chacha_set_nonce96 nettle_chacha_set_nonce96 #define chacha_set_counter nettle_chacha_set_counter +#define chacha_set_counter32 nettle_chacha_set_counter32 #define chacha_crypt nettle_chacha_crypt +#define chacha_crypt32 nettle_chacha_crypt32
/* Currently, only 256-bit keys are supported. */ #define CHACHA_KEY_SIZE 32 @@ -55,6 +57,7 @@ extern "C" { #define CHACHA_NONCE_SIZE 8 #define CHACHA_NONCE96_SIZE 12 #define CHACHA_COUNTER_SIZE 8 +#define CHACHA_COUNTER32_SIZE 4
#define _CHACHA_STATE_LENGTH 16
@@ -86,10 +89,17 @@ chacha_set_nonce96(struct chacha_ctx *ctx, const uint8_t *nonce); void chacha_set_counter(struct chacha_ctx *ctx, const uint8_t *counter);
+void +chacha_set_counter32(struct chacha_ctx *ctx, const uint8_t *counter); + void chacha_crypt(struct chacha_ctx *ctx, size_t length, uint8_t *dst, const uint8_t *src);
+void +chacha_crypt32(struct chacha_ctx *ctx, size_t length, + uint8_t *dst, const uint8_t *src); + #ifdef __cplusplus } #endif diff --git a/nettle.texinfo b/nettle.texinfo index 0b339f51..fe44f6af 100644 --- a/nettle.texinfo +++ b/nettle.texinfo @@ -1700,6 +1700,37 @@ all but the last call @emph{must} use a length that is a multiple of @code{CHACHA_BLOCK_SIZE}. @end deftypefun
+@subsubsection 32-bit counter variant + +While the original paper uses 64-bit counter value, the variant defined +in @cite{RFC 8439} uses 32-bit counter value. This variant is +particularly useful for @pxref{ChaCha-Poly1305} AEAD construction, which +supports 12-octet nonces. + +@defvr Constant CHACHA_NONCE96_SIZE +Size of the nonce, 12. +@end defvr + +@defvr Constant CHACHA_COUNTER32_SIZE +Size of the counter, 4. +@end defvr + +@deftypefun void chacha_set_nonce96 (struct chacha_ctx *@var{ctx}, const uint8_t *@var{nonce}) +Sets the nonce. This is similar to the above @code{chacha_set_nonce}, +but the input is always of size @code{CHACHA_NONCE96_SIZE}, 12 octets. +@end deftypefun + +@deftypefun void chacha_set_counter32 (struct chacha_ctx *@var{ctx}, const uint8_t *@var{counter}) +Sets the block counter. This is similar to the above @code{chacha_set_counter}, +but the input is always of size @code{CHACHA_COUNTER32_SIZE}, 4 octets. +@end deftypefun + +@deftypefun void chacha_crypt32 (struct chacha_ctx *@var{ctx}, size_t @var{length}, uint8_t *@var{dst}, const uint8_t *@var{src}) +Encrypts or decrypts the data of a message, using ChaCha. This is similar to the +above @code{chacha_crypt}, but it assumes the internal counter value is 32-bit +long and the nonce is 96-bit long. +@end deftypefun + @subsection DES @cindex DES DES is the old Data Encryption Standard, specified by NIST. It uses a diff --git a/testsuite/chacha-test.c b/testsuite/chacha-test.c index 6875d4bb..fb8f1db7 100644 --- a/testsuite/chacha-test.c +++ b/testsuite/chacha-test.c @@ -71,9 +71,23 @@ _test_chacha(const struct tstring *key, const struct tstring *nonce, die ("Bad nonce size %u.\n", (unsigned) nonce->length);
if (counter) - chacha_set_counter(&ctx, counter->data); + { + if (counter->length == CHACHA_COUNTER_SIZE) + { + ASSERT (nonce->length == CHACHA_NONCE_SIZE); + chacha_set_counter(&ctx, counter->data); + } + else if (counter->length == CHACHA_COUNTER32_SIZE) + { + ASSERT (nonce->length == CHACHA_NONCE96_SIZE); + chacha_set_counter32(&ctx, counter->data); + } + }
- chacha_crypt (&ctx, length, data, data); + if (nonce->length == CHACHA_NONCE_SIZE) + chacha_crypt (&ctx, length, data, data); + else + chacha_crypt32 (&ctx, length, data, data);
ASSERT (data[-1] == 17); ASSERT (data[length] == 17); @@ -666,8 +680,20 @@ test_main(void) "b5129cd1de164eb9 cbd083e8a2503c4e"), 20);
- /* This is identical to the 96-bit nonce test, but it manually sets - the counter value */ + /* This is identical to the above 96-bit nonce test, but it manually + sets the 32-bit counter value */ + test_chacha_with_counter(SHEX("0001020304050607 08090a0b0c0d0e0f" + "1011121314151617 18191a1b1c1d1e1f"), + SHEX("000000090000004a 00000000"), + SHEX("10f1e7e4d13b5915 500fdd1fa32071c4" + "c7d1f4c733c06803 0422aa9ac3d46c4e" + "d2826446079faa09 14c2d705d98b02a2" + "b5129cd1de164eb9 cbd083e8a2503c4e"), + 20, + SHEX("01000000")); + + /* This is identical to the above 96-bit nonce test, but it manually + sets the 64-bit counter value */ test_chacha_with_counter(SHEX("0001020304050607 08090a0b0c0d0e0f" "1011121314151617 18191a1b1c1d1e1f"), SHEX("0000004a00000000"),
From: Daiki Ueno dueno@redhat.com
While the documentation said the nonce size is 8 octets, the implementation actually assumed 12 octets following RFC 7539.
Signed-off-by: Daiki Ueno dueno@redhat.com --- nettle.texinfo | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-)
diff --git a/nettle.texinfo b/nettle.texinfo index fe44f6af..418f46d8 100644 --- a/nettle.texinfo +++ b/nettle.texinfo @@ -3323,17 +3323,12 @@ except that @var{cipher} and @var{f} are replaced with a context structure. ChaCha-Poly1305 is a combination of the ChaCha stream cipher and the poly1305 message authentication code (@pxref{Poly1305}). It originates from the NaCl cryptographic library by D. J. Bernstein et al, which -defines a similar construction but with Salsa20 instead of ChaCha. - -Nettle's implementation ChaCha-Poly1305 should be considered -@strong{experimental}. At the time of this writing, there is no -authoritative specification for ChaCha-Poly1305, and a couple of -different incompatible variants. Nettle implements it using the original -definition of ChaCha, with 64 bits (8 octets) each for the nonce and the -block counter. Some protocols prefer to use nonces of 12 bytes, and it's -a small change to ChaCha to use the upper 32 bits of the block counter -as a nonce, instead limiting message size to @math{2^32} blocks or 256 -GBytes, but that variant is currently not supported. +defines a similar construction but with Salsa20 instead of ChaCha. + +Nettle's implementation of ChaCha-Poly1305 follows @cite{RFC 8439}, +where the ChaCha cipher is initialized with a 12-byte nonce and a 4-byte +block counter. This allows up to 256 gigabytes of data to be encrypted +using the same key.
For ChaCha-Poly1305, the ChaCha cipher is initialized with a key, of 256 bits, and a per-message nonce. The first block of the key stream @@ -3362,7 +3357,7 @@ ChaCha-Poly1305 key size, 32. @end defvr
@defvr Constant CHACHA_POLY1305_NONCE_SIZE -Same as the ChaCha nonce size, 16. +ChaCha-Poly1305 nonce size, 12. @end defvr
@defvr Constant CHACHA_POLY1305_DIGEST_SIZE
Daiki Ueno ueno@gnu.org writes:
From: Daiki Ueno dueno@redhat.com
The ChaCha20 based header protection algorithm in QUIC requires a way to set the initial value of counter: https://quicwg.org/base-drafts/draft-ietf-quic-tls.html#name-chacha20-based-...
This will add a new function chacha_set_counter, which takes an 8-octet initial value of the block counter.
I've merged all three patches to master-updates. Two nits below:
+void +chacha_crypt32(struct chacha_ctx *ctx,
size_t length,
uint8_t *c,
const uint8_t *m)
+{
- if (!length)
- return;
- for (;;)
- {
uint32_t x[_CHACHA_STATE_LENGTH];
_chacha_core (x, ctx->state, CHACHA_ROUNDS);
++ctx->state[12];
/* stopping at 2^70 length per nonce is user's responsibility */
Should be 2^38, not 2^70, right?
+Nettle's implementation of ChaCha-Poly1305 follows @cite{RFC 8439}, +where the ChaCha cipher is initialized with a 12-byte nonce and a 4-byte +block counter. This allows up to 256 gigabytes of data to be encrypted +using the same key.
Should be "same key and nonce"; the counter size limits the size of a message, but the nonce allows for many messages using the same key.
I'll fix these.
It would be nice with a test case where the first 32 bits of the counter wrap around, which is the only case where chacha_crypt and chacha_crypt32 behave differently. Is that something you can look into?
Thanks! /Niels
nettle-bugs@lists.lysator.liu.se