I'm hardly an expert on this, but if I remember correctly, the rekey rate
for good security is mostly dependent on the cipher block size. I left my
reference books at home; so, I can't come up with a reference for you, but I
would take Chris' "I'm deeply unsure of what impact that would have
on the security of the cipher" comment seriously and switch to a cipher
with a 128 bit block length (AES or Camelia).
From: openssh-unix-dev <openssh-unix-dev-bounces+herbie.robinson=stratus.com
at mindrot.org> On Behalf Of Damien Miller
Sent: Wednesday, March 29, 2023 2:38 PM
To: Chris Rapier <rapier at psc.edu>
Cc: Christian Weisgerber <naddy at mips.inka.de>; openssh-unix-dev at
mindrot.org
Subject: [EXTERNAL] Re: ChaCha20 Rekey Frequency
[EXTERNAL SENDER: This email originated from outside of Stratus Technologies. Do
not click links or open attachments unless you recognize the sender and know the
content is safe.]
On Wed, 29 Mar 2023, Chris Rapier wrote:
> I was wondering if there was something specific to the internal chacha20
> cipher as opposed to OpenSSL implementation.
>
> I can't just change the block size because it breaks compatibility. I
can do
> something like as a hack (though it would probably be better to do it with
the
> compat function):
>
> if (strstr(enc->name, "chacha"))
> *max_blocks = (u_int64_t)1 << (16*2);
> else if (enc->block_size >= 16)
> *max_blocks = (u_int64_t)1 << (enc->block_size*2);
> else
> *max_blocks = ((u_int64_t)1 << 30) / enc->block_size;
> if (state->rekey_limit)
>
> to force it to reduce the rekey rate but I'm deeply unsure of what
impact that
> would have on the security of the cipher as it's implemented.
Especially the
> without-openssl internal implementation.
This is what I'm playing with at the moment:
diff --git a/cipher.c b/cipher.c
index c7664a3..ec6fa4f 100644
--- a/cipher.c
+++ b/cipher.c
@@ -150,6 +150,39 @@ cipher_blocksize(const struct sshcipher *c)
return (c->block_size);
}
+uint64_t
+cipher_rekey_blocks(const struct sshcipher *c)
+{
+ /*
+ * Chacha20-Poly1305 does not benefit from data-based rekeying,
+ * per "The Security of ChaCha20-Poly1305 in the Multi-user Setting",
+ * Degabriele, J. P., Govinden, J, Gunther, F. and Paterson K.
+ * ACM CCS 2021;
https://eprint.iacr.org/2023/085.pdf<https://eprint.iacr.org/2023/085.pdf>
+ *
+ * Cryptanalysis aside, we do still want do need to prevent the SSH
+ * sequence number wrapping and also to rekey to provide some
+ * protection for long lived sessions against key disclosure at the
+ * endpoints, so arrange for rekeying every 2**32 blocks as the
+ * 128-bit block ciphers do (i.e. every 32GB data).
+ */
+ if ((c->flags & CFLAG_CHACHAPOLY) != 0)
+ return (uint64_t)1 << 32;
+ /*
+ * The 2^(blocksize*2) limit is too expensive for 3DES,
+ * so enforce a 1GB data limit for small blocksizes.
+ * See discussion in RFC4344 section 3.2.
+ */
+ if (c->block_size < 16)
+ return ((uint64_t)1 << 30) / c->block_size;
+ /*
+ * Otherwise, use the RFC4344 s3.2 recommendation of 2**(L/4) blocks
+ * before rekeying where L is the blocksize in bits.
+ * Most other ciphers have a 128 bit blocksize, so this equates to
+ * 2**32 blocks / 64GB data.
+ */
+ return (uint64_t)1 << (c->block_size * 2);
+}
+
u_int
cipher_keylen(const struct sshcipher *c)
{
diff --git a/cipher.h b/cipher.h
index 1a591cd..68be9ed 100644
--- a/cipher.h
+++ b/cipher.h
@@ -63,6 +63,7 @@ int cipher_get_length(struct sshcipher_ctx *, u_int *, u_int,
const u_char *, u_int);
void cipher_free(struct sshcipher_ctx *);
u_int cipher_blocksize(const struct sshcipher *);
+uint64_t cipher_rekey_blocks(const struct sshcipher *);
u_int cipher_keylen(const struct sshcipher *);
u_int cipher_seclen(const struct sshcipher *);
u_int cipher_authlen(const struct sshcipher *);
diff --git a/packet.c b/packet.c
index a71820f..377f608 100644
--- a/packet.c
+++ b/packet.c
@@ -55,6 +55,7 @@
#include <poll.h>
#include <signal.h>
#include <time.h>
+#include <util.h>
#ifdef WITH_ZLIB
#include <zlib.h>
@@ -850,6 +851,7 @@ ssh_set_newkeys(struct ssh *ssh, int mode)
const char *wmsg;
int r, crypt_type;
const char *dir = mode == MODE_OUT ? "out" : "in";
+ char blocks_s[FMT_SCALED_STRSIZE], bytes_s[FMT_SCALED_STRSIZE];
debug2_f("mode %d", mode);
@@ -917,20 +919,18 @@ ssh_set_newkeys(struct ssh *ssh, int mode)
}
comp->enabled = 1;
}
- /*
- * The 2^(blocksize*2) limit is too expensive for 3DES,
- * so enforce a 1GB limit for small blocksizes.
- * See RFC4344 section 3.2.
- */
- if (enc->block_size >= 16)
- *max_blocks = (u_int64_t)1 << (enc->block_size*2);
- else
- *max_blocks = ((u_int64_t)1 << 30) / enc->block_size;
+ *max_blocks = cipher_rekey_blocks(enc->cipher);
if (state->rekey_limit)
*max_blocks = MINIMUM(*max_blocks,
state->rekey_limit / enc->block_size);
- debug("rekey %s after %llu blocks", dir,
- (unsigned long long)*max_blocks);
+
+ strlcpy(blocks_s, "?", sizeof(blocks_s));
+ strlcpy(bytes_s, "?", sizeof(bytes_s));
+ if (*max_blocks * enc->block_size < LLONG_MAX) {
+ fmt_scaled((long long)*max_blocks, blocks_s);
+ fmt_scaled((long long)*max_blocks * enc->block_size, bytes_s);
+ }
+ debug("rekey %s after %s blocks / %sB data", dir, blocks_s,
bytes_s);
return 0;
}
_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev at mindrot.org<mailto:openssh-unix-dev at mindrot.org>
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev<https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev>