intermediate commit for handshake

Working on negotiations between GO and C version of IPFS
This commit is contained in:
John Jones 2017-02-08 22:51:35 -05:00
parent be9f278ebf
commit 773c980f1f
5 changed files with 193 additions and 71 deletions

View file

@ -187,6 +187,9 @@ exit:
*/ */
int libp2p_crypto_public_key_to_peer_id(struct PublicKey* public_key, char** peer_id) { int libp2p_crypto_public_key_to_peer_id(struct PublicKey* public_key, char** peer_id) {
/**
* Converting to a peer id involves protobufing the struct PublicKey, SHA256 it, turn it into a MultiHash and base58 it
*/
size_t protobuf_len = libp2p_crypto_public_key_protobuf_encode_size(public_key); size_t protobuf_len = libp2p_crypto_public_key_protobuf_encode_size(public_key);
unsigned char protobuf[protobuf_len]; unsigned char protobuf[protobuf_len];
@ -198,6 +201,7 @@ int libp2p_crypto_public_key_to_peer_id(struct PublicKey* public_key, char** pee
size_t final_id_size = 100; size_t final_id_size = 100;
unsigned char final_id[final_id_size]; unsigned char final_id[final_id_size];
memset(final_id, 0, final_id_size); memset(final_id, 0, final_id_size);
// turn it into a multihash and base58 it
if (!PrettyID(final_id, &final_id_size, hashed, 32)) if (!PrettyID(final_id, &final_id_size, hashed, 32))
return 0; return 0;
*peer_id = (char*)malloc(final_id_size + 1); *peer_id = (char*)malloc(final_id_size + 1);

View file

@ -3,6 +3,7 @@
struct Propose { struct Propose {
unsigned char* rand; unsigned char* rand;
size_t rand_size; size_t rand_size;
// a protobuf'd struct PublicKey (defined in crypto/key.h)
unsigned char* public_key; unsigned char* public_key;
size_t public_key_size; size_t public_key_size;
char* exchanges; char* exchanges;

View file

@ -33,49 +33,46 @@ int libp2p_net_multistream_send(int socket_fd, const unsigned char* data, size_t
return num_bytes; return num_bytes;
} }
/***
* Parse the incoming data, removing the size indicator at the front of the array
* @param incoming the incoming data
* @param incoming_size the size of the incoming data
* @param outgoing the buffer to hold the outgoing data, allocated within this function
* @param outgoing_size the outgoing size
* @returns true(1) on success, otherwise false(0)
*/
int libp2p_net_multistream_parse_results(char* incoming, size_t incoming_size, char** outgoing, size_t* outgoing_size) {
int retVal = 0;
if (incoming_size > 0) {
// TODO: handle this differently
// read the varint
// allocate memory
*outgoing = (char*)malloc(incoming_size - 1);
if (*outgoing == NULL)
return 0;
// copy in the data
memcpy(*outgoing, &incoming[1], incoming_size - 1);
*outgoing_size = incoming_size - 1;
retVal = 1;
}
return retVal;
}
/** /**
* Read from a multistream socket * Read from a multistream socket
* @param socket_fd the socket file descriptor * @param socket_fd the socket file descriptor
* @param results where to put the results. NOTE: this memory is allocated * @param results where to put the results. NOTE: this memory is allocated
* @param results_size the size of the results in bytes * @param results_size the size of the results in bytes
* @returns true(1) on success, otherwise false(0) * @returns number of bytes received
*/ */
int libp2p_net_multistream_receive(int socket_fd, char** results, size_t* results_size) { int libp2p_net_multistream_receive(int socket_fd, char** results, size_t* results_size) {
int bytes = 0; int bytes = 0;
size_t buffer_size = 65535; size_t buffer_size = 65535;
char buffer[buffer_size]; char buffer[buffer_size];
char* pos = buffer;
int num_bytes = 0;
// first read the varint
while(1) {
unsigned char c;
bytes = socket_read(socket_fd, (char*)&c, 1, 0);
pos[0] = c;
if (c >> 7 == 0) {
pos[1] = 0;
num_bytes = varint_decode((unsigned char*)buffer, strlen(buffer), NULL);
break;
}
pos++;
}
if (num_bytes <= 0)
return 0;
// now read the number of bytes we've found
bytes = socket_read(socket_fd, buffer, num_bytes, 0);
bytes = socket_read(socket_fd, buffer, buffer_size, 0);
if (bytes == 0) if (bytes == 0)
return 0; return 0;
// parse the results, removing the leading size indicator // parse the results, removing the leading size indicator
return libp2p_net_multistream_parse_results(buffer, bytes, results, results_size); *results = malloc(bytes);
memcpy(*results, buffer, bytes);
*results_size = bytes;
return bytes;
} }

View file

@ -101,7 +101,7 @@ int libp2p_secio_propose_protobuf_encode(struct Propose* in, unsigned char* buff
*/ */
int libp2p_secio_propose_protobuf_decode(unsigned char* buffer, size_t buffer_length, struct Propose** out) { int libp2p_secio_propose_protobuf_decode(unsigned char* buffer, size_t buffer_length, struct Propose** out) {
size_t pos = 0; size_t pos = 0;
int retVal = 0; int retVal = 0, got_something = 0;;
if ( (*out = libp2p_secio_propose_new()) == NULL) if ( (*out = libp2p_secio_propose_new()) == NULL)
goto exit; goto exit;
@ -113,37 +113,45 @@ int libp2p_secio_propose_protobuf_decode(unsigned char* buffer, size_t buffer_le
if (protobuf_decode_field_and_type(&buffer[pos], buffer_length, &field_no, &field_type, &bytes_read) == 0) { if (protobuf_decode_field_and_type(&buffer[pos], buffer_length, &field_no, &field_type, &bytes_read) == 0) {
goto exit; goto exit;
} }
if (field_no < 1 || field_no > 5) {
fprintf(stderr, "Invalid character in Propose protobuf at position %lu. Value: %02x\n", pos, buffer[pos]);
}
pos += bytes_read; pos += bytes_read;
switch(field_no) { switch(field_no) {
case (1): // rand case (1): // rand
if (protobuf_decode_length_delimited(&buffer[pos], buffer_length - pos, (char**)&((*out)->rand), &((*out)->rand_size), &bytes_read) == 0) if (protobuf_decode_length_delimited(&buffer[pos], buffer_length - pos, (char**)&((*out)->rand), &((*out)->rand_size), &bytes_read) == 0)
goto exit; goto exit;
pos += bytes_read; pos += bytes_read;
got_something = 1;
break; break;
case (2): // public key case (2): // public key
if (protobuf_decode_length_delimited(&buffer[pos], buffer_length - pos, (char**)&((*out)->public_key), &((*out)->public_key_size), &bytes_read) == 0) if (protobuf_decode_length_delimited(&buffer[pos], buffer_length - pos, (char**)&((*out)->public_key), &((*out)->public_key_size), &bytes_read) == 0)
goto exit; goto exit;
pos += bytes_read; pos += bytes_read;
got_something = 1;
break; break;
case (3): // ciphers case (3): // ciphers
if (protobuf_decode_length_delimited(&buffer[pos], buffer_length - pos, (char**)&((*out)->ciphers), &((*out)->ciphers_size), &bytes_read) == 0) if (protobuf_decode_length_delimited(&buffer[pos], buffer_length - pos, (char**)&((*out)->ciphers), &((*out)->ciphers_size), &bytes_read) == 0)
goto exit; goto exit;
pos += bytes_read; pos += bytes_read;
got_something = 1;
break; break;
case (4): // exchanges case (4): // exchanges
if (protobuf_decode_length_delimited(&buffer[pos], buffer_length - pos, (char**)&((*out)->exchanges), &((*out)->exchanges_size), &bytes_read) == 0) if (protobuf_decode_length_delimited(&buffer[pos], buffer_length - pos, (char**)&((*out)->exchanges), &((*out)->exchanges_size), &bytes_read) == 0)
goto exit; goto exit;
pos += bytes_read; pos += bytes_read;
got_something = 1;
break; break;
case (5): // hashes case (5): // hashes
if (protobuf_decode_length_delimited(&buffer[pos], buffer_length - pos, (char**)&((*out)->hashes), &((*out)->hashes_size), &bytes_read) == 0) if (protobuf_decode_length_delimited(&buffer[pos], buffer_length - pos, (char**)&((*out)->hashes), &((*out)->hashes_size), &bytes_read) == 0)
goto exit; goto exit;
pos += bytes_read; pos += bytes_read;
got_something = 1;
break; break;
} }
} }
retVal = 1; retVal = got_something;
exit: exit:
if (retVal == 0) { if (retVal == 0) {

View file

@ -2,11 +2,14 @@
#include <stdio.h> // for debugging, can remove #include <stdio.h> // for debugging, can remove
#include <string.h> #include <string.h>
#include <stdint.h> #include <stdint.h>
#include <errno.h>
#include <arpa/inet.h>
#include "libp2p/secio/secio.h" #include "libp2p/secio/secio.h"
#include "libp2p/secio/propose.h" #include "libp2p/secio/propose.h"
#include "libp2p/secio/exchange.h" #include "libp2p/secio/exchange.h"
#include "libp2p/net/multistream.h" #include "libp2p/net/multistream.h"
#include "libp2p/net/p2pnet.h"
#include "libp2p/crypto/ephemeral.h" #include "libp2p/crypto/ephemeral.h"
#include "libp2p/crypto/sha1.h" #include "libp2p/crypto/sha1.h"
#include "libp2p/crypto/sha256.h" #include "libp2p/crypto/sha256.h"
@ -196,7 +199,8 @@ int libp2p_secio_sign(struct PrivateKey* private_key, const char* in, size_t in_
rsa_key.der = (char*)private_key->data; rsa_key.der = (char*)private_key->data;
rsa_key.der_length = private_key->data_size; rsa_key.der_length = private_key->data_size;
// SHA2-256 signatures are 32 bytes // SHA2-256 signatures are 32 bytes
*signature = (unsigned char*)malloc(32); *signature_size = 32;
*signature = (unsigned char*)malloc(*signature_size);
return libp2p_crypto_rsa_sign(&rsa_key, in, in_length, *signature); return libp2p_crypto_rsa_sign(&rsa_key, in, in_length, *signature);
} }
// TODO: Implement this method for non-RSA // TODO: Implement this method for non-RSA
@ -361,15 +365,104 @@ int libp2p_secio_make_mac_and_cipher(struct SecureSession* session) {
return 0; return 0;
} }
int libp2p_secio_write(struct SecureSession* session, unsigned char* bytes, size_t length) { int libp2p_secio_write(struct SecureSession* session, unsigned char* bytes, size_t data_length) {
// TODO: Implement this method 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 {
written_this_time = socket_write(session->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; return 0;
} }
}
int libp2p_secio_read(struct SecureSession* session, unsigned char** bytes, size_t* bytes_read) { left = left - written_this_time;
// TODO: Implement this method } while (left > 0);
// then send the actual data
left = data_length;
written = 0;
do {
written_this_time = socket_write(session->socket_descriptor, (char*)&bytes[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; 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;
}
int libp2p_secio_read(struct SecureSession* session, unsigned char** results, size_t* results_size) {
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 {
read_this_time = socket_read(session->socket_descriptor, &size[read], 1, 0);
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;
}
}
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);
// now read the number of bytes we've found, minus the 4 that we just read
buffer_size = ntohl(buffer_size);
// JMJ
fprintf(stderr, "which switching bits results in %u.\n", buffer_size);
if (buffer_size == 0)
return 0;
left = buffer_size;
read = 0;
read_this_time = 0;
*results = malloc(left);
unsigned char* ptr = *results;
do {
read_this_time = socket_read(session->socket_descriptor, (char*)&ptr[read], left, 0);
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;
}
/*** /***
* performs initial communication over an insecure channel to share * performs initial communication over an insecure channel to share
@ -406,41 +499,15 @@ int libp2p_secio_handshake(struct SecureSession* local_session, struct RsaPrivat
if (bytes_written <= 0) if (bytes_written <= 0)
goto exit; goto exit;
// we should get back the protocol to signify it was accepted, as well as the protobuf of the Propose struct // we should get back the secio confirmation
bytes_written = libp2p_net_multistream_receive(local_session->socket_descriptor, (char**)&results, &results_size); bytes_written = libp2p_net_multistream_receive(local_session->socket_descriptor, (char**)&results, &results_size);
if (bytes_written < 1 || strstr((char*)results, "secio") == NULL) if (bytes_written < 1 || strstr((char*)results, "secio") == NULL)
goto exit; goto exit;
// skip to the protobuf section
propose_in_bytes = (unsigned char*)strchr((char*)results, '\n');
if (propose_in_bytes == NULL)
goto exit;
// are we at the end of the buffer?
if (propose_in_bytes - results + 1 >= results_size) {
free(results);
// read some more
bytes_written = libp2p_net_multistream_receive(local_session->socket_descriptor, (char**)&results, &results_size);
propose_in_bytes = results;
} else {
propose_in_bytes++;
}
propose_in_size = results_size - (propose_in_bytes - results);
if (!libp2p_secio_propose_protobuf_decode(propose_in_bytes, propose_in_size, &propose_in))
goto exit;
// clear results
free(results); free(results);
results = NULL; results = NULL;
results_size = 0; results_size = 0;
// 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
char* remote_peer_id;
libp2p_crypto_public_key_to_peer_id(public_key, &remote_peer_id);
//TODO: make sure we're not talking to ourself //TODO: make sure we're not talking to ourself
// generate 16 byte nonce // generate 16 byte nonce
@ -451,17 +518,54 @@ int libp2p_secio_handshake(struct SecureSession* local_session, struct RsaPrivat
propose_out = libp2p_secio_propose_new(); propose_out = libp2p_secio_propose_new();
libp2p_secio_propose_set_property((void**)&propose_out->rand, &propose_out->rand_size, local_session->nonce, 16); libp2p_secio_propose_set_property((void**)&propose_out->rand, &propose_out->rand_size, local_session->nonce, 16);
// we have their information, now we need to gather ours. // public key - protobuf it and stick it in propose_out
// public key struct PublicKey pub_key;
propose_out->public_key_size = public_key->data_size; pub_key.type = KEYTYPE_RSA;
propose_out->public_key = (unsigned char*)malloc(public_key->data_size); pub_key.data_size = private_key->public_key_length;
memcpy(propose_out->public_key, public_key->data, public_key->data_size); pub_key.data = malloc(pub_key.data_size);
results_size = libp2p_crypto_public_key_protobuf_encode_size(&pub_key);
results = malloc(results_size);
if (libp2p_crypto_public_key_protobuf_encode(&pub_key, results, result_size, &results_size) == 0)
goto exit;
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;
// supported exchanges // supported exchanges
libp2p_secio_propose_set_property((void**)&propose_out->exchanges, &propose_out->exchanges_size, SupportedExchanges, strlen(SupportedExchanges)); libp2p_secio_propose_set_property((void**)&propose_out->exchanges, &propose_out->exchanges_size, SupportedExchanges, strlen(SupportedExchanges));
// supported ciphers // supported ciphers
libp2p_secio_propose_set_property((void**)&propose_out->ciphers, &propose_out->ciphers_size, SupportedCiphers, strlen(SupportedCiphers)); libp2p_secio_propose_set_property((void**)&propose_out->ciphers, &propose_out->ciphers_size, SupportedCiphers, strlen(SupportedCiphers));
// supported hashes // supported hashes
libp2p_secio_propose_set_property((void**)&propose_out->hashes, &propose_out->hashes_size, SupportedHashes, strlen(SupportedHashes)); libp2p_secio_propose_set_property((void**)&propose_out->hashes, &propose_out->hashes_size, SupportedHashes, strlen(SupportedHashes));
// send proposal
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;
bytes_written = libp2p_secio_write(local_session, propose_out_bytes, propose_out_size);
if (bytes_written < propose_out_size)
goto exit;
// try to get the propose object from the server
bytes_written = libp2p_secio_read(local_session, &propose_in_bytes, &propose_in_size);
if (bytes_written <= 0)
goto exit;
if (!libp2p_secio_propose_protobuf_decode(propose_in_bytes, propose_in_size, &propose_in))
goto exit;
// 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
char* remote_peer_id;
libp2p_crypto_public_key_to_peer_id(public_key, &remote_peer_id);
// negotiate encryption parameters NOTE: SelectBest must match, otherwise this won't work // negotiate encryption parameters NOTE: SelectBest must match, otherwise this won't work
// first determine order // first determine order
libp2p_secio_hash(propose_in, order_hash_in); libp2p_secio_hash(propose_in, order_hash_in);
@ -474,6 +578,7 @@ int libp2p_secio_handshake(struct SecureSession* local_session, struct RsaPrivat
// hash // hash
libp2p_secio_select_best(order, propose_out->hashes, propose_out->hashes_size, propose_in->hashes, propose_in->hashes_size, &local_session->chosen_hash); libp2p_secio_select_best(order, propose_out->hashes, propose_out->hashes_size, propose_in->hashes, propose_in->hashes_size, &local_session->chosen_hash);
// prepare exchange of encryption parameters // prepare exchange of encryption parameters
struct SecureSession remote_session; struct SecureSession remote_session;
remote_session.chosen_cipher = local_session->chosen_cipher; remote_session.chosen_cipher = local_session->chosen_cipher;
@ -515,11 +620,11 @@ int libp2p_secio_handshake(struct SecureSession* local_session, struct RsaPrivat
if (exchange_out_protobuf == NULL) if (exchange_out_protobuf == NULL)
goto exit; goto exit;
libp2p_secio_exchange_protobuf_encode(exchange_out, exchange_out_protobuf, exchange_out_protobuf_size, &bytes_written); libp2p_secio_exchange_protobuf_encode(exchange_out, exchange_out_protobuf, exchange_out_protobuf_size, &bytes_written);
bytes_written = libp2p_net_multistream_send(local_session->socket_descriptor, exchange_out_protobuf, exchange_out_protobuf_size); bytes_written = libp2p_secio_write(local_session, exchange_out_protobuf, exchange_out_protobuf_size);
free(exchange_out_protobuf); free(exchange_out_protobuf);
// receive Exchange packet // receive Exchange packet
bytes_written = libp2p_net_multistream_receive(local_session->socket_descriptor, (char**)&results, &results_size); bytes_written = libp2p_secio_read(local_session, &results, &results_size);
if (bytes_written == 0) if (bytes_written == 0)
goto exit; goto exit;
libp2p_secio_exchange_protobuf_decode(results, results_size, &exchange_in); libp2p_secio_exchange_protobuf_decode(results, results_size, &exchange_in);
@ -568,6 +673,13 @@ int libp2p_secio_handshake(struct SecureSession* local_session, struct RsaPrivat
exit: exit:
if (propose_in_bytes != NULL)
free(propose_in_bytes);
if (propose_out_bytes != NULL)
free(propose_out_bytes);
if (results != NULL)
free(results);
libp2p_secio_propose_free(propose_out); libp2p_secio_propose_free(propose_out);
libp2p_secio_propose_free(propose_in); libp2p_secio_propose_free(propose_in);