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-11-02 16:11:50 +00:00
# include "libp2p/net/connectionstream.h"
2017-09-07 23:46:03 +00:00
# include "libp2p/os/utils.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-11-02 18:20:40 +00:00
static struct StreamMessage * secio_buffered_message ;
static size_t secio_buffered_message_pos = - 1 ;
2017-10-23 20:21:50 +00:00
int libp2p_secio_can_handle ( const struct StreamMessage * msg ) {
2017-09-07 23:46:03 +00:00
const char * protocol = " /secio/1.0.0 " ;
2017-08-09 13:03:40 +00:00
// sanity checks
2017-10-23 20:21:50 +00:00
if ( msg - > data_size < 12 )
2017-08-09 13:03:40 +00:00
return 0 ;
2017-10-23 20:21:50 +00:00
char * result = strnstr ( ( char * ) msg - > data , protocol , msg - > data_size ) ;
if ( result ! = NULL & & result = = ( char * ) msg - > data )
2017-08-09 16:12:16 +00:00
return 1 ;
return 0 ;
2017-02-01 12:52:09 +00:00
}
2017-08-09 16:12:16 +00:00
/***
* Handle a secio message
2017-10-23 20:21:50 +00:00
* @ param msg the incoming message
2017-08-09 16:12:16 +00:00
* @ param session_context who is attempting to connect
* @ param protocol_context a SecioContext that contains the needed information
2017-08-31 11:41:06 +00:00
* @ returns < 0 on error , 0 if okay ( does not allow daemon to continue looping )
2017-08-09 16:12:16 +00:00
*/
2017-10-23 20:21:50 +00:00
int libp2p_secio_handle_message ( const struct StreamMessage * msg , struct SessionContext * session_context , void * protocol_context ) {
2017-08-09 17:08:57 +00:00
libp2p_logger_debug ( " secio " , " Handling incoming secio message. \n " ) ;
2017-08-09 13:03:40 +00:00
struct SecioContext * ctx = ( struct SecioContext * ) protocol_context ;
2017-08-31 21:40:35 +00:00
// send them the protocol
2017-10-23 20:21:50 +00:00
if ( ! libp2p_secio_send_protocol ( ctx ) )
2017-08-31 21:40:35 +00:00
return - 1 ;
2017-10-23 20:21:50 +00:00
int retVal = libp2p_secio_handshake ( ctx ) ;
2017-08-09 16:12:16 +00:00
if ( retVal )
return 0 ;
return - 1 ;
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
* @ 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 ) {
2017-11-02 16:11:50 +00:00
// debugging:
if ( libp2p_logger_watching_class ( " secio " ) ) {
fprintf ( stdout , " Verifying signature of %d bytes: " , ( int ) in_length ) ;
for ( int i = 0 ; i < 32 ; i + + ) {
fprintf ( stdout , " %02x " , signature [ i ] ) ;
}
fprintf ( stdout , " \n " ) ;
}
2017-02-06 22:11:22 +00:00
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-11-02 16:11:50 +00:00
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-11-02 16:11:50 +00:00
int retVal = libp2p_crypto_rsa_sign ( & rsa_key , in , in_length , signature , signature_size ) ;
// debugging
if ( retVal & & libp2p_logger_watching_class ( " secio " ) ) {
unsigned char * ptr = * signature ;
fprintf ( stdout , " Signature generated from %d bytes: " , ( int ) in_length ) ;
for ( int i = 0 ; i < * signature_size ; i + + ) {
fprintf ( stdout , " %02x " , ptr [ i ] ) ;
}
fprintf ( stdout , " \n " ) ;
}
return retVal ;
2017-02-06 22:11:22 +00:00
}
2017-11-02 16:11:50 +00:00
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-08-31 21:40:35 +00:00
/***
* Send the protocol string to the remote stream
2017-10-23 20:21:50 +00:00
* @ param ctx the context
2017-08-31 21:40:35 +00:00
* @ returns true ( 1 ) on success , false ( 0 ) otherwise
*/
2017-10-23 20:21:50 +00:00
int libp2p_secio_send_protocol ( struct SecioContext * ctx ) {
2017-08-31 21:40:35 +00:00
char * protocol = " /secio/1.0.0 \n " ;
2017-10-23 14:47:54 +00:00
struct StreamMessage outgoing ;
outgoing . data = ( uint8_t * ) protocol ;
outgoing . data_size = strlen ( protocol ) ;
2017-10-23 20:21:50 +00:00
return ctx - > stream - > parent_stream - > write ( ctx - > stream - > parent_stream - > stream_context , & outgoing ) ;
2017-08-31 21:40:35 +00:00
}
/***
* Attempt to read the secio protocol as a reply from the remote
2017-10-23 20:21:50 +00:00
* @ param ctx the context
2017-08-31 21:40:35 +00:00
* @ returns true ( 1 ) if we received what we think we should have , false ( 0 ) otherwise
*/
2017-10-23 20:21:50 +00:00
int libp2p_secio_receive_protocol ( struct SecioContext * ctx ) {
2017-08-31 21:40:35 +00:00
char * protocol = " /secio/1.0.0 \n " ;
int numSecs = 30 ;
2017-10-23 14:01:03 +00:00
int retVal = 0 ;
struct StreamMessage * buffer = NULL ;
2017-10-23 20:21:50 +00:00
ctx - > stream - > parent_stream - > read ( ctx - > stream - > parent_stream - > stream_context , & buffer , numSecs ) ;
2017-10-23 14:01:03 +00:00
if ( buffer = = NULL ) {
libp2p_logger_error ( " secio " , " Expected the secio protocol header, but received NULL. \n " ) ;
} else {
// see if they sent the correct response
if ( strncmp ( protocol , ( char * ) buffer - > data , strlen ( protocol ) ) = = 0 ) {
retVal = 1 ;
}
else {
libp2p_logger_error ( " secio " , " Expected the secio protocol header, but received %s. \n " , buffer ) ;
2017-09-04 16:01:17 +00:00
}
2017-08-31 21:40:35 +00:00
}
2017-10-23 14:01:03 +00:00
libp2p_stream_message_free ( buffer ) ;
return retVal ;
2017-08-31 21:40:35 +00:00
}
2017-10-25 17:28:53 +00:00
/**
* Navigate down the tree of streams to get the raw socket descriptor
* @ param stream the stream
* @ returns the raw socket descriptor
*/
int libp2p_secio_get_socket_descriptor ( struct Stream * stream ) {
struct Stream * current = stream ;
while ( current - > parent_stream ! = NULL )
current = current - > parent_stream ;
struct ConnectionContext * ctx = current - > stream_context ;
return ctx - > socket_descriptor ;
}
/***
* 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
*/
int libp2p_secio_unencrypted_write ( struct Stream * secio_stream , struct StreamMessage * msg ) {
int num_bytes = 0 ;
int socket_descriptor = libp2p_secio_get_socket_descriptor ( secio_stream ) ;
if ( msg ! = NULL & & msg - > data_size > 0 ) { // only do this is if there is something to send
// first send the size
uint32_t size = htonl ( msg - > data_size ) ;
char * size_as_char = ( char * ) & size ;
int left = 4 ;
int written = 0 ;
int written_this_time = 0 ;
do {
written_this_time = socket_write ( socket_descriptor , & size_as_char [ written ] , left , 0 ) ;
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 = msg - > data_size ;
written = 0 ;
do {
written_this_time = socket_write ( socket_descriptor , ( char * ) & msg - > data [ written ] , left , 0 ) ;
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 ;
}
/***
* 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
*/
int libp2p_secio_unencrypted_read ( struct Stream * secio_stream , struct StreamMessage * * msg , int timeout_secs ) {
uint32_t buffer_size = 0 ;
if ( secio_stream = = NULL ) {
libp2p_logger_error ( " secio " , " Attempted unencrypted read on invalid session. \n " ) ;
return 0 ;
}
int socket_descriptor = libp2p_secio_get_socket_descriptor ( secio_stream ) ;
// first read the 4 byte integer
char * size = ( char * ) & buffer_size ;
int left = 4 ;
int read = 0 ;
int read_this_time = 0 ;
int time_left = timeout_secs ;
do {
read_this_time = socket_read ( socket_descriptor , & size [ read ] , 1 , 0 , timeout_secs ) ;
if ( read_this_time < = 0 ) {
if ( errno = = EINPROGRESS ) {
time_left - - ;
sleep ( 1 ) ;
if ( time_left = = 0 )
break ;
continue ;
}
if ( errno = = EAGAIN | | errno = = EWOULDBLOCK ) {
// TODO: use epoll or select to wait for socket to be writable
libp2p_logger_debug ( " secio " , " Attempted read, but got EAGAIN or EWOULDBLOCK. Code %d. \n " , errno ) ;
return 0 ;
} else {
// is this really an error?
if ( errno ! = 0 ) {
libp2p_logger_error ( " secio " , " Error in libp2p_secio_unencrypted_read: %s \n " , strerror ( errno ) ) ;
return 0 ;
}
else
libp2p_logger_error ( " secio " , " Error in libp2p_secio_unencrypted_read: 0 bytes read, but errno shows no error. Trying again. \n " ) ;
}
} else {
left = left - read_this_time ;
read + = read_this_time ;
}
} while ( left > 0 ) ;
buffer_size = ntohl ( buffer_size ) ;
if ( buffer_size = = 0 ) {
libp2p_logger_error ( " secio " , " unencrypted read buffer size is 0. \n " ) ;
return 0 ;
}
// now read the number of bytes we've found, minus the 4 that we just read
left = buffer_size ;
read = 0 ;
read_this_time = 0 ;
* msg = libp2p_stream_message_new ( ) ;
struct StreamMessage * m = * msg ;
if ( m = = NULL ) {
libp2p_logger_error ( " secio " , " Unable to allocate memory for the incoming message. Size: %ulld " , left ) ;
return 0 ;
}
m - > data_size = left ;
m - > data = ( uint8_t * ) malloc ( left ) ;
unsigned char * ptr = m - > data ;
time_left = timeout_secs ;
do {
read_this_time = socket_read ( socket_descriptor , ( char * ) & ptr [ read ] , left , 0 , timeout_secs ) ;
if ( read_this_time < 0 ) {
if ( errno = = EINPROGRESS ) {
sleep ( 1 ) ;
time_left - - ;
if ( time_left = = 0 )
break ;
continue ;
}
read_this_time = 0 ;
if ( ( errno = = EAGAIN ) | | ( errno = = EWOULDBLOCK ) ) {
// TODO: use epoll or select to wait for socket to be writable
} else {
libp2p_logger_error ( " secio " , " read from socket returned %d. \n " , errno ) ;
return 0 ;
}
} else if ( read_this_time = = 0 ) {
// socket_read returned 0, which it shouldn't
libp2p_logger_error ( " secio " , " socket_read returned 0 trying to read from stream %d. \n " , socket_descriptor ) ;
return 0 ;
}
left = left - read_this_time ;
2017-11-02 16:11:50 +00:00
read + = read_this_time ;
2017-10-25 17:28:53 +00:00
} while ( left > 0 ) ;
m - > data_size = buffer_size ;
return buffer_size ;
}
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
* @ returns the number of bytes written
*/
2017-10-23 14:47:54 +00:00
int libp2p_secio_encrypted_write ( void * stream_context , struct StreamMessage * bytes ) {
2017-10-23 20:21:50 +00:00
struct SecioContext * ctx = ( struct SecioContext * ) stream_context ;
struct Stream * parent_stream = ctx - > stream - > parent_stream ;
struct SessionContext * session_context = ctx - > session_context ;
2017-03-07 00:03:04 +00:00
// writer uses the local cipher and mac
2017-10-23 20:21:50 +00:00
struct StreamMessage outgoing ;
if ( ! libp2p_secio_encrypt ( session_context , bytes - > data , bytes - > data_size , & outgoing . data , & outgoing . data_size ) ) {
2017-07-17 19:38:48 +00:00
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-10-23 20:21:50 +00:00
2017-11-02 16:11:50 +00:00
libp2p_logger_debug ( " secio " , " About to write %d bytes. \n " , ( int ) outgoing . data_size ) ;
2017-10-25 17:28:53 +00:00
int retVal = libp2p_secio_unencrypted_write ( parent_stream , & outgoing ) ;
2017-07-17 19:38:48 +00:00
if ( ! retVal ) {
libp2p_logger_error ( " secio " , " secio_unencrypted_write returned false \n " ) ;
}
2017-10-23 20:21:50 +00:00
free ( outgoing . data ) ;
2017-03-08 10:53:59 +00:00
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-10-23 14:01:03 +00:00
int libp2p_secio_decrypt ( struct SessionContext * session , const unsigned char * incoming , size_t incoming_size , struct StreamMessage * * outgoing ) {
2017-03-09 05:12:04 +00:00
size_t data_section_size = incoming_size - 32 ;
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
2017-10-23 14:01:03 +00:00
* outgoing = libp2p_stream_message_new ( ) ;
struct StreamMessage * message = * outgoing ;
if ( message ! = NULL ) {
message - > data_size = incoming_size ;
message - > data = ( uint8_t * ) malloc ( incoming_size ) ;
memcpy ( message - > data , 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 ) ;
2017-10-23 14:01:03 +00:00
* outgoing = libp2p_stream_message_new ( ) ;
struct StreamMessage * message = * outgoing ;
message - > data_size = data_section_size ;
message - > data = ( uint8_t * ) malloc ( data_section_size ) ;
if ( message - > data = = NULL ) {
libp2p_stream_message_free ( message ) ;
* outgoing = NULL ;
return 0 ;
}
memcpy ( message - > data , buffer , data_section_size ) ;
2017-04-27 04:56:18 +00:00
free ( buffer ) ;
2017-07-13 12:27:10 +00:00
2017-10-23 14:01:03 +00:00
return message - > data_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
* @ returns the number of bytes read
*/
2017-10-23 14:01:03 +00:00
int libp2p_secio_encrypted_read ( void * stream_context , struct StreamMessage * * bytes , int timeout_secs ) {
2017-04-27 04:56:18 +00:00
int retVal = 0 ;
2017-10-23 20:21:50 +00:00
struct SecioContext * ctx = ( struct SecioContext * ) stream_context ;
struct Stream * parent_stream = ctx - > stream - > parent_stream ;
2017-03-07 00:03:04 +00:00
// reader uses the remote cipher and mac
// read the data
2017-10-23 20:21:50 +00:00
struct StreamMessage * msg = NULL ;
2017-10-25 17:28:53 +00:00
if ( ! libp2p_secio_unencrypted_read ( parent_stream , & msg , 10 ) ) {
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-10-23 20:21:50 +00:00
retVal = libp2p_secio_decrypt ( ctx - > session_context , msg - > data , msg - > data_size , 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 :
2017-10-23 20:21:50 +00:00
libp2p_stream_message_free ( msg ) ;
2017-04-27 04:56:18 +00:00
return retVal ;
2017-03-07 00:03:04 +00:00
}
2017-10-25 17:28:53 +00:00
struct Libp2pPeer * libp2p_secio_get_peer_or_add ( struct Peerstore * peerstore , struct SessionContext * local_session ) {
struct Libp2pPeer * remote_peer = NULL ;
// see if we already have this peer
if ( peerstore ! = NULL )
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 ( ) ;
// 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 ;
}
}
remote_peer - > sessionContext = local_session ;
// add a multiaddress to the peer (if we have what we need)
char url [ 100 ] ;
sprintf ( url , " /ip4/%s/tcp/%d/ipfs/%s " , local_session - > host , local_session - > port , libp2p_peer_id_to_string ( remote_peer ) ) ;
struct MultiAddress * ma = multiaddress_new_from_string ( url ) ;
if ( ma = = NULL ) {
libp2p_logger_error ( " secio " , " Unable to generate MultiAddress from [%s]. \n " , url ) ;
libp2p_peer_free ( remote_peer ) ;
return NULL ;
} else {
libp2p_logger_debug ( " secio " , " Adding %s to peer %s. \n " , url , libp2p_peer_id_to_string ( remote_peer ) ) ;
struct Libp2pLinkedList * ma_ll = libp2p_utils_linked_list_new ( ) ;
ma_ll - > item = ma ;
remote_peer - > addr_head = ma_ll ;
}
if ( peerstore ! = NULL ) {
libp2p_logger_debug ( " secio " , " New connection. Adding Peer to Peerstore. \n " ) ;
libp2p_peerstore_add_peer ( peerstore , remote_peer ) ;
}
} else {
// we already had one in the peerstore
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 ;
}
}
remote_peer - > connection_type = CONNECTION_TYPE_CONNECTED ;
return remote_peer ;
}
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-08-31 17:49:43 +00:00
* @ param peerstore the collection of peers
2017-02-01 12:52:09 +00:00
* @ returns true ( 1 ) on success , false ( 0 ) otherwise
*/
2017-10-23 20:21:50 +00:00
int libp2p_secio_handshake ( struct SecioContext * secio_context ) {
2017-02-06 22:11:22 +00:00
int retVal = 0 ;
2017-10-25 17:28:53 +00:00
size_t bytes_written = 0 ;
2017-10-23 20:21:50 +00:00
struct StreamMessage * incoming = NULL ;
struct StreamMessage outgoing ; // used for outgoing messages
2017-02-06 22:11:22 +00:00
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-08-03 16:15:40 +00:00
struct Libp2pPeer * remote_peer = NULL ;
2017-02-01 12:52:09 +00:00
2017-10-23 20:21:50 +00:00
struct SessionContext * local_session = secio_context - > session_context ;
struct RsaPrivateKey * private_key = secio_context - > private_key ;
struct Peerstore * peerstore = secio_context - > peer_store ;
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-10-25 17:28:53 +00:00
propose_out = libp2p_secio_propose_build ( local_session - > local_nonce , private_key ,
SupportedExchanges , SupportedCiphers , SupportedHashes ) ;
2017-02-09 03:51:35 +00:00
2017-11-02 16:11:50 +00:00
if ( libp2p_logger_watching_class ( " secio " ) ) {
fprintf ( stdout , " Our public key: " ) ;
for ( int i = 0 ; i < propose_out - > public_key_size ; i + + ) {
fprintf ( stdout , " %02x " , propose_out - > public_key [ i ] ) ;
}
fprintf ( stdout , " \n " ) ;
}
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-10-25 17:28:53 +00:00
// now send the Propose struct
2017-10-23 20:21:50 +00:00
outgoing . data = propose_out_bytes ;
outgoing . data_size = propose_out_size ;
2017-10-25 17:28:53 +00:00
bytes_written = libp2p_secio_unencrypted_write ( secio_context - > stream , & outgoing ) ;
2017-08-31 17:49:43 +00:00
2017-08-09 15:09:28 +00:00
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-10-25 17:28:53 +00:00
goto exit ;
2017-08-09 15:09:28 +00:00
}
2017-10-25 17:28:53 +00:00
// try to get the Propose struct from the remote peer
bytes_written = libp2p_secio_unencrypted_read ( secio_context - > stream , & incoming , 10 ) ;
2017-08-09 15:23:24 +00:00
if ( bytes_written < = 0 ) {
libp2p_logger_error ( " secio " , " Unable to get the remote's Propose struct. \n " ) ;
goto exit ;
2017-09-04 16:01:17 +00:00
} else {
2017-11-02 16:11:50 +00:00
libp2p_logger_debug ( " secio " , " Received their propose struct. \n " ) ;
2017-08-09 14:12:16 +00:00
}
2017-08-09 15:32:01 +00:00
2017-11-02 16:11:50 +00:00
// we need the propose bytes for later, so saving them off
propose_in_bytes = malloc ( incoming - > data_size ) ;
memcpy ( propose_in_bytes , incoming - > data , incoming - > data_size ) ;
propose_in_size = incoming - > data_size ;
libp2p_stream_message_free ( incoming ) ;
incoming = NULL ;
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
2017-11-02 16:11:50 +00:00
if ( propose_in - > rand_size ! = 16 ) {
libp2p_logger_error ( " secio " , " Their nonce is not 16 bytes! \n " ) ;
2017-03-07 00:03:04 +00:00
goto exit ;
2017-11-02 16:11:50 +00:00
}
2017-03-07 00:03:04 +00:00
memcpy ( local_session - > remote_nonce , propose_in - > rand , 16 ) ;
2017-10-25 17:28:53 +00:00
2017-11-02 16:11:50 +00:00
// debugging
if ( libp2p_logger_watching_class ( " secio " ) ) {
fprintf ( stdout , " Our nonce: " ) ;
for ( int i = 0 ; i < 16 ; i + + ) {
fprintf ( stdout , " %02x " , local_session - > local_nonce [ i ] ) ;
}
fprintf ( stdout , " \n Their nonce: " ) ;
for ( int i = 0 ; i < 16 ; i + + ) {
fprintf ( stdout , " %02x " , local_session - > remote_nonce [ i ] ) ;
}
fprintf ( stdout , " \n " ) ;
}
if ( libp2p_logger_watching_class ( " secio " ) ) {
fprintf ( stdout , " Their public key (length %d): " , ( int ) propose_in - > public_key_size ) ;
for ( int i = 0 ; i < propose_in - > public_key_size ; i + + ) {
fprintf ( stdout , " %02x " , propose_in - > public_key [ i ] ) ;
}
fprintf ( stdout , " \n " ) ;
}
2017-02-09 03:51:35 +00:00
if ( ! libp2p_crypto_public_key_protobuf_decode ( propose_in - > public_key , propose_in - > public_key_size , & public_key ) )
goto exit ;
2017-10-25 17:28:53 +00:00
2017-02-09 03:51:35 +00:00
// 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-11-02 16:11:50 +00:00
libp2p_logger_debug ( " secio " , " Their Peer ID: %s. \n " , local_session - > remote_peer_id ) ;
2017-10-25 17:28:53 +00:00
// pull the peer from the peerstore if it is there
remote_peer = libp2p_secio_get_peer_or_add ( peerstore , local_session ) ;
2017-08-03 16:15:40 +00:00
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-11-02 16:11:50 +00:00
if ( libp2p_logger_watching_class ( " secio " ) ) {
fprintf ( stdout , " Building buffer to sign. \n " ) ;
fprintf ( stdout , " Propose in size : %d \n " , ( int ) propose_in_size ) ;
fprintf ( stdout , " Propose out size : %d \n " , ( int ) propose_out_size ) ;
fprintf ( stdout , " Epemeral key size: %d \n " , ( int ) local_session - > ephemeral_private_key - > public_key - > bytes_size ) ;
}
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
2017-10-25 17:28:53 +00:00
exchange_out = libp2p_secio_exchange_build ( local_session , private_key , char_buffer , char_buffer_length ) ;
2017-03-07 00:03:04 +00:00
if ( exchange_out = = NULL )
goto exit ;
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-10-23 20:21:50 +00:00
outgoing . data = exchange_out_protobuf ;
outgoing . data_size = exchange_out_protobuf_size ;
2017-10-25 17:28:53 +00:00
bytes_written = libp2p_secio_unencrypted_write ( secio_context - > stream , & outgoing ) ;
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-09-04 16:01:17 +00:00
} else {
2017-11-02 16:11:50 +00:00
libp2p_logger_debug ( " secio " , " Sent exchange_out. Size: %d. \n " , bytes_written ) ;
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-10-25 17:28:53 +00:00
bytes_written = libp2p_secio_unencrypted_read ( secio_context - > stream , & incoming , 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-09-04 16:01:17 +00:00
} else {
2017-11-02 16:11:50 +00:00
libp2p_logger_debug ( " secio " , " Read exchange packet. Size: %d. \n " , bytes_written ) ;
2017-08-03 17:17:17 +00:00
}
2017-10-23 20:21:50 +00:00
libp2p_secio_exchange_protobuf_decode ( incoming - > data , incoming - > data_size , & exchange_in ) ;
libp2p_stream_message_free ( incoming ) ;
incoming = NULL ;
2017-03-07 00:03:04 +00:00
// 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-10-23 14:47:54 +00:00
outgoing . data = ( uint8_t * ) local_session - > remote_nonce ;
outgoing . data_size = 16 ;
2017-11-02 16:11:50 +00:00
if ( libp2p_secio_encrypted_write ( secio_context , & outgoing ) < = 0 ) {
2017-07-13 12:27:10 +00:00
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-11-02 16:11:50 +00:00
int bytes_read = libp2p_secio_encrypted_read ( secio_context , & incoming , 10 ) ;
2017-10-23 20:21:50 +00:00
if ( bytes_read < = 0 | | incoming = = NULL ) {
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
}
2017-10-23 20:21:50 +00:00
if ( incoming - > data_size ! = 16 ) {
libp2p_logger_error ( " secio " , " Results_size should be 16 but was %d \n " , incoming - > data_size ) ;
2017-02-06 22:11:22 +00:00
goto exit ;
2017-04-03 17:43:05 +00:00
}
2017-10-23 20:21:50 +00:00
if ( libp2p_secio_bytes_compare ( incoming - > data , ( 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-10-23 20:21:50 +00:00
libp2p_stream_message_free ( incoming ) ;
incoming = NULL ;
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-02-01 17:14:52 +00:00
retVal = 1 ;
2017-09-14 16:35:16 +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-10-23 20:21:50 +00:00
libp2p_stream_message_free ( incoming ) ;
2017-02-01 17:14:52 +00:00
2017-10-23 14:01:03 +00:00
if ( retVal ! = 1 ) {
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
}
2017-11-02 16:11:50 +00:00
2017-11-02 18:20:40 +00:00
int libp2p_secio_peek ( void * stream_context ) {
if ( stream_context = = NULL ) {
return - 1 ;
}
struct SecioContext * ctx = ( struct SecioContext * ) stream_context ;
return ctx - > stream - > parent_stream - > peek ( ctx - > stream - > parent_stream - > stream_context ) ;
}
int libp2p_secio_read_raw ( void * stream_context , uint8_t * buffer , int buffer_size , int timeout_secs ) {
if ( stream_context = = NULL ) {
return - 1 ;
}
struct SecioContext * ctx = ( struct SecioContext * ) stream_context ;
if ( secio_buffered_message_pos = = - 1 ) {
// we need to get info from the network
if ( ! ctx - > stream - > read ( ctx - > stream - > stream_context , & secio_buffered_message , timeout_secs ) ) {
return - 1 ;
}
secio_buffered_message_pos = 0 ;
}
int max_to_read = ( buffer_size > secio_buffered_message - > data_size ? secio_buffered_message - > data_size : buffer_size ) ;
memcpy ( buffer , & secio_buffered_message - > data [ secio_buffered_message_pos ] , max_to_read ) ;
secio_buffered_message_pos + = max_to_read ;
if ( secio_buffered_message_pos = = secio_buffered_message - > data_size ) {
// we read everything
libp2p_stream_message_free ( secio_buffered_message ) ;
secio_buffered_message = NULL ;
secio_buffered_message_pos = - 1 ;
} else {
// we didn't read everything.
secio_buffered_message_pos = max_to_read ;
}
return max_to_read ;
}
2017-11-02 16:11:50 +00:00
/***
* Initiates a secio handshake . Use this method when you want to initiate a secio
* session . This should not be used to respond to incoming secio requests
* @ param parent_stream the parent stream
* @ param remote_peer the remote peer
* @ param peerstore the peerstore
* @ param rsa_private_key the local private key
* @ returns a Secio Stream
*/
struct Stream * libp2p_secio_stream_new ( struct Stream * parent_stream , struct Libp2pPeer * remote_peer , struct Peerstore * peerstore , struct RsaPrivateKey * rsa_private_key ) {
struct Stream * new_stream = libp2p_stream_new ( ) ;
if ( new_stream ! = NULL ) {
struct SecioContext * ctx = ( struct SecioContext * ) malloc ( sizeof ( struct SecioContext ) ) ;
if ( ctx = = NULL ) {
libp2p_stream_free ( new_stream ) ;
new_stream = NULL ;
return NULL ;
}
new_stream - > stream_context = ctx ;
ctx - > stream = new_stream ;
ctx - > session_context = remote_peer - > sessionContext ;
ctx - > peer_store = peerstore ;
ctx - > private_key = rsa_private_key ;
new_stream - > parent_stream = parent_stream ;
new_stream - > close = libp2p_secio_shutdown ;
2017-11-02 18:20:40 +00:00
new_stream - > peek = libp2p_secio_peek ;
2017-11-02 16:11:50 +00:00
new_stream - > read = libp2p_secio_encrypted_read ;
2017-11-02 18:20:40 +00:00
new_stream - > read_raw = libp2p_secio_read_raw ;
2017-11-02 16:11:50 +00:00
new_stream - > write = libp2p_secio_encrypted_write ;
if ( ! libp2p_secio_send_protocol ( ctx )
| | ! libp2p_secio_receive_protocol ( ctx )
| | ! libp2p_secio_handshake ( ctx ) ) {
libp2p_stream_free ( new_stream ) ;
new_stream = NULL ;
}
}
return new_stream ;
}