2017-02-01 17:14:52 +00:00
# include <stdlib.h>
2017-02-02 23:27:57 +00:00
# include <stdio.h> // for debugging, can remove
2017-02-01 17:14:52 +00:00
# include <string.h>
2017-02-02 19:10:12 +00:00
# include <stdint.h>
2017-02-09 03:51:35 +00:00
# include <errno.h>
# include <arpa/inet.h>
2017-07-17 20:24:37 +00:00
# if defined(__APPLE__) || defined(__MACH__)
2017-07-17 20:23:29 +00:00
# include <machine/endian.h>
# else
2017-03-09 05:12:04 +00:00
# include <endian.h>
2017-07-17 20:23:29 +00:00
# endif
2017-04-03 17:43:05 +00:00
# include <stdarg.h>
2017-02-01 17:14:52 +00:00
2017-02-01 12:52:09 +00:00
# include "libp2p/secio/secio.h"
2017-02-01 17:14:52 +00:00
# include "libp2p/secio/propose.h"
2017-02-06 22:11:22 +00:00
# include "libp2p/secio/exchange.h"
2017-02-02 20:43:35 +00:00
# include "libp2p/net/multistream.h"
2017-02-09 03:51:35 +00:00
# include "libp2p/net/p2pnet.h"
2017-02-06 22:11:22 +00:00
# include "libp2p/crypto/ephemeral.h"
2017-02-08 16:08:05 +00:00
# include "libp2p/crypto/sha1.h"
# include "libp2p/crypto/sha256.h"
# include "libp2p/crypto/sha512.h"
2017-02-06 22:11:22 +00:00
# include "libp2p/utils/string_list.h"
# include "libp2p/utils/vector.h"
2017-04-03 18:20:12 +00:00
# include "libp2p/utils/logger.h"
2017-08-09 13:03:40 +00:00
# include "libp2p/net/protocol.h"
2017-03-09 05:12:04 +00:00
# include "mbedtls/md.h"
2017-03-09 13:41:59 +00:00
# include "mbedtls/cipher.h"
2017-03-09 05:12:04 +00:00
# include "mbedtls/md_internal.h"
2017-07-13 12:27:10 +00:00
# include "mbedtls/aes.h"
2017-02-01 12:52:09 +00:00
const char * SupportedExchanges = " P-256,P-384,P-521 " ;
const char * SupportedCiphers = " AES-256,AES-128,Blowfish " ;
const char * SupportedHashes = " SHA256,SHA512 " ;
2017-08-09 13:03:40 +00:00
struct SecioContext {
struct RsaPrivateKey * private_key ;
struct Peerstore * peer_store ;
} ;
int libp2p_secio_can_handle ( const uint8_t * incoming , size_t incoming_size ) {
// sanity checks
if ( incoming_size < 11 )
return 0 ;
char * result = strstr ( ( char * ) incoming , " /ipfs/secio " ) ;
if ( result ! = NULL & & result = = ( char * ) incoming )
return 0 ;
return 1 ;
2017-02-01 12:52:09 +00:00
}
2017-08-09 13:03:40 +00:00
int libp2p_secio_handle_message ( const uint8_t * incoming , size_t incoming_size , struct SessionContext * session_context , void * protocol_context ) {
struct SecioContext * ctx = ( struct SecioContext * ) protocol_context ;
2017-08-09 15:23:24 +00:00
return libp2p_secio_handshake ( session_context , ctx - > private_key , ctx - > peer_store ) ;
2017-08-09 13:03:40 +00:00
}
int libp2p_secio_shutdown ( void * context ) {
free ( context ) ;
return 1 ;
}
struct Libp2pProtocolHandler * libp2p_secio_build_protocol_handler ( struct RsaPrivateKey * private_key , struct Peerstore * peer_store ) {
struct Libp2pProtocolHandler * handler = ( struct Libp2pProtocolHandler * ) malloc ( sizeof ( struct Libp2pProtocolHandler ) ) ;
if ( handler ! = NULL ) {
struct SecioContext * context = ( struct SecioContext * ) malloc ( sizeof ( struct SecioContext ) ) ;
context - > private_key = private_key ;
context - > peer_store = peer_store ;
handler - > context = context ;
handler - > CanHandle = libp2p_secio_can_handle ;
handler - > HandleMessage = libp2p_secio_handle_message ;
handler - > Shutdown = libp2p_secio_shutdown ;
}
return handler ;
2017-02-01 12:52:09 +00:00
}
2017-02-02 19:10:12 +00:00
/**
* Generate a random nonce
* @ param results where to put the results
* @ param length the length of the nonce
* @ returns true ( 1 ) on success , otherwise false ( 0 )
*/
2017-07-13 12:27:10 +00:00
int libp2p_secio_generate_nonce ( unsigned char * results , int length ) {
2017-02-03 00:27:50 +00:00
FILE * fd = fopen ( " /dev/urandom " , " r " ) ;
fread ( results , 1 , length , fd ) ;
fclose ( fd ) ;
2017-02-02 19:10:12 +00:00
return 1 ;
}
2017-07-13 23:30:18 +00:00
/***
* Used for debugging . Turns an incoming byte array into a decimal string
* @ param incoming the incoming byte array
* @ param incoming_size the size of the byte array
* @ returns the string
*/
char * secio_to_hex ( unsigned char * incoming , size_t incoming_size ) {
static char str [ 3000 ] ;
memset ( str , 0 , 3000 ) ;
for ( int i = 0 ; i < incoming_size ; i + + ) {
sprintf ( & str [ i * 4 ] , " %03d " , incoming [ i ] ) ;
}
return str ;
}
2017-02-06 22:11:22 +00:00
/**
* Compute a hash based on a Propose struct
* @ param in the struct Propose
* @ param result where to put the result ( should be char [ 32 ] )
* @ returns true ( 1 ) on success
*/
2017-03-09 05:12:04 +00:00
int libp2p_secio_hash ( unsigned char * key , size_t key_size , unsigned char * nonce , size_t nonce_size , unsigned char result [ 32 ] ) {
2017-02-06 22:11:22 +00:00
// append public key and nonce
2017-07-13 23:30:18 +00:00
unsigned char * appended = malloc ( key_size + nonce_size ) ;
memcpy ( appended , key , key_size ) ;
memcpy ( & appended [ key_size ] , nonce , nonce_size ) ;
// hash it
libp2p_crypto_hashing_sha256 ( appended , key_size + nonce_size , result ) ;
free ( appended ) ;
2017-03-09 05:12:04 +00:00
return 1 ;
2017-02-06 22:11:22 +00:00
}
/***
* Compare 2 hashes lexicographically
* @ param a the a side
* @ param b the b side
* @ param length the length of a and b
* @ returns a - 1 , 0 , or 1
*/
2017-07-13 12:27:10 +00:00
int libp2p_secio_bytes_compare ( const unsigned char * a , const unsigned char * b , int length ) {
2017-02-06 22:11:22 +00:00
for ( int i = 0 ; i < length ; i + + ) {
if ( b [ i ] > a [ i ] )
return - 1 ;
if ( a [ i ] > b [ i ] )
return 1 ;
}
return 0 ;
}
2017-07-13 23:30:18 +00:00
/**
* Used for debugging keys
*/
/*
int libp2p_secio_log_keys ( struct SessionContext * session ) {
if ( libp2p_logger_watching_class ( " secio " ) ) {
libp2p_logger_debug ( " secio " , " Shared: %s \n " , secio_to_hex ( session - > shared_key , session - > shared_key_size ) ) ;
libp2p_logger_debug ( " secio " , " k1 IV: %s \n " , secio_to_hex ( session - > local_stretched_key - > iv , session - > local_stretched_key - > iv_size ) ) ;
libp2p_logger_debug ( " secio " , " k1 MAC: %s \n " , secio_to_hex ( session - > local_stretched_key - > cipher_key , session - > local_stretched_key - > cipher_size ) ) ;
libp2p_logger_debug ( " secio " , " k1 CIPHER: %s \n " , secio_to_hex ( session - > local_stretched_key - > mac_key , session - > local_stretched_key - > mac_size ) ) ;
libp2p_logger_debug ( " secio " , " k2 IV: %s \n " , secio_to_hex ( session - > remote_stretched_key - > iv , session - > remote_stretched_key - > iv_size ) ) ;
libp2p_logger_debug ( " secio " , " k2 MAC: %s \n " , secio_to_hex ( session - > remote_stretched_key - > cipher_key , session - > remote_stretched_key - > cipher_size ) ) ;
libp2p_logger_debug ( " secio " , " k2 CIPHER: %s \n " , secio_to_hex ( session - > remote_stretched_key - > mac_key , session - > remote_stretched_key - > mac_size ) ) ;
}
return 1 ;
}
*/
2017-07-13 12:27:10 +00:00
/***
* Using values in the Propose struct , determine the order that will be used for the MACs
* @ param remote the struct from the remote side
* @ param local the struct from this side
* @ returns - 1 or 1 that will be used to determine who is first
*/
2017-07-13 23:30:18 +00:00
int libp2p_secio_determine_order ( struct Propose * remote , struct Propose * local ) {
2017-07-13 12:27:10 +00:00
unsigned char hash1 [ 32 ] ;
unsigned char hash2 [ 32 ] ;
libp2p_secio_hash ( remote - > public_key , remote - > public_key_size , local - > rand , local - > rand_size , hash1 ) ;
libp2p_secio_hash ( local - > public_key , local - > public_key_size , remote - > rand , remote - > rand_size , hash2 ) ;
2017-07-13 23:30:18 +00:00
2017-07-13 12:27:10 +00:00
return libp2p_secio_bytes_compare ( hash1 , hash2 , 32 ) ;
}
2017-02-06 22:11:22 +00:00
int libp2p_secio_string_allocate ( char * in , char * * out ) {
* out = ( char * ) malloc ( strlen ( in ) + 1 ) ;
strcpy ( * out , in ) ;
return 1 ;
}
struct StringList * libp2p_secio_split_list ( const char * list , int list_size ) {
struct StringList * head = NULL ;
struct StringList * last = NULL ;
struct StringList * current = NULL ;
char * curr_tok = NULL ;
// make a copy
char copy [ list_size + 1 ] ;
memcpy ( & copy [ 0 ] , list , list_size ) ;
copy [ list_size ] = 0 ;
curr_tok = strtok ( copy , " , " ) ;
while ( curr_tok ! = NULL ) {
current = libp2p_utils_string_list_new ( ) ;
libp2p_secio_string_allocate ( curr_tok , & current - > string ) ;
if ( head = = NULL ) {
head = current ;
last = current ;
} else {
last - > next = current ;
}
last = current ;
curr_tok = strtok ( NULL , " , " ) ;
}
return head ;
}
/**
* Compare 2 lists , and pick the best one
* @ param order which carries more weight
* @ param local_list the list to compare
* @ param local_list_size the size of the list
* @ param remote_list the list to compare
* @ param remote_list_size the size of the list
* @ param results where to put the results ( NOTE : Allocate memory for this )
* @ returns true ( 1 ) on success , otherwise , false ( 0 )
*/
int libp2p_secio_select_best ( int order , const char * local_list , int local_list_size , const char * remote_list , int remote_list_size , char * * results ) {
struct StringList * lead_head = libp2p_secio_split_list ( local_list , local_list_size ) ;
struct StringList * follower_head = NULL ;
struct StringList * lead = NULL ;
struct StringList * follower = NULL ;
int match = 0 ;
//shortcut
if ( order = = 0 )
{
libp2p_secio_string_allocate ( lead_head - > string , results ) ;
2017-02-09 08:34:12 +00:00
match = 1 ;
goto exit ;
2017-02-06 22:11:22 +00:00
}
// this list doesn't match. Do further investigation
if ( order > 0 ) { // lead is local
follower_head = libp2p_secio_split_list ( remote_list , remote_list_size ) ;
} else {
follower_head = lead_head ;
lead_head = libp2p_secio_split_list ( remote_list , remote_list_size ) ;
}
lead = lead_head ;
follower = follower_head ;
// now work through the list, looking for a match
while ( lead ! = NULL ) {
while ( follower ! = NULL ) {
if ( strcmp ( lead - > string , follower - > string ) = = 0 ) {
2017-02-09 08:34:12 +00:00
libp2p_secio_string_allocate ( lead - > string , results ) ;
2017-02-06 22:11:22 +00:00
match = 1 ;
break ;
}
follower = follower - > next ;
}
if ( match )
break ;
follower = follower_head ;
lead = lead - > next ;
}
2017-02-09 08:34:12 +00:00
exit :
if ( lead_head ! = NULL )
libp2p_utils_string_list_free ( lead_head ) ;
if ( follower_head ! = NULL )
libp2p_utils_string_list_free ( follower_head ) ;
return match ;
2017-02-06 22:11:22 +00:00
}
/**
* Check to see if the signature is correct based on the given bytes in " in "
* @ param public_key the public key to use
* @ param in the bytes that were signed
* @ param in_length the number of bytes
* @ param signature the signature that was given to us
* @ param signature_length the length of the signature
* @ returns true ( 1 ) if the signature is correct , false ( 0 ) otherwise
*/
int libp2p_secio_verify_signature ( struct PublicKey * public_key , const unsigned char * in , size_t in_length , unsigned char * signature ) {
if ( public_key - > type = = KEYTYPE_RSA ) {
2017-02-09 17:50:28 +00:00
struct RsaPublicKey rsa_key = { 0 } ;
2017-02-06 22:11:22 +00:00
rsa_key . der = ( char * ) public_key - > data ;
rsa_key . der_length = public_key - > data_size ;
return libp2p_crypto_rsa_verify ( & rsa_key , in , in_length , signature ) ;
}
// TODO: Implement this method for non-RSA
return 0 ;
}
2017-03-07 00:03:04 +00:00
/**
* Sign data
* @ param private_key the key to use
* @ param in the bytes to sign
* @ param in_length the number of bytes
* @ param signature the result
* @ param signature_size the size of the result
* @ returns true ( 1 ) on success , otherwise false ( 0 )
*/
2017-02-08 17:32:41 +00:00
int libp2p_secio_sign ( struct PrivateKey * private_key , const char * in , size_t in_length , unsigned char * * signature , size_t * signature_size ) {
2017-02-06 22:11:22 +00:00
if ( private_key - > type = = KEYTYPE_RSA ) {
2017-02-09 17:50:28 +00:00
struct RsaPrivateKey rsa_key = { 0 } ;
2017-02-06 22:11:22 +00:00
rsa_key . der = ( char * ) private_key - > data ;
rsa_key . der_length = private_key - > data_size ;
2017-02-09 17:50:28 +00:00
return libp2p_crypto_rsa_sign ( & rsa_key , in , in_length , signature , signature_size ) ;
2017-02-06 22:11:22 +00:00
}
// TODO: Implement this method for non-RSA
return 0 ;
}
/**
2017-02-08 16:08:05 +00:00
* Generate 2 keys by stretching the secret key
* @ param cipherType the cipher type ( i . e . " AES-128 " )
* @ param hashType the hash type ( i . e . " SHA256 " )
* @ param secret the secret key
* @ param secret_size the length of the secret key
* @ param k1 one of the resultant keys
* @ param k2 one of the resultant keys
* @ returns true ( 1 ) on success , otherwise 0 ( false )
2017-02-06 22:11:22 +00:00
*/
2017-07-13 23:30:18 +00:00
int libp2p_secio_stretch_keys ( char * cipherType , char * hashType , unsigned char * secret , size_t secret_size ,
struct StretchedKey * * k1_ptr , struct StretchedKey * * k2_ptr ) {
2017-03-09 15:00:45 +00:00
int retVal = 0 , num_filled = 0 , hmac_size = 20 ;
2017-07-17 21:14:20 +00:00
struct StretchedKey * k1 = NULL ;
struct StretchedKey * k2 = NULL ;
2017-02-08 16:08:05 +00:00
unsigned char * result = NULL ; ;
size_t result_size = 0 ;
2017-03-09 05:12:04 +00:00
char * seed = " key expansion " ;
2017-03-07 00:03:04 +00:00
unsigned char * temp = NULL ;
2017-03-09 05:12:04 +00:00
unsigned char a_hash [ 32 ] ;
unsigned char b_hash [ 32 ] ;
2017-02-08 16:08:05 +00:00
k1 = libp2p_crypto_ephemeral_stretched_key_new ( ) ;
if ( k1 = = NULL )
goto exit ;
k2 = libp2p_crypto_ephemeral_stretched_key_new ( ) ;
if ( k2_ptr = = NULL )
goto exit ;
2017-02-06 22:11:22 +00:00
2017-02-08 16:08:05 +00:00
// pick the right cipher
if ( strcmp ( cipherType , " AES-128 " ) = = 0 ) {
k1 - > iv_size = 16 ;
k2 - > iv_size = 16 ;
k1 - > cipher_size = 16 ;
k2 - > cipher_size = 16 ;
} else if ( strcmp ( cipherType , " AES-256 " ) = = 0 ) {
k1 - > iv_size = 16 ;
k2 - > iv_size = 16 ;
k1 - > cipher_size = 32 ;
k2 - > cipher_size = 32 ;
} else if ( strcmp ( cipherType , " Blowfish " ) = = 0 ) {
k1 - > iv_size = 8 ;
k2 - > iv_size = 8 ;
k1 - > cipher_size = 32 ;
k2 - > cipher_size = 32 ;
} else {
goto exit ;
}
// pick the right hash
2017-03-09 15:00:45 +00:00
// TODO: this
/*
2017-02-08 16:08:05 +00:00
if ( strcmp ( hashType , " SHA1 " ) = = 0 ) {
hash_func = libp2p_crypto_hashing_sha1 ;
hash_size = 40 ;
} else if ( strcmp ( hashType , " SHA256 " ) = = 0 ) {
hash_func = libp2p_crypto_hashing_sha256 ;
hash_size = 32 ;
} else if ( strcmp ( hashType , " SHA512 " ) = = 0 ) {
hash_func = libp2p_crypto_hashing_sha512 ;
hash_size = 64 ;
} else {
goto exit ;
}
2017-03-09 15:00:45 +00:00
*/
2017-02-06 22:11:22 +00:00
2017-03-09 05:12:04 +00:00
//TODO: make this work for all hashes, not just SHA256
2017-02-08 16:08:05 +00:00
result_size = 2 * ( k1 - > iv_size + k1 - > cipher_size * hmac_size ) ;
result = malloc ( result_size ) ;
if ( result = = NULL )
goto exit ;
2017-02-06 22:11:22 +00:00
2017-03-09 05:12:04 +00:00
mbedtls_md_context_t ctx ;
mbedtls_md_setup ( & ctx , & mbedtls_sha256_info , 1 ) ;
mbedtls_md_hmac_starts ( & ctx , secret , secret_size ) ;
mbedtls_md_hmac_update ( & ctx , ( unsigned char * ) seed , strlen ( seed ) ) ;
mbedtls_md_hmac_finish ( & ctx , a_hash ) ;
2017-02-08 16:08:05 +00:00
// now we have our first hash. Begin to fill the result buffer
while ( num_filled < result_size ) {
2017-03-09 05:12:04 +00:00
mbedtls_md_hmac_reset ( & ctx ) ;
mbedtls_md_hmac_update ( & ctx , a_hash , 32 ) ;
mbedtls_md_hmac_update ( & ctx , ( unsigned char * ) seed , strlen ( seed ) ) ;
mbedtls_md_hmac_finish ( & ctx , b_hash ) ;
int todo = 32 ;
if ( todo + num_filled > result_size )
todo = result_size - num_filled ;
memcpy ( & result [ num_filled ] , b_hash , todo ) ;
num_filled + = todo ;
mbedtls_md_hmac_reset ( & ctx ) ;
mbedtls_md_hmac_update ( & ctx , a_hash , 32 ) ;
mbedtls_md_hmac_finish ( & ctx , a_hash ) ;
2017-02-06 22:11:22 +00:00
}
2017-04-27 04:56:18 +00:00
mbedtls_md_free ( & ctx ) ;
2017-02-06 22:11:22 +00:00
2017-02-08 16:08:05 +00:00
// now we have a big result. Cut it up into pieces
if ( temp ! = NULL )
free ( temp ) ;
2017-03-07 00:03:04 +00:00
temp = result ;
2017-03-08 17:18:29 +00:00
k1 - > mac_size = hmac_size ;
2017-02-08 16:08:05 +00:00
k1 - > iv = malloc ( k1 - > iv_size ) ;
memcpy ( k1 - > iv , temp , k1 - > iv_size ) ;
temp + = k1 - > iv_size ;
k1 - > cipher_key = malloc ( k1 - > cipher_size ) ;
memcpy ( k1 - > cipher_key , temp , k1 - > cipher_size ) ;
temp + = k1 - > cipher_size ;
k1 - > mac_key = malloc ( k1 - > mac_size ) ;
memcpy ( k1 - > mac_key , temp , k1 - > mac_size ) ;
temp + = k1 - > mac_size ;
2017-03-08 17:18:29 +00:00
k2 - > mac_size = hmac_size ;
2017-02-08 16:08:05 +00:00
k2 - > iv = malloc ( k2 - > iv_size ) ;
memcpy ( k2 - > iv , temp , k2 - > iv_size ) ;
temp + = k2 - > iv_size ;
k2 - > cipher_key = malloc ( k2 - > cipher_size ) ;
memcpy ( k2 - > cipher_key , temp , k2 - > cipher_size ) ;
temp + = k2 - > cipher_size ;
k2 - > mac_key = malloc ( k2 - > mac_size ) ;
memcpy ( k2 - > mac_key , temp , k2 - > mac_size ) ;
temp + = k2 - > mac_size ;
2017-02-06 22:11:22 +00:00
2017-03-02 21:14:52 +00:00
temp = NULL ;
2017-02-08 16:08:05 +00:00
retVal = 1 ;
// cleanup
exit :
* k1_ptr = k1 ;
* k2_ptr = k2 ;
if ( retVal ! = 1 ) {
if ( * k1_ptr ! = NULL )
libp2p_crypto_ephemeral_stretched_key_free ( * k1_ptr ) ;
if ( * k2_ptr ! = NULL )
libp2p_crypto_ephemeral_stretched_key_free ( * k2_ptr ) ;
* k1_ptr = NULL ;
* k2_ptr = NULL ;
}
if ( temp ! = NULL )
free ( temp ) ;
if ( result ! = NULL )
free ( result ) ;
return retVal ;
2017-02-06 22:11:22 +00:00
}
2017-03-19 12:42:52 +00:00
int libp2p_secio_make_mac_and_cipher ( struct SessionContext * session , struct StretchedKey * stretched_key ) {
2017-03-02 21:14:52 +00:00
// mac
if ( strcmp ( session - > chosen_hash , " SHA1 " ) = = 0 ) {
2017-03-07 00:03:04 +00:00
stretched_key - > mac_size = 40 ;
2017-03-02 21:14:52 +00:00
} else if ( strcmp ( session - > chosen_hash , " SHA512 " ) = = 0 ) {
2017-03-08 10:53:59 +00:00
stretched_key - > mac_size = 64 ;
2017-03-02 21:14:52 +00:00
} else if ( strcmp ( session - > chosen_hash , " SHA256 " ) = = 0 ) {
2017-03-09 05:12:04 +00:00
//stretched_key->mac_size = 32;
2017-03-02 21:14:52 +00:00
} else {
return 0 ;
}
2017-03-08 10:53:59 +00:00
//TODO: Research this question..
// this was already made during the key stretch. Why make it again?
/*
2017-03-07 00:03:04 +00:00
stretched_key - > mac_key = malloc ( stretched_key - > mac_size ) ;
session - > mac_function ( stretched_key - > cipher_key , stretched_key - > cipher_size , stretched_key - > mac_key ) ;
2017-03-08 10:53:59 +00:00
*/
2017-03-02 21:14:52 +00:00
// block cipher
2017-03-07 00:03:04 +00:00
if ( strcmp ( session - > chosen_cipher , " AES-128 " ) | | strcmp ( session - > chosen_cipher , " AES-256 " ) = = 0 ) {
//we already have the key
2017-03-02 21:14:52 +00:00
} else if ( strcmp ( session - > chosen_cipher , " Blowfish " ) = = 0 ) {
2017-03-07 00:03:04 +00:00
//TODO: Implement blowfish
return 0 ;
2017-03-02 21:14:52 +00:00
} else {
return 0 ;
}
2017-03-07 00:03:04 +00:00
//TODO: set up the encrypted streams
2017-03-02 21:14:52 +00:00
return 1 ;
2017-02-06 22:11:22 +00:00
}
2017-03-07 00:03:04 +00:00
/***
* Write bytes to an unencrypted stream
* @ param session the session information
* @ param bytes the bytes to write
* @ param data_length the number of bytes to write
* @ returns the number of bytes written
*/
2017-03-19 12:42:52 +00:00
int libp2p_secio_unencrypted_write ( struct SessionContext * session , unsigned char * bytes , size_t data_length ) {
2017-02-09 03:51:35 +00:00
int num_bytes = 0 ;
if ( data_length > 0 ) { // only do this is if there is something to send
// first send the size
uint32_t size = htonl ( data_length ) ;
char * size_as_char = ( char * ) & size ;
int left = 4 ;
int written = 0 ;
int written_this_time = 0 ;
do {
2017-07-27 17:06:27 +00:00
written_this_time = socket_write ( * ( ( int * ) session - > default_stream - > socket_descriptor ) , & size_as_char [ written ] , left , 0 ) ;
2017-02-09 03:51:35 +00:00
if ( written_this_time < 0 ) {
written_this_time = 0 ;
if ( ( errno = = EAGAIN ) | | ( errno = = EWOULDBLOCK ) ) {
// TODO: use epoll or select to wait for socket to be writable
} else {
return 0 ;
}
}
left = left - written_this_time ;
} while ( left > 0 ) ;
// then send the actual data
left = data_length ;
written = 0 ;
do {
2017-07-27 17:06:27 +00:00
written_this_time = socket_write ( * ( ( int * ) session - > default_stream - > socket_descriptor ) , ( char * ) & bytes [ written ] , left , 0 ) ;
2017-02-09 03:51:35 +00:00
if ( written_this_time < 0 ) {
written_this_time = 0 ;
if ( ( errno = = EAGAIN ) | | ( errno = = EWOULDBLOCK ) ) {
// TODO: use epoll or select to wait for socket to be writable
} else {
return 0 ;
}
}
left = left - written_this_time ;
written + = written_this_time ;
} while ( left > 0 ) ;
num_bytes = written ;
} // there was something to send
return num_bytes ;
2017-02-06 22:11:22 +00:00
}
2017-03-07 00:03:04 +00:00
/***
* Read bytes from the incoming stream
* @ param session the session information
* @ param results where to put the bytes read
* @ param results_size the size of the results
* @ returns the number of bytes read
*/
2017-04-17 19:03:27 +00:00
int libp2p_secio_unencrypted_read ( struct SessionContext * session , unsigned char * * results , size_t * results_size , int timeout_secs ) {
2017-02-09 03:51:35 +00:00
uint32_t buffer_size ;
// first read the 4 byte integer
char * size = ( char * ) & buffer_size ;
int left = 4 ;
int read = 0 ;
int read_this_time = 0 ;
do {
2017-04-17 19:03:27 +00:00
read_this_time = socket_read ( * ( ( int * ) session - > insecure_stream - > socket_descriptor ) , & size [ read ] , 1 , 0 , timeout_secs ) ;
2017-02-09 03:51:35 +00:00
if ( read_this_time < 0 ) {
read_this_time = 0 ;
if ( ( errno = = EAGAIN ) | | ( errno = = EWOULDBLOCK ) ) {
// TODO: use epoll or select to wait for socket to be writable
2017-07-31 15:00:32 +00:00
return 0 ;
2017-02-09 03:51:35 +00:00
} else {
2017-07-17 19:38:48 +00:00
libp2p_logger_error ( " secio " , " Error in libp2p_secio_unencrypted_read: %s \n " , strerror ( errno ) ) ;
2017-02-09 03:51:35 +00:00
return 0 ;
}
}
if ( read = = 0 & & size [ 0 ] = = 10 ) {
// a spurious \n
// write over this value by not adding it
} else {
left = left - read_this_time ;
read + = read_this_time ;
}
} while ( left > 0 ) ;
buffer_size = ntohl ( buffer_size ) ;
if ( buffer_size = = 0 )
return 0 ;
2017-02-09 17:50:28 +00:00
// now read the number of bytes we've found, minus the 4 that we just read
2017-02-09 03:51:35 +00:00
left = buffer_size ;
read = 0 ;
read_this_time = 0 ;
* results = malloc ( left ) ;
unsigned char * ptr = * results ;
do {
2017-04-17 19:03:27 +00:00
read_this_time = socket_read ( * ( ( int * ) session - > insecure_stream - > socket_descriptor ) , ( char * ) & ptr [ read ] , left , 0 , timeout_secs ) ;
2017-02-09 03:51:35 +00:00
if ( read_this_time < 0 ) {
read_this_time = 0 ;
if ( ( errno = = EAGAIN ) | | ( errno = = EWOULDBLOCK ) ) {
// TODO: use epoll or select to wait for socket to be writable
} else {
return 0 ;
}
}
left = left - read_this_time ;
} while ( left > 0 ) ;
* results_size = buffer_size ;
return buffer_size ;
2017-02-06 22:11:22 +00:00
}
2017-07-13 12:27:10 +00:00
/**
* Initialize state for the sha256 stream cipher
* @ param session the SessionContext struct that contains the variables to initialize
* @ returns 1
*/
int libp2p_secio_initialize_crypto ( struct SessionContext * session ) {
session - > aes_decode_nonce_offset = 0 ;
session - > aes_encode_nonce_offset = 0 ;
memset ( session - > aes_decode_stream_block , 0 , 16 ) ;
memset ( session - > aes_encode_stream_block , 0 , 16 ) ;
return 1 ;
}
2017-03-07 00:03:04 +00:00
/**
* Encrypt data before being sent out an insecure stream
* @ param session the session information
* @ param incoming the incoming data
* @ param incoming_size the size of the incoming data
* @ param outgoing where to put the results
* @ param outgoing_size the amount of memory allocated
* @ returns true ( 1 ) on success , otherwise false ( 0 )
*/
2017-07-13 12:27:10 +00:00
int libp2p_secio_encrypt ( struct SessionContext * session , const unsigned char * incoming , size_t incoming_size , unsigned char * * outgoing , size_t * outgoing_size ) {
2017-03-09 13:41:59 +00:00
unsigned char * buffer = NULL ;
2017-04-27 04:56:18 +00:00
size_t buffer_size = 0 , original_buffer_size = 0 ;
2017-03-09 13:41:59 +00:00
//TODO switch between ciphers
2017-07-13 12:27:10 +00:00
mbedtls_aes_context cipher_ctx ;
mbedtls_aes_init ( & cipher_ctx ) ;
if ( mbedtls_aes_setkey_enc ( & cipher_ctx , session - > local_stretched_key - > cipher_key , session - > local_stretched_key - > cipher_size * 8 ) ) {
fprintf ( stderr , " Unable to set key for cipher \n " ) ;
return 0 ;
}
2017-04-27 04:56:18 +00:00
original_buffer_size = incoming_size ;
original_buffer_size + = 32 ;
buffer_size = original_buffer_size ;
buffer = malloc ( original_buffer_size ) ;
memset ( buffer , 0 , original_buffer_size ) ;
2017-07-13 12:27:10 +00:00
if ( mbedtls_aes_crypt_ctr ( & cipher_ctx , incoming_size , & session - > aes_encode_nonce_offset , session - > local_stretched_key - > iv , session - > aes_encode_stream_block , incoming , buffer ) ) {
fprintf ( stderr , " Unable to update cipher \n " ) ;
return 0 ;
}
buffer_size = incoming_size ;
2017-04-27 04:56:18 +00:00
// Now, buffer size may be set differently than original_buffer_size
// The "incoming" is now encrypted, and is in the first part of the buffer
2017-07-13 12:27:10 +00:00
mbedtls_aes_free ( & cipher_ctx ) ;
2017-03-09 13:41:59 +00:00
2017-04-27 04:56:18 +00:00
// mac the data
2017-03-09 05:12:04 +00:00
mbedtls_md_context_t ctx ;
mbedtls_md_setup ( & ctx , & mbedtls_sha256_info , 1 ) ;
mbedtls_md_hmac_starts ( & ctx , session - > local_stretched_key - > mac_key , session - > local_stretched_key - > mac_size ) ;
2017-03-09 13:41:59 +00:00
mbedtls_md_hmac_update ( & ctx , buffer , buffer_size ) ;
2017-04-27 04:56:18 +00:00
// this will tack the mac onto the end of the buffer
2017-03-09 13:41:59 +00:00
mbedtls_md_hmac_finish ( & ctx , & buffer [ buffer_size ] ) ;
2017-03-09 05:12:04 +00:00
mbedtls_md_free ( & ctx ) ;
2017-03-09 13:41:59 +00:00
// put it all in outgoing
2017-04-27 04:56:18 +00:00
* outgoing_size = original_buffer_size ;
2017-03-09 13:41:59 +00:00
* outgoing = malloc ( * outgoing_size ) ;
2017-04-27 04:56:18 +00:00
memset ( * outgoing , 0 , * outgoing_size ) ;
memcpy ( * outgoing , buffer , original_buffer_size ) ;
2017-03-09 13:41:59 +00:00
free ( buffer ) ;
2017-03-07 00:03:04 +00:00
return 1 ;
}
/**
* Write to an encrypted stream
* @ param session the session parameters
* @ param bytes the bytes to write
* @ param num_bytes the number of bytes to write
* @ returns the number of bytes written
*/
2017-03-09 15:00:45 +00:00
int libp2p_secio_encrypted_write ( void * stream_context , const unsigned char * bytes , size_t num_bytes ) {
2017-03-19 12:42:52 +00:00
struct SessionContext * session = ( struct SessionContext * ) stream_context ;
2017-03-07 00:03:04 +00:00
// writer uses the local cipher and mac
unsigned char * buffer = NULL ;
size_t buffer_size = 0 ;
2017-07-17 19:38:48 +00:00
if ( ! libp2p_secio_encrypt ( session , bytes , num_bytes , & buffer , & buffer_size ) ) {
libp2p_logger_error ( " secio " , " secio_encrypt returned false. \n " ) ;
2017-03-07 00:03:04 +00:00
return 0 ;
2017-07-17 19:38:48 +00:00
}
2017-03-08 10:53:59 +00:00
int retVal = libp2p_secio_unencrypted_write ( session , buffer , buffer_size ) ;
2017-07-17 19:38:48 +00:00
if ( ! retVal ) {
libp2p_logger_error ( " secio " , " secio_unencrypted_write returned false \n " ) ;
}
2017-03-08 10:53:59 +00:00
free ( buffer ) ;
return retVal ;
2017-03-07 00:03:04 +00:00
}
/**
* Unencrypt data that was read from the stream
* @ param session the session information
* @ param incoming the incoming bytes
* @ param incoming_size the number of incoming bytes
* @ param outgoing where to put the results
* @ param outgoing_size the amount of memory allocated for the results
* @ returns number of unencrypted bytes
*/
2017-07-13 12:27:10 +00:00
int libp2p_secio_decrypt ( struct SessionContext * session , const unsigned char * incoming , size_t incoming_size , unsigned char * * outgoing , size_t * outgoing_size ) {
2017-03-09 05:12:04 +00:00
size_t data_section_size = incoming_size - 32 ;
* outgoing_size = 0 ;
2017-03-09 13:41:59 +00:00
unsigned char * buffer ;
2017-03-09 05:12:04 +00:00
// verify MAC
//TODO make this more generic to use more than SHA256
mbedtls_md_context_t ctx ;
mbedtls_md_setup ( & ctx , & mbedtls_sha256_info , 1 ) ;
mbedtls_md_hmac_starts ( & ctx , session - > remote_stretched_key - > mac_key , session - > remote_stretched_key - > mac_size ) ;
mbedtls_md_hmac_update ( & ctx , incoming , data_section_size ) ;
unsigned char generated_mac [ 32 ] ;
mbedtls_md_hmac_finish ( & ctx , generated_mac ) ;
mbedtls_md_free ( & ctx ) ;
// 2. check the mac to see if it is the same
int retVal = memcmp ( & incoming [ data_section_size ] , generated_mac , 32 ) ;
2017-07-13 12:27:10 +00:00
if ( retVal ! = 0 ) {
// MAC verification failed
2017-07-13 14:01:50 +00:00
libp2p_logger_error ( " secio " , " libp2p_secio_decrypt: MAC verification failed. \n " ) ;
2017-07-13 23:30:18 +00:00
// copy the raw bytes into outgoing for further analysis
* outgoing = ( unsigned char * ) malloc ( incoming_size ) ;
* outgoing_size = incoming_size ;
memcpy ( * outgoing , incoming , incoming_size ) ;
2017-07-13 12:27:10 +00:00
return 0 ;
}
// The MAC checks out. Now decipher the data section
mbedtls_aes_context cipher_ctx ;
mbedtls_aes_init ( & cipher_ctx ) ;
if ( mbedtls_aes_setkey_enc ( & cipher_ctx , session - > remote_stretched_key - > cipher_key , session - > remote_stretched_key - > cipher_size * 8 ) ) {
2017-07-17 19:38:48 +00:00
libp2p_logger_error ( " secio " , " Unable to set key for cipher. \n " ) ;
2017-07-13 12:27:10 +00:00
return 0 ;
}
buffer = malloc ( data_section_size ) ;
if ( mbedtls_aes_crypt_ctr ( & cipher_ctx , data_section_size , & session - > aes_decode_nonce_offset , session - > remote_stretched_key - > iv , session - > aes_decode_stream_block , incoming , buffer ) ) {
2017-07-17 19:38:48 +00:00
libp2p_logger_error ( " secio " , " Unable to update cipher. \n " ) ;
2017-03-09 05:12:04 +00:00
return 0 ;
2017-07-13 12:27:10 +00:00
}
2017-03-09 13:41:59 +00:00
2017-07-13 12:27:10 +00:00
mbedtls_aes_free ( & cipher_ctx ) ;
* outgoing = malloc ( data_section_size ) ;
* outgoing_size = data_section_size ;
memcpy ( * outgoing , buffer , data_section_size ) ;
2017-04-27 04:56:18 +00:00
free ( buffer ) ;
2017-07-13 12:27:10 +00:00
2017-03-09 13:41:59 +00:00
return * outgoing_size ;
2017-03-07 00:03:04 +00:00
}
/**
* Read from an encrypted stream
* @ param session the session parameters
* @ param bytes where the bytes will be stored
* @ param num_bytes the number of bytes read from the stream
* @ returns the number of bytes read
*/
2017-04-17 19:03:27 +00:00
int libp2p_secio_encrypted_read ( void * stream_context , unsigned char * * bytes , size_t * num_bytes , int timeout_secs ) {
2017-04-27 04:56:18 +00:00
int retVal = 0 ;
2017-03-19 12:42:52 +00:00
struct SessionContext * session = ( struct SessionContext * ) stream_context ;
2017-03-07 00:03:04 +00:00
// reader uses the remote cipher and mac
// read the data
unsigned char * incoming = NULL ;
size_t incoming_size = 0 ;
2017-07-13 23:30:18 +00:00
if ( libp2p_secio_unencrypted_read ( session , & incoming , & incoming_size , timeout_secs ) < = 0 ) {
2017-07-17 19:38:48 +00:00
libp2p_logger_error ( " secio " , " Unencrypted_read returned false. \n " ) ;
2017-04-27 04:56:18 +00:00
goto exit ;
2017-07-13 23:30:18 +00:00
}
2017-04-27 04:56:18 +00:00
retVal = libp2p_secio_decrypt ( session , incoming , incoming_size , bytes , num_bytes ) ;
2017-07-17 19:38:48 +00:00
if ( ! retVal )
libp2p_logger_error ( " secio " , " Decrypting incoming stream returned false. \n " ) ;
2017-04-27 04:56:18 +00:00
exit :
if ( incoming ! = NULL )
free ( incoming ) ;
return retVal ;
2017-03-07 00:03:04 +00:00
}
2017-02-01 12:52:09 +00:00
/***
* performs initial communication over an insecure channel to share
* keys , IDs , and initiate connection . This is a framed messaging system
2017-02-03 00:09:20 +00:00
* NOTE : session must contain a valid socket_descriptor that is a multistream .
2017-03-07 00:03:04 +00:00
* @ param local_session the secure session to be filled
2017-02-06 22:11:22 +00:00
* @ param private_key our private key to use
2017-03-07 00:03:04 +00:00
* @ param remote_requested it is the other side that requested the upgrade to secio
2017-02-01 12:52:09 +00:00
* @ returns true ( 1 ) on success , false ( 0 ) otherwise
*/
2017-08-09 15:23:24 +00:00
int libp2p_secio_handshake ( struct SessionContext * local_session , struct RsaPrivateKey * private_key , struct Peerstore * peerstore ) {
2017-02-06 22:11:22 +00:00
int retVal = 0 ;
size_t results_size = 0 , bytes_written = 0 ;
unsigned char * propose_in_bytes = NULL ; // the remote protobuf
size_t propose_in_size = 0 ;
unsigned char * propose_out_bytes = NULL ; // the local protobuf
size_t propose_out_size = 0 ;
2017-02-02 20:43:35 +00:00
unsigned char * results = NULL ;
2017-02-02 19:10:12 +00:00
struct Propose * propose_out = NULL ;
struct Propose * propose_in = NULL ;
struct PublicKey * public_key = NULL ;
2017-02-09 17:50:28 +00:00
int order = 0 ; ;
2017-02-09 09:55:16 +00:00
struct Exchange * exchange_in = NULL ;
struct Exchange * exchange_out = NULL ;
2017-02-09 17:50:28 +00:00
unsigned char * exchange_out_protobuf = NULL ;
size_t exchange_out_protobuf_size = 0 ;
2017-03-07 00:03:04 +00:00
char * char_buffer = NULL ;
size_t char_buffer_length = 0 ;
2017-02-08 16:08:05 +00:00
struct StretchedKey * k1 = NULL , * k2 = NULL ;
2017-03-07 00:03:04 +00:00
struct PrivateKey * priv = NULL ;
2017-02-09 17:50:28 +00:00
struct PublicKey pub_key = { 0 } ;
2017-08-03 16:15:40 +00:00
struct Libp2pPeer * remote_peer = NULL ;
2017-02-01 12:52:09 +00:00
2017-02-06 22:11:22 +00:00
//TODO: make sure we're not talking to ourself
2017-08-09 14:12:16 +00:00
// send the protocol id and the outgoing Propose struct
2017-03-02 21:14:52 +00:00
2017-02-02 23:27:57 +00:00
// generate 16 byte nonce
2017-03-07 00:03:04 +00:00
if ( ! libp2p_secio_generate_nonce ( & local_session - > local_nonce [ 0 ] , 16 ) ) {
2017-02-02 23:27:57 +00:00
goto exit ;
}
2017-02-09 17:50:28 +00:00
// Build the proposal to be sent to the new connection:
2017-02-02 23:27:57 +00:00
propose_out = libp2p_secio_propose_new ( ) ;
2017-03-07 00:03:04 +00:00
libp2p_secio_propose_set_property ( ( void * * ) & propose_out - > rand , & propose_out - > rand_size , local_session - > local_nonce , 16 ) ;
2017-02-02 23:27:57 +00:00
2017-02-09 03:51:35 +00:00
// public key - protobuf it and stick it in propose_out
pub_key . type = KEYTYPE_RSA ;
pub_key . data_size = private_key - > public_key_length ;
pub_key . data = malloc ( pub_key . data_size ) ;
2017-02-09 08:34:12 +00:00
memcpy ( pub_key . data , private_key - > public_key_der , private_key - > public_key_length ) ;
2017-02-09 03:51:35 +00:00
results_size = libp2p_crypto_public_key_protobuf_encode_size ( & pub_key ) ;
results = malloc ( results_size ) ;
2017-02-09 08:34:12 +00:00
if ( results = = NULL ) {
free ( pub_key . data ) ;
2017-02-09 03:51:35 +00:00
goto exit ;
2017-02-09 08:34:12 +00:00
}
if ( libp2p_crypto_public_key_protobuf_encode ( & pub_key , results , results_size , & results_size ) = = 0 ) {
free ( pub_key . data ) ;
goto exit ;
}
free ( pub_key . data ) ;
2017-02-09 03:51:35 +00:00
propose_out - > public_key_size = results_size ;
propose_out - > public_key = malloc ( results_size ) ;
memcpy ( propose_out - > public_key , results , results_size ) ;
free ( results ) ;
results = NULL ;
results_size = 0 ;
2017-02-02 23:27:57 +00:00
// supported exchanges
libp2p_secio_propose_set_property ( ( void * * ) & propose_out - > exchanges , & propose_out - > exchanges_size , SupportedExchanges , strlen ( SupportedExchanges ) ) ;
// supported ciphers
libp2p_secio_propose_set_property ( ( void * * ) & propose_out - > ciphers , & propose_out - > ciphers_size , SupportedCiphers , strlen ( SupportedCiphers ) ) ;
// supported hashes
2017-02-06 22:11:22 +00:00
libp2p_secio_propose_set_property ( ( void * * ) & propose_out - > hashes , & propose_out - > hashes_size , SupportedHashes , strlen ( SupportedHashes ) ) ;
2017-02-09 03:51:35 +00:00
2017-08-09 14:12:16 +00:00
// protobuf the proposal
2017-02-09 03:51:35 +00:00
propose_out_size = libp2p_secio_propose_protobuf_encode_size ( propose_out ) ;
propose_out_bytes = ( unsigned char * ) malloc ( propose_out_size ) ;
if ( libp2p_secio_propose_protobuf_encode ( propose_out , propose_out_bytes , propose_out_size , & propose_out_size ) = = 0 )
goto exit ;
2017-08-09 15:09:28 +00:00
// send the protocol id first
2017-08-09 14:12:16 +00:00
const unsigned char * protocol = ( unsigned char * ) " /secio/1.0.0 \n " ;
int protocol_len = strlen ( ( char * ) protocol ) ;
2017-08-09 15:09:28 +00:00
if ( ! local_session - > default_stream - > write ( local_session , protocol , protocol_len ) )
2017-02-23 16:15:48 +00:00
goto exit ;
2017-08-09 15:09:28 +00:00
// now send the Propose struct
bytes_written = libp2p_secio_unencrypted_write ( local_session , propose_out_bytes , propose_out_size ) ;
if ( bytes_written ! = propose_out_size ) {
libp2p_logger_error ( " secio " , " Sent propose_out, but did not write the correct number of bytes. Should be %d but was %d. \n " , propose_out_size , bytes_written ) ;
}
2017-08-09 15:23:24 +00:00
// try to get the Propse struct from the remote peer
bytes_written = libp2p_secio_unencrypted_read ( local_session , & propose_in_bytes , & propose_in_size , 10 ) ;
if ( bytes_written < = 0 ) {
libp2p_logger_error ( " secio " , " Unable to get the remote's Propose struct. \n " ) ;
goto exit ;
2017-08-09 14:12:16 +00:00
}
2017-08-09 15:32:01 +00:00
if ( ! libp2p_secio_propose_protobuf_decode ( propose_in_bytes , propose_in_size , & propose_in ) ) {
2017-08-09 15:23:24 +00:00
libp2p_logger_error ( " secio " , " Unable to un-protobuf the remote's Propose struct \n " ) ;
2017-02-09 03:51:35 +00:00
goto exit ;
2017-08-09 14:12:16 +00:00
}
2017-02-09 03:51:35 +00:00
2017-03-07 00:03:04 +00:00
// get their nonce
if ( propose_in - > rand_size ! = 16 )
goto exit ;
memcpy ( local_session - > remote_nonce , propose_in - > rand , 16 ) ;
2017-02-09 03:51:35 +00:00
// get public key and put it in a struct PublicKey
if ( ! libp2p_crypto_public_key_protobuf_decode ( propose_in - > public_key , propose_in - > public_key_size , & public_key ) )
goto exit ;
// generate their peer id
2017-07-31 20:18:17 +00:00
libp2p_crypto_public_key_to_peer_id ( public_key , & local_session - > remote_peer_id ) ;
2017-02-09 03:51:35 +00:00
2017-08-03 16:15:40 +00:00
// see if we already have this peer
int new_peer = 0 ;
remote_peer = libp2p_peerstore_get_peer ( peerstore , ( unsigned char * ) local_session - > remote_peer_id , strlen ( local_session - > remote_peer_id ) ) ;
if ( remote_peer = = NULL ) {
remote_peer = libp2p_peer_new ( ) ;
new_peer = 1 ;
// put peer information in Libp2pPeer struct
remote_peer - > id_size = strlen ( local_session - > remote_peer_id ) ;
if ( remote_peer - > id_size > 0 ) {
remote_peer - > id = malloc ( remote_peer - > id_size + 1 ) ;
if ( remote_peer - > id ! = NULL ) {
memcpy ( remote_peer - > id , local_session - > remote_peer_id , remote_peer - > id_size ) ;
remote_peer - > id [ remote_peer - > id_size ] = 0 ;
}
2017-07-31 21:36:08 +00:00
}
2017-08-03 22:48:11 +00:00
remote_peer - > sessionContext = local_session ;
2017-08-03 16:15:40 +00:00
} else {
2017-08-03 19:34:40 +00:00
if ( remote_peer - > sessionContext ! = local_session ) {
// clean up old session context
libp2p_logger_debug ( " secio " , " Same remote connected. Replacing SessionContext. \n " ) ;
libp2p_session_context_free ( remote_peer - > sessionContext ) ;
remote_peer - > sessionContext = local_session ;
}
2017-08-03 16:15:40 +00:00
}
remote_peer - > connection_type = CONNECTION_TYPE_CONNECTED ;
2017-02-01 12:52:09 +00:00
// negotiate encryption parameters NOTE: SelectBest must match, otherwise this won't work
2017-02-06 22:11:22 +00:00
// first determine order
2017-07-13 12:27:10 +00:00
order = libp2p_secio_determine_order ( propose_in , propose_out ) ;
2017-02-01 12:52:09 +00:00
// curve
2017-02-09 08:34:12 +00:00
if ( libp2p_secio_select_best ( order , propose_out - > exchanges , propose_out - > exchanges_size , propose_in - > exchanges , propose_in - > exchanges_size , & local_session - > chosen_curve ) = = 0 )
goto exit ;
2017-02-01 12:52:09 +00:00
// cipher
2017-02-09 08:34:12 +00:00
if ( libp2p_secio_select_best ( order , propose_out - > ciphers , propose_out - > ciphers_size , propose_in - > ciphers , propose_in - > ciphers_size , & local_session - > chosen_cipher ) = = 0 )
goto exit ;
2017-02-01 12:52:09 +00:00
// hash
2017-02-09 08:34:12 +00:00
if ( libp2p_secio_select_best ( order , propose_out - > hashes , propose_out - > hashes_size , propose_in - > hashes , propose_in - > hashes_size , & local_session - > chosen_hash ) = = 0 )
goto exit ;
2017-02-09 03:51:35 +00:00
2017-02-06 22:11:22 +00:00
// generate EphemeralPubKey
2017-03-08 17:18:29 +00:00
if ( libp2p_crypto_ephemeral_keypair_generate ( local_session - > chosen_curve , & local_session - > ephemeral_private_key ) = = 0 )
2017-02-08 17:32:41 +00:00
goto exit ;
2017-02-06 22:11:22 +00:00
// build buffer to sign
2017-03-08 17:18:29 +00:00
char_buffer_length = propose_in_size + propose_out_size + local_session - > ephemeral_private_key - > public_key - > bytes_size - 1 ;
2017-03-07 00:03:04 +00:00
char_buffer = malloc ( char_buffer_length ) ;
2017-02-06 22:11:22 +00:00
if ( char_buffer = = NULL )
goto exit ;
2017-03-08 17:18:29 +00:00
memcpy ( & char_buffer [ 0 ] , propose_out_bytes , propose_out_size ) ;
memcpy ( & char_buffer [ propose_out_size ] , propose_in_bytes , propose_in_size ) ;
memcpy ( & char_buffer [ propose_in_size + propose_out_size ] , & local_session - > ephemeral_private_key - > public_key - > bytes [ 1 ] , local_session - > ephemeral_private_key - > public_key - > bytes_size - 1 ) ;
2017-02-08 16:08:05 +00:00
2017-02-06 22:11:22 +00:00
// send Exchange packet
exchange_out = libp2p_secio_exchange_new ( ) ;
2017-03-07 00:03:04 +00:00
if ( exchange_out = = NULL )
goto exit ;
2017-03-08 17:18:29 +00:00
// don't send the first byte (to stay compatible with GO version)
exchange_out - > epubkey = ( unsigned char * ) malloc ( local_session - > ephemeral_private_key - > public_key - > bytes_size - 1 ) ;
2017-03-07 00:03:04 +00:00
if ( exchange_out - > epubkey = = NULL )
goto exit ;
2017-03-08 17:18:29 +00:00
memcpy ( exchange_out - > epubkey , & local_session - > ephemeral_private_key - > public_key - > bytes [ 1 ] , local_session - > ephemeral_private_key - > public_key - > bytes_size - 1 ) ;
exchange_out - > epubkey_size = local_session - > ephemeral_private_key - > public_key - > bytes_size - 1 ;
2017-02-09 09:55:16 +00:00
2017-03-07 00:03:04 +00:00
priv = libp2p_crypto_private_key_new ( ) ;
priv - > type = KEYTYPE_RSA ;
priv - > data = ( unsigned char * ) private_key - > der ;
priv - > data_size = private_key - > der_length ;
libp2p_secio_sign ( priv , char_buffer , char_buffer_length , & exchange_out - > signature , & exchange_out - > signature_size ) ;
free ( char_buffer ) ;
2017-02-09 08:34:12 +00:00
char_buffer = NULL ;
2017-03-07 00:03:04 +00:00
// yes, this is an improper disposal, but it gets the job done without fuss
free ( priv ) ;
2017-02-01 12:52:09 +00:00
2017-02-06 22:11:22 +00:00
exchange_out_protobuf_size = libp2p_secio_exchange_protobuf_encode_size ( exchange_out ) ;
exchange_out_protobuf = ( unsigned char * ) malloc ( exchange_out_protobuf_size ) ;
if ( exchange_out_protobuf = = NULL )
goto exit ;
libp2p_secio_exchange_protobuf_encode ( exchange_out , exchange_out_protobuf , exchange_out_protobuf_size , & bytes_written ) ;
2017-02-09 17:50:28 +00:00
exchange_out_protobuf_size = bytes_written ;
2017-03-07 00:03:04 +00:00
2017-07-13 12:27:10 +00:00
libp2p_logger_log ( " secio " , LOGLEVEL_DEBUG , " Writing exchange_out \n " ) ;
2017-03-07 00:03:04 +00:00
bytes_written = libp2p_secio_unencrypted_write ( local_session , exchange_out_protobuf , exchange_out_protobuf_size ) ;
2017-08-09 15:09:28 +00:00
if ( exchange_out_protobuf_size ! = bytes_written ) {
libp2p_logger_error ( " secio " , " Unable to write exchange_out \n " ) ;
2017-02-09 17:50:28 +00:00
goto exit ;
2017-08-09 15:09:28 +00:00
}
2017-02-06 22:11:22 +00:00
free ( exchange_out_protobuf ) ;
2017-02-09 17:50:28 +00:00
exchange_out_protobuf = NULL ;
2017-03-07 00:03:04 +00:00
// end of send Exchange packet
// receive Exchange packet
2017-07-13 12:27:10 +00:00
libp2p_logger_log ( " secio " , LOGLEVEL_DEBUG , " Reading exchange packet \n " ) ;
2017-04-17 19:03:27 +00:00
bytes_written = libp2p_secio_unencrypted_read ( local_session , & results , & results_size , 10 ) ;
2017-08-03 17:17:17 +00:00
if ( bytes_written = = 0 ) {
2017-08-09 15:09:28 +00:00
libp2p_logger_error ( " secio " , " unable to read exchange packet. \n " ) ;
2017-08-03 17:17:17 +00:00
libp2p_peer_handle_connection_error ( remote_peer ) ;
2017-03-07 00:03:04 +00:00
goto exit ;
2017-08-03 17:17:17 +00:00
}
2017-03-07 00:03:04 +00:00
libp2p_secio_exchange_protobuf_decode ( results , results_size , & exchange_in ) ;
free ( results ) ;
results = NULL ;
// end of receive Exchange packet
2017-02-01 12:52:09 +00:00
// parse and verify
2017-03-08 17:18:29 +00:00
local_session - > remote_ephemeral_public_key_size = exchange_in - > epubkey_size + 1 ;
local_session - > remote_ephemeral_public_key = malloc ( local_session - > remote_ephemeral_public_key_size ) ;
local_session - > remote_ephemeral_public_key [ 0 ] = exchange_in - > epubkey_size ;
memcpy ( & local_session - > remote_ephemeral_public_key [ 1 ] , exchange_in - > epubkey , exchange_in - > epubkey_size ) ;
2017-02-01 12:52:09 +00:00
2017-03-09 15:00:45 +00:00
// signature verification
2017-03-08 17:18:29 +00:00
char_buffer_length = propose_in_size + propose_out_size + local_session - > remote_ephemeral_public_key_size - 1 ;
char_buffer = malloc ( char_buffer_length ) ;
2017-08-09 15:09:28 +00:00
if ( char_buffer = = NULL ) {
libp2p_logger_error ( " secio " , " Unable to allocate memory for signature verification. \n " ) ;
2017-02-06 22:11:22 +00:00
goto exit ;
2017-08-09 15:09:28 +00:00
}
2017-03-07 00:03:04 +00:00
memcpy ( & char_buffer [ 0 ] , propose_in_bytes , propose_in_size ) ;
memcpy ( & char_buffer [ propose_in_size ] , propose_out_bytes , propose_out_size ) ;
2017-03-08 17:18:29 +00:00
memcpy ( & char_buffer [ propose_in_size + propose_out_size ] , & local_session - > remote_ephemeral_public_key [ 1 ] , local_session - > remote_ephemeral_public_key_size - 1 ) ;
2017-08-09 15:09:28 +00:00
if ( ! libp2p_secio_verify_signature ( public_key , ( unsigned char * ) char_buffer , char_buffer_length , exchange_in - > signature ) ) {
libp2p_logger_error ( " secio " , " Unable to verify signature. \n " ) ;
2017-03-08 17:18:29 +00:00
goto exit ;
2017-08-09 15:09:28 +00:00
}
2017-03-07 00:03:04 +00:00
free ( char_buffer ) ;
2017-02-09 08:34:12 +00:00
char_buffer = NULL ;
2017-02-06 22:11:22 +00:00
2017-03-08 17:18:29 +00:00
// 2.2 generate shared key
2017-08-09 15:09:28 +00:00
if ( ! libp2p_crypto_ephemeral_generate_shared_secret ( local_session - > ephemeral_private_key , local_session - > remote_ephemeral_public_key , local_session - > remote_ephemeral_public_key_size ) ) {
libp2p_logger_error ( " secio " , " Unable to generte shared secret. \n " ) ;
2017-03-08 17:18:29 +00:00
goto exit ;
2017-08-09 15:09:28 +00:00
}
2017-02-06 22:11:22 +00:00
2017-03-09 05:12:04 +00:00
local_session - > shared_key_size = local_session - > ephemeral_private_key - > public_key - > shared_key_size ;
local_session - > shared_key = malloc ( local_session - > shared_key_size ) ;
memcpy ( local_session - > shared_key , local_session - > ephemeral_private_key - > public_key - > shared_key , local_session - > shared_key_size ) ;
2017-02-06 22:11:22 +00:00
// generate 2 sets of keys (stretching)
2017-07-13 23:30:18 +00:00
if ( ! libp2p_secio_stretch_keys ( local_session - > chosen_cipher , local_session - > chosen_hash , local_session - > shared_key , local_session - > shared_key_size , & k1 , & k2 ) ) {
libp2p_logger_error ( " secio " , " Unable to stretch keys. \n " ) ;
2017-03-08 17:18:29 +00:00
goto exit ;
2017-07-13 23:30:18 +00:00
}
2017-02-06 22:11:22 +00:00
2017-07-13 23:30:18 +00:00
//libp2p_logger_debug("secio", "Order value is %d.\n", order);
if ( order > 0 ) {
2017-03-07 00:03:04 +00:00
local_session - > local_stretched_key = k1 ;
local_session - > remote_stretched_key = k2 ;
2017-02-08 16:08:05 +00:00
} else {
2017-03-07 00:03:04 +00:00
local_session - > local_stretched_key = k2 ;
local_session - > remote_stretched_key = k1 ;
2017-02-08 16:08:05 +00:00
}
2017-03-09 05:12:04 +00:00
2017-02-06 22:11:22 +00:00
// prepare MAC + cipher
2017-03-07 00:03:04 +00:00
if ( strcmp ( local_session - > chosen_hash , " SHA1 " ) = = 0 ) {
local_session - > mac_function = libp2p_crypto_hashing_sha1 ;
} else if ( strcmp ( local_session - > chosen_hash , " SHA512 " ) = = 0 ) {
local_session - > mac_function = libp2p_crypto_hashing_sha512 ;
} else if ( strcmp ( local_session - > chosen_hash , " SHA256 " ) = = 0 ) {
local_session - > mac_function = libp2p_crypto_hashing_sha256 ;
} else {
2017-08-09 15:09:28 +00:00
libp2p_logger_error ( " secio " , " Unable to pick a hash function. \n " ) ;
goto exit ;
2017-03-07 00:03:04 +00:00
}
2017-02-06 22:11:22 +00:00
2017-07-13 23:30:18 +00:00
// this doesn't do much. It is here to match the GO code and maybe eventually remind us
// that there is more work to do for compatibility to GO
2017-03-07 00:03:04 +00:00
libp2p_secio_make_mac_and_cipher ( local_session , local_session - > local_stretched_key ) ;
libp2p_secio_make_mac_and_cipher ( local_session , local_session - > remote_stretched_key ) ;
2017-02-01 12:52:09 +00:00
2017-07-13 12:27:10 +00:00
// now we actually start encrypting things...
libp2p_secio_initialize_crypto ( local_session ) ;
2017-07-13 23:30:18 +00:00
// send their nonce to verify encryption works
2017-07-13 12:27:10 +00:00
libp2p_logger_log ( " secio " , LOGLEVEL_DEBUG , " Sending their nonce \n " ) ;
if ( libp2p_secio_encrypted_write ( local_session , ( unsigned char * ) local_session - > remote_nonce , 16 ) < = 0 ) {
libp2p_logger_error ( " secio " , " Encrytped write returned 0 or less. \n " ) ;
2017-03-07 00:03:04 +00:00
goto exit ;
2017-07-13 12:27:10 +00:00
}
2017-03-09 13:41:59 +00:00
2017-03-07 00:03:04 +00:00
// receive our nonce to verify encryption works
2017-07-13 12:27:10 +00:00
libp2p_logger_log ( " secio " , LOGLEVEL_DEBUG , " Receiving our nonce \n " ) ;
2017-07-13 23:30:18 +00:00
results = NULL ;
2017-04-17 19:03:27 +00:00
int bytes_read = libp2p_secio_encrypted_read ( local_session , & results , & results_size , 10 ) ;
2017-04-03 17:43:05 +00:00
if ( bytes_read < = 0 ) {
2017-08-09 15:09:28 +00:00
libp2p_logger_error ( " secio " , " Encrypted read returned %d \n " , bytes_read ) ;
2017-03-07 00:03:04 +00:00
goto exit ;
2017-04-03 17:43:05 +00:00
}
if ( results_size ! = 16 ) {
2017-08-09 15:09:28 +00:00
libp2p_logger_error ( " secio " , " Results_size should be 16 but was %d \n " , results_size ) ;
2017-02-06 22:11:22 +00:00
goto exit ;
2017-04-03 17:43:05 +00:00
}
2017-07-13 12:27:10 +00:00
if ( libp2p_secio_bytes_compare ( results , ( unsigned char * ) local_session - > local_nonce , 16 ) ! = 0 ) {
2017-08-09 15:09:28 +00:00
libp2p_logger_error ( " secio " , " Bytes of nonce did not match \n " ) ;
2017-02-06 22:11:22 +00:00
goto exit ;
2017-04-03 17:43:05 +00:00
}
2017-02-01 12:52:09 +00:00
2017-03-09 15:00:45 +00:00
// set up the secure stream in the struct
2017-04-04 01:54:41 +00:00
local_session - > secure_stream = local_session - > insecure_stream ;
2017-03-09 15:00:45 +00:00
local_session - > secure_stream - > read = libp2p_secio_encrypted_read ;
local_session - > secure_stream - > write = libp2p_secio_encrypted_write ;
// set secure as default
local_session - > default_stream = local_session - > secure_stream ;
2017-08-03 19:43:19 +00:00
if ( new_peer ) {
libp2p_logger_debug ( " secio " , " New connection. Adding Peer to Peerstore. \n " ) ;
libp2p_peerstore_add_peer ( peerstore , remote_peer ) ;
}
2017-02-01 17:14:52 +00:00
retVal = 1 ;
2017-07-13 12:27:10 +00:00
libp2p_logger_log ( " secio " , LOGLEVEL_DEBUG , " Handshake complete \n " ) ;
2017-02-01 17:14:52 +00:00
exit :
2017-08-09 15:23:24 +00:00
if ( propose_in_bytes ! = NULL )
free ( propose_in_bytes ) ;
2017-02-09 03:51:35 +00:00
if ( propose_out_bytes ! = NULL )
free ( propose_out_bytes ) ;
if ( results ! = NULL )
free ( results ) ;
2017-02-09 08:34:12 +00:00
if ( char_buffer ! = NULL )
2017-03-07 00:03:04 +00:00
free ( char_buffer ) ;
2017-02-09 08:34:12 +00:00
if ( public_key ! = NULL )
libp2p_crypto_public_key_free ( public_key ) ;
2017-02-09 09:55:16 +00:00
if ( exchange_out ! = NULL )
libp2p_secio_exchange_free ( exchange_out ) ;
2017-02-09 17:50:28 +00:00
if ( exchange_out_protobuf ! = NULL )
free ( exchange_out_protobuf ) ;
2017-03-07 00:03:04 +00:00
if ( exchange_in ! = NULL )
libp2p_secio_exchange_free ( exchange_in ) ;
2017-02-09 03:51:35 +00:00
2017-02-02 19:10:12 +00:00
libp2p_secio_propose_free ( propose_out ) ;
libp2p_secio_propose_free ( propose_in ) ;
2017-02-01 17:14:52 +00:00
2017-04-03 16:55:03 +00:00
if ( retVal = = 1 ) {
2017-07-13 12:27:10 +00:00
libp2p_logger_log ( " secio " , LOGLEVEL_DEBUG , " Handshake success! \n " ) ;
2017-04-03 16:55:03 +00:00
} else {
2017-07-13 12:27:10 +00:00
libp2p_logger_log ( " secio " , LOGLEVEL_DEBUG , " Handshake returning false \n " ) ;
2017-04-03 16:55:03 +00:00
}
2017-02-01 17:14:52 +00:00
return retVal ;
2017-02-01 12:52:09 +00:00
}