Author Topic: [C] Secure UDP Library (sudp)  (Read 2506 times)

0 Members and 1 Guest are viewing this topic.

Offline nslay

  • Hero Member
  • *****
  • Posts: 786
  • Giraffe meat, mmm
    • View Profile
[C] Secure UDP Library (sudp)
« on: December 22, 2006, 04:01:57 am »
The Secure UDP Library (or sudp for short) is a callback driven UDP communications library that handles all aspects of key agreement and encryption.  The purpose of this library is to provide (semi) secure UDP communications for my tunnel project where the data transfered is small pieces of information (usually forwarded packets), but it is general enough to use for other things.  Presently, it uses Diffie-Hellman with DSA signatures for key agreement and Blowfish (CFB mode) for the encryption.

Synopsis
Code: [Select]
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <openssl/dsa.h>
#include <openssl/engine.h>
#include <openssl/sha.h>
#include <openssl/blowfish.h>

#include "fdman.h"
#include "hash.h"

#ifndef SUDP_H
#define SUDP_H

#ifndef SUDP_TIMEOUT
#define SUDP_TIMEOUT 60
#endif

/* Message format */
#define SUDP_MSGSIZE 8192
#define SUDP_HDRSIZE ( sizeof(uint8_t)+sizeof(uint16_t) )
#define SUDP_DATSIZE ( SUDP_MSGSIZE-SUDP_HDRSIZE )

/* States */
#define SUDP_WAITING 0
#define SUDP_WAITING_PUBKEY 1
#define SUDP_WAITING_BIGNUM 2
#define SUDP_WAITING_SIGNATURE 3
#define SUDP_COMPLETE 4

/* Protocol */
#define SUDP_PROTO_RESET 0x00
#define SUDP_PROTO_CONNECT 0x01
#define SUDP_PROTO_DISCONNECT 0x02
#define SUDP_PROTO_PUBKEY 0x03
#define SUDP_PROTO_BIGNUM 0x04
#define SUDP_PROTO_SIGNATURE 0x05
#define SUDP_PROTO_DATA 0x06

/* Callback events */
#define SUDP_RESET 0
#define SUDP_CONNECT 1
#define SUDP_DISCONNECT 2
#define SUDP_PUBKEY 3
#define SUDP_READ 4

struct sudp;
struct sudpcon;

typedef void (*sudp_cb)( struct sudp *sd, struct sudpcon *sdc );

struct sudpcon {
struct sockaddr addr;
socklen_t addrlen;
int state, event, in_num, out_num;
DSA *dsa_pubkey;
unsigned char md[SHA_DIGEST_LENGTH], in_ivec[8], out_ivec[8];
BF_KEY key;
uint16_t in_seq, out_seq;
void *msg, *arg;
size_t msgsz;
time_t to;
};

struct sudp {
int fd;
struct fdman *fdm;
DSA *dsa_privkey;
struct hash_table ht;
sudp_cb cb;
};

int sudp_init( struct sudp *sd, struct fdman *fdm, sudp_cb cb, DSA *dsa_privkey );
void sudp_free( struct sudp *sd );
int sudp_bind( struct sudp *sd, struct sockaddr *addr, socklen_t addrlen );
int sudp_connect( struct sudp *sd, struct sockaddr *addr, socklen_t addrlen );
void sudp_disconnect( struct sudp *sd, struct sudpcon *sdc );
int sudp_send( struct sudp *sd, struct sudpcon *sdc, void *data, size_t sz );
void sudp_timeout( struct sudp *sd );

#endif

Parameters
Code: [Select]
struct sudpcon {
struct sockaddr addr; /* Remote address */
socklen_t addrlen; /* Length of addr */
int state, event, in_num, out_num; /* event indicates the reason for callback */
DSA *dsa_pubkey; /* The remote DSA public key */
unsigned char md[SHA_DIGEST_LENGTH], in_ivec[8], out_ivec[8];
BF_KEY key;
uint16_t in_seq, out_seq;
void *msg, *arg; /* These are used for callback, msg points to decrypted data, arg is arbitrary */
size_t msgsz; /* This is the size of the the decrypted text */
time_t to;
};

struct sudp {
int fd; /* This is the UDP socket descriptor */
struct fdman *fdm; /* This is the fdman (see post on fdman for more information) */
DSA *dsa_privkey; /* DSA private key */
struct hash_table ht; /* Hash table used to store sudpcon descriptors */
sudp_cb cb; /* Callback function */
};

Functions
Code: [Select]
int sudp_init( struct sudp *sd, struct fdman *fdm, sudp_cb cb, DSA *dsa_privkey ); /* This initializes a secure udp manager */
void sudp_free( struct sudp *sd ); /* This frees the data used by the secure udp manager */
int sudp_bind( struct sudp *sd, struct sockaddr *addr, socklen_t addrlen ); /* This binds the secure udp manager to a specific address and/or port */
int sudp_connect( struct sudp *sd, struct sockaddr *addr, socklen_t addrlen ); /* This "connects", or rather, attempts to establish an encrypted session with specified address */
void sudp_disconnect( struct sudp *sd, struct sudpcon *sdc ); /* This "disconnects", or rather alerts the end point of the terminated session */
int sudp_send( struct sudp *sd, struct sudpcon *sdc, void *data, size_t sz ); /* This sends encrypted data to the address described by the sudpcon descriptor */
void sudp_timeout( struct sudp *sd ); /* This cleans up idle "connections" */

Callback events
Code: [Select]
typedef void (*sudp_cb)( struct sudp *sd, struct sudpcon *sdc ); /* The callback function prototype */

/* Callback events */
#define SUDP_RESET 0 /* If any packets are lost, the encryption parameters are automatically reset to maintain the encrypted session ... it lets you know packets were lost, this callback is a convenience! */
#define SUDP_CONNECT 1 /* This callback lets you know an encrypted session was successfully established */
#define SUDP_DISCONNECT 2 /* This callback lets you know remote side terminated the session, or the session timed out */
#define SUDP_PUBKEY 3 /* This lets you know when a public key is received, it is intended to let the application check its cache to see if there is evesdropping */
#define SUDP_READ 4 /* This lets you know data was read from the socket, sdc->msg and sdc->msgsz point to the decrypted data and size respectively */

UDP is extremely unreliable!  Only use this library if you only wish to transfer small pieces of data and do so semi-securely.

Example
This is a simple peer-to-peer chat program.  It uses sudp to encrypt messages between two peers.
Code: [Select]
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <openssl/dsa.h>
#include <openssl/engine.h>
#include <openssl/err.h>
#include <sys/time.h>
#include <time.h>
#include <sysexits.h>
#include "sudp.h"
#include "fdman.h"

#define PORT "3421"

struct sudpcon *chat = NULL;
char *remote_host = NULL;

struct addrinfo hints = { AI_PASSIVE, AF_INET, SOCK_DGRAM, IPPROTO_UDP };

void usage( void ) {
extern char *__progname;
printf( "%s: [-h remote host ]\n", __progname );
exit(EX_USAGE);
}

void chatcb( struct sudp *sd, struct sudpcon *sdc ) {
uint8_t msg[SUDP_DATSIZE+1];
const char *addr = inet_ntoa( ((struct sockaddr_in *)&sdc->addr)->sin_addr );
switch( sdc->event ) {
case SUDP_RESET:
printf( "Packets lost, reset!\n" );
break;
case SUDP_CONNECT:
if ( chat != NULL ) {
printf( "%s connected but we're already connected!\n", addr );
sudp_disconnect( sd, sdc );
break;
}
chat = sdc;
printf( "Connected to %s.\n", addr );
break;
case SUDP_DISCONNECT:
if ( sdc == chat ) chat = NULL;
printf( "Lost connection with %s!\n", addr );
break;
case SUDP_PUBKEY:
printf( "Recieved public key from %s!\n", addr );
break;
case SUDP_READ:
memcpy( msg, sdc->msg, sdc->msgsz );
msg[sdc->msgsz] = '\0';
printf( "%s: %s", addr, msg );
break;
}
}

void kbdcb( struct fdarg *fda ) {
struct sudp *sd = (struct sudp *)(fda->arg);
uint8_t msg[SUDP_DATSIZE];
ssize_t msgsz;

msgsz = read( fda->fd, msg, sizeof(msg) );
if ( chat == NULL ) return;
if ( sudp_send( sd, chat, msg, msgsz ) ) {
printf( "Send error!\n" );
sudp_disconnect( sd, chat );
chat = NULL;
}
}

int main( int argc, char **argv ) {
char c;
DSA *dsa;
struct fdman fdm;
struct fdarg fda;
struct sudp sd;
struct addrinfo *res;
struct timeval to = {0,0}, tv;
int arg;

while ( (c = getopt( argc, argv, "h:" )) != -1 ) {
switch( c ) {
case 'h':
remote_host = strdup(optarg);
hints.ai_flags = AI_CANONNAME;
break;
default:
usage();
}
}

/* Seed the random generator */
srandom( time(NULL) );

/* Generate private DSA key */
dsa = DSA_generate_parameters( 1024, NULL, 0, NULL, NULL, NULL, NULL );
if ( dsa == NULL ) {
printf( "%s\n", ERR_error_string(ERR_get_error(),NULL) );
exit(EX_SOFTWARE);
}
if ( !DSA_generate_key(dsa) ) {
printf( "%s\n", ERR_error_string(ERR_get_error(),NULL) );
exit(EX_SOFTWARE);
}

/* Initiaize fd manager */
fdman_init( &fdm );
if ( sudp_init( &sd, &fdm, chatcb, dsa ) ) {
printf( "Unable to initialize secure UDP!\n" );
exit(EX_SOFTWARE);
}

/* Setup keyboard callback */
fda.fd = STDIN_FILENO;
fda.opt = FDMAN_READ;
fda.arg = &sd;
fda.cb = kbdcb;

fdman_set( &fdm, &fda );

/* Resolve address */
if ( arg = getaddrinfo( remote_host, PORT, &hints, &res ) ) {
printf( "%s\n", gai_strerror(arg) );
exit(EX_OSERR);
}

if ( remote_host == NULL ) {
printf( "Listening on port %s\n", PORT );
if ( sudp_bind( &sd, res->ai_addr, res->ai_addrlen ) ) {
perror( "sudp_bind()" );
exit(EX_OSERR);
}
}
else {
printf( "Attempting to connect to %s\n", remote_host );
if ( sudp_connect( &sd, res->ai_addr, res->ai_addrlen ) ) {
perror( "sudp_connect()" );
exit(EX_OSERR);
}
}

gettimeofday( &tv, NULL );
tv.tv_sec += SUDP_TIMEOUT;
while ( fdman_select( &fdm, &to ) >= 0 ) {
gettimeofday( &to, NULL );
if ( timercmp( &to, &tv, >= ) ) {
/* Kill idle "connections" */
sudp_timeout( &sd );
tv = to;
tv.tv_sec += SUDP_TIMEOUT;
}
timersub( &tv, &to, &to );
}

printf( "Exiting ...\n" );

return 0;
}

Here's a screenshot of the example program in action:


The source: http://nslay.36bit.com/sudp.tar.gz
An adorable giant isopod!