diff --git a/termdemo/Makefile b/termdemo/Makefile new file mode 100644 index 0000000..15ff7cd --- /dev/null +++ b/termdemo/Makefile @@ -0,0 +1,43 @@ +# SecLink Terminal Demo Makefile for DJGPP cross-compilation + +DJGPP_PREFIX = $(HOME)/djgpp/djgpp +CC = $(DJGPP_PREFIX)/bin/i586-pc-msdosdjgpp-gcc +CFLAGS = -O2 -Wall -Wextra -march=i486 -mtune=i586 -I../dvx -I../seclink -I../security +LDFLAGS = -L../lib -ldvx -lseclink -lpacket -lsecurity -lrs232 -lm + +OBJDIR = ../obj/termdemo +BINDIR = ../bin +LIBDIR = ../lib + +SRCS = termdemo.c +OBJS = $(patsubst %.c,$(OBJDIR)/%.o,$(SRCS)) +TARGET = $(BINDIR)/termdemo.exe + +.PHONY: all clean libs + +all: libs $(TARGET) + +libs: + $(MAKE) -C ../dvx + $(MAKE) -C ../rs232 + $(MAKE) -C ../packet + $(MAKE) -C ../security + $(MAKE) -C ../seclink + +$(TARGET): $(OBJS) $(LIBDIR)/libdvx.a $(LIBDIR)/libseclink.a $(LIBDIR)/libpacket.a $(LIBDIR)/libsecurity.a $(LIBDIR)/librs232.a | $(BINDIR) + $(CC) $(CFLAGS) -o $@ $(OBJS) $(LDFLAGS) + +$(OBJDIR)/%.o: %.c | $(OBJDIR) + $(CC) $(CFLAGS) -c -o $@ $< + +$(OBJDIR): + mkdir -p $(OBJDIR) + +$(BINDIR): + mkdir -p $(BINDIR) + +# Dependencies +$(OBJDIR)/termdemo.o: termdemo.c ../dvx/dvxApp.h ../dvx/dvxWidget.h ../seclink/secLink.h ../security/security.h + +clean: + rm -rf $(OBJDIR) $(TARGET) diff --git a/termdemo/README.md b/termdemo/README.md new file mode 100644 index 0000000..05045ff --- /dev/null +++ b/termdemo/README.md @@ -0,0 +1,150 @@ +# SecLink Terminal Demo + +DOS terminal emulator combining the DV/X windowed GUI with SecLink +encrypted serial communication. Connects to a remote BBS through the +SecLink proxy, providing a full ANSI terminal in a DESQview/X-style +window with encrypted transport. + +## Architecture + +``` +termdemo (DOS, 86Box) + | + +--- DV/X GUI windowed desktop, ANSI terminal widget + | + +--- SecLink encrypted serial link + | | + | +--- packet HDLC framing, CRC, retransmit + | +--- security DH key exchange, XTEA-CTR cipher + | +--- rs232 ISR-driven UART I/O + | + COM port (86Box emulated modem) + | + TCP:2323 + | + secproxy (Linux) + | + TCP:23 + | + Remote BBS +``` + +All traffic between the terminal and the proxy is encrypted via +XTEA-CTR on SecLink channel 0. The proxy decrypts and forwards +plaintext to the BBS over telnet. + +## Usage + +``` +termdemo [com_port] [baud_rate] +``` + +| Argument | Default | Description | +|-------------|---------|------------------------------| +| `com_port` | 1 | COM port number (1-4) | +| `baud_rate` | 115200 | Serial baud rate | + +``` +termdemo # COM1 at 115200 +termdemo 2 # COM2 at 115200 +termdemo 1 57600 # COM1 at 57600 +termdemo -h # show usage +``` + +## Startup Sequence + +1. Seed the RNG from hardware entropy +2. Open SecLink on the specified COM port (8N1, no handshake) +3. Perform DH key exchange (blocks until the proxy completes its side) +4. Initialize the DV/X GUI (1024x768, 16bpp VESA) +5. Create a resizable terminal window with menu bar and status bar +6. Enter the main loop + +The handshake completes in text mode before the GUI starts, so the +DOS console shows progress messages during connection setup. + +## Main Loop + +Each iteration: + +1. `dvxUpdate()` — process mouse, keyboard, paint, and window events +2. `secLinkPoll()` — read serial data, decrypt, deliver to ring buffer +3. `wgtAnsiTermPoll()` — drain ring buffer into the ANSI parser + +## Data Flow + +``` +BBS → proxy → serial → secLinkPoll() → onRecv() → ring buffer + → commRead() → wgtAnsiTermWrite() → ANSI parser → screen + +Keyboard → widgetAnsiTermOnKey() → commWrite() + → secLinkSend() → serial → proxy → BBS +``` + +A 4KB ring buffer bridges the SecLink receive callback (which fires +during `secLinkPoll()`) and the terminal widget's comm read interface +(which is polled by `wgtAnsiTermPoll()`). + +## GUI + +- **Window**: resizable, titled "SecLink Terminal" +- **Menu bar**: File → Quit +- **Terminal**: 80x25 ANSI terminal widget with 1000-line scrollback +- **Status bar**: shows COM port, baud rate, and encryption status + +The ANSI terminal widget supports standard escape sequences including +cursor control, SGR colors (16-color CGA palette), erase, scroll, +insert/delete lines, and DEC private modes (cursor visibility, line +wrap). + +## Test Setup + +1. Start the SecLink proxy on the Linux host: + + ``` + secproxy 2323 bbs.example.com 23 + ``` + +2. Configure 86Box with a COM port pointing at the proxy's listen port + (TCP client mode, port 2323, no telnet negotiation) + +3. Run the terminal inside 86Box: + + ``` + termdemo + ``` + +4. The handshake completes, the GUI appears, and BBS output is + displayed in the terminal window + +## Building + +``` +make # builds ../bin/termdemo.exe +make clean # removes objects and binary +``` + +The Makefile builds all dependency libraries automatically. Objects +are placed in `../obj/termdemo/`, the binary in `../bin/`. + +## Dependencies + +All libraries are in `../lib/`: + +| Library | Purpose | +|------------------|--------------------------------------| +| `libdvx.a` | DV/X windowed GUI and widget system | +| `libseclink.a` | Secure serial link wrapper | +| `libpacket.a` | HDLC framing and reliability | +| `libsecurity.a` | DH key exchange and XTEA cipher | +| `librs232.a` | ISR-driven UART serial driver | + +Target: DJGPP cross-compiler, 486+ CPU, VESA VBE 2.0+ video. + +## Files + +``` +termdemo/ + termdemo.c terminal emulator program + Makefile DJGPP cross-compilation build +``` diff --git a/termdemo/termdemo.c b/termdemo/termdemo.c new file mode 100644 index 0000000..86447ed --- /dev/null +++ b/termdemo/termdemo.c @@ -0,0 +1,271 @@ +// termdemo.c — SecLink terminal emulator demo +// +// Uses DV/X GUI ANSI terminal widget with SecLink encrypted serial link +// to provide a BBS terminal over a secured serial connection. +// +// Usage: termdemo [com_port] [baud_rate] +// com_port — 1-4 (default 1) +// baud_rate — baud rate (default 115200) + +#include "dvxApp.h" +#include "dvxWidget.h" +#include "secLink.h" +#include "security.h" + +#include +#include +#include + +// ============================================================ +// Constants +// ============================================================ + +#define CMD_FILE_QUIT 100 + +#define RECV_BUF_SIZE 4096 + +#define CHANNEL_DATA 0 + +// ============================================================ +// Types +// ============================================================ + +// Bridges secLink receive callback and terminal widget comm interface +typedef struct { + SecLinkT *link; + AppContextT *app; + WidgetT *term; + uint8_t recvBuf[RECV_BUF_SIZE]; + int32_t recvHead; + int32_t recvTail; +} TermContextT; + +// ============================================================ +// Prototypes +// ============================================================ + +static int32_t commRead(void *ctx, uint8_t *buf, int32_t maxLen); +static int32_t commWrite(void *ctx, const uint8_t *data, int32_t len); +static void onCloseCb(WindowT *win); +static void onMenuCb(WindowT *win, int32_t menuId); +static void onRecv(void *ctx, const uint8_t *data, int len, uint8_t channel); + + +// ============================================================ +// commRead — drain receive ring buffer for terminal widget +// ============================================================ + +static int32_t commRead(void *ctx, uint8_t *buf, int32_t maxLen) { + TermContextT *tc = (TermContextT *)ctx; + int32_t count = 0; + + while (count < maxLen && tc->recvTail != tc->recvHead) { + buf[count++] = tc->recvBuf[tc->recvTail]; + tc->recvTail = (tc->recvTail + 1) % RECV_BUF_SIZE; + } + + return count; +} + + +// ============================================================ +// commWrite — send keystrokes via secLink (encrypted, channel 0) +// ============================================================ + +static int32_t commWrite(void *ctx, const uint8_t *data, int32_t len) { + TermContextT *tc = (TermContextT *)ctx; + int rc = secLinkSend(tc->link, data, len, CHANNEL_DATA, true, false); + + return (rc == SECLINK_SUCCESS) ? len : 0; +} + + +// ============================================================ +// onCloseCb — quit when the terminal window is closed +// ============================================================ + +static void onCloseCb(WindowT *win) { + AppContextT *ctx = (AppContextT *)win->userData; + + if (ctx) { + dvxQuit(ctx); + } +} + + +// ============================================================ +// onMenuCb — handle menu commands +// ============================================================ + +static void onMenuCb(WindowT *win, int32_t menuId) { + AppContextT *ctx = (AppContextT *)win->userData; + + switch (menuId) { + case CMD_FILE_QUIT: + if (ctx) { + dvxQuit(ctx); + } + break; + } +} + + +// ============================================================ +// onRecv — secLink receive callback, fills ring buffer +// ============================================================ + +static void onRecv(void *ctx, const uint8_t *data, int len, uint8_t channel) { + (void)channel; + TermContextT *tc = (TermContextT *)ctx; + + for (int i = 0; i < len; i++) { + int32_t next = (tc->recvHead + 1) % RECV_BUF_SIZE; + + if (next == tc->recvTail) { + break; + } + + tc->recvBuf[tc->recvHead] = data[i]; + tc->recvHead = next; + } +} + + +// ============================================================ +// main +// ============================================================ + +int main(int argc, char *argv[]) { + int comPort = 0; + int32_t baudRate = 115200; + + // Parse command line + if (argc >= 2) { + if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0) { + printf("Usage: termdemo [com_port] [baud_rate]\n"); + printf(" com_port -- 1-4 (default 1)\n"); + printf(" baud_rate -- baud rate (default 115200)\n"); + return 0; + } + + comPort = atoi(argv[1]) - 1; + + if (comPort < 0 || comPort > 3) { + fprintf(stderr, "Invalid COM port (must be 1-4)\n"); + return 1; + } + } + + if (argc >= 3) { + baudRate = atol(argv[2]); + + if (baudRate <= 0) { + fprintf(stderr, "Invalid baud rate\n"); + return 1; + } + } + + printf("SecLink Terminal Demo\n"); + printf("COM%d at %ld 8N1\n\n", comPort + 1, (long)baudRate); + + // Seed the RNG with hardware entropy + printf("Gathering entropy...\n"); + uint8_t entropy[16]; + int got = secRngGatherEntropy(entropy, sizeof(entropy)); + secRngSeed(entropy, got); + + // Initialize terminal context + TermContextT tc; + memset(&tc, 0, sizeof(tc)); + + // Open secLink on the specified COM port + printf("Opening SecLink on COM%d...\n", comPort + 1); + tc.link = secLinkOpen(comPort, baudRate, 8, 'N', 1, 0, onRecv, &tc); + + if (!tc.link) { + fprintf(stderr, "Failed to open SecLink\n"); + return 1; + } + + // Perform DH key exchange (blocks until both sides complete) + printf("Performing key exchange (waiting for remote side)...\n"); + int rc = secLinkHandshake(tc.link); + + if (rc != SECLINK_SUCCESS) { + fprintf(stderr, "Handshake failed (%d)\n", rc); + secLinkClose(tc.link); + return 1; + } + + printf("Secure link established.\n\n"); + + // Initialize DV/X GUI + AppContextT ctx; + + printf("Initializing video...\n"); + + if (dvxInit(&ctx, 1024, 768, 16) != 0) { + fprintf(stderr, "Failed to initialize DV/X GUI\n"); + secLinkClose(tc.link); + return 1; + } + + tc.app = &ctx; + + // Create the terminal window + WindowT *win = dvxCreateWindow(&ctx, "SecLink Terminal", 40, 30, 680, 460, true); + + if (!win) { + dvxShutdown(&ctx); + secLinkClose(tc.link); + return 1; + } + + win->userData = &ctx; + win->onClose = onCloseCb; + win->onMenu = onMenuCb; + + // File menu + MenuBarT *bar = wmAddMenuBar(win); + + if (bar) { + MenuT *fileMenu = wmAddMenu(bar, "File"); + + if (fileMenu) { + wmAddMenuItem(fileMenu, "Quit", CMD_FILE_QUIT); + } + } + + // Widget tree: terminal + status bar + WidgetT *root = wgtInitWindow(&ctx, win); + WidgetT *term = wgtAnsiTerm(root, 80, 25); + + term->weight = 100; + wgtAnsiTermSetScrollback(term, 1000); + tc.term = term; + + // Connect terminal widget to secLink via comm interface + wgtAnsiTermSetComm(term, &tc, commRead, commWrite); + + // Status bar showing connection info + WidgetT *sb = wgtStatusBar(root); + char statusText[64]; + snprintf(statusText, sizeof(statusText), "COM%d %ld 8N1 [Encrypted]", comPort + 1, (long)baudRate); + wgtLabel(sb, statusText); + + wgtInvalidate(root); + + // Main loop — poll secLink and terminal each frame + while (dvxUpdate(&ctx)) { + secLinkPoll(tc.link); + wgtAnsiTermPoll(term); + } + + // Cleanup + dvxShutdown(&ctx); + secLinkClose(tc.link); + + printf("SecLink Terminal Demo ended.\n"); + + return 0; +}