7 KiB
Security -- DH Key Exchange and XTEA-CTR Cipher
Cryptographic library providing Diffie-Hellman key exchange and XTEA symmetric encryption, optimized for 486-class DOS hardware running under DJGPP/DPMI.
Components
Diffie-Hellman Key Exchange
- 1024-bit MODP group (RFC 2409 Group 2 safe prime)
- 256-bit private exponents for fast computation on 486 CPUs
- Montgomery multiplication (CIOS variant) for modular exponentiation
- Lazy-initialized Montgomery constants (R^2 mod p, -p0^-1 mod 2^32)
XTEA Cipher (CTR Mode)
- 128-bit key, 64-bit block size, 32 rounds
- CTR mode -- encrypt and decrypt are the same XOR operation
- No lookup tables, no key schedule -- just shifts, adds, and XORs
- Ideal for constrained environments with small key setup cost
Pseudo-Random Number Generator
- XTEA-CTR based DRBG (deterministic random bit generator)
- Hardware entropy from PIT counter (~10 bits) and BIOS tick count
- Supports additional entropy injection (keyboard timing, mouse, etc.)
- Auto-seeds from hardware on first use if not explicitly seeded
Performance
At serial port speeds, encryption overhead is minimal:
| Speed | Blocks/sec | CPU cycles/sec | % of 33 MHz 486 |
|---|---|---|---|
| 9600 | 120 | ~240K | < 1% |
| 57600 | 720 | ~1.4M | ~4% |
| 115200 | 1440 | ~2.9M | ~9% |
DH key exchange takes approximately 0.3s at 66 MHz or 0.6s at 33 MHz (256-bit private exponent, 1024-bit modulus).
API Reference
Constants
| Name | Value | Description |
|---|---|---|
SEC_DH_KEY_SIZE |
128 | DH public key size (bytes) |
SEC_XTEA_KEY_SIZE |
16 | XTEA key size (bytes) |
SEC_SUCCESS |
0 | Success |
SEC_ERR_PARAM |
-1 | Invalid parameter |
SEC_ERR_NOT_READY |
-2 | Keys not yet generated/derived |
SEC_ERR_ALLOC |
-3 | Memory allocation failed |
Types
typedef struct SecDhS SecDhT; // Opaque DH context
typedef struct SecCipherS SecCipherT; // Opaque cipher context
RNG Functions
int secRngGatherEntropy(uint8_t *buf, int len);
Reads hardware entropy sources (PIT counter, BIOS tick count). Returns the number of bytes written. Provides roughly 20 bits of true entropy.
void secRngSeed(const uint8_t *entropy, int len);
Initializes the DRBG with the given entropy. XOR-folds the input into the XTEA key, derives the counter, and mixes state by generating and discarding 64 bytes.
void secRngAddEntropy(const uint8_t *data, int len);
Mixes additional entropy into the running RNG state without resetting it. Use this to stir in keyboard timing, mouse jitter, or other runtime entropy.
void secRngBytes(uint8_t *buf, int len);
Generates len pseudo-random bytes. Auto-seeds from hardware if not
previously seeded.
Diffie-Hellman Functions
SecDhT *secDhCreate(void);
Allocates a new DH context. Returns NULL on allocation failure.
int secDhGenerateKeys(SecDhT *dh);
Generates a 256-bit random private key and computes the corresponding 1024-bit public key (g^private mod p). The RNG should be seeded first.
int secDhGetPublicKey(SecDhT *dh, uint8_t *buf, int *len);
Exports the public key into buf. On entry, *len must be at least
SEC_DH_KEY_SIZE (128). On return, *len is set to 128.
int secDhComputeSecret(SecDhT *dh, const uint8_t *remotePub, int len);
Computes the shared secret from the remote side's public key. Validates that the remote key is in range [2, p-2] to prevent small-subgroup attacks.
int secDhDeriveKey(SecDhT *dh, uint8_t *key, int keyLen);
Derives a symmetric key by XOR-folding the 128-byte shared secret
down to keyLen bytes.
void secDhDestroy(SecDhT *dh);
Securely zeroes and frees the DH context (private key, shared secret).
Cipher Functions
SecCipherT *secCipherCreate(const uint8_t *key);
Creates an XTEA-CTR cipher context with the given 16-byte key. Counter starts at zero.
void secCipherCrypt(SecCipherT *c, uint8_t *data, int len);
Encrypts or decrypts data in place. CTR mode is symmetric -- the
same operation encrypts and decrypts.
void secCipherSetNonce(SecCipherT *c, uint32_t nonceLo, uint32_t nonceHi);
Sets the 64-bit nonce/counter. Call before encrypting if you need a specific starting counter value.
void secCipherDestroy(SecCipherT *c);
Securely zeroes and frees the cipher context.
Example
Full Key Exchange
#include "security.h"
#include <string.h>
// Seed the RNG
uint8_t entropy[16];
secRngGatherEntropy(entropy, sizeof(entropy));
secRngSeed(entropy, sizeof(entropy));
// Create DH context and generate keys
SecDhT *dh = secDhCreate();
secDhGenerateKeys(dh);
// Export public key to send to remote
uint8_t myPub[SEC_DH_KEY_SIZE];
int pubLen = SEC_DH_KEY_SIZE;
secDhGetPublicKey(dh, myPub, &pubLen);
// ... send myPub to remote, receive remotePub ...
// Compute shared secret and derive a 16-byte key
secDhComputeSecret(dh, remotePub, SEC_DH_KEY_SIZE);
uint8_t key[SEC_XTEA_KEY_SIZE];
secDhDeriveKey(dh, key, SEC_XTEA_KEY_SIZE);
secDhDestroy(dh);
// Create cipher and encrypt
SecCipherT *cipher = secCipherCreate(key);
uint8_t message[] = "Secret message";
secCipherCrypt(cipher, message, sizeof(message));
// message is now encrypted
// Decrypt (same operation -- CTR mode is symmetric)
// Reset counter first if using the same cipher context
secCipherSetNonce(cipher, 0, 0);
secCipherCrypt(cipher, message, sizeof(message));
// message is now plaintext again
secCipherDestroy(cipher);
Standalone Encryption
// XTEA-CTR can be used independently of DH
uint8_t key[SEC_XTEA_KEY_SIZE] = { /* your key */ };
SecCipherT *c = secCipherCreate(key);
uint8_t data[1024];
// ... fill data ...
secCipherCrypt(c, data, sizeof(data)); // encrypt in place
secCipherDestroy(c);
Implementation Details
BigNum Arithmetic
All modular arithmetic uses a 1024-bit big number type (BigNumT)
stored as 32 x uint32_t words in little-endian order. Operations:
- Add, subtract, compare, shift-left-1, bit test
- Montgomery multiplication (CIOS with implicit right-shift)
- Modular exponentiation (left-to-right binary square-and-multiply)
Montgomery Multiplication
The CIOS (Coarsely Integrated Operand Scanning) variant computes
a * b * R^-1 mod m in a single pass with implicit division by the
word base. Constants are computed once on first DH use:
R^2 mod p-- via 2048 iterations of shift-and-conditional-subtract-p[0]^-1 mod 2^32-- via Newton's method (5 iterations)
Secure Zeroing
Key material is erased using a volatile-pointer loop that the compiler cannot optimize away, preventing sensitive data from lingering in memory.
Building
make # builds ../lib/libsecurity.a
make clean # removes objects and library
Target: DJGPP cross-compiler, 486+ CPU.