// 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 #include #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