diff --git a/include/libp2p/secio/propose.h b/include/libp2p/secio/propose.h new file mode 100644 index 0000000..80fd6d1 --- /dev/null +++ b/include/libp2p/secio/propose.h @@ -0,0 +1,40 @@ +#pragma once + +struct Propose { + unsigned char* rand; + size_t rand_size; + unsigned char* public_key; + size_t public_key_size; + char* exchanges; + size_t exchanges_size; + char* ciphers; + size_t ciphers_size; + char* hashes; + size_t hashes_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_propose_protobuf_encode_size(struct Propose* in); + +/** + * Encode the struct Propose 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_propose_protobuf_encode(struct Propose* in, unsigned char* buffer, size_t max_buffer_length, size_t* bytes_written); + +/** + * Turns a protobuf array into a Propose struct + * @param buffer the protobuf array + * @param max_buffer_length the length of the buffer + * @param out a pointer to the new struct Propose NOTE: this method allocates memory + * @returns true(1) on success, otherwise false(0) + */ +int libp2p_secio_propose_protobuf_decode(unsigned char* buffer, size_t max_buffer_length, struct Propose** out); diff --git a/include/libp2p/secio/secio.h b/include/libp2p/secio/secio.h new file mode 100644 index 0000000..f573f73 --- /dev/null +++ b/include/libp2p/secio/secio.h @@ -0,0 +1,10 @@ +#pragma once + +/** + * A secure connection + */ + +struct SecureSession { + int socket_descriptor; + +}; diff --git a/secio/propose.c b/secio/propose.c new file mode 100644 index 0000000..82bbbb1 --- /dev/null +++ b/secio/propose.c @@ -0,0 +1,147 @@ +#include + +#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)); + if (retVal == NULL) + return NULL; + memset((void*)retVal, 0, sizeof(struct Propose)); + return retVal; +} + +void libp2p_secio_propose_free( struct Propose* in) { + if (in != NULL) { + if (in->rand != NULL) + free(in->rand); + if (in->public_key != NULL) + free(in->public_key); + if (in->ciphers != NULL) + free(in->ciphers); + if (in->exchanges != NULL) + free(in->exchanges); + if (in->hashes != NULL) + free(in->hashes); + free(in); + } +} + +/** + * 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_propose_protobuf_encode_size(struct Propose* in) { + size_t retVal = 0; + retVal += 11 + in->rand_size; + retVal += 11 + in->public_key_size; + retVal += 11 + in->ciphers_size; + retVal += 11 + in->exchanges_size; + retVal += 11 + in->hashes_size; + return retVal; +} + +/** + * Encode the struct Propose 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_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; + *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; + *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; + *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; + *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; + *bytes_written += bytes_used; + return 1; +} + +/** + * Turns a protobuf array into a Propose struct + * @param buffer the protobuf array + * @param buffer_length the length of the buffer + * @param out a pointer to the new struct Propose NOTE: this method allocates memory + * @returns true(1) on success, otherwise false(0) + */ +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; + + 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): // rand + if (protobuf_decode_length_delimited(&buffer[pos], buffer_length - pos, (char**)&((*out)->rand), &((*out)->rand_size), &bytes_read) == 0) + goto exit; + pos += bytes_read; + break; + 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) + goto exit; + pos += bytes_read; + break; + case (3): // ciphers + if (protobuf_decode_length_delimited(&buffer[pos], buffer_length - pos, (char**)&((*out)->ciphers), &((*out)->ciphers_size), &bytes_read) == 0) + goto exit; + pos += bytes_read; + break; + case (4): // exchanges + if (protobuf_decode_length_delimited(&buffer[pos], buffer_length - pos, (char**)&((*out)->exchanges), &((*out)->exchanges_size), &bytes_read) == 0) + goto exit; + pos += bytes_read; + break; + case (5): // hashes + if (protobuf_decode_length_delimited(&buffer[pos], buffer_length - pos, (char**)&((*out)->hashes), &((*out)->hashes_size), &bytes_read) == 0) + goto exit; + pos += bytes_read; + break; + } + } + + retVal = 1; + +exit: + if (retVal == 0) { + libp2p_secio_propose_free(*out); + } + if (temp_buffer != NULL) + free(temp_buffer); + + return retVal; +} diff --git a/secio/secio.c b/secio/secio.c new file mode 100644 index 0000000..1da0aff --- /dev/null +++ b/secio/secio.c @@ -0,0 +1,71 @@ +#include +#include "libp2p/secio/secio.h" + +const char* SupportedExchanges = "P-256,P-384,P-521"; +const char* SupportedCiphers = "AES-256,AES-128,Blowfish"; +const char* SupportedHashes = "SHA256,SHA512"; + +/*** + * Create a new SecureSession struct + * @returns a pointer to a new SecureSession object + */ +struct SecureSession* libp2p_secio_secure_session_new() { + struct SecureSession* ss = (struct SecureSession*) malloc(sizeof(struct SecureSession)); + if (ss == NULL) + return NULL; + ss->socket_descriptor = -1; + return ss; +} + +/*** + * Clean up resources from a SecureSession struct + * @param in the SecureSession to be deallocated + */ +void libp2p_secio_secure_session_free(struct SecureSession* in) { + //TODO: should we close the socket? + free(in); +} + +/*** + * performs initial communication over an insecure channel to share + * keys, IDs, and initiate connection. This is a framed messaging system + * @param session the secure session to be filled + * @returns true(1) on success, false(0) otherwise + */ +int libp2p_secio_secure_session_handshake(struct SecureSession* session, struct RsaPrivateKey* private_key) { + + // generate 16 byte nonce + char nonceOut[16]; + if (!generateNonce(nonceOut, 16)) + return 0; + + // will need: + // public key + // supported exchanges + // supported ciphers + // supported hashes + + // send request + // receive response + + // get public key + // generate their peer id + + // negotiate encryption parameters NOTE: SelectBest must match, otherwise this won't work + // curve + // cipher + // hash + + // prepare exchange of encryption parameters + + // send + + // receive + + // parse and verify + + // generate keys for mac and encryption + + // send expected message (local nonce) to verify encryption works + +}