So, I've been working on the prototype's guts these past few days. The protocol of the prototype GREATLY differs to the proof-of-concept program I wrote over the summer. The prototype has been named tunsl, it is a spinoff of an ioctl in FreeBSD's tun interface TUNSLMODE which has tun prepend a sockaddr to each packet read.
The message is formatted as follows: <uint8_t event> <uint8_t hop> <in_addr_t from> <in_addr_t to> <void data>
These messages are sent over the secure UDP library I wrote ...
These are the event bytes:
/* Protocol */
#define TUNSL_PROTO_RESET 0x00 /* Tell other side to restore cryptographic parameters to initial state (used when a message is lost ... could be from a lost packet or dead peer) */
#define TUNSL_PROTO_TRYAGAIN 0x01 /* During a DH/DSS handshake, if a message is lost, the handshake needs to start over again */
#define TUNSL_PROTO_WHOHASS 0x02 /* This is a route request */
#define TUNSL_PROTO_WHOHASR 0x03 /* This is a route response */
#define TUNSL_PROTO_BIGNUM 0x04 /* This is used in the handshake to transfer a shared DH parameter */
#define TUNSL_PROTO_SIGNATURE 0x05 /* This is used in the handshake to make sure both logical routes have the same secret key */
#define TUNSL_PROTO_PACKET 0x06 /* This is used for packet data (ENCRYPTED) */
hop is an incremental integer used to measure how many peers a message has been routed through ... the intent of this is to prevent to infinite relays (peer 1 sends this to peer 2 which sends back to peer 1, etc...)
from is the originating virtual IP address and likewise dest is the destination virtual IP address.
Here is a detailed description of each event:
TUNSL_PROTO_RESET
TUNSL_PROTO_RESET (although I haven't yet coded support for this yes) is supposed to be used after a secure packet route has been established. A packet route is an independent logical route ... it does not depend on the underlying p2p network or the peers. In fact, a packet may be routed through any routable peer. A peer is said to be routable if it is able to route to a specific destination address or to another routable peer. Anyhow, since I am using Blowfish (CFB mode) to handle the encryption, there are various parameters (the IVs and sequence numbers) that are essential to proper decryption. If a message goes missing then these parameters need to be set a mutual known state ... I chose to set them back to their initial state.
TUNSL_PROTO_TRYAGAIN
I haven't yet coded support for this event either, but its supposed to be used to tell the destination to try the handshake again. This is caused when a message or packet have gone missing.
TUNSL_PROTO_WHOHASS
This is an event that I have coded. This is used to request a route. This event is also a vehicle for a public key. Since the public key is used to generate the virtual IP address, this can be used to check the authenticity of this message. Each peer should cache the public key from this message. While it may be time consuming to generate public keys that will collide with a specific address to spoof, caching the keys will decrease network saturation when another peer requests the same route ... likewise, this helps prevent spoof attacks.
TUNSL_PROTO_WHOHASR
This event is used to respond to a route request, it is also a vehicle for a public key and its authenticity may also be established. Likewise, this key should be cached. The real intent of the WHOHASS and WHOHASR aside of establishing routes, is public key exchange.
TUNSL_PROTO_BIGNUM
In OpenSSL, a BIGNUM is an arbitrarily sized integer. This event is used to send a shared parameter for the Diffie-Hellman key exchange.
TUNSL_PROTO_SIGNATURE
This is used after both ends of a route have computed the secret key and it ensures that both ends have the same secret key.
TUNSL_PROTO_PACKET
This is used to route packets, the contents of this message are encrypted. To know when a packet is lost, a sequence number is encrypted with the packet.
<uint16_t seq> <void packet>
Security:
When routing a packet, a random routable peer will be picked each time. This way no one routable peer accumulates all the packets and the network load is distributed.
Since there is no concept of middle, a man-in-middle attack is very difficult to accomplish. Even if you do somehow manage to man-in-middle a packet route, you will most likely not receive every packet (as mentioned above) and your cryptographic parameters will go out of sync.
It is very difficult to spoof WHOHASS and WHOHASR requests because their authenticity can be tested (besides, these keys are cached not only for performance but also to help prevent a spoof attack). The authenticity is established by taking a SHA-1 hash of the public key and then generating an IP address given the netblock and netmask.
It is done as follows
IP = netblock | ( ~netmask & 4 byte truncated SHA-1 ) ... if this address does not match the from address, then the message is bad.
Since its very difficult to generate a SHA-1 collision, and very time consuming to generate a public/private key pair, its fairly difficult to spoof ... however, we are talking about 160 bit -> 32 bit map and a collision is more likely. The smaller the netmask, the easier to create 2 public keys that generate the same IP. However, for a small netmask, you're probably just a group of friends playing Quake over the VLAN anyways.
That concludes my description ... I'll let everyone know when the working prototype is done (its very close to done!).