127 lines
5.7 KiB
C
127 lines
5.7 KiB
C
// The MIT License (MIT)
|
|
//
|
|
// Copyright (C) 2026 Scott Duensing
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to
|
|
// deal in the Software without restriction, including without limitation the
|
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
// sell copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
// IN THE SOFTWARE.
|
|
|
|
// Secure serial link -- convenience wrapper tying rs232 + packet + security
|
|
//
|
|
// This is the top-level API for the serial/networking stack. It composes
|
|
// three layers into one:
|
|
// rs232 -- ISR-driven UART I/O with ring buffers
|
|
// packet -- HDLC framing + CRC-16 + Go-Back-N ARQ (reliable delivery)
|
|
// security -- DH key exchange + XTEA-CTR encryption
|
|
//
|
|
// Usage:
|
|
// 1. secLinkOpen() -- opens COM port, sets up packet framing
|
|
// 2. secLinkHandshake() -- DH key exchange (blocks until both sides complete)
|
|
// 3. secLinkSend() -- send data (optionally encrypted) on a channel
|
|
// 4. secLinkPoll() -- receive, decrypt if needed, deliver to callback
|
|
// 5. secLinkClose() -- tear everything down
|
|
//
|
|
// Channel multiplexing:
|
|
// Each packet carries a one-byte header: bit 7 = encrypted flag,
|
|
// bits 6..0 = channel number (0-127). This allows multiple logical streams
|
|
// (e.g., terminal data, file transfer, control messages) over a single
|
|
// serial link without needing separate framing or sequencing per stream.
|
|
// The callback receives plaintext regardless of whether encryption was used.
|
|
//
|
|
// Mixed clear/encrypted traffic:
|
|
// Unencrypted packets can be sent before or after the handshake. This
|
|
// allows a startup protocol (e.g., version negotiation) before keys are
|
|
// exchanged. Encrypted packets require a completed handshake.
|
|
|
|
#ifndef SECLINK_H
|
|
#define SECLINK_H
|
|
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include "../packet/packet.h"
|
|
|
|
// Error codes
|
|
#define SECLINK_SUCCESS 0
|
|
#define SECLINK_ERR_PARAM -1
|
|
#define SECLINK_ERR_SERIAL -2
|
|
#define SECLINK_ERR_ALLOC -3
|
|
#define SECLINK_ERR_HANDSHAKE -4
|
|
#define SECLINK_ERR_NOT_READY -5
|
|
#define SECLINK_ERR_SEND -6
|
|
#define SECLINK_ERR_TIMEOUT -7
|
|
|
|
// Channel header is one byte: bit 7 = encrypted flag, bits 6..0 = channel
|
|
#define SECLINK_CHAN_HDR_SIZE 1
|
|
|
|
// Max plaintext payload per send (packet max minus the channel header)
|
|
#define SECLINK_MAX_PAYLOAD (PKT_MAX_PAYLOAD - SECLINK_CHAN_HDR_SIZE)
|
|
|
|
// Channel limits. MAX_CHANNEL is the highest valid channel index;
|
|
// NUM_CHANNELS is the array size needed to hold one slot per channel.
|
|
#define SECLINK_MAX_CHANNEL 127
|
|
#define SECLINK_NUM_CHANNELS (SECLINK_MAX_CHANNEL + 1)
|
|
|
|
// Recommended per-channel receive buffer size for callers that buffer
|
|
// inbound data between the SecLinkRecvT callback and application read.
|
|
// 4096 bytes holds many full secLink payloads (SECLINK_MAX_PAYLOAD=254).
|
|
#define SECLINK_CHAN_BUF_SIZE 4096
|
|
|
|
// Receive callback -- delivers plaintext with channel number
|
|
typedef void (*SecLinkRecvT)(void *ctx, const uint8_t *data, int len, uint8_t channel);
|
|
|
|
// Opaque handle
|
|
typedef struct SecLinkS SecLinkT;
|
|
|
|
|
|
// Open a secure serial link. Opens the COM port and packet layer.
|
|
// Handshake must be called separately before sending encrypted data.
|
|
SecLinkT *secLinkOpen(int com, int32_t bps, int dataBits, char parity, int stopBits, int handshake, SecLinkRecvT callback, void *ctx);
|
|
|
|
// Close the link. Frees all resources and closes the COM port.
|
|
void secLinkClose(SecLinkT *link);
|
|
|
|
// Perform DH key exchange. Blocks until both sides have exchanged keys
|
|
// and derived cipher keys, or until an internal timeout expires (returns
|
|
// SECLINK_ERR_TIMEOUT if the peer never responds). RNG must be seeded
|
|
// before calling this.
|
|
int secLinkHandshake(SecLinkT *link);
|
|
|
|
// Get number of unacknowledged packets in the transmit window.
|
|
int secLinkGetPending(SecLinkT *link);
|
|
|
|
// Returns true if handshake is complete and link is ready for data.
|
|
bool secLinkIsReady(SecLinkT *link);
|
|
|
|
// Poll for incoming data. Decrypts if needed and delivers to callback.
|
|
// Returns number of packets delivered, or negative on error.
|
|
int secLinkPoll(SecLinkT *link);
|
|
|
|
// Send data on a channel. If encrypt is true, data is encrypted before
|
|
// sending (requires completed handshake). Clear packets can be sent
|
|
// without a handshake. len must be 1..SECLINK_MAX_PAYLOAD.
|
|
// If block is true, waits for transmit window space.
|
|
// A blocking send pumps the receive callback; an encrypted send re-entered
|
|
// from that callback while an encrypted send is in progress is refused with
|
|
// SECLINK_ERR_SEND to keep the cipher streams in sync.
|
|
int secLinkSend(SecLinkT *link, const uint8_t *data, int len, uint8_t channel, bool encrypt, bool block);
|
|
|
|
// Send an arbitrarily large buffer by splitting it into SECLINK_MAX_PAYLOAD
|
|
// chunks. Always blocks until all data is sent. Returns SECLINK_SUCCESS
|
|
// or the first error encountered.
|
|
int secLinkSendBuf(SecLinkT *link, const uint8_t *data, int len, uint8_t channel, bool encrypt);
|
|
|
|
#endif
|