Skip to content

crypto: Add standalone CHACHA20 stream cipher on the unified enc path.#19396

Merged
acassis merged 1 commit into
apache:masterfrom
ThePassionate:crypto-chacha20-unified
Jul 11, 2026
Merged

crypto: Add standalone CHACHA20 stream cipher on the unified enc path.#19396
acassis merged 1 commit into
apache:masterfrom
ThePassionate:crypto-chacha20-unified

Conversation

@ThePassionate

Copy link
Copy Markdown
Contributor

Summary

Add a raw CRYPTO_CHACHA20 stream cipher to the OCF crypto framework (/dev/crypto) so applications such as an SSH server (chacha20-poly1305) can use ChaCha20 directly, not only as part of the ChaCha20-Poly1305 AEAD.

Instead of introducing a separate multi-buffer stream path (parallel encrypt_multi/decrypt_multi callbacks), extend the existing enc_xform encrypt/decrypt callback signature with a length argument:

void (*encrypt)(caddr_t, FAR uint8_t *, size_t len);
void (*decrypt)(caddr_t, FAR uint8_t *, size_t len);

With that single change every cipher, block or stream, flows through the same swcr_encdec path. swcr_encdec already handles a short final block via buflen = MIN(i, blocksize), so arbitrary-length data works without a second code path. This is exactly how the existing stream ciphers (AES-CTR/OFB/CFB) already behave: the cipher keeps its own counter in the context and swcr_encdec feeds it whole blocks (only the last one may be shorter). chacha20_crypt likewise relies on the underlying chacha state block counter (input[12]) to continue the keystream across calls, so no per-call keystream caching is needed.

  • xform.h/xform.c: add size_t len to encrypt/decrypt; add enc_xform_chacha20 (blocksize 64, 12-byte IV, RFC 8439 layout).
  • chachapoly.c/chacha_private.h: chacha_ivsetup now uses a 4-byte counter and a 12-byte nonce (RFC 8439); chacha20_crypt encrypts the requested length in one pass, mirroring aes_ctr_crypt.
  • cryptodev.c/cryptosoft.c: register CRYPTO_CHACHA20 as a txform cipher and route new sessions to enc_xform_chacha20.

This keeps all ciphers on one uniform path instead of maintaining two, and any future stream cipher drops in with just an xform table entry.

Impact

extends an internal kernel callback signature (enc_xform encrypt/decrypt). All in-tree implementations are updated in the same commit and the user-facing /dev/crypto ABI is unchanged, so this is self-contained and not a breaking change for existing configurations.

Testing

Build host: Ubuntu Linux x86_64, GCC (host sim toolchain)
Target: sim:crypto (CONFIG_ARCH=sim)
Enabled regression tests (cover both code paths through swcr_encdec):

3DES-CBC, AES-CBC — block ciphers, non-reinit CBC branch
AES-CTR — stream cipher, reinit branch (same path as the new CHACHA20)
AES-XTS — block cipher with reinit
CHACHA20 — new algorithm, incl. a 375-byte multi-block vector
HMAC — auth path

    NuttShell (NSH) NuttX-13.0.0-RC0
    nsh> des3cbc
    ok, encrypt with /dev/crypto, decrypt with /dev/crypto
    nsh> aescbc
    aescbc test ok
    nsh> aesctr
    OK test vector 0
    OK test vector 1
    OK test vector 2
    OK test vector 3
    OK test vector 4
    OK test vector 5
    OK test vector 6
    OK test vector 7
    OK test vector 8
    nsh> aesxts
    OK encrypt test vector 0
    OK decrypt test vector 0
    OK encrypt test vector 1
    OK decrypt test vector 1
    OK encrypt test vector 2
    OK decrypt test vector 2
    OK encrypt test vector 3
    OK decrypt test vector 3
    OK encrypt test vector 4
    OK decrypt test vector 4
    OK encrypt test vector 5
    OK decrypt test vector 5
    OK encrypt test vector 6
    OK decrypt test vector 6
    OK encrypt test vector 7
    OK decrypt test vector 7
    OK encrypt test vector 8
    OK decrypt test vector 8
    OK encrypt test vector 9
    OK decrypt test vector 9
    OK encrypt test vector 10
    OK decrypt test vector 10
    OK encrypt test vector 11
    OK decrypt test vector 11
    OK encrypt test vector 12
    OK decrypt test vector 12
    OK encrypt test vector 13
    OK decrypt test vector 13
    nsh> chacha20
    run(0) start
    OK test vector 0
    run(1) start
    OK test vector 1
    chacha20: 2/2 vectors passed
    nsh> hmac
    hmac md5 success
    hmac md5 success
    hmac md5 success
    hmac md5 success
    hmac md5 success
    hmac sha1 success
    hmac sha1 success
    hmac sha1 success
    hmac sha1 success
    hmac sha1 success
    hmac sha256 success
    hmac sha256 success
    hmac sha256 success
    hmac sha256 success
    hmac sha256 success
    nsh>

Note: Please adhere to Contributing Guidelines.

@xiaoxiang781216

Copy link
Copy Markdown
Contributor

please fix the conflict, @ThePassionate

@ThePassionate ThePassionate force-pushed the crypto-chacha20-unified branch from 2d0582a to a4ef1d6 Compare July 10, 2026 12:37
@github-actions github-actions Bot added Size: M The size of the change in this PR is medium Area: Crypto labels Jul 10, 2026
…once.

Expose the ChaCha20 stream cipher and the ChaCha20-Poly1305 AEAD through
the OCF crypto framework (/dev/crypto) so applications such as an SSH
server (chacha20-poly1305) can use them directly, and fix the underlying
ChaCha nonce/counter layout so both match RFC 8439.

RFC 8439 nonce layout fix
-------------------------
chacha_ivsetup() previously used the original DJB layout: a 64-bit block
counter (input[12..13]) followed by a 64-bit nonce (input[14..15]). RFC
8439 defines a 32-bit block counter (input[12]) and a 96-bit / 12-byte
nonce (input[13..15]). With the old layout the existing ChaCha20-Poly1305
AEAD could not reproduce the RFC 8439 test vectors (the last 4 bytes of a
12-byte nonce were consumed as the high half of the counter). This
commit switches chacha_ivsetup() to the RFC 8439 layout and updates the
ChaCha20-Poly1305 one-shot helpers to pass a 12-byte nonce accordingly.

Standalone ChaCha20 on the unified enc path
-------------------------------------------
Instead of introducing a separate multi-buffer stream path (parallel
encrypt_multi/decrypt_multi callbacks), extend the existing enc_xform
encrypt/decrypt callback signature with a length argument:

  void (*encrypt)(caddr_t, FAR uint8_t *, size_t len);
  void (*decrypt)(caddr_t, FAR uint8_t *, size_t len);

With that single change every cipher, block or stream, flows through the
same swcr_encdec path. swcr_encdec already handles a short final block
via buflen = MIN(i, blocksize), so arbitrary-length data works without a
second code path. This is exactly how the existing stream ciphers
(AES-CTR/OFB/CFB) already behave: the cipher keeps its own counter in the
context and swcr_encdec feeds it whole blocks (only the last one may be
shorter). chacha20_crypt likewise relies on the underlying chacha state
block counter (input[12]) to continue the keystream across calls, so no
per-call keystream caching is needed.

  * chacha_private.h: chacha_ivsetup uses a 4-byte counter and a 12-byte
    nonce (RFC 8439).
  * chachapoly.c / chachapoly.h: split reinit into chacha20_reinit (raw,
    counter 0) and chachapoly_reinit (AEAD, counter 1); chacha20_crypt
    takes a length and encrypts it in one pass, mirroring aes_ctr_crypt;
    12-byte nonce for the one-shot AEAD helpers.
  * xform.h / xform.c: add size_t len to encrypt/decrypt; add
    enc_xform_chacha20 (blocksize 64, 12-byte IV).
  * cryptodev.c / cryptosoft.c: register CRYPTO_CHACHA20 as a txform
    cipher, route new sessions to enc_xform_chacha20, feed the AEAD AAD
    through crp_aad/crp_aadlen, and handle the short final block in
    swcr_encdec.
  * cryptodev.h: add CRYPTO_CHACHA20; bump EALG_MAX_BLOCK_LEN to 64.

This keeps all ciphers on one uniform path instead of maintaining two,
and any future stream cipher drops in with just an xform table entry.

Impact: extends an internal kernel callback signature (enc_xform
encrypt/decrypt). All in-tree implementations are updated in the same
commit and the user-facing /dev/crypto ABI is unchanged, so this is
self-contained and not a breaking change for existing configurations.

Testing:
  Build host: Ubuntu Linux x86_64, GCC (host sim toolchain)
  Target: sim:crypto (CONFIG_ARCH=sim)
  Ran the crypto test apps. ChaCha20 uses RFC 8439 2.4.2 vectors
  (including a 375-byte multi-block vector exercising cross-block counter
  continuity); ChaCha20-Poly1305 uses the RFC 8439 2.8.2 AEAD vector.
  A full regression of the other ciphers was run to confirm the extended
  encrypt/decrypt signature does not change their behaviour:

    nsh> chacha20
    chacha20: 2/2 vectors passed
    nsh> chachapoly
    OK test vector 0
    chachapoly: 1/1 vectors passed
    nsh> des3cbc          -> all vectors OK
    nsh> aescbc           -> all vectors OK
    nsh> aesctr           -> all vectors OK
    nsh> aesxts           -> 14 vectors OK (encrypt + decrypt)
    nsh> hmac             -> md5 / sha1 / sha256 all success

Signed-off-by: makejian <makejian@xiaomi.com>
@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown

MemBrowse Memory Report

No memory changes detected for:

@ThePassionate ThePassionate force-pushed the crypto-chacha20-unified branch from a4ef1d6 to 08edec3 Compare July 10, 2026 12:43
@FelipeMdeO

Copy link
Copy Markdown
Contributor

Hello @ThePassionate , nice done.
I believe your implementation is more mature than mine because you've likely been using and testing it for longer, thanks for sharing.

I will test dropbear app using it and I share the results with you, can you wait 1 ~ 2 days, please?

Also, thanks for sharing @xiaoxiang781216 .

@FelipeMdeO

Copy link
Copy Markdown
Contributor

Hello @ThePassionate and @xiaoxiang781216, one compatibility issue I'd like to flag, though, since my use case is exactly the one mentioned in your summary (SSH chacha20-poly1305@openssh.com): this PR changes chacha_ivsetup to the RFC 8439/IETF layout (32-bit counter in input[12] + 96-bit nonce in input[13..15]), but the OpenSSH cipher uses the original DJB construction — 64-bit counter (input[12..13]) + 64-bit nonce (input[14..15]).

Would you be open to accommodating the DJB 64/64 variant as well — e.g. accepting a 16-byte IV (8-byte LE counter + 8-byte nonce) like FreeBSD's enc_xform_chacha20 does, or keeping both layouts selectable? With that, your unified path would cover the SSH case too and I can build the Dropbear side directly on top of it.

@FelipeMdeO

Copy link
Copy Markdown
Contributor

Hello, I added DJB algo in your implementation, please take a look this patch: https://github.com/FelipeMdeO/nuttx/tree/feature/crypto-chacha20-djb.
I can open PR after your merge or you can check and add it to your PR.

@xiaoxiang781216

Copy link
Copy Markdown
Contributor

Hello, I added DJB algo in your implementation, please take a look this patch: https://github.com/FelipeMdeO/nuttx/tree/feature/crypto-chacha20-djb. I can open PR after your merge or you can check and add it to your PR.

let's merge your change after this pr get merged.

@acassis acassis merged commit bdd68ed into apache:master Jul 11, 2026
53 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area: Crypto Size: M The size of the change in this PR is medium

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants