crypto: Add standalone CHACHA20 stream cipher on the unified enc path.#19396
Conversation
|
please fix the conflict, @ThePassionate |
2d0582a to
a4ef1d6
Compare
…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>
a4ef1d6 to
08edec3
Compare
|
Hello @ThePassionate , nice done. 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 . |
|
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. |
|
Hello, I added DJB algo in your implementation, please take a look this patch: https://github.com/FelipeMdeO/nuttx/tree/feature/crypto-chacha20-djb. |
let's merge your change after this pr get merged. |
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.
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
Note: Please adhere to Contributing Guidelines.