[[ NOTE: need to fix up my INBOX detection a bit. I may have sent from
my personal address. ]]
Ilya Maximets <[email protected]> writes:
> On 10/12/26 5:11 PM, Aaron Conole wrote:
>> This commit makes the ChaCha20 CSPRNG prefer using
>> OpenSSL's SIMD optimized ChaCha20 on platforms which
>> are compiled with such support. This represents a
>> major improvement on performance, without impacting
>> the underlying CSPRNG functionality.
>>
>> Signed-off-by: Aaron Conole <[email protected]>
>> ---
> A different LLM had the following to say about this patch
> (some parts seem reasonable to a human posting this, but not
> fully validated):
>
> ────────────────────────────────────────
> REVIEW: 6da48001f "random: Provide OpenSSL EVP cipher as a CSPRNG source."
> ────────────────────────────────────────
> commit 6da48001f44958ae6d32cf7b3cfd9c36fc8401a9 ("random: Provide OpenSSL EVP
> cipher as a CSPRNG source.")
> Author: Aaron Conole <[email protected]>
>
> This patch adds an OpenSSL EVP ChaCha20 backend for the cs_random CSPRNG
> in lib/random.c. When HAVE_OPENSSL is defined, it replaces the in-tree
> ChaCha20 implementation at compile time.
>
>> +static void
>> +csprng_seed(struct csprng *c)
>> +{
>> + uint8_t key[CHACHA_KEYLEN];
>> + uint8_t iv[CHACHA_K_INPUT];
>> +
>> + get_entropy_or_die(key, sizeof key);
>> + get_entropy_or_die(iv, sizeof iv);
>> +
>> + if (EVP_EncryptInit_ex(c->state, EVP_chacha20(), NULL, key, iv) != 1) {
>> + VLOG_FATAL("cs_random: EVP reseed failure.");
>> + }
>
> What happens here when OpenSSL can't provide ChaCha20? On OpenSSL 3 with
> FIPS-only default properties (RHEL in FIPS mode, for example), ChaCha20
> is not an approved algorithm. The implicit fetch would fail and this
> would call VLOG_FATAL on the first cs_random_bytes() call.
>
> The call chain looks like this:
>
> conntrack_init() or NAT tuple selection
> nat_random_uint32()
> cs_random_uint32()
> cs_random_bytes()
> csprng_seed()
> EVP_EncryptInit_ex() fails -> VLOG_FATAL
>
> On an affected host, ovs-vswitchd with a userspace datapath would exit
> when the datapath is created, or at the latest on the first NAT
> connection. Before this patch, cs_random_uint32() could not fail this
> way.
Okay, this is a good catch. We should keep the chacha20 fallback in the
case that OpenSSL EVP can't provide that stream.
>> +struct csprng {
>> +#ifndef HAVE_OPENSSL
>> + uint32_t state[16]; /* ChaCha20 state:
>> + * constant|key|counter|nonce. */
>> +#else
>> + EVP_CIPHER_CTX *state; /* EVP instance for OpenSSL. */
>> +#endif
> [...]
>> +#ifndef HAVE_OPENSSL
>
> The backend is picked only from HAVE_OPENSSL, which is set for TLS
> support. Could the in-tree ChaCha20 stay compiled in as a runtime
> fallback when EVP_EncryptInit_ex() fails? Also, there is no configure
> check for EVP_chacha20(), so won't builds against an OpenSSL with
> OPENSSL_NO_CHACHA fail to compile?
See above, we should check for OPENSSL_NO_CHACHA.
> ---
>
>> +static void
>> +csprng_refill(struct csprng *c)
>> +{
>> + static const uint8_t zero[CSPRNG_BUFSZ];
>> + int outl;
>> +
>> + if (EVP_EncryptUpdate(c->state, c->buf, &outl, zero, sizeof c->buf) !=
>> 1 ||
>> + outl != sizeof c->buf) {
>> + VLOG_FATAL("cs_random: EVP fill failure.");
>> + }
>> +
>> + c->pos = 0;
>> +}
>
> This EVP csprng_refill() doesn't do the fast key erasure that the
> built-in version does. The built-in version rekeys from the first 32
> bytes of each refill and then wipes them:
>
> csprng_rekey(c, c->buf);
> memset(c->buf, 0, CHACHA_KEYLEN);
>
> Here, the key and IV from csprng_seed() stay in the heap-allocated
> EVP_CIPHER_CTX until the next reseed, which comes after CSPRNG_RESEED
> (1600 KiB) of output. The local key[] and iv[] memsets in csprng_seed()
> don't clear the copies held inside the context. Since ChaCha20 is
> seekable, anyone who can read process memory (a core dump, for example)
> could regenerate all output since the last reseed. That includes the
> conntrack hash basis and NAT port choices. The backtracking window goes
> from 4 KiB to about 1.6 MiB.
>
> Could this rekey the EVP context from the first CHACHA_KEYLEN plus IV
> bytes of each refill and skip those bytes, the same way the C path
> does?
I can have an additional internal seed() function that will allow
pulling from the stream buffer. I didn't benchmark it, but it could
actually be 'faster' by reducing the syscalls.
>> +/* This is the EVP based CSPRNG using the OpenSSL EVP framework to create a
> [...]
>> + * Technically, we don't re-write the seeded variable on reinitialization,
>> which
>> + * represents a difference in forward secrecy between the two
>> implementations,
>
> This comment acknowledges the forward secrecy difference. The commit
> message, however, says the change is made without impacting the
> underlying CSPRNG functionality. Should the commit message mention
> this?
I can mention it. It really isn't such a huge deal, since
reconstruction would still only be for bytes in the buf[] in flight. If
I update the seed()/refill() functions as above, then I think it
probably could even qualify.
>> + /* c->buf isn't fully available for reading due to
>> + * the initial 32-bytes from the block being used as a
>> + * seeding counter. In the case of the OpenSSL EVP
>> + * implementation, we still skip to keep it simple,
>> + * although technically there are the extra KEYLEN bytes. */
>
> Is this comment accurate? The EVP csprng_refill() sets c->pos = 0 and
> serves every byte of c->buf, so nothing is skipped. Only the assertion
> bound is reduced.
It may be confusing. The skip isn't a good wording here, so I'll fix it
up. We simply treat the buffer smaller - but again with the above
changes, it won't matter.
> ---
>
>> {
>> +#ifndef HAVE_OPENSSL
>> const uint32_t input[16] = {
> [...]
>> ovs_assert(!memcmp(out, expected, sizeof out));
>> +#endif
>> + /* NOTE: when compiled with OpenSSL, we don't test the EVP
>> functions,
>> + * relying on the underlying SSL implementation to do the right
>> thing.
>> + * Keep the message below for the library.at test. */
>> printf("ok: RFC 8439 2.3.2 block function\n");
>
> In an OpenSSL build, test_random_main() prints the "ok: RFC 8439 2.3.2
> block function" line without running any check, so library.at passes
> without testing anything. The EVP path has no test at all, including
> the 16-byte IV layout (32-bit counter plus 96-bit nonce) passed to
> EVP_chacha20() in csprng_seed(). Could a known-answer test be added for
> the EVP path?
It seems silly to add code for this.
> Also, lib/random.h still declares chacha20_block() unconditionally:
>
> void chacha20_block(const uint32_t input[CHACHA_K_INPUT],
> uint8_t out[CHACHA_BLOCK]);
>
> Its definition in lib/random.c is now inside #ifndef HAVE_OPENSSL.
> Wouldn't any caller fail to link in OpenSSL builds? Should the
> declaration get the same guard?
Since we'll implement the chacha20 fallback, then this won't have a
guard, and the other internal functions won't either.
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev