This is an automated email from the ASF dual-hosted git repository.
acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx-apps.git
The following commit(s) were added to refs/heads/master by this push:
new 355d090d8 netutils/dropbear: back chacha20-poly1305 with NuttX
/dev/crypto
355d090d8 is described below
commit 355d090d838906c61a4931923b7c540105e09c96
Author: Felipe Moura <[email protected]>
AuthorDate: Fri Jul 24 20:18:38 2026 -0300
netutils/dropbear: back chacha20-poly1305 with NuttX /dev/crypto
Replace the bundled libtomcrypt chacha20-poly1305 implementation with an
adapter that drives the NuttX crypto device: the SSH construction maps onto
CRYPTO_CHACHA20_DJB (the original 64-bit counter/nonce ChaCha20
parameterization used by [email protected]) for the packet
length and payload streams, and onto CRYPTO_POLY1305 for the authentication
tag (plain MACs are driven in two steps through /dev/crypto: COP_FLAG_UPDATE
feeds the data, a final call retrieves the tag).
Signed-off-by: Felipe Moura <[email protected]>
---
netutils/dropbear/CMakeLists.txt | 4 +
netutils/dropbear/Kconfig | 6 +
netutils/dropbear/Makefile | 5 +
netutils/dropbear/port/dropbear_chachapoly.c | 389 +++++++++++++++++++++++++++
4 files changed, 404 insertions(+)
diff --git a/netutils/dropbear/CMakeLists.txt b/netutils/dropbear/CMakeLists.txt
index 96cbcc233..c429a2a0d 100644
--- a/netutils/dropbear/CMakeLists.txt
+++ b/netutils/dropbear/CMakeLists.txt
@@ -142,6 +142,10 @@ if(CONFIG_NETUTILS_DROPBEAR)
list(FILTER LIBTOMCRYPT_SRCS EXCLUDE REGEX ".*/mac/hmac/hmac_done\\.c$")
list(APPEND DROPBEAR_SRCS port/dropbear_ltc_hmac_sha256.c)
+ # Replace the bundled chacha20-poly1305 with the /dev/crypto adapter.
+ list(REMOVE_ITEM DROPBEAR_SRCS dropbear/src/chachapoly.c)
+ list(APPEND DROPBEAR_SRCS port/dropbear_chachapoly.c)
+
list(APPEND DROPBEAR_SRCS ${LIBTOMCRYPT_SRCS})
if(CONFIG_NETUTILS_DROPBEAR_SCP)
diff --git a/netutils/dropbear/Kconfig b/netutils/dropbear/Kconfig
index ae37135cb..99066ee2e 100644
--- a/netutils/dropbear/Kconfig
+++ b/netutils/dropbear/Kconfig
@@ -37,6 +37,12 @@ menuconfig NETUTILS_DROPBEAR
hash states (hashkeys() in common-kex.c), which cannot be
represented by a kernel crypto session.
+ The port also implements the [email protected]
cipher
+ through the NuttX crypto device (/dev/crypto), using the
+ CRYPTO_CHACHA20_DJB and CRYPTO_POLY1305 algorithms instead of
+ the bundled libtomcrypt implementation, which is dropped from
+ the build.
+
if NETUTILS_DROPBEAR
config NETUTILS_DROPBEAR_STACKSIZE
diff --git a/netutils/dropbear/Makefile b/netutils/dropbear/Makefile
index 145e5c5ae..f36398088 100644
--- a/netutils/dropbear/Makefile
+++ b/netutils/dropbear/Makefile
@@ -129,6 +129,11 @@ TOMCRYPT_SRCS := $(filter-out \
CSRCS += port/dropbear_ltc_hmac_sha256.c
+# Replace the bundled chacha20-poly1305 with the /dev/crypto adapter.
+
+CSRCS := $(filter-out dropbear/src/chachapoly.c,$(CSRCS))
+CSRCS += port/dropbear_chachapoly.c
+
CSRCS += $(TOMMATH_SRCS)
CSRCS += $(TOMCRYPT_SRCS)
diff --git a/netutils/dropbear/port/dropbear_chachapoly.c
b/netutils/dropbear/port/dropbear_chachapoly.c
new file mode 100644
index 000000000..f670d14ca
--- /dev/null
+++ b/netutils/dropbear/port/dropbear_chachapoly.c
@@ -0,0 +1,389 @@
+/****************************************************************************
+ * apps/netutils/dropbear/port/dropbear_chachapoly.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/* [email protected] backed by the NuttX crypto device.
+ *
+ * The SSH construction (see OpenSSH PROTOCOL.chacha20poly1305) uses the
+ * original DJB ChaCha20 parameterization: a 64-bit block counter in state
+ * words 12..13 and a 64-bit nonce (the packet sequence number, big endian)
+ * in words 14..15. This maps to the kernel CRYPTO_CHACHA20_DJB transform,
+ * whose 16-byte IV is loaded verbatim into words 12..15 as a 64-bit
+ * little-endian counter followed by the 64-bit nonce. The Poly1305 tag is
+ * computed with the kernel CRYPTO_POLY1305 transform, keyed with the first
+ * keystream block (counter 0) of the main key, as the protocol requires.
+ */
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include "includes.h"
+
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <crypto/cryptodev.h>
+
+#include "algo.h"
+#include "dbutil.h"
+#include "chachapoly.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define CHACHA20_KEY_LEN 32
+#define CHACHA20_BLOCKSIZE 8
+#define CHACHA20_IV_LEN 16
+#define POLY1305_KEY_LEN 32
+#define POLY1305_TAG_LEN 16
+
+/* The keystream comes from the NuttX crypto device, so each upstream
+ * chacha_state is unused and its input[] buffer just stores the 32-byte key,
+ * keeping the upstream header unpatched.
+ */
+
+#define KEY_MAIN(s) ((FAR unsigned char *)(s)->chacha.input)
+#define KEY_HEADER(s) ((FAR unsigned char *)(s)->header.input)
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct ltc_cipher_descriptor g_dropbear_chachapoly_dummy =
+{
+ .name = NULL
+};
+
+static const struct dropbear_hash g_dropbear_chachapoly_mac =
+{
+ NULL,
+ POLY1305_KEY_LEN,
+ POLY1305_TAG_LEN
+};
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+const struct dropbear_cipher dropbear_chachapoly =
+{
+ &g_dropbear_chachapoly_dummy,
+ CHACHA20_KEY_LEN * 2,
+ CHACHA20_BLOCKSIZE
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/* Open a fresh /dev/crypto session descriptor. NuttX file descriptors are
+ * owned by the task group, so the fd is never cached across calls: each
+ * operation opens it in the task that uses it and closes it when done.
+ */
+
+static int dropbear_cryptodev_open(void)
+{
+ int fd;
+ int cfd;
+
+ fd = open("/dev/crypto", O_RDWR);
+ if (fd < 0)
+ {
+ return -1;
+ }
+
+ if (ioctl(fd, CRIOGET, &cfd) < 0)
+ {
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+
+ /* Keep the descriptor out of the NSH shells Dropbear forks and execs for
+ * each SSH session.
+ */
+
+ if (fcntl(cfd, F_SETFD, FD_CLOEXEC) < 0)
+ {
+ close(cfd);
+ return -1;
+ }
+
+ return cfd;
+}
+
+/* One ChaCha20 (DJB layout) pass: encrypt/decrypt len bytes with the given
+ * 64-bit block counter and the packet sequence number as nonce.
+ */
+
+static int dropbear_chacha(int cfd, FAR const unsigned char *key,
+ unsigned int seq, uint64_t counter,
+ FAR const unsigned char *in,
+ FAR unsigned char *out, size_t len)
+{
+ struct session_op session;
+ struct crypt_op cryp;
+ unsigned char iv[CHACHA20_IV_LEN];
+ int ret = CRYPT_ERROR;
+ int i;
+
+ /* IV = 64-bit little-endian block counter || 64-bit nonce. The nonce is
+ * the packet sequence number stored big endian, as OpenSSH does.
+ */
+
+ for (i = 0; i < 8; i++)
+ {
+ iv[i] = (unsigned char)(counter >> (8 * i));
+ }
+
+ STORE64H((uint64_t)seq, iv + 8);
+
+ memset(&session, 0, sizeof(session));
+ session.cipher = CRYPTO_CHACHA20_DJB;
+ session.key = (caddr_t)key;
+ session.keylen = CHACHA20_KEY_LEN;
+ if (ioctl(cfd, CIOCGSESSION, &session) < 0)
+ {
+ return CRYPT_ERROR;
+ }
+
+ memset(&cryp, 0, sizeof(cryp));
+ cryp.ses = session.ses;
+ cryp.op = COP_ENCRYPT;
+ cryp.len = len;
+ cryp.src = (caddr_t)in;
+ cryp.dst = (caddr_t)out;
+ cryp.iv = (caddr_t)iv;
+ cryp.ivlen = sizeof(iv);
+ if (ioctl(cfd, CIOCCRYPT, &cryp) == 0)
+ {
+ ret = CRYPT_OK;
+ }
+
+ ioctl(cfd, CIOCFSESSION, &session.ses);
+ return ret;
+}
+
+static int dropbear_poly1305(int cfd, FAR const unsigned char *key,
+ FAR const unsigned char *in, size_t len,
+ FAR unsigned char *tag)
+{
+ struct session_op session;
+ struct crypt_op cryp;
+ int ret = CRYPT_ERROR;
+
+ memset(&session, 0, sizeof(session));
+ session.mac = CRYPTO_POLY1305;
+ session.mackey = (caddr_t)key;
+ session.mackeylen = POLY1305_KEY_LEN;
+ if (ioctl(cfd, CIOCGSESSION, &session) < 0)
+ {
+ return CRYPT_ERROR;
+ }
+
+ /* Plain (non-HMAC) MACs are driven in two steps through /dev/crypto:
+ * COP_FLAG_UPDATE feeds the data, then a final call without the flag
+ * writes out the tag.
+ */
+
+ memset(&cryp, 0, sizeof(cryp));
+ cryp.ses = session.ses;
+ cryp.op = COP_ENCRYPT;
+ cryp.flags = COP_FLAG_UPDATE;
+ cryp.len = len;
+ cryp.src = (caddr_t)in;
+ if (ioctl(cfd, CIOCCRYPT, &cryp) == 0)
+ {
+ cryp.flags = 0;
+ cryp.len = 0;
+ cryp.src = NULL;
+ cryp.mac = (caddr_t)tag;
+ if (ioctl(cfd, CIOCCRYPT, &cryp) == 0)
+ {
+ ret = CRYPT_OK;
+ }
+ }
+
+ ioctl(cfd, CIOCFSESSION, &session.ses);
+ return ret;
+}
+
+static int dropbear_chachapoly_start(int cipher,
+ FAR const unsigned char *iv,
+ FAR const unsigned char *key,
+ int keylen, int num_rounds,
+ FAR void *cipher_state)
+{
+ FAR dropbear_chachapoly_state *state = cipher_state;
+ int cfd;
+
+ UNUSED(cipher);
+ UNUSED(iv);
+
+ if (keylen != CHACHA20_KEY_LEN * 2 || num_rounds != 0)
+ {
+ return CRYPT_ERROR;
+ }
+
+ /* Validate that the crypto device is reachable at cipher setup. */
+
+ cfd = dropbear_cryptodev_open();
+ if (cfd < 0)
+ {
+ return CRYPT_ERROR;
+ }
+
+ close(cfd);
+
+ memcpy(KEY_MAIN(state), key, CHACHA20_KEY_LEN);
+ memcpy(KEY_HEADER(state), key + CHACHA20_KEY_LEN, CHACHA20_KEY_LEN);
+ return CRYPT_OK;
+}
+
+static int dropbear_chachapoly_crypt(unsigned int seq,
+ FAR const unsigned char *in,
+ FAR unsigned char *out,
+ unsigned long len, unsigned long taglen,
+ FAR void *cipher_state,
+ int direction)
+{
+ FAR dropbear_chachapoly_state *state = cipher_state;
+ unsigned char key[POLY1305_KEY_LEN];
+ unsigned char tag[POLY1305_TAG_LEN];
+ unsigned char zero[POLY1305_KEY_LEN];
+ int cfd;
+ int ret = CRYPT_ERROR;
+
+ if (len < 4 || taglen != POLY1305_TAG_LEN)
+ {
+ return CRYPT_ERROR;
+ }
+
+ cfd = dropbear_cryptodev_open();
+ if (cfd < 0)
+ {
+ return CRYPT_ERROR;
+ }
+
+ /* Poly1305 key = first keystream block of the main key at counter 0 */
+
+ memset(zero, 0, sizeof(zero));
+ if (dropbear_chacha(cfd, KEY_MAIN(state), seq, 0, zero, key,
+ sizeof(key)) != CRYPT_OK)
+ {
+ goto out;
+ }
+
+ if (direction == LTC_DECRYPT)
+ {
+ if (dropbear_poly1305(cfd, key, in, len, tag) != CRYPT_OK)
+ {
+ goto out;
+ }
+
+ if (constant_time_memcmp(in + len, tag, sizeof(tag)) != 0)
+ {
+ goto out;
+ }
+ }
+
+ /* Packet length: header key, counter 0. Payload: main key, counter 1. */
+
+ if (dropbear_chacha(cfd, KEY_HEADER(state), seq, 0, in, out, 4) !=
+ CRYPT_OK)
+ {
+ goto out;
+ }
+
+ if (dropbear_chacha(cfd, KEY_MAIN(state), seq, 1, in + 4, out + 4,
+ len - 4) != CRYPT_OK)
+ {
+ goto out;
+ }
+
+ if (direction == LTC_ENCRYPT)
+ {
+ if (dropbear_poly1305(cfd, key, out, len, out + len) != CRYPT_OK)
+ {
+ goto out;
+ }
+ }
+
+ ret = CRYPT_OK;
+
+out:
+ close(cfd);
+ zeromem(key, sizeof(key));
+ zeromem(tag, sizeof(tag));
+ return ret;
+}
+
+static int
+dropbear_chachapoly_getlength(unsigned int seq, FAR const unsigned char *in,
+ FAR unsigned int *outlen, unsigned long len,
+ FAR void *cipher_state)
+{
+ FAR dropbear_chachapoly_state *state = cipher_state;
+ unsigned char buf[4];
+ int cfd;
+ int ret;
+
+ if (len < sizeof(buf))
+ {
+ return CRYPT_ERROR;
+ }
+
+ cfd = dropbear_cryptodev_open();
+ if (cfd < 0)
+ {
+ return CRYPT_ERROR;
+ }
+
+ ret = dropbear_chacha(cfd, KEY_HEADER(state), seq, 0, in, buf,
+ sizeof(buf));
+ close(cfd);
+ if (ret != CRYPT_OK)
+ {
+ return CRYPT_ERROR;
+ }
+
+ LOAD32H(*outlen, buf);
+ return CRYPT_OK;
+}
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+const struct dropbear_cipher_mode dropbear_mode_chachapoly =
+{
+ dropbear_chachapoly_start,
+ NULL,
+ NULL,
+ dropbear_chachapoly_crypt,
+ dropbear_chachapoly_getlength,
+ &g_dropbear_chachapoly_mac
+};