From a480c0195a78f56116b3bf58fe17d930bf4e64f4 Mon Sep 17 00:00:00 2001 From: irungentoo Date: Fri, 5 Jul 2013 17:00:39 -0400 Subject: Crypto done(still need to test it a bit more thought) Replaced chars with uint8_t Added a new test program. Added some functions to Lossless UDP. And some other stuff. --- core/net_crypto.c | 622 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 622 insertions(+) create mode 100644 core/net_crypto.c (limited to 'core/net_crypto.c') diff --git a/core/net_crypto.c b/core/net_crypto.c new file mode 100644 index 00000000..38a72205 --- /dev/null +++ b/core/net_crypto.c @@ -0,0 +1,622 @@ +/* net_crypto.c +* +* Functions for the core network crypto. +* See also: docs/Crypto.txt +* +* NOTE: This code has to be perfect. We don't mess around with encryption. +* +*/ + + +#include "net_crypto.h" + + +//Our public and secret keys. +uint8_t self_public_key[crypto_box_PUBLICKEYBYTES]; +uint8_t self_secret_key[crypto_box_SECRETKEYBYTES]; + + +typedef struct +{ + uint8_t public_key[crypto_box_PUBLICKEYBYTES]; + uint8_t recv_nonce[crypto_box_NONCEBYTES];//nonce of recieved packets + uint8_t sent_nonce[crypto_box_NONCEBYTES];//nonce of sent packets. + uint8_t status;//0 if no connection, 1 we have sent a handshake, 2 if connexion is not confirmed yet + //(we have recieved a hanshake but no empty data packet), 3 if the connection is established. + //4 if the connection is timed out. + uint16_t number; //Lossless_UDP connection number corresponding to this connection. + +}Crypto_Connection; + +#define MAX_CRYPTO_CONNECTIONS 256 + +Crypto_Connection crypto_connections[MAX_CRYPTO_CONNECTIONS]; + +#define MAX_FRIEND_REQUESTS 32 + +//keeps track of the connection numbers for friends request so we can check later if they were sent +int outbound_friendrequests[MAX_FRIEND_REQUESTS]; + +#define MAX_INCOMING 64 + +//keeps track of the connection numbers for friends request so we can check later if they were sent +int incoming_connections[MAX_INCOMING]; + +//encrypts plain of length length to encrypted of length + 16 using the +//public key(32 bytes) of the reciever and a 24 byte nonce +//return -1 if there was a problem. +//return length of encrypted data if everything was fine. +int encrypt_data(uint8_t * public_key, uint8_t * nonce, uint8_t * plain, uint32_t length, uint8_t * encrypted) +{ + if(length - crypto_box_BOXZEROBYTES + crypto_box_ZEROBYTES > MAX_DATA_SIZE || length == 0) + { + return -1; + } + + uint8_t temp_plain[MAX_DATA_SIZE + crypto_box_ZEROBYTES - crypto_box_BOXZEROBYTES] = {0}; + uint8_t temp_encrypted[MAX_DATA_SIZE + crypto_box_ZEROBYTES]; + uint8_t zeroes[crypto_box_BOXZEROBYTES] = {0}; + + memcpy(temp_plain + crypto_box_ZEROBYTES, plain, length);//pad the message with 32 0 bytes. + + crypto_box(temp_encrypted, temp_plain, length + crypto_box_ZEROBYTES, nonce, public_key, self_secret_key); + + //if encryption is successful the first crypto_box_BOXZEROBYTES of the message will be zero + if(memcmp(temp_encrypted, zeroes, crypto_box_BOXZEROBYTES) != 0) + { + return -1; + } + //unpad the encrypted message + memcpy(encrypted, temp_encrypted + crypto_box_BOXZEROBYTES, length - crypto_box_BOXZEROBYTES + crypto_box_ZEROBYTES); + return length - crypto_box_BOXZEROBYTES + crypto_box_ZEROBYTES; +} + +//decrypts encrypted of length length to plain of length length - 16 using the +//public key(32 bytes) of the sender and a 24 byte nonce +//return -1 if there was a problem(decryption failed) +//return length of plain data if everything was fine. +int decrypt_data(uint8_t * public_key, uint8_t * nonce, uint8_t * encrypted, uint32_t length, uint8_t * plain) +{ + if(length > MAX_DATA_SIZE || length <= crypto_box_BOXZEROBYTES) + { + return -1; + } + uint8_t temp_plain[MAX_DATA_SIZE - crypto_box_ZEROBYTES + crypto_box_BOXZEROBYTES]; + uint8_t temp_encrypted[MAX_DATA_SIZE + crypto_box_ZEROBYTES] = {0}; + uint8_t zeroes[crypto_box_ZEROBYTES] = {0}; + + memcpy(temp_encrypted + crypto_box_BOXZEROBYTES, encrypted, length);//pad the message with 16 0 bytes. + + if(crypto_box_open(temp_plain, temp_encrypted, length + crypto_box_BOXZEROBYTES, + nonce, public_key, self_secret_key) == -1) + { + return -1; + } + //if decryption is successful the first crypto_box_ZEROBYTES of the message will be zero + if(memcmp(temp_plain, zeroes, crypto_box_ZEROBYTES) != 0) + { + return -1; + } + //unpad the plain message + memcpy(plain, temp_plain + crypto_box_ZEROBYTES, length - crypto_box_ZEROBYTES + crypto_box_BOXZEROBYTES); + return length - crypto_box_ZEROBYTES + crypto_box_BOXZEROBYTES; +} + +//increment the given nonce by 1 +void increment_nonce(uint8_t * nonce) +{ + uint32_t i; + for(i = 0; i < crypto_box_NONCEBYTES; i++) + { + nonce[i]++; + if(nonce[i] != 0) + { + break; + } + } +} + +//fill the given nonce with random bytes. +//TODO: make this more optimized +void random_nonce(uint8_t * nonce) +{ + uint32_t i; + for(i = 0; i < crypto_box_NONCEBYTES; i++) + { + nonce[i] = random_int() % 256; + } +} + +//return 0 if there is no received data in the buffer +//return -1 if the packet was discarded. +//return length of recieved data if successful +int read_cryptpacket(int crypt_connection_id, uint8_t * data) +{ + if(crypto_connections[crypt_connection_id].status != 3) + { + return 0; + } + uint8_t temp_data[MAX_DATA_SIZE]; + int length = read_packet(crypto_connections[crypt_connection_id].number, temp_data); + if(length == 0) + { + return 0; + } + if(temp_data[0] != 3) + { + return -1; + } + int len = decrypt_data(crypto_connections[crypt_connection_id].public_key, + crypto_connections[crypt_connection_id].recv_nonce, temp_data + 1, length - 1, data); + if(len != -1) + { + increment_nonce(crypto_connections[crypt_connection_id].recv_nonce); + return len; + } + return -1; +} + + +//return 0 if data could not be put in packet queue +//return 1 if data was put into the queue +int write_cryptpacket(int crypt_connection_id, uint8_t * data, uint32_t length) +{ + if(length - crypto_box_BOXZEROBYTES + crypto_box_ZEROBYTES > MAX_DATA_SIZE - 1) + { + return 0; + } + if(crypto_connections[crypt_connection_id].status != 3) + { + return 0; + } + uint8_t temp_data[MAX_DATA_SIZE]; + int len = encrypt_data(crypto_connections[crypt_connection_id].public_key, + crypto_connections[crypt_connection_id].sent_nonce, data, length, temp_data + 1); + if(len == -1) + { + return 0; + } + temp_data[0] = 3; + if(write_packet(crypto_connections[crypt_connection_id].number, temp_data, len + 1) == 0) + { + return 0; + } + increment_nonce(crypto_connections[crypt_connection_id].sent_nonce); + return 1; +} + +//send a friend request to peer with public_key and ip_port. +//Data represents the data we send with the friends request. +//returns -1 on failure +//returns a positive friend request id that can be used later to see if it was sent correctly on success. +int send_friendrequest(uint8_t * public_key, IP_Port ip_port, uint8_t * data, uint32_t length) +{ + if(length > MAX_DATA_SIZE - 1 - crypto_box_PUBLICKEYBYTES - crypto_box_NONCEBYTES) + { + return -1; + } + uint32_t i; + for(i = 0; i < MAX_FRIEND_REQUESTS; i++) + { + if(outbound_friendrequests[i] == -1) + { + break; + } + } + if(i == MAX_FRIEND_REQUESTS) + { + return -1; + } + uint8_t temp_data[MAX_DATA_SIZE]; + uint8_t nonce[crypto_box_NONCEBYTES]; + random_nonce(nonce); + int len = encrypt_data(public_key, nonce, data, length, 1 + crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES + temp_data); + if(len == -1) + { + return -1; + } + temp_data[0] = 1; + memcpy(temp_data + 1, self_public_key, crypto_box_PUBLICKEYBYTES); + memcpy(temp_data + 1 + crypto_box_PUBLICKEYBYTES, nonce, crypto_box_NONCEBYTES); + int id = new_connection(ip_port); + if(id == -1) + { + return -1; + } + if(write_packet(id, temp_data, len + 1 + crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES) == 1) + { + outbound_friendrequests[i] = id; + return i; + } + return -1; +} + +//return -1 if failure +//return 0 if connection is still trying to send the request. +//return 1 if sent correctly +//return 2 if connection timed out +int check_friendrequest(int friend_request) +{ + if(friend_request < 0 || friend_request > MAX_FRIEND_REQUESTS) + { + return -1; + } + if(outbound_friendrequests[friend_request] == -1) + { + return -1; + } + if(sendqueue(outbound_friendrequests[friend_request]) == 0) + { + kill_connection(outbound_friendrequests[friend_request]); + outbound_friendrequests[friend_request] = -1; + return 1; + } + int status = is_connected(outbound_friendrequests[friend_request]); + if(status == 4) + { + kill_connection(outbound_friendrequests[friend_request]); + outbound_friendrequests[friend_request] = -1; + return 2; + } + if(status == 0) + { + outbound_friendrequests[friend_request] = -1; + return 2; + } + return 0; +} + +//Send a crypto handshake packet containing an encrypted secret nonce to peer with connection_id and public_key +//the packet is encrypted with a random nonce which is sent in plain text with the packet +int send_cryptohandshake(int connection_id, uint8_t * public_key, uint8_t * secret_nonce) +{ + uint8_t temp_data[MAX_DATA_SIZE]; + uint8_t nonce[crypto_box_NONCEBYTES]; + random_nonce(nonce); + int len = encrypt_data(public_key, nonce, secret_nonce, crypto_box_NONCEBYTES, + 1 + crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES + temp_data); + if(len == -1) + { + return 0; + } + temp_data[0] = 2; + memcpy(temp_data + 1, self_public_key, crypto_box_PUBLICKEYBYTES); + memcpy(temp_data + 1 + crypto_box_PUBLICKEYBYTES, nonce, crypto_box_NONCEBYTES); + return write_packet(connection_id, temp_data, len + 1 + crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES); +} + +//Extract secret nonce and public_key from a packet(data) with length length +//return 1 if successful +//return 0 if failure +int handle_cryptohandshake(uint8_t * public_key, uint8_t * secret_nonce, uint8_t * data, uint16_t length) +{ + int pad = (- crypto_box_BOXZEROBYTES + crypto_box_ZEROBYTES); + if(length != 1 + crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES + crypto_box_NONCEBYTES + pad) + { + return 0; + } + if(data[0] != 2) + { + return 0; + } + memcpy(public_key, data + 1, crypto_box_PUBLICKEYBYTES); + int len = decrypt_data(public_key, data + 1 + crypto_box_PUBLICKEYBYTES, + data + 1 + crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES, crypto_box_NONCEBYTES + pad, secret_nonce); + if(len != crypto_box_NONCEBYTES) + { + return 0; + } + return 1; +} + + +//puts the public key of the friend if public_key, the data from the request +//in data if a friend request was sent to us and returns the length of the data. +//return -1 if no valid friend requests. +int handle_friendrequest(uint8_t * public_key, uint8_t * data) +{ + uint32_t i; + for(i = 0; i < MAX_INCOMING; i++) + { + if(incoming_connections[i] != -1) + { + if(id_packet(incoming_connections[i]) == 1) + { + uint8_t temp_data[MAX_DATA_SIZE]; + uint16_t len = read_packet(incoming_connections[i], temp_data); + if(len > crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES + 1 + - crypto_box_BOXZEROBYTES + crypto_box_ZEROBYTES) + { + memcpy(public_key, temp_data + 1, crypto_box_PUBLICKEYBYTES); + uint8_t nonce[crypto_box_NONCEBYTES]; + memcpy(nonce, temp_data + 1 + crypto_box_PUBLICKEYBYTES, crypto_box_NONCEBYTES); + int len1 = decrypt_data(public_key, nonce, temp_data + 1 + crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES, + len - (crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES + 1), data); + if(len1 != -1) + { + kill_connection_in(incoming_connections[i], 1); //conection is useless now, kill it in 1 seconds + incoming_connections[i] = -1; + return len1; + } + } + kill_connection(incoming_connections[i]); //conection is useless now, kill it. + incoming_connections[i] = -1; + } + } + } + return -1; +} + + +//Start a secure connection with other peer who has public_key and ip_port +//returns -1 if failure +//returns crypt_connection_id of the initialized connection if everything went well. +int crypto_connect(uint8_t * public_key, IP_Port ip_port) +{ + uint32_t i; + if(getconnection_id(ip_port) != -1) + { + return -1; + } + for(i = 0; i < MAX_CRYPTO_CONNECTIONS; i++) + { + if(crypto_connections[i].status == 0) + { + int id = new_connection(ip_port); + if(id == -1) + { + return -1; + } + crypto_connections[i].number = id; + crypto_connections[i].status = 1; + random_nonce(crypto_connections[i].recv_nonce); + memcpy(crypto_connections[i].public_key, public_key, crypto_box_PUBLICKEYBYTES); + if(send_cryptohandshake(id, public_key, crypto_connections[i].recv_nonce) == 1) + { + increment_nonce(crypto_connections[i].recv_nonce); + return i; + } + return -1;//this should never happen. + } + } + return -1; +} + +//handle an incoming connection +//return -1 if no crypto inbound connection +//return incomming connection id (Lossless_UDP one) if there is an incomming crypto connection +//Put the public key of the peer in public_key and the secret_nonce from the handshake into secret_nonce +//to accept it see: accept_crypto_inbound(...) +//to refuse it just call kill_connection(...) on the connection id +int crypto_inbound(uint8_t * public_key, uint8_t * secret_nonce) +{ + uint32_t i; + for(i = 0; i < MAX_INCOMING; i++) + { + if(incoming_connections[i] != -1) + { + if(id_packet(incoming_connections[i]) == 2) + { + uint8_t temp_data[MAX_DATA_SIZE]; + uint16_t len = read_packet(incoming_connections[i], temp_data); + if(handle_cryptohandshake(public_key, secret_nonce, temp_data, len)) + { + int connection_id = incoming_connections[i]; + incoming_connections[i] = -1;//remove this connection from the incoming connection list. + return connection_id; + } + } + } + } + return -1; +} + +//kill a crypto connection +//return 0 if killed successfully +//return 1 if there was a problem. +int crypto_kill(int crypt_connection_id) +{ + if(crypt_connection_id < 0 || crypt_connection_id >= MAX_CRYPTO_CONNECTIONS) + { + return 1; + } + if(crypto_connections[crypt_connection_id].status != 0) + { + crypto_connections[crypt_connection_id].status = 0; + kill_connection(crypto_connections[crypt_connection_id].number); + return 0; + } + return 1; +} + + +//accept an incoming connection using the parameters provided by crypto_inbound +//return -1 if not successful +//returns the crypt_connection_id if successful +int accept_crypto_inbound(int connection_id, uint8_t * public_key, uint8_t * secret_nonce) +{ + uint32_t i; + if(connection_id == -1) + { + return -1; + } + for(i = 0; i < MAX_CRYPTO_CONNECTIONS; i++) + { + if(crypto_connections[i].status == 0) + { + crypto_connections[i].number = connection_id; + crypto_connections[i].status = 2; + random_nonce(crypto_connections[i].recv_nonce); + memcpy(crypto_connections[i].sent_nonce, secret_nonce, crypto_box_NONCEBYTES); + increment_nonce(crypto_connections[i].sent_nonce); + memcpy(crypto_connections[i].public_key, public_key, crypto_box_PUBLICKEYBYTES); + if(send_cryptohandshake(connection_id, public_key, crypto_connections[i].recv_nonce) == 1) + { + increment_nonce(crypto_connections[i].recv_nonce); + uint32_t zero = 0; + crypto_connections[i].status = 3;//connection status needs to be 3 for write_cryptpacket() to work + write_cryptpacket(i, ((uint8_t *)&zero), sizeof(zero)); + crypto_connections[i].status = 2;//set it to its proper value right after. + return i; + } + return -1;//this should never happen. + } + } + return -1; +} + +//return 0 if no connection, 1 we have sent a handshake, 2 if connexion is not confirmed yet +//(we have recieved a hanshake but no empty data packet), 3 if the connection is established. +//4 if the connection is timed out and wating to be killed +int is_cryptoconnected(int crypt_connection_id) +{ + if(crypt_connection_id >= 0 && crypt_connection_id < MAX_CRYPTO_CONNECTIONS) + { + return crypto_connections[crypt_connection_id].status; + } + return 0; +} + + +//Generate our public and private keys +//Only call this function the first time the program starts. +void new_keys() +{ + crypto_box_keypair(self_public_key,self_secret_key); +} + +//TODO: optimize this +//adds an incoming connection to the incoming_connection list. +//returns 0 if successful +//returns 1 if failure +int new_incoming(int id) +{ + uint32_t i; + for(i = 0; i < MAX_INCOMING; i++) + { + if(incoming_connections[i] == -1) + { + incoming_connections[i] = id; + return 0; + } + } + return 1; +} + +//TODO: optimize this +//handle all new incoming connections. +void handle_incomings() +{ + int income; + while(1) + { + income = incoming_connection(); + if(income == -1) + { + break; + } + if(new_incoming(income)) + { + break; + } + } +} + +//handle recieved packets for not yet established crypto connections. +void recieve_crypto() +{ + uint32_t i; + for(i = 0; i < MAX_CRYPTO_CONNECTIONS; i++) + { + if(crypto_connections[i].status == 1) + { + if(id_packet(crypto_connections[i].number) == 2)//handle handshake packet. + { + uint8_t temp_data[MAX_DATA_SIZE]; + uint8_t secret_nonce[crypto_box_NONCEBYTES]; + uint8_t public_key[crypto_box_PUBLICKEYBYTES]; + uint16_t len = read_packet(crypto_connections[i].number, temp_data); + if(handle_cryptohandshake(public_key, secret_nonce, temp_data, len)) + { + if(memcmp(public_key, crypto_connections[i].public_key, crypto_box_PUBLICKEYBYTES) == 0) + { + memcpy(crypto_connections[i].sent_nonce, secret_nonce, crypto_box_NONCEBYTES); + increment_nonce(crypto_connections[i].sent_nonce); + uint32_t zero = 0; + crypto_connections[i].status = 3;//connection status needs to be 3 for write_cryptpacket() to work + write_cryptpacket(i, ((uint8_t *)&zero), sizeof(zero)); + crypto_connections[i].status = 2;//set it to its proper value right after. + } + } + } + else if(id_packet(crypto_connections[i].number) != -1) + { + //This should not happen + //kill the connection if it does + crypto_kill(crypto_connections[i].number); + } + + } + if(crypto_connections[i].status == 2) + { + if(id_packet(crypto_connections[i].number) == 3) + { + uint8_t temp_data[MAX_DATA_SIZE]; + uint8_t data[MAX_DATA_SIZE]; + int length = read_packet(crypto_connections[i].number, temp_data); + int len = decrypt_data(crypto_connections[i].public_key, + crypto_connections[i].recv_nonce, temp_data + 1, length - 1, data); + uint32_t zero = 0; + if(len == sizeof(uint32_t) && memcmp(((uint8_t *)&zero), data, sizeof(uint32_t)) == 0) + { + increment_nonce(crypto_connections[i].recv_nonce); + crypto_connections[i].status = 3; + } + else + { + //This should not happen + //kill the connection if it does + crypto_kill(crypto_connections[i].number); + } + } + else if(id_packet(crypto_connections[i].number) != -1) + { + //This should not happen + //kill the connection if it does + crypto_kill(crypto_connections[i].number); + } + } + } +} + +//run this to (re)initialize net_crypto +//sets all the global connection variables to their default values. +void initNetCrypto() +{ + memset(crypto_connections, 0 ,sizeof(crypto_connections)); + memset(outbound_friendrequests, -1 ,sizeof(outbound_friendrequests)); + memset(incoming_connections, -1 ,sizeof(incoming_connections)); +} + +void killTimedout() +{ + uint32_t i; + for(i = 0; i < MAX_CRYPTO_CONNECTIONS; i++) + { + if(is_connected(crypto_connections[i].number) == 4) + { + crypto_connections[i].status = 4; + } + } +} + +//main loop +void doNetCrypto() +{ + //TODO:check if friend requests were sent correctly + //handle new incoming connections + //handle friend requests + handle_incomings(); + recieve_crypto(); + killTimedout(); +} -- cgit v1.2.3