DVX_GUI/seclink/README.md

295 lines
7.9 KiB
Markdown

# SecLink — Secure Serial Link Library
SecLink is a convenience wrapper that ties together three lower-level
libraries into a single API for reliable, optionally encrypted serial
communication:
- **rs232** — ISR-driven UART I/O with ring buffers and flow control
- **packet** — HDLC-style framing with CRC-16 and sliding window reliability
- **security** — 1024-bit Diffie-Hellman key exchange and XTEA-CTR encryption
## Architecture
```
Application
|
[secLink] channels, optional encryption
|
[packet] framing, CRC, retransmit, ordering
|
[rs232] ISR-driven UART, ring buffers, flow control
|
UART
```
SecLink adds a one-byte header to every packet:
```
Bit 7 Bits 6..0
----- ---------
Encrypt Channel (0-127)
```
This allows mixing encrypted and cleartext traffic on up to 128
independent logical channels over a single serial link.
## Lifecycle
```
secLinkOpen() Open COM port and packet layer
secLinkHandshake() DH key exchange (blocks until complete)
secLinkSend() Send a packet (encrypted or clear)
secLinkPoll() Receive and deliver packets to callback
secLinkClose() Tear down everything
```
The handshake is only required if you intend to send encrypted packets.
Cleartext packets can be sent immediately after `secLinkOpen()`.
## API Reference
### Types
```c
// Receive callback — called for each incoming packet with plaintext
typedef void (*SecLinkRecvT)(void *ctx, const uint8_t *data, int len, uint8_t channel);
// Opaque connection handle
typedef struct SecLinkS SecLinkT;
```
### Constants
| Name | Value | Description |
|-------------------------|-------|----------------------------------------|
| `SECLINK_MAX_PAYLOAD` | 255 | Max bytes per `secLinkSend()` call |
| `SECLINK_MAX_CHANNEL` | 127 | Highest valid channel number |
| `SECLINK_SUCCESS` | 0 | Operation succeeded |
| `SECLINK_ERR_PARAM` | -1 | Invalid parameter |
| `SECLINK_ERR_SERIAL` | -2 | Serial port error |
| `SECLINK_ERR_ALLOC` | -3 | Memory allocation failed |
| `SECLINK_ERR_HANDSHAKE` | -4 | Key exchange failed |
| `SECLINK_ERR_NOT_READY` | -5 | Encryption requested before handshake |
| `SECLINK_ERR_SEND` | -6 | Packet layer send failed |
### Functions
#### secLinkOpen
```c
SecLinkT *secLinkOpen(int com, int32_t bps, int dataBits, char parity,
int stopBits, int handshake,
SecLinkRecvT callback, void *ctx);
```
Opens the COM port via rs232, creates the packet layer, and returns a
link handle. Returns `NULL` on failure. The callback is invoked from
`secLinkPoll()` for each received packet.
#### secLinkClose
```c
void secLinkClose(SecLinkT *link);
```
Destroys cipher contexts, closes the packet layer and COM port, and
frees all memory.
#### secLinkHandshake
```c
int secLinkHandshake(SecLinkT *link);
```
Performs a Diffie-Hellman key exchange. Blocks until both sides have
exchanged public keys and derived cipher keys. The RNG must be seeded
(via `secRngSeed()` or `secRngAddEntropy()`) before calling this.
Each side derives separate TX and RX keys from the shared secret,
using public key ordering to determine directionality. This prevents
CTR counter collisions.
#### secLinkGetPending
```c
int secLinkGetPending(SecLinkT *link);
```
Returns the number of unacknowledged packets in the transmit window.
Useful for non-blocking send loops to determine if there is room to
send more data.
#### secLinkIsReady
```c
bool secLinkIsReady(SecLinkT *link);
```
Returns `true` if the handshake is complete and the link is ready for
encrypted communication.
#### secLinkPoll
```c
int secLinkPoll(SecLinkT *link);
```
Reads available serial data, processes received frames, handles ACKs
and retransmits. Decrypts encrypted packets and delivers plaintext to
the receive callback. Returns the number of packets delivered, or
negative on error.
Must be called frequently (e.g. in your main loop).
#### secLinkSend
```c
int secLinkSend(SecLinkT *link, const uint8_t *data, int len,
uint8_t channel, bool encrypt, bool block);
```
Sends up to `SECLINK_MAX_PAYLOAD` (255) bytes on the given channel.
- `channel` — logical channel number (0-127)
- `encrypt` — if `true`, encrypts the payload (requires completed handshake)
- `block` — if `true`, waits for transmit window space; if `false`,
returns `SECLINK_ERR_SEND` when the window is full
Cleartext packets (`encrypt = false`) can be sent before the handshake.
#### secLinkSendBuf
```c
int secLinkSendBuf(SecLinkT *link, const uint8_t *data, int len,
uint8_t channel, bool encrypt);
```
Sends an arbitrarily large buffer by splitting it into
`SECLINK_MAX_PAYLOAD`-byte chunks. Always blocks until all data is
sent. Returns `SECLINK_SUCCESS` or the first error encountered.
## Examples
### Basic encrypted link
```c
#include "secLink.h"
#include "../security/security.h"
void onRecv(void *ctx, const uint8_t *data, int len, uint8_t channel) {
// handle received plaintext on 'channel'
}
int main(void) {
// Seed the RNG before handshake
uint8_t entropy[16];
secRngGatherEntropy(entropy, sizeof(entropy));
secRngSeed(entropy, sizeof(entropy));
// Open link on COM1 at 115200 8N1
SecLinkT *link = secLinkOpen(0, 115200, 8, 'N', 1, 0, onRecv, NULL);
if (!link) {
return 1;
}
// Key exchange (blocks until both sides complete)
if (secLinkHandshake(link) != SECLINK_SUCCESS) {
secLinkClose(link);
return 1;
}
// Send encrypted data on channel 0
const char *msg = "Hello, secure world!";
secLinkSend(link, (const uint8_t *)msg, strlen(msg), 0, true, true);
// Main loop
while (1) {
secLinkPoll(link);
}
secLinkClose(link);
return 0;
}
```
### Mixed encrypted and cleartext channels
```c
#define CHAN_CONTROL 0 // cleartext control channel
#define CHAN_DATA 1 // encrypted data channel
// Send a cleartext status message (no handshake needed)
secLinkSend(link, statusMsg, statusLen, CHAN_CONTROL, false, true);
// Send encrypted payload (requires completed handshake)
secLinkSend(link, payload, payloadLen, CHAN_DATA, true, true);
```
### Non-blocking file transfer
```c
int sendFile(SecLinkT *link, const uint8_t *fileData, int fileSize,
uint8_t channel, bool encrypt, int windowSize) {
int offset = 0;
int bytesLeft = fileSize;
while (bytesLeft > 0) {
secLinkPoll(link); // process ACKs, free window slots
if (secLinkGetPending(link) < windowSize) {
int chunk = bytesLeft;
if (chunk > SECLINK_MAX_PAYLOAD) {
chunk = SECLINK_MAX_PAYLOAD;
}
int rc = secLinkSend(link, fileData + offset, chunk,
channel, encrypt, false);
if (rc == SECLINK_SUCCESS) {
offset += chunk;
bytesLeft -= chunk;
}
// SECLINK_ERR_SEND means window full, just retry next iteration
}
// Application can do other work here:
// update progress bar, check for cancel, etc.
}
// Drain remaining ACKs
while (secLinkGetPending(link) > 0) {
secLinkPoll(link);
}
return SECLINK_SUCCESS;
}
```
### Blocking bulk transfer
```c
// Send an entire file in one call (blocks until complete)
secLinkSendBuf(link, fileData, fileSize, CHAN_DATA, true);
```
## Building
```
make # builds ../lib/libseclink.a
make clean # removes objects and library
```
Link against all four libraries:
```
-lseclink -lpacket -lsecurity -lrs232
```
## Dependencies
SecLink requires these libraries (all in `../lib/`):
- `librs232.a` — serial port driver
- `libpacket.a` — packet framing and reliability
- `libsecurity.a` — DH key exchange and XTEA cipher
Target: DJGPP cross-compiler, 486+ CPU.