https://gitlab.synchro.net/main/sbbs/-/commit/8f25a245bb9a6abc4b191b69
Added Files:
3rdp/dist/Botan.tar.xz src/build/botan.gmake src/ssh/posix/threads.c threads.h src/ssh/win32/threads.c threads.h src/syncterm/ini_crypt.c ini_crypt.h src/syncterm/legacy_ciphers/idea.c legacy_ciphers.h rc2.c register.c src/syncterm/xp_crypt.h xp_crypt_botan3.cpp xp_crypt_none.c xp_crypt_openssl.c xp_sndfile.c xp_sndfile.h xp_tls.h xp_tls_botan3.cpp xp_tls_none.c xp_tls_openssl.c src/xpdev/extdeps.mk
Modified Files:
.gitlab-ci-unix.yml .gitlab-ci.yml 3rdp/build/Common.gmake GNUmakefile src/build/Common.gmake src/doors/gac/gac_bj/src/GNUmakefile src/doors/gac/gac_fc/src/GNUmakefile src/doors/gac/gac_wh/src/GNUmakefile src/ssh/CMakeLists.txt TODO.md deucessh-portable.h src/ssh/kex/sntrup761.c src/ssh/key_algo/rsa-sha2-256-botan.cpp rsa-sha2-512-botan.cpp ssh-ed25519-botan.cpp src/ssh/ssh-internal.h ssh-trans.c src/syncterm/CHANGES CMakeLists.txt GNUmakefile audio_apc.c bbslist.c bbslist.h build.bat conn.c release.bat ssh.c ssh.h syncterm.c syncterm.h targets.mk telnets.c webget.c src/xpdev/CMakeLists.txt Common.gmake GNUmakefile ini_file.c ini_file.h xpbeep.c
Log Message:
SyncTERM: drop Cryptlib — move SSH to DeuceSSH + TLS/symmetric crypto
to OpenSSL or Botan 3
SyncTERM's SSH, TLS (TelnetS + HTTPS cache refresh), and encrypted-
bbslist.ini paths all ran through a single monolithic Cryptlib
dependency. Cryptlib is aging, actively deprecated in parts of
Synchronet, and its opaque session model fit awkwardly with
SyncTERM's ring-buffered I/O threads. This branch ports all of it:
- SSH: Cryptlib → DeuceSSH (src/ssh/, from-scratch library
already maintained in this repo).
- TLS client: Cryptlib → xp_tls shim backed by OpenSSL or Botan 3.
- Encrypted
bbslist.ini: Cryptlib KDF + ciphers → xp_crypt shim backed by the
same two backends.
DeuceSSH's explicit-callback API maps cleanly onto SyncTERM's ring
buffers; its test suite + branch coverage live alongside. All three
workloads share a single backend choice at compile time.
Architecture
------------
- xp_tls.[ch] (syncterm/): thin client-TLS API — open/push/pop/
flush/close — matching the shape of the old inline
cryptPushData / cryptPopData calls in telnets.c and
webget.c. OpenSSL / Botan 3 / none stubs.
- xp_crypt.[ch] (syncterm/): thin symmetric-crypto + KDF API —
xp_crypt_open(algo, keySize, salt, kdf, pwd, encrypt)
→ handle-based process(buf, n) semantics. Same
backend trio.
- ini_crypt.c (syncterm/): iniReadEncryptedFile /
iniWriteEncryptedFile built on xp_crypt + ini_file.c.
Moved out of xpdev so libxpdev / libsbbs /
Synchronet server binaries don't transitively
depend on OpenSSL / Botan 3 when they don't use TLS
or encrypted INI themselves.
- src/ssh/ DeuceSSH: src/syncterm/ssh.c rewritten against its
native session + channel API. Algorithm preferences
registered once at init; host-key callback does
SHA-256 fingerprint verification; authentication
tries pubkey → password → keyboard-interactive;
channel setup passes terminal type + PTY size; resize
via dssh_chan_send_window_change.
- SFTP pubkey
upload: ssh.c's add_public_key() rewritten on top of a
DeuceSSH "sftp" subsystem channel. src/sftp/ itself
had zero Cryptlib references and is unchanged.
Encrypted bbslist.ini format
----------------------------
Reader honours both on-disk formats so existing files keep opening:
v1 (Cryptlib era): PBKDF2-HMAC-SHA256, KDFiterations literal. Any
of 3DES / IDEA / CAST / RC2 / RC4 / AES / ChaCha20.
v2 (new writes): scrypt, N=2^15 / r=8 / p=1 by default — KDF
parameters embedded in the header so the reader
never has to guess. Write-side cipher choice
restricted to AES-256 and ChaCha20; the list-
encryption menu drops the legacy-cipher options.
Legacy-read support: OpenSSL's legacy provider (3DES / CAST5 / RC4)
is loaded lazily on first decrypt; IDEA and RC2
don't exist in either backend today, so
SyncTERM ships bundled decrypt-only reference
implementations (src/syncterm/legacy_ciphers/)
registered with xp_crypt at startup.
Automatic v1 → v2 migration:
iniReadBBSList does a one-shot migration
immediately after a successful decrypt of the
user bbslist. A PBKDF2 KDF or a
3DES/IDEA/CAST/RC2/RC4 cipher triggers
re-encryption as AES-256 + scrypt and a matching
update to syncterm.ini's KeyDerivationIterations
setting. Both files (bbslist r+b,
syncterm.ini r+) are opened and syncterm.ini is
pre-read before either is mutated; any open or
write failure emits a uifc.msg and exit(
EXIT_FAILURE) so in-memory algo / keysize / KDF
spec can't drift out of sync with disk. On the
good-path the bbslist is rewritten first, then
the syncterm.ini setting; the original PBKDF2
iteration hint survives until both writes
commit.
KeyDerivationIterations:
Now a KDF-spec string in syncterm.ini, not an
integer. New installs get "scrypt-N15" (N=2^15
= 32,768, ~16 MiB). Cryptlib-era digit values
(e.g. "50000") are still recognised on read as
the PBKDF2 iteration hint for v1 files and
survive until the auto-migration above rewrites
them. The config menu still takes an integer
from the user — scrypt's cost_log2 (8..24) —
stored as "scrypt-N<X>"; writer parses the
string and falls back to the compiled-in default
for anything it can't interpret as scrypt, so
a PBKDF2-shaped write is impossible by
construction. iniWriteEncryptedFile's parameter
type switched from int KDFiterations to
const char *kdf_spec to match.
SSH host-key fingerprints
-------------------------
Stored SHA-1 fingerprints from pre-migration bbslist.ini files remain
valid on first reconnect — on a match, the stored value is silently
upgraded to SHA-256. The three-option mismatch dialog
(Disconnect / Update / Ignore) is preserved.
Build-time knobs
----------------
- DEUCESSH_BACKEND / OpenSSL | Botan. Shared probe in
XP_CRYPTO_BACKEND build/Common.gmake picks one for the
whole tree so every subproject of a
given source tree + command-line
agrees. Cached libxpdev_mt.a
interoperates with its downstream
linkers.
- USE_VENDORED_BOTAN Fall back to building Botan 3 from
3rdp/dist/Botan.tar.xz when neither
system Botan 3.6+ nor OpenSSL 3.0+ is
available. Windows cross-compile
always uses vendored. A shared
3rdp/build `botan:` delegation target
mirrors the existing `cryptlib:`
machinery.
- WITHOUT_DEUCESSH Drops src/syncterm/ssh.c,
CONN_TYPE_SSH / _SSHNA, the SFTP
pubkey-upload bridge, and the
DeuceSSH sub-build.
- WITHOUT_CRYPTO Drops src/syncterm/telnets.c,
CONN_TYPE_TELNETS, the encrypted
bbslist.ini menu + dispatch, and the
bundled legacy-cipher reference
code. webget.c still compiles; its
HTTPS paths no-op. xp_tls / xp_crypt
fall back to "none" stub backends
so nothing drags in OpenSSL or
Botan 3.
Auto-degradations so CI stays honest across the runner fleet:
- Python 3 absent on Windows (needed for vendored Botan's
configure.py) → WITHOUT_CRYPTO + WITHOUT_DEUCESSH.
- Compiler cmake can't drive at C17, OR the SDK/libc doesn't ship
the C11 runtime functions DeuceSSH uses (pre-10.15 macOS SDKs lack
timespec_get / TIME_UTC even with an AppleClang that nominally
supports -std=c17) → DeuceSSH configure fails → WITHOUT_DEUCESSH;
the rest of syncterm still builds. DeuceSSH's CMakeLists probes
timespec_get explicitly — CMake's C_STANDARD 17 +
C_STANDARD_REQUIRED ON only checks compiler-version tables, not
the actual SDK. DeuceSSH stays strict-C17 as its upstream-
portability guarantee; the probe covers the handful of platforms
that fall outside that window.
The SyncTERM Build Options dialog reflects all of this — [*] / [ ]
columns for DeuceSSH, OpenSSL, Botan 3, JPEG XL, libsndfile, display
backends, audio backends — so users can tell at a glance what the
binary they're running actually has.
MinGW + MSVC + older macOS portability
--------------------------------------
DeuceSSH is strict C17. Added thin shims where the libc falls short
of C11's <threads.h> requirement:
- src/ssh/posix/threads.{h,c}: pthread → C11 threads wrapper
(mtx_*, cnd_*, thrd_*). Platform-
gated in DeuceSSH's CMakeLists via
a HAVE_THREADS_IN_LIBC probe. Covers
macOS pre-14 SDK, older NetBSD, any
libc without <threads.h>.
- src/ssh/win32/threads.{h,c}: CRITICAL_SECTION / CONDITION_VARIABLE
/ _beginthreadex wrapper. MinGW-w64
and MSVC both lack the libc header.
xpdev MSVC cmake: added /experimental:c11atomics (the hand-written xpdev.vcxproj has it; cmake path was missing it) and three C source
files that had been in the gmake objects.mk but not the cmake
SOURCE list (os_info.c, rwlockwrap.c, stbuf.c).
SyncTERM MSVC Windows build moved onto cmake: src/syncterm/build.bat
calls VsDevCmd.bat then cmake / nmake / msbuild. The hand-written SyncTERM.vcxproj + helpers are no longer the path of record.
Build ordering + CI
-------------------
The top-level all-target rule is Botan → xpdev-mt → ciolib-mt →
uifc-mt → (sftp-mt + deucessh) → syncterm. Both gmake and cmake
paths pre-probe DeuceSSH's configure once the crypto backend it
needs is on disk; if cmake can't produce deucessh.pc for any reason
(C17 unsupported, incomplete C11 runtime, missing crypto headers,
etc.), WITHOUT_DEUCESSH is set automatically and the main build
continues.
New .gitlab-ci-unix.yml [botan] job mirrors [cryptlib]: it runs
`gmake botan`, archives 3rdp/*.release/botan, and on systems where
the build is a no-op (system Botan/OpenSSL detected) produces an empty-directory placeholder archive so `tar -T /dev/null` never has
to emit an empty tarball. The .pc file is rewritten to use ${pcfiledir}-relative prefix at install time so the archive relocates
cleanly across runner concurrent-ids. [syncterm] extracts the
archive; other jobs that don't build SyncTERM don't (dropping the
botan.tgz extract + [botan] dep from 20+ non-syncterm jobs).
[syncterm-cmake] also extracts the archive rather than building a
second vendored copy via its own ExternalProject.
Files moved / renamed
---------------------
src/xpdev/{xp_tls,xp_crypt}*.[ch,cpp] →
src/syncterm/{xp_tls,xp_crypt}*.[ch,cpp]
src/xpdev/ini_crypt.c → src/syncterm/ini_crypt.c
src/xpdev/ini_file.h : encrypted-INI decls moved to new
src/syncterm/ini_crypt.h (including
enum iniCryptAlgo); ini_file.h stays
crypto-free.
src/syncterm/sndfile.{h,c} → src/syncterm/xp_sndfile.{h,c}
(the old name shadowed libsndfile's
<sndfile.h> when the syncterm source dir
was on -I path).
Verification
------------
SSH:
- password auth, pubkey auth, keyboard-interactive auth against
Synchronet test BBS.
- first-connect fingerprint prompt; reconnect uses stored
fingerprint; stored-SHA-1 upgrade to SHA-256 on next connect.
- SFTP pubkey upload round-trip.
TLS:
- TelnetS handshake + interactive session against a known TelnetS
BBS (Dave's, vert).
- HTTPS: `lst://` bbslist refresh path; ETag / Last-Modified
caching behaves unchanged.
Encrypted bbslist.ini:
- Read-compat: pre-migration files (AES-256, 3DES, ChaCha20, IDEA,
RC2) all decrypt and populate entries. v1 PBKDF2 header is
honoured; KDFiterations hint in syncterm.ini supplies the count.
- Auto-migration: v1 files (PBKDF2 or legacy cipher) are
re-encrypted as AES-256/scrypt in one shot at decrypt time,
and syncterm.ini's KeyDerivationIterations is updated to
"scrypt-N15" the same way. Any migration failure
exit()s — next run retries from the untouched v1 state.
- Round-trip: save encrypted → reload → verify entries.
- Legacy-write UI gone — menu offers AES-256 + ChaCha20 only.
- Scrypt params embedded in v2 header; reader reconstructs N / r /
p from there rather than guessing.
Automated:
- cterm_test + termtest pass unchanged.
- Disconnect from an active SSH session doesn't leak threads.
Co-Authored-By: Claude Opus 4.7 (1M context) <
[email protected]>
---
■ Synchronet ■ Vertrauen ■ Home of Synchronet ■ [vert/cvs/bbs].synchro.net