Skip to content

netutils/dropbear: initial Dropbear SSH server port for NuttX#3532

Open
FelipeMdeO wants to merge 2 commits into
apache:masterfrom
FelipeMdeO:feature/dropbear-esp32c3-port
Open

netutils/dropbear: initial Dropbear SSH server port for NuttX#3532
FelipeMdeO wants to merge 2 commits into
apache:masterfrom
FelipeMdeO:feature/dropbear-esp32c3-port

Conversation

@FelipeMdeO

Copy link
Copy Markdown
Contributor

Summary

This PR adds two related changes that together bring up an SSH server
on the ESP32-C3 DevKit board using the Dropbear application:

boards/risc-v/esp32c3/esp32c3-devkit/configs/dropbear

A new dropbear defconfig is introduced for the ESP32-C3 DevKit board.
It wires up the Dropbear SSH server application together with:

  • Wi-Fi STA mode with DHCP client (WAPI tooling included for link
    bring-up at boot).
  • urandom device for key material.
  • SPIFFS on SPI flash (/data mountpoint) to persist the host key and
    the password database.
  • FSUTILS_PASSWD pointing to /data/passwd as the credential store,
    replacing a previous Dropbear-specific password-file path.
  • ECDSA host key stored at /data/dropbear_ecdsa_host_key.
  • NSH autostart of the dropbear task on every boot.
  • PTY support and Ctrl-C signal delivery enabled for interactive
    sessions.
  • CONFIG_NETUTILS_DROPBEAR_STACKSIZE pinned to 65536 bytes; the
    default 32 KiB overflows during key exchange on this RISC-V target.
  • CONFIG_NETUTILS_DROPBEAR_LISTEN_RETRY_MAX=120 so the daemon keeps
    retrying until the Wi-Fi link is fully up.

Wi-Fi credentials (myssid / mypasswd) are placeholders and must be
set via menuconfig before flashing.

crypto: expose ChaCha20 stream helpers

Dropbear uses the chacha20-poly1305@openssh.com cipher, which requires
a stateful, multi-call ChaCha20 stream interface rather than the single-
block interface currently exposed by crypto/chachapoly.c. Three helpers
and a context struct are added:

  • struct chacha20_stream_ctx — opaque wrapper around chacha_ctx.
  • chacha20_stream_setkey() — initialise the key.
  • chacha20_stream_ivctr64() — set IV and 64-bit counter.
  • chacha20_stream_crypt() — encrypt/decrypt an arbitrary-length buffer.

All three functions are thin wrappers over the existing chacha_*
primitives; no new algorithm code is introduced.

Impact

  • New board configuration: the dropbear defconfig is additive and
    does not affect any existing configuration.
  • New public API: three functions and one struct are added to
    include/crypto/chachapoly.h. The change is purely additive; existing
    users of chacha20_setkey / chacha20_crypt are unaffected.
  • Build: no impact on boards or configurations that do not select
    CONFIG_NETUTILS_DROPBEAR.
  • Security: host keys and credentials live on a SPIFFS partition
    under /data; they are generated at first run and persist across
    reboots. Wi-Fi credentials must be provisioned by the user before
    flashing.

Testing

Host: Linux x86_64, GCC RISC-V toolchain
Board: ESP32-C3 DevKit (rev 0.4)

Build:

./tools/configure.sh esp32c3-devkit:dropbear
make -j$(nproc)
make flash ESPTOOL_PORT=/dev/ttyUSB0

First-time user provisioning (serial console):

The NuttX passwd file lives on SPIFFS (/data/passwd) and is empty on a
fresh flash. Before the first SSH login, create a user from the NSH
serial console:

nsh> useradd root <password>

The ECDSA host key is generated automatically on first boot.

Boot log shows Dropbear listening after Wi-Fi association:

NuttShell (NSH) NuttX-12.6.0
nsh> loaded ECDSA P-256 host key from /data/dropbear_ecdsa_host_key
     using NuttX passwd auth at /data/passwd
     dropbear: listening on port 2222

SSH connection from the host:

$ ssh -p 2222 root@<board-ip>
root@<board-ip>'s password: <password>
NuttShell (NSH) NuttX-12.6.0
nsh>

@acassis

acassis commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

@FelipeMdeO nice work! Kudos!!!

Some suggestions to improve this PR:

  • many of these patches are just to fix the UNUSED()s, moving it from function parameters to inside at the end of the function, please talk with the original author, maybe he accepts to move it to the end of the function;

Comment thread nshlib/nsh_dropbear.c
for (i = 0; i < len; i++)
ninfo("Starting the Dropbear SSH server\n");

ret = nsh_parse(&pstate->cn_vtbl, cmdline);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why to use it instead of doing like Telnet does: "nsh_session(pstate, NSH_LOGIN_TELNET, argc, argv);" ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello Alan, nsh_dropbearstart() mirrors nsh_telnetstart() (nsh_telnetd.c), not nsh_telnetmain(). This is a bit tricky, but let me try to explain.

SSH can't reuse the nsh_session() path: unlike Telnet's line protocol, each SSH connection multiplexes channels and the shell must run on a PTY (for SIGINT/Ctrl-C, window size, raw mode). So the per-session NSH is spawned inside the Dropbear app (dropbear_nshsession.c) over a PTY pair, with no equivalent nsh_session() call at the nshlib layer.

We have this diff "approach" because telnet can hand the socket straight to nsh_session() because it's a plain-text line protocol while SSH transfer data uses encrypted data.

Comment thread netutils/dropbear/port/nuttx_config.h Outdated
Comment on lines +27 to +36
#define DISABLE_LASTLOG 1
#define DISABLE_PAM 1
#define DISABLE_PUTUTLINE 1
#define DISABLE_PUTUTXLINE 1
#define DISABLE_SYSLOG 1
#define DISABLE_UTMP 1
#define DISABLE_UTMPX 1
#define DISABLE_WTMP 1
#define DISABLE_WTMPX 1
#define DISABLE_ZLIB 1

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these variable could be controlled from Kconfig definitions

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello Alan, good point. Most of that DISABLE_* block isn't really configurable, for example: DISABLE_PAM, DISABLE_LASTLOG, but other configs can be controlled by user, like CONFIG_LIB_ZLIB.
I will move to Kconfig what can be controlled by user.

Comment on lines +69 to +73
#define HAVE_STRUCT_ADDRINFO 1
#define HAVE_STRUCT_IN6_ADDR 1
#define HAVE_STRUCT_SOCKADDR_IN6 1
#define HAVE_STRUCT_SOCKADDR_STORAGE 1
#define HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY 1

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some of these definitions cannot be hard-code, i.e.: HAVE_STRUCT_SOCKADDR_IN6, HAVE_STRUCT_SOCKADDR_IN6, could be disabled if IPv6 support on NuttX is not enabled.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

About HAVE_STRUCT_SOCKADDR_IN6:
The fake-rfc2553.h dropbear file has the source code below:

#ifndef HAVE_STRUCT_IN6_ADDR
struct in6_addr {
	u_int8_t	s6_addr[16];
};
#endif /* !HAVE_STRUCT_IN6_ADDR */

The issue is that the nuttx has the same struct in6_addr, so I need "disable" this code sector to be able compile.

Look NuttX file netinet/in.h, it always define s6_addr:

#define s6_addr               in6_u.u6_addr8
#define s6_addr16             in6_u.u6_addr16
#define s6_addr32             in6_u.u6_addr32

I am using this macro equal 1 because s6_addr always exist in nuttx.

Comment thread netutils/dropbear/Kconfig Outdated
Comment thread netutils/dropbear/Kconfig Outdated
Comment thread fsutils/passwd/passwd_adduser.c Outdated
@FelipeMdeO

Copy link
Copy Markdown
Contributor Author

I am reviewing your comments and doing improvement in the source code, I will ping you all to review again in the next days. tks.

@FelipeMdeO

Copy link
Copy Markdown
Contributor Author

@FelipeMdeO nice work! Kudos!!!

Some suggestions to improve this PR:

* many of these patches are just to fix the UNUSED()s, moving it from function parameters to inside at the end of the function, please talk with the original author, maybe he accepts to move it to the end of the function;

Hello @acassis, @xiaoxiang781216. Could you please confirm whether you would like me to contact the original author? I am concerned about his position regarding Xiaoxiang’s PR: mkj/dropbear#437

@FelipeMdeO

Copy link
Copy Markdown
Contributor Author

Guys, I opened the following discussion in the dropbear's repo: mkj/dropbear#440

Integrated SSH daemon authenticating against FSUTILS_PASSWD, with an
ECDSA P-256 host key and an NSH session over a PTY per connection.
Built from the upstream tarball with NuttX crypto and POSIX shims.

Signed-off-by: Felipe Moura <moura.fmo@gmail.com>
Add README describing build, configuration and usage of the port.

Signed-off-by: Felipe Moura <moura.fmo@gmail.com>
@FelipeMdeO FelipeMdeO force-pushed the feature/dropbear-esp32c3-port branch from 9c4ff8e to af8bba4 Compare June 14, 2026 21:48
@FelipeMdeO FelipeMdeO marked this pull request as ready for review June 14, 2026 21:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants