Addition of more handshake code for connecting to other nodes

yamux
John Jones 2017-02-01 12:14:52 -05:00
parent 6d9a9e0e70
commit 513b778561
10 changed files with 308 additions and 24 deletions

View File

@ -11,6 +11,7 @@ compile:
cd hashmap; make all;
cd record; make all;
cd routing; make all;
cd secio; make all;
ar rcs libp2p.a $(OBJS)
test: compile
@ -27,5 +28,6 @@ clean:
cd test; make clean;
cd record; make clean;
cd routing; make clean;
cd secio; make all;
rm -rf libp2p.a

77
crypto/key.c Normal file
View File

@ -0,0 +1,77 @@
#include <stdio.h>
/**
* Utilities for public keys
*/
enum KeyType { KEYTYPE_RSA, KEYTYPE_ED25519, KEYTYPE_INVALID };
struct PublicKey {
enum KeyType key_type;
char* raw_key;
size_t raw_key_size;
};
struct PublicKey* libp2p_crypto_public_key_new() {
struct PublicKey* retVal = malloc(sizeof(struct PublicKey));
if (retVal == NULL)
return NULL;
retVal->key_type = KEYTYPE_INVALID;
retVal->raw_key = NULL;
retVal->raw_key_size = 0;
return retVal;
}
void libp2p_crypto_public_key_free(struct PublicKey* in) {
if (in != NULL) {
if (in->raw_key != NULL)
free(in->raw_key);
free(in);
in = NULL;
}
}
/**
* Unmarshal a public key from a protobuf
*/
int libp2p_crypto_public_key_protobuf_decode(unsigned char* buffer, size_t buffer_length, struct PublicKey** out) {
// first field is type (RSA vs ED25519)
// second field is the public key
size_t pos = 0;
int retVal = 0;
if ( (*out = libp2p_crypto_public_key_new()) == NULL)
goto exit;
while(pos < buffer_length) {
size_t bytes_read = 0;
int field_no;
enum WireType field_type;
if (protobuf_decode_field_and_type(&buffer[pos], buffer_length, &field_no, &field_type, &bytes_read) == 0) {
goto exit;
}
pos += bytes_read;
switch(field_no) {
case (1): // type
if (protobuf_decode_varint(&buffer[pos], buffer_length - pos, &((*out)->key_type), &bytes_read) == 0)
goto exit;
pos += bytes_read;
break;
case (2): // key
if (protobuf_decode_length_delimited(&buffer[pos], buffer_length - pos, (char**)&((*out)->raw_key), &((*out)->raw_key_size), &bytes_read) == 0)
goto exit;
pos += bytes_read;
break;
}
}
retVal = 1;
exit:
if (retVal == 0) {
libp2p_crypto_public_key_free(*out);
}
return retVal;
}

View File

@ -0,0 +1,29 @@
#pragma once
/**
* Utilities for public keys
*/
enum KeyType { KEYTYPE_RSA, KEYTYPE_ED25519, KEYTYPE_INVALID };
struct PublicKey {
enum KeyType type;
unsigned char* data;
size_t data_size;
};
struct PrivateKey {
enum KeyType type;
unsigned char* data;
size_t data_size;
};
struct PublicKey* libp2p_crypto_public_key_new();
void libp2p_crypto_public_key_free(struct PublicKey* in);
/**
* Unmarshal a public key from a protobuf
*/
int libp2p_crypto_public_key_protobuf_decode(unsigned char* buffer, size_t buffer_length, struct PublicKey** out);
int libp2p_crypto_private_key_protobuf_decode(unsigned char* buffer, size_t buffer_length, struct PublicKey** out);

View File

@ -0,0 +1,34 @@
#pragma once
struct Exchange {
unsigned char* epubkey;
size_t epubkey_size;
unsigned char* signature;
size_t signature_size;
};
/**
* retrieves the approximate size of an encoded version of the passed in struct
* @param in the struct to look at
* @reutrns the size of buffer needed
*/
size_t libp2p_secio_exchange_protobuf_encode_size(struct Exchange* in);
/**
* Encode the struct Exchange in protobuf format
* @param in the struct to be encoded
* @param buffer where to put the results
* @param max_buffer_length the max to write
* @param bytes_written how many bytes were written to the buffer
* @returns true(1) on success, otherwise false(0)
*/
int libp2p_secio_exchange_protobuf_encode(struct Exchange* in, unsigned char* buffer, size_t max_buffer_length, size_t* bytes_written);
/**
* Turns a protobuf array into an Exchange struct
* @param buffer the protobuf array
* @param max_buffer_length the length of the buffer
* @param out a pointer to the new struct Exchange NOTE: this method allocates memory
* @returns true(1) on success, otherwise false(0)
*/
int libp2p_secio_exchange_protobuf_decode(unsigned char* buffer, size_t max_buffer_length, struct Exchange** out);

View File

@ -13,6 +13,18 @@ struct Propose {
size_t hashes_size;
};
struct Propose* libp2p_secio_propose_new();
void libp2p_secio_propose_free(struct Propose* in);
/**
* Helper to set the property to a value
* @param to the property
* @param to_size the size that matches the property
* @param from the value to be set in the property
* @param from_size the size of from
* @returns true(1) on success, otherwise false(0)
*/
int libp2p_secio_propose_set_property(void** to, size_t* to_size, void* from, size_t from_size);
/**
* retrieves the approximate size of an encoded version of the passed in struct
* @param in the struct to look at

View File

@ -1,10 +1,13 @@
#pragma once
#include "libp2p/crypto/key.h"
/**
* A secure connection
*/
struct SecureSession {
int socket_descriptor;
struct PublicKey remote_key;
int remote_peer_id;
};

18
secio/Makefile Normal file
View File

@ -0,0 +1,18 @@
CC = gcc
CFLAGS = -O0 -Wall -I../include -I../../c-protobuf
ifdef DEBUG
CFLAGS += -g3
endif
LFLAGS =
DEPS =
OBJS = exchange.o propose.o secio.o
%.o: %.c $(DEPS)
$(CC) -c -o $@ $< $(CFLAGS)
all: $(OBJS)
clean:
rm -f *.o

View File

@ -1,14 +1,14 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "protobuf.h"
#include "libp2p/secio/propose.h"
// rand pubkey exchanges ciphers hashes
enum WireType secio_propose_message_fields[] = { WIRETYPE_LENGTH_DELIMITED, WIRETYPE_LENGTH_DELIMITED, WIRETYPE_LENGTH_DELIMITED, WIRETYPE_LENGTH_DELIMITED, WIRETYPE_LENGTH_DELIMITED };
// epubkey signature
enum WireType secio_exchange_message_fields[] = { WIRETYPE_LENGTH_DELIMITED, WIRETYPE_LENGTH_DELIMITED };
struct Propose* libp2p_secio_propose_new() {
struct Propose* retVal = (struct Propose*)malloc(sizeof(struct propose));
struct Propose* retVal = (struct Propose*)malloc(sizeof(struct Propose));
if (retVal == NULL)
return NULL;
memset((void*)retVal, 0, sizeof(struct Propose));
@ -28,9 +28,21 @@ void libp2p_secio_propose_free( struct Propose* in) {
if (in->hashes != NULL)
free(in->hashes);
free(in);
in = NULL;
}
}
int libp2p_secio_propose_set_property(void** to, size_t* to_size, void* from, size_t from_size) {
if (*to != NULL)
free(*to);
*to = (void*)malloc(from_size);
if (*to == NULL)
return 0;
memcpy(*to, from, from_size);
*to_size = from_size;
return 1;
}
/**
* retrieves the approximate size of an encoded version of the passed in struct
* @param in the struct to look at
@ -56,27 +68,26 @@ size_t libp2p_secio_propose_protobuf_encode_size(struct Propose* in) {
*/
int libp2p_secio_propose_protobuf_encode(struct Propose* in, unsigned char* buffer, size_t max_buffer_length, size_t* bytes_written) {
*bytes_written = 0;
int retVal;
size_t bytes_used;
// rand
if (!protobuf_encode_length_delimited(1, secio_propose_message_fields[0], in->rand, in->rand_size, &buffer[*bytes_written], max_buffer_length - *bytes_written, &bytes_used))
return -1;
if (!protobuf_encode_length_delimited(1, secio_propose_message_fields[0], (char*)in->rand, in->rand_size, &buffer[*bytes_written], max_buffer_length - *bytes_written, &bytes_used))
return 0;
*bytes_written += bytes_used;
// public key
if (!protobuf_encode_length_delimited(2, secio_propose_message_fields[1], in->public_key, in->public_key_size, &buffer[*bytes_written], max_buffer_length - *bytes_written, &bytes_used))
return -1;
if (!protobuf_encode_length_delimited(2, secio_propose_message_fields[1], (char*)in->public_key, in->public_key_size, &buffer[*bytes_written], max_buffer_length - *bytes_written, &bytes_used))
return 0;
*bytes_written += bytes_used;
// ciphers
if (!protobuf_encode_length_delimited(3, secio_propose_message_fields[2], in->ciphers, in->ciphers_size, &buffer[*bytes_written], max_buffer_length - *bytes_written, &bytes_used))
return -1;
return 0;
*bytes_written += bytes_used;
// exchanges
if (!protobuf_encode_length_delimited(4, secio_propose_message_fields[3], in->exchanges, in->exchanges_size, &buffer[*bytes_written], max_buffer_length - *bytes_written, &bytes_used))
return -1;
return 0;
*bytes_written += bytes_used;
// hashes
if (!protobuf_encode_length_delimited(5, secio_propose_message_fields[4], in->hashes, in->hashes_size, &buffer[*bytes_written], max_buffer_length - *bytes_written, &bytes_used))
return -1;
return 0;
*bytes_written += bytes_used;
return 1;
}
@ -91,8 +102,6 @@ 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) {
size_t pos = 0;
int retVal = 0;
unsigned char* temp_buffer = NULL;
size_t temp_size;
if (libp2p_secio_propose_new(out) == 0)
goto exit;
@ -140,8 +149,5 @@ exit:
if (retVal == 0) {
libp2p_secio_propose_free(*out);
}
if (temp_buffer != NULL)
free(temp_buffer);
return retVal;
}

View File

@ -1,5 +1,8 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "libp2p/secio/secio.h"
#include "libp2p/secio/propose.h"
const char* SupportedExchanges = "P-256,P-384,P-521";
const char* SupportedCiphers = "AES-256,AES-128,Blowfish";
@ -33,20 +36,46 @@ void libp2p_secio_secure_session_free(struct SecureSession* in) {
* @returns true(1) on success, false(0) otherwise
*/
int libp2p_secio_secure_session_handshake(struct SecureSession* session, struct RsaPrivateKey* private_key) {
int retVal = 0, protobuf_size = 0, results_size = 0;
unsigned char* protobuf = 0;;
unsigned char* results;
struct Propose* propose = NULL;
struct SocketMuxer* socketMuxer;
// generate 16 byte nonce
char nonceOut[16];
if (!generateNonce(nonceOut, 16))
return 0;
if (!generateNonce(&nonceOut, 16)) {
goto exit;
}
propose = libp2p_secio_propose_new();
libp2p_secio_propose_set_property(&propose->rand, &propose->rand_size, nonceOut, 16);
// will need:
// public key
// TODO: public key
// supported exchanges
libp2p_secio_propose_set_property(&propose->exchanges, &propose->exchanges_size, SupportedExchanges, strlen(SupportedExchanges));
// supported ciphers
libp2p_secio_propose_set_property(&propose->ciphers, &propose->ciphers_size, SupportedCiphers, strlen(SupportedCiphers));
// supported hashes
libp2p_secio_propose_set_property(&propose->hashes, &propose->exchanges_size, SupportedHashes, strlen(SupportedHashes));
// send request
// receive response
// send request (protobuf, then send)
protobuf_size = libp2p_secio_propose_protobuf_encode_size(propose);
protobuf = (unsigned char*) malloc(protobuf_size);
if (protobuf == NULL)
goto exit;
if (!libp2p_secio_propose_protobuf_encode(propose, protobuf, protobuf_size, &protobuf_size))
goto exit;
libp2p_secio_propose_free(propose);
if (!libp2p_net_socket_muxer_send(socketMuxer, protobuf, protobuf_size))
goto exit;
// receive response (turn back into a Propose struct)
if (!libp2p_net_socket_muxer_receive(socketMuxer, &results, &results_size))
goto exit;
if (!libp2p_secio_propose_protobuf_decode(results, results_size, &propose))
goto exit;
// get public key
// generate their peer id
@ -68,4 +97,14 @@ int libp2p_secio_secure_session_handshake(struct SecureSession* session, struct
// send expected message (local nonce) to verify encryption works
retVal = 1;
exit:
ipfs_secio_propose_free(propose);
if (protobuf != NULL)
free(protobuf);
return retVal;
}

64
test/crypto/test_key.h Normal file
View File

@ -0,0 +1,64 @@
#include <stdio.h>
#include "libp2p/crypto/key.h"
int test_protobuf_private_key() {
int retVal = 0;
size_t decode_base64_size = 0;
unsigned char* decode_base64;
// this is a base64 encoded private key. It makes it easier to test if it is in base64 form
char* orig_priv_key = "CAASpwkwggSjAgEAAoIBAQDTDJBWjDzS/HxDNOHazvzH2bu9CPMVHUrrvKRdBUM5ansL6/CC3MVZ6HVm4O6QHRapN6EF2CbrTgI4KBOXIL125Xo8MlROnyfXYk3O5q2tgwL/MbW8kXjtkyCfBak7MUoLOdLU7Svg0gkl3l+uDAiDcCLnwJVcFfq9ch6z4wMOhYJqE5dtx0uXxn6IuKWl1B69FTvBXCc0thw8Rw54b941FDcsBH5ttV9mRNCJym3poZ5qalNgXlxoIIB+PUx5QD+aq7KMJdpAX8HkapBntCOahP/QUceRmma0grlZLeYkH6/oi/hIrM6se3KUZ+F6tBuDFys8UAZy/X2BCUbKjbxtAgMBAAECggEANWfQfpYuLhXGPBt9q6kFPm1SnJtPJ+CpvM2XqhJS2IyhZnrl+bd0GTRBwS7aL42s1lVFYf04nAK5fQxnKK8YQqX/MIxr2RldM5ukpN6qxGWKtJkXrAgD2dqJPrRoBpqKahzPxSHfIJ0Fw5dqDtjsrpYJvyt0oEDPmnDuZAbmFx4sJqnesPNhKxtRMBx1+yxGVuRVJjHcqAgqPqwNiuoMEaYMY+G9yzT6vza8ovCpbX7BBIgM5fAT9PD8TBG//Vu9THvj/ZomiVG2qv6RL0qQyVb+DUzPZz1amBsSvahtXCl72jA3JwAZ943RxSR66P934S0ashkVwLUi46z/EAbJ4QKBgQDojGIO07BEVL2+7VxlGL9XGZQp4Y3qlhh2zDDQGwkCq/KQ+BdNYWitPwqRl9GqFLgpmeQIhyHTOa/IThx+AXGKVQ24ROH+skUs4IbO6R3qY7BKtb5lkZE/Yln09x70BBngUYAzh/rtnsXO3cl1x2XDDqUbCwlGcDAs8Jh/6UnvQwKBgQDoVSQs7Uq9MJCGIUM2bixX89tHzSxq5mn9wMD3/XRVfT5Ua8YkYBuzcmlcT39N7L5BwuyFqX3Vi7lv/Ya/qaQP6XkrZ8W1OAaTlYewfE5ZgknJqSpXcNWhABKeNmqndvqyQ/8HNCv/j8AdraGB2DGO57Xso5J0CQ43W/U9+QIyjwKBgHLL2hw3o+wXaRO3WMUPUmVM2zdRgR0suybp5a7Vqb0H5NZrohUw4NulIzJ8H6Q2VjMzJL6Q9sGu2HepF6ecTtBa7ErqtiVlG4Dr1aCOs5XhYEWBMlwxX+JKSt4Cn+UVoTB7Cy5lEhn7JurX0Xuy0ylXMWoIKKv89cs5eg6quzTBAoGAaq9eEztLjKCWXOE9SetBdYnG8aunb9cqaJlwgu/h0bfXPVDYBbAUSEyLURY4MQI7Q1tM3Pu9iqfEmUZj7/LoIV5mg6X9RX/alT6etk3+dF+9nlqN1OU9U9cCtZ/rTcb2y5EptJcidRH/eCFY/pTV/PcttOJPx/S4kHcroC+N8MUCgYEA6DA5QHxHfNN6Nxv+pEzy2DIxFe9RrBxS+KPBsra1C8jgdeMf4EmfU0Nox92V0q0bRrD5ztqQwSONI0hSRb1iiMWR6MuFnAFajUJfASjjIlZ6nIQjQslI7vjlvYyyHS/p/Codxap+yJlTLWwVEOXp2D9pWwiMq1xEyf0TH1BosvM=";
struct PrivateKey* private_key = libp2p_crypto_private_key_new();
struct RsaPrivateKey rsa_private_key = {0};
// convert from base64
// 1) take the private key and turn it back into bytes (decode base 64)
decode_base64_size = libp2p_crypto_encoding_base64_decode_size(strlen(orig_priv_key));
decode_base64 = (unsigned char*)malloc(decode_base64_size);
memset(decode_base64, 0, decode_base64_size);
int retVal = libp2p_crypto_encoding_base64_decode((unsigned char*)orig_priv_key, strlen(orig_priv_key), &decode_base64[0], decode_base64_size, &decode_base64_size);
if (retVal == 0)
goto exit;
// the first 5 bytes [0-4] are protobuf metadata before the DER encoded private key
// byte 0 is "Tag 1 which is a varint"
// byte 1 is the value of the varint
// byte 2 is "Tag 2 which is a type 2, length delimited field"
// bytes 3 & 4 is a varint with the value of 1191, which is the number of bytes that follow
if (!libp2p_crypto_private_key_protobuf_decode(decode_base64, decode_base64_size, &private_key))
goto exit;
// 2) take the bytes of the private key and turn it back into a private key struct
if (!libp2p_crypto_encoding_x509_der_to_private_key(private_key->data, private_key->data_size, &rsa_private_key))
goto exit;
// 2b) take the private key and fill in the public key DER
if (!libp2p_crypto_rsa_private_key_fill_public_key(&rsa_private_key))
goto exit;
// 3) grab the public key, hash it, then base58 it
unsigned char hashed[32];
ID_FromPK_non_null_terminated((char*)hashed, (unsigned char*)rsa_private_key.public_key_der, rsa_private_key.public_key_length);
size_t final_id_size = 1600;
unsigned char final_id[final_id_size];
memset(final_id, 0, final_id_size);
if (!PrettyID(final_id, &final_id_size, hashed, 32))
goto exit;
// 4) compare results
if (orig_peer_id_size != final_id_size)
goto exit;
if (strncmp(orig_peer_id, (char*)final_id, final_id_size) != 0)
goto exit;
retVal = 1;
exit:
if (decode_base64 != NULL)
free(decode_base64);
return retVal;
}