From 783855fe26a07c61a3ddab9b4fb3ad2b2db557f2 Mon Sep 17 00:00:00 2001 From: John Jones Date: Mon, 6 Feb 2017 17:11:22 -0500 Subject: [PATCH] in the middle of ephemeral encryption implementation --- Makefile | 11 +- crypto/Makefile | 2 +- crypto/ephemeral.c | 73 +++++++ crypto/rsa.c | 62 +++++- include/libp2p/crypto/ephemeral.h | 23 +++ include/libp2p/crypto/rsa.h | 9 +- include/libp2p/secio/exchange.h | 2 + include/libp2p/secio/secio.h | 9 + include/libp2p/utils/string_list.h | 8 + include/libp2p/utils/vector.h | 24 +++ secio/char_vector.c | 38 ++++ secio/secio.c | 319 ++++++++++++++++++++++++++--- test/crypto/test_ephemeral.h | 13 ++ test/crypto/test_rsa.h | 40 +++- test/testit.c | 9 +- thirdparty/Makefile | 3 +- utils/Makefile | 18 ++ utils/string_list.c | 26 +++ utils/vector.c | 44 ++++ 19 files changed, 686 insertions(+), 47 deletions(-) create mode 100644 crypto/ephemeral.c create mode 100644 include/libp2p/crypto/ephemeral.h create mode 100644 include/libp2p/utils/string_list.h create mode 100644 include/libp2p/utils/vector.h create mode 100644 secio/char_vector.c create mode 100644 test/crypto/test_ephemeral.h create mode 100644 utils/Makefile create mode 100644 utils/string_list.c create mode 100644 utils/vector.c diff --git a/Makefile b/Makefile index a147db5..5770f92 100644 --- a/Makefile +++ b/Makefile @@ -8,9 +8,10 @@ OBJS = crypto/*.o crypto/encoding/*.o \ net/*.o \ record/*.o \ routing/*.o \ - secio/*.o + secio/*.o \ + utils/*.o -link: $(OBJS) +link: ar rcs libp2p.a $(OBJS) compile: @@ -21,6 +22,7 @@ compile: cd record; make all; cd routing; make all; cd secio; make all; + cd utils; make all; test: compile link cd test; make all; @@ -34,9 +36,10 @@ clean: cd hashmap; make clean; cd net; make clean; cd thirdparty; make clean - cd test; make clean; cd record; make clean; cd routing; make clean; - cd secio; make all; + cd secio; make clean; + cd utils; make clean; + cd test; make clean; rm -rf libp2p.a diff --git a/crypto/Makefile b/crypto/Makefile index 825d6d9..2fb00a8 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -2,7 +2,7 @@ CC = gcc CFLAGS = -O0 -I../include -I../../c-protobuf -I../../c-multihash/include -g3 LFLAGS = DEPS = -OBJS = rsa.o sha256.o sha512.o sha1.o key.o peerutils.o +OBJS = rsa.o sha256.o sha512.o sha1.o key.o peerutils.o ephemeral.o %.o: %.c $(DEPS) $(CC) -c -o $@ $< $(CFLAGS) diff --git a/crypto/ephemeral.c b/crypto/ephemeral.c new file mode 100644 index 0000000..1e15e34 --- /dev/null +++ b/crypto/ephemeral.c @@ -0,0 +1,73 @@ +#include +#include + +#include "mbedtls/config.h" +#include "mbedtls/ecdsa.h" +#include "mbedtls/entropy.h" +#include "mbedtls/ctr_drbg.h" +#include "libp2p/crypto/ephemeral.h" + +struct EphemeralPrivateKey* libp2p_crypto_ephemeral_key_new(uint64_t priv, uint64_t x, uint64_t y) { + struct EphemeralPrivateKey* results = (struct EphemeralPrivateKey*)malloc(sizeof(struct EphemeralPrivateKey)); + if (results != NULL) { + results->secret_key = priv; + results->public_key = (struct EphemeralPublicKey*)malloc(sizeof(struct EphemeralPublicKey)); + if (results->public_key == NULL) { + free(results); + results = NULL; + } else { + results->public_key->x = x; + results->public_key->y = y; + } + } + return results; +} + +void libp2p_crypto_ephemeral_key_free(struct EphemeralPrivateKey* in) { + if (in != NULL) { + if (in->public_key != NULL) + free(in->public_key); + free(in); + } +} + +/** + * Generate a Ephemeral Public Key as well as a shared key + * @param curve the curve to use (P-256, P-384, or P-521) + * @param private_key the struct to store the generated key + * @returns true(1) on success, otherwise false(0) + */ +int libp2p_crypto_ephemeral_key_generate(char* curve, struct EphemeralPrivateKey** private_key) { + int retVal = 0; + mbedtls_ecdsa_context ctx; + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + + int selected_curve = 0; + if (strcmp(curve, "P-256") == 0) + selected_curve = MBEDTLS_ECP_DP_SECP256R1; + else if (strcmp(curve, "P-384") == 0) + selected_curve = MBEDTLS_ECP_DP_SECP384R1; + else + selected_curve = MBEDTLS_ECP_DP_SECP521R1; + + char* pers = "bitShares"; + + mbedtls_ecdsa_init(&ctx); + + // seed random number generator + mbedtls_entropy_init(&entropy); + mbedtls_ctr_drbg_init(&ctr_drbg); + if (mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, (const unsigned char*)pers, strlen(pers)) != 0) + goto exit; + + if (mbedtls_ecdsa_genkey(&ctx, selected_curve, mbedtls_ctr_drbg_random, &ctr_drbg) != 0) + goto exit; + + *private_key = libp2p_crypto_ephemeral_key_new(*ctx.d->p, *ctx.Q->X.p, *ctx.Q->Y.p); + retVal = 1; + + exit: + + return retVal; +} diff --git a/crypto/rsa.c b/crypto/rsa.c index 42257de..93b7c82 100644 --- a/crypto/rsa.c +++ b/crypto/rsa.c @@ -236,24 +236,74 @@ int libp2p_crypto_rsa_rsa_private_key_free(struct RsaPrivateKey* private_key) { * @param result the resultant signature. Note: should be pre-allocated and be the size of the private key (i.e. 2048 bit key can store a sig in 256 bytes) * @returns true(1) on successs, otherwise false(0) */ -int libp2p_crypto_rsa_sign(struct RsaPrivateKey* private_key, unsigned char* message, size_t message_length, unsigned char* result) { +int libp2p_crypto_rsa_sign(struct RsaPrivateKey* private_key, const unsigned char* message, size_t message_length, unsigned char* result) { unsigned char output[32]; libp2p_crypto_hashing_sha256(message, message_length, output); - mbedtls_rsa_context ctx; + // make a pk_context from the private key + mbedtls_pk_context private_context; + mbedtls_pk_init(&private_context); + mbedtls_pk_parse_key(&private_context, (unsigned char*)private_key->der, private_key->der_length, NULL, 0); + + // gety just the RSA portion of the context + mbedtls_rsa_context* ctx = mbedtls_pk_rsa(private_context); mbedtls_ctr_drbg_context ctr_drbg; - mbedtls_rsa_init(&ctx, MBEDTLS_RSA_PKCS_V15, 0); mbedtls_ctr_drbg_init(&ctr_drbg); - int retVal = mbedtls_rsa_rsassa_pkcs1_v15_sign( &ctx, - mbedtls_ctr_drbg_random, + + // sign + int retVal = mbedtls_rsa_rsassa_pkcs1_v15_sign(ctx, + NULL, //mbedtls_ctr_drbg_random, &ctr_drbg, MBEDTLS_RSA_PRIVATE, MBEDTLS_MD_SHA256, 32, output, result ); + //int retVal = mbedtls_rsa_private(&ctx, NULL, NULL, message, result); + // cleanup mbedtls_ctr_drbg_free(&ctr_drbg); - //mbetdls_rsa_free(ctx); + mbedtls_pk_free(&private_context); return retVal == 0; } +/** + * verify a signature + *@param public_key the public key to use + *@param message the message to compare to the signature + *@param message_length the length of the message + *@param signature the signature that was given + *@returns true(1) if the signature matches the SHA2-256 hash of message, false(0) otherwise + */ +int libp2p_crypto_rsa_verify(struct RsaPublicKey* public_key, const unsigned char* message, size_t message_length, const unsigned char* signature) { + + // hash the message + unsigned char output[32]; + libp2p_crypto_hashing_sha256(message, message_length, output); + + // make a pk_context from the public key + mbedtls_pk_context public_context; + mbedtls_pk_init(&public_context); + mbedtls_pk_parse_public_key(&public_context, (unsigned char*)public_key->der, public_key->der_length); + + mbedtls_rsa_context* ctx = mbedtls_pk_rsa(public_context); + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_ctr_drbg_init(&ctr_drbg); + + int retVal = mbedtls_rsa_rsassa_pkcs1_v15_verify(ctx, // the rsa public key has to be in the context + NULL, // random number generator, but not needed because this is not a private key + NULL, //mbedtls_ctr_drbg_random, // random number generator + MBEDTLS_RSA_PUBLIC, // mode RSA_PUBLIC or RSA_PRIVATE + MBEDTLS_MD_SHA256, // type of message digest + 32, // ignored because we know it from the parameter previous + output, signature); // the actual signature to compare + + mbedtls_ctr_drbg_free(&ctr_drbg); + + return retVal == 0; +} + + + + + + diff --git a/include/libp2p/crypto/ephemeral.h b/include/libp2p/crypto/ephemeral.h new file mode 100644 index 0000000..44210e1 --- /dev/null +++ b/include/libp2p/crypto/ephemeral.h @@ -0,0 +1,23 @@ +#pragma once + +/** + * General helpers for ephemeral keys + */ + +struct EphemeralPublicKey { + uint64_t x; + uint64_t y; +}; + +struct EphemeralPrivateKey { + uint64_t secret_key; + struct EphemeralPublicKey* public_key; +}; + +/** + * Generate a Ephemeral Public Key as well as a shared key + * @param curve the curve to use (P-256, P-384, or P-521) + * @param private_key where to store the private key + * @reutrns true(1) on success, otherwise false(0) + */ +int libp2p_crypto_ephemeral_key_generate(char* curve, struct EphemeralPrivateKey* private_key); diff --git a/include/libp2p/crypto/rsa.h b/include/libp2p/crypto/rsa.h index 1307f73..3d0a3fb 100644 --- a/include/libp2p/crypto/rsa.h +++ b/include/libp2p/crypto/rsa.h @@ -3,6 +3,11 @@ #include +struct RsaPublicKey { + char* der; + size_t der_length; +}; + struct RsaPrivateKey { // the basics of a key pair unsigned long long QP; @@ -53,6 +58,8 @@ int libp2p_crypto_rsa_rsa_private_key_free(struct RsaPrivateKey* private_key); * @param result the resultant signature. Note: should be pre-allocated and be the size of the private key (i.e. 2048) * @returns true(1) on successs, otherwise false(0) */ -int libp2p_crypto_rsa_sign(struct RsaPrivateKey* private_key, unsigned char* message, size_t message_length, unsigned char* result); +int libp2p_crypto_rsa_sign(struct RsaPrivateKey* private_key, const unsigned char* message, size_t message_length, unsigned char* result); + +int libp2p_crypto_rsa_verify(struct RsaPublicKey* public_key, const unsigned char* message, size_t message_length, const unsigned char* signature); #endif /* rsa_h */ diff --git a/include/libp2p/secio/exchange.h b/include/libp2p/secio/exchange.h index 8e94a9c..e79d24e 100644 --- a/include/libp2p/secio/exchange.h +++ b/include/libp2p/secio/exchange.h @@ -7,6 +7,8 @@ struct Exchange { size_t signature_size; }; +struct Exchange* libp2p_secio_exchange_new(); + /** * retrieves the approximate size of an encoded version of the passed in struct * @param in the struct to look at diff --git a/include/libp2p/secio/secio.h b/include/libp2p/secio/secio.h index 878e1fc..2c20673 100644 --- a/include/libp2p/secio/secio.h +++ b/include/libp2p/secio/secio.h @@ -18,6 +18,15 @@ struct SecureSession { int socket_descriptor; struct PublicKey remote_key; char* remote_peer_id; + // filled in during negotiations + char* chosen_curve; + char* chosen_cipher; + char* chosen_hash; + unsigned char* ephemeral_public_key; + size_t ephemeral_public_key_size; + unsigned char* shared_key; + size_t shared_key_size; + char nonce[16]; }; /*** diff --git a/include/libp2p/utils/string_list.h b/include/libp2p/utils/string_list.h new file mode 100644 index 0000000..6d845fd --- /dev/null +++ b/include/libp2p/utils/string_list.h @@ -0,0 +1,8 @@ +struct StringList { + char* string; + struct StringList* next; +}; + +struct StringList* libp2p_utils_string_list_new(); + +void libp2p_utils_string_list_free(struct StringList* in); diff --git a/include/libp2p/utils/vector.h b/include/libp2p/utils/vector.h new file mode 100644 index 0000000..6220234 --- /dev/null +++ b/include/libp2p/utils/vector.h @@ -0,0 +1,24 @@ +#pragma once + +/*** + * A very simple vector implementation for unsigned chars + */ + +/** + * The struct + */ +struct Libp2pVector { + unsigned char* buffer; + size_t buffer_size; +}; + +/** + * Create and destroy + */ +struct Libp2pVector* libp2p_utils_vector_new(); +void libp2p_utils_vector_free(struct Libp2pVector* vector); + +/** + * Add bytes to vector + */ +int libp2p_utils_vector_add(struct Libp2pVector* vector, unsigned char* in_bytes, size_t in_size); diff --git a/secio/char_vector.c b/secio/char_vector.c new file mode 100644 index 0000000..73d4295 --- /dev/null +++ b/secio/char_vector.c @@ -0,0 +1,38 @@ +#include + +#include "libp2p/secio/char_vector.h" + +struct UnsignedCharVector* char_vector_new() { + struct UnsignedCharVector* vector = (struct UnsignedCharVector*)malloc(sizeof(struct UnsignedCharVector)); + vector->buffer = NULL; + vector->buffer_size = 0; + return vector; +} + +void char_vector_free(struct UnsignedCharVector* vector) { + if (vector != NULL) { + if (vector->buffer != NULL) + free(vector->buffer); + vector->buffer = NULL; + vector->buffer_size = 0; + free(vector); + vector = NULL; + } +} + +int char_vector_add(struct UnsignedCharVector* vector, unsigned char* in_bytes, size_t in_size) { + // make new memory + if (vector->buffer == NULL) { + vector->buffer = (unsigned char*)malloc(in_size); + if (vector->buffer == NULL) + return 0; + vector->buffer_size = in_size; + } else { + vector->buffer = (unsigned char*)realloc(vector->buffer_size + in_size); + if (vector->buffer == NULL) + return 0; + memcpy(&vector->buffer[vector->buffer_size], in_bytes, in_size); + vector->buffer_size = in_size + vector->buffer_size; + } + return 1; +} diff --git a/secio/secio.c b/secio/secio.c index 92b6c41..37fca59 100644 --- a/secio/secio.c +++ b/secio/secio.c @@ -5,8 +5,12 @@ #include "libp2p/secio/secio.h" #include "libp2p/secio/propose.h" -//#include "libp2p/net/p2pnet.h" +#include "libp2p/secio/exchange.h" #include "libp2p/net/multistream.h" +#include "libp2p/crypto/sha256.h" +#include "libp2p/crypto/ephemeral.h" +#include "libp2p/utils/string_list.h" +#include "libp2p/utils/vector.h" const char* SupportedExchanges = "P-256,P-384,P-521"; const char* SupportedCiphers = "AES-256,AES-128,Blowfish"; @@ -46,42 +50,246 @@ int libp2p_secio_generate_nonce(char* results, int length) { return 1; } +/** + * 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 + */ +int libp2p_secio_hash(struct Propose* in, unsigned char result[32]) { + // append public key and nonce + unsigned char buffer[in->public_key_size + in->rand_size]; + memcpy(buffer, in->public_key, in->public_key_size); + memcpy(&buffer[in->public_key_size], in->rand, in->rand_size); + return libp2p_crypto_hashing_sha256(buffer, in->public_key_size + in->rand_size, result); +} + +/*** + * 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 + */ +int libp2p_secio_bytes_compare(const char* a, const char* b, int length) { + for(int i = 0; i < length; i++) { + if (b[i] > a[i]) + return -1; + if (a[i] > b[i]) + return 1; + } + return 0; +} + +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(©[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, ¤t->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); + libp2p_utils_string_list_free(lead_head); + return 1; + } + + // 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) { + match = 1; + break; + } + follower = follower->next; + } + if (match) + break; + follower = follower_head; + lead = lead->next; + } + if (!match) + return 0; + return 1; +} + +/** + * Check to see if the signature is correct based on the given bytes in "in" + * @param public_key the public key to use + * @param in the bytes that were signed + * @param in_length the number of bytes + * @param signature the signature that was given to us + * @param signature_length the length of the signature + * @returns true(1) if the signature is correct, false(0) otherwise + */ +int libp2p_secio_verify_signature(struct PublicKey* public_key, const unsigned char* in, size_t in_length, unsigned char* signature) { + if (public_key->type == KEYTYPE_RSA) { + struct RsaPublicKey rsa_key; + 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; +} + +int libp2p_secio_sign(struct PrivateKey* private_key, unsigned char* in, size_t in_length, unsigned char** signature, size_t* signature_size) { + if (private_key->type == KEYTYPE_RSA) { + struct RsaPrivateKey rsa_key; + rsa_key.der = (char*)private_key->data; + rsa_key.der_length = private_key->data_size; + // SHA2-256 signatures are 32 bytes + *signature = (unsigned char*)malloc(32); + return libp2p_crypto_rsa_sign(&rsa_key, in, in_length, *signature); + } + // TODO: Implement this method for non-RSA + return 0; +} + +/** + * This will generate the ephimeral key and the shared key and place them in the session struct + * @param in the incoming Exchange struct + * @param session where to put the generated keys + * @returns true(1) on success, otherwise 0 + */ +int libp2p_secio_generate_public_and_shared_key(struct Exchange* in, struct SecureSession* session) { + // TODO: Implement this method + // pick the right curve method + if (strcmp(session->chosen_curve, "P-256") == 0) { + + } else if (strcmp(session->chosen_curve, "P-384") == 0) { + + } else if (strcmp(session->chosen_curve, "P-521") == 0) { + + } + // generate priv, x, and y + + // marshal x and y into a public key + return 0; +} + +int libp2p_secio_stretch_keys(struct SecureSession* local_session, struct SecureSession* remote_session, int order_preference) { + // TODO: Implement this method + return 0; +} + +int libp2p_secio_make_mac_and_cipher(struct SecureSession* session) { + // TODO: Implement this method + return 0; +} + +int libp2p_secio_write(struct SecureSession* session, unsigned char* bytes, size_t length) { + // TODO: Implement this method + return 0; +} + +int libp2p_secio_read(struct SecureSession* session, unsigned char** bytes, size_t* bytes_read) { + // TODO: Implement this method + return 0; +} + /*** * performs initial communication over an insecure channel to share * keys, IDs, and initiate connection. This is a framed messaging system * NOTE: session must contain a valid socket_descriptor that is a multistream. * @param session the secure session to be filled - * @param private_key the private key to use + * @param private_key our private key to use * @returns true(1) on success, false(0) otherwise */ -int libp2p_secio_handshake(struct SecureSession* session, struct RsaPrivateKey* private_key) { - int retVal = 0, bytes_written = 0; - size_t protobuf_size = 0, results_size = 0; - unsigned char* protobuf = 0; +int libp2p_secio_handshake(struct SecureSession* local_session, struct RsaPrivateKey* private_key) { + int retVal = 0; + size_t results_size = 0, bytes_written = 0; + unsigned char* propose_in_bytes = NULL; // the remote protobuf + size_t propose_in_size = 0; + unsigned char* propose_out_bytes = NULL; // the local protobuf + size_t propose_out_size = 0; unsigned char* results = NULL; struct Propose* propose_out = NULL; struct Propose* propose_in = NULL; struct PublicKey* public_key = NULL; + unsigned char order_hash_in[32]; + unsigned char order_hash_out[32]; + int order; + struct Exchange* exchange_out; + unsigned char* exchange_out_protobuf; + size_t exchange_out_protobuf_size; + struct Exchange* exchange_in; + struct Libp2pVector* char_buffer; const unsigned char* protocol = (unsigned char*)"/secio/1.0.0\n"; - bytes_written = libp2p_net_multistream_send(session->socket_descriptor, protocol, strlen((char*)protocol)); + bytes_written = libp2p_net_multistream_send(local_session->socket_descriptor, protocol, strlen((char*)protocol)); if (bytes_written <= 0) goto exit; // we should get back the protocol to signify it was accepted, as well as the protobuf of the Propose struct - bytes_written = libp2p_net_multistream_receive(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) goto exit; // skip to the protobuf section - protobuf = (unsigned char*)strchr((char*)results, '\n'); - if (protobuf == NULL) + propose_in_bytes = (unsigned char*)strchr((char*)results, '\n'); + if (propose_in_bytes == NULL) goto exit; - protobuf++; - protobuf_size = results_size - (protobuf - results); + propose_in_bytes++; + propose_in_size = results_size - (propose_in_bytes - results); - if (!libp2p_secio_propose_protobuf_decode(protobuf, protobuf_size, &propose_in)) + if (!libp2p_secio_propose_protobuf_decode(propose_in_bytes, propose_in_size, &propose_in)) goto exit; // clear results @@ -96,18 +304,17 @@ int libp2p_secio_handshake(struct SecureSession* session, struct RsaPrivateKey* 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 + // generate 16 byte nonce - char nonceOut[16]; - if (!libp2p_secio_generate_nonce(&nonceOut[0], 16)) { + if (!libp2p_secio_generate_nonce(&local_session->nonce[0], 16)) { goto exit; } propose_out = libp2p_secio_propose_new(); - libp2p_secio_propose_set_property((void**)&propose_out->rand, &propose_out->rand_size, nonceOut, 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. - - // will need: // public key propose_out->public_key_size = public_key->data_size; propose_out->public_key = (unsigned char*)malloc(public_key->data_size); @@ -117,23 +324,89 @@ int libp2p_secio_handshake(struct SecureSession* session, struct RsaPrivateKey* // supported ciphers libp2p_secio_propose_set_property((void**)&propose_out->ciphers, &propose_out->ciphers_size, SupportedCiphers, strlen(SupportedCiphers)); // supported hashes - libp2p_secio_propose_set_property((void**)&propose_out->hashes, &propose_out->exchanges_size, SupportedHashes, strlen(SupportedHashes)); + libp2p_secio_propose_set_property((void**)&propose_out->hashes, &propose_out->hashes_size, SupportedHashes, strlen(SupportedHashes)); // negotiate encryption parameters NOTE: SelectBest must match, otherwise this won't work + // first determine order + libp2p_secio_hash(propose_in, order_hash_in); + libp2p_secio_hash(propose_out, order_hash_out); + order = libp2p_secio_bytes_compare((char*)order_hash_in, (char*)order_hash_out, 32); // curve + libp2p_secio_select_best(order, propose_out->exchanges, propose_out->exchanges_size, propose_in->exchanges, propose_in->exchanges_size, &local_session->chosen_curve); // cipher + libp2p_secio_select_best(order, propose_out->ciphers, propose_out->ciphers_size, propose_in->ciphers, propose_in->ciphers_size, &local_session->chosen_cipher); // 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 + struct SecureSession remote_session; + remote_session.chosen_cipher = local_session->chosen_cipher; + remote_session.chosen_curve = local_session->chosen_curve; + remote_session.chosen_hash = local_session->chosen_hash; - // send + // generate EphemeralPubKey + struct EphemeralPrivateKey e_private_key; + libp2p_crypto_ephemeral_key_generate(local_session->chosen_curve, &e_private_key); + // build buffer to sign + char_buffer = libp2p_utils_vector_new(); + if (char_buffer == NULL) + goto exit; + libp2p_utils_vector_add(char_buffer, propose_in_bytes, propose_in_size); + libp2p_utils_vector_add(char_buffer, propose_out_bytes, propose_out_size); + libp2p_utils_vector_add(char_buffer, local_session->ephemeral_public_key, local_session->ephemeral_public_key_size); + // send Exchange packet + exchange_out = libp2p_secio_exchange_new(); + exchange_out->epubkey = (unsigned char*)malloc(local_session->ephemeral_public_key_size); + memcpy(exchange_out->epubkey, local_session->ephemeral_public_key, local_session->ephemeral_public_key_size); + exchange_out->epubkey_size = local_session->ephemeral_public_key_size; + struct PrivateKey priv; + priv.data = (unsigned char*)private_key->der; + priv.data_size = private_key->der_length; + libp2p_secio_sign(&priv, char_buffer->buffer, char_buffer->buffer_size, &exchange_out->signature, &exchange_out->signature_size); + libp2p_utils_vector_free(char_buffer); - // receive + 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); + bytes_written = libp2p_net_multistream_send(local_session->socket_descriptor, exchange_out_protobuf, exchange_out_protobuf_size); + free(exchange_out_protobuf); + + // receive Exchange packet + bytes_written = libp2p_net_multistream_receive(local_session->socket_descriptor, (char**)&results, &results_size); + libp2p_secio_exchange_protobuf_decode(results, results_size, &exchange_in); // parse and verify + remote_session.ephemeral_public_key = exchange_in->epubkey; + remote_session.ephemeral_public_key_size = exchange_in->epubkey_size; - // generate keys for mac and encryption + char_buffer = libp2p_utils_vector_new(); + if (char_buffer == NULL) + goto exit; + libp2p_utils_vector_add(char_buffer, propose_in_bytes, propose_in_size); + libp2p_utils_vector_add(char_buffer, propose_out_bytes, propose_out_size); + libp2p_utils_vector_add(char_buffer, remote_session.ephemeral_public_key, remote_session.ephemeral_public_key_size); + if (!libp2p_secio_verify_signature(public_key, char_buffer->buffer, char_buffer->buffer_size, exchange_in->signature)) + goto exit; + libp2p_utils_vector_free(char_buffer); + + // 2.2 generate shared key NOTE: this was done above + + // generate 2 sets of keys (stretching) + libp2p_secio_stretch_keys(local_session, &remote_session, order); + + // prepare MAC + cipher + + libp2p_secio_make_mac_and_cipher(local_session); + libp2p_secio_make_mac_and_cipher(&remote_session); // send expected message (local nonce) to verify encryption works + libp2p_secio_write(local_session, (unsigned char*)local_session->nonce, 16); + libp2p_secio_read(local_session, &results, &results_size); + if (results_size != 16) + goto exit; + if (libp2p_secio_bytes_compare((char*)results, local_session->nonce, 16) != 0) + goto exit; retVal = 1; @@ -141,8 +414,6 @@ int libp2p_secio_handshake(struct SecureSession* session, struct RsaPrivateKey* libp2p_secio_propose_free(propose_out); libp2p_secio_propose_free(propose_in); - if (protobuf != NULL) - free(protobuf); return retVal; diff --git a/test/crypto/test_ephemeral.h b/test/crypto/test_ephemeral.h new file mode 100644 index 0000000..0b7769c --- /dev/null +++ b/test/crypto/test_ephemeral.h @@ -0,0 +1,13 @@ +#include + +#include "libp2p/crypto/ephemeral.h" +/** + * Try to generate an ephemeral private key + */ +int test_ephemeral_key_generate() { + struct EphemeralPrivateKey private_key; + int retVal = libp2p_crypto_ephemeral_key_generate("P-256", &private_key); + if (retVal && private_key->secret_key > 0 && private_key->public_key->x > 0 && private_key->public_key->y > 0) + return 1; + return 0; +} diff --git a/test/crypto/test_rsa.h b/test/crypto/test_rsa.h index 3854c88..3ba0f1c 100644 --- a/test/crypto/test_rsa.h +++ b/test/crypto/test_rsa.h @@ -1,5 +1,4 @@ -#ifndef test_rsa_h -#define test_rsa_h +#pragma once #include #include @@ -25,11 +24,6 @@ int test_crypto_rsa_private_key_der() { if (private_key.der == NULL) return 0; - // print out public key - //for (int i = 0; i < private_key.public_key_length; i++) { - // printf("%02x", private_key.public_key_der[i]); - //} - //printf("\n"); return 1; } @@ -178,5 +172,35 @@ int test_crypto_rsa_public_key_to_peer_id() { return 1; } +int test_crypto_rsa_signing() { + // generate a public and private key pair + struct RsaPrivateKey private_key; + libp2p_crypto_rsa_generate_keypair(&private_key, 2048); -#endif /* test_rsa_h */ + struct RsaPublicKey public_key; + public_key.der = private_key.public_key_der; + public_key.der_length = private_key.public_key_length; + + // generate some bytes to test with + size_t num_bytes = 1000; + unsigned char bytes[num_bytes]; + int val = 0; + for (size_t i = 0; i < num_bytes; i++) { + if (val > 255) + val = 0; + bytes[i] = val; + val++; + } + + char result[256]; + + // sign the buffer + if (libp2p_crypto_rsa_sign(&private_key, bytes, num_bytes, &result[0]) == 0) + return 0; + + // verify the signature + if (libp2p_crypto_rsa_verify(&public_key, bytes, num_bytes, &result[0]) == 0) + return 0; + + return 1; +} diff --git a/test/testit.c b/test/testit.c index 9dcb77e..2530019 100644 --- a/test/testit.c +++ b/test/testit.c @@ -4,6 +4,7 @@ #include "crypto/test_base58.h" #include "crypto/test_base32.h" #include "crypto/test_key.h" +#include "crypto/test_ephemeral.h" #include "test_secio.h" #include "test_mbedtls.h" #include "test_multistream.h" @@ -13,6 +14,7 @@ const char* names[] = { "test_mbedtls_varint_128_binary", "test_mbedtls_varint_128_string", "test_crypto_rsa_private_key_der", + "test_crypto_rsa_signing", "test_crypto_rsa_public_key_to_peer_id", "test_crypto_x509_der_to_private2", "test_crypto_x509_der_to_private", @@ -31,7 +33,8 @@ const char* names[] = { "test_protobuf_private_key", "test_secio_handshake", "test_multistream_connect", - "test_multistream_get_list" + "test_multistream_get_list", + "test_ephemeral_key_generate" }; int (*funcs[])(void) = { @@ -39,6 +42,7 @@ int (*funcs[])(void) = { test_mbedtls_varint_128_binary, test_mbedtls_varint_128_string, test_crypto_rsa_private_key_der, + test_crypto_rsa_signing, test_crypto_rsa_public_key_to_peer_id, test_crypto_x509_der_to_private2, test_crypto_x509_der_to_private, @@ -57,7 +61,8 @@ int (*funcs[])(void) = { test_protobuf_private_key, test_secio_handshake, test_multistream_connect, - test_multistream_get_list + test_multistream_get_list, + test_ephemeral_key_generate, }; int testit(const char* name, int (*func)(void)) { diff --git a/thirdparty/Makefile b/thirdparty/Makefile index 3b9398e..6c7351a 100644 --- a/thirdparty/Makefile +++ b/thirdparty/Makefile @@ -2,4 +2,5 @@ all: cd mbedtls; make all; clean: - cd mbedtls; make clean; \ No newline at end of file + # this is a waste of time. Uncomment if you make changes to mbedtls + #cd mbedtls; make clean; \ No newline at end of file diff --git a/utils/Makefile b/utils/Makefile new file mode 100644 index 0000000..bf6ccf9 --- /dev/null +++ b/utils/Makefile @@ -0,0 +1,18 @@ +CC = gcc +CFLAGS = -O0 -Wall -I../include + +ifdef DEBUG +CFLAGS += -g3 +endif + +LFLAGS = +DEPS = +OBJS = string_list.o vector.o + +%.o: %.c $(DEPS) + $(CC) -c -o $@ $< $(CFLAGS) + +all: $(OBJS) + +clean: + rm -f *.o diff --git a/utils/string_list.c b/utils/string_list.c new file mode 100644 index 0000000..d1d5f72 --- /dev/null +++ b/utils/string_list.c @@ -0,0 +1,26 @@ +#include + +#include "libp2p/utils/string_list.h" + +struct StringList* libp2p_utils_string_list_new() { + struct StringList* list = (struct StringList*)malloc(sizeof(struct StringList)); + if (list != NULL) + { + list->next = NULL; + list->string = NULL; + } + return list; +} + +void libp2p_utils_string_list_free(struct StringList* list) { + struct StringList* current = list; + struct StringList* temp = NULL; + + while(current != NULL) { + if (current->string != NULL) + free(current->string); + temp = current->next; + free(current); + current = temp; + } +} diff --git a/utils/vector.c b/utils/vector.c new file mode 100644 index 0000000..3de22ad --- /dev/null +++ b/utils/vector.c @@ -0,0 +1,44 @@ +#include +#include + +#include "libp2p/utils/vector.h" + +/** + * Allocate memory for a new Libp2pVector + * @returns a new Libp2pVector or NULL if it couldn't do it + */ +struct Libp2pVector* libp2p_utils_vector_new() { + struct Libp2pVector* out = (struct Libp2pVector*)malloc(sizeof(struct Libp2pVector)); + if (out != NULL) { + out->buffer = NULL; + out->buffer_size = 0; + } + return out; +} + +void libp2p_utils_vector_free(struct Libp2pVector* vector) { + if (vector != NULL) { + if (vector->buffer != NULL) + free(vector->buffer); + vector->buffer_size = 0; + free(vector); + vector = NULL; + } +} + +/** + * Add bytes to vector + */ +int libp2p_utils_vector_add(struct Libp2pVector* vector, unsigned char* in_bytes, size_t in_size) { + if (in_size > 0) { + if (vector->buffer == NULL) { + vector->buffer = (unsigned char*)malloc(in_size); + memcpy(vector->buffer, in_bytes, in_size); + } else { + vector->buffer = (unsigned char*)realloc(vector->buffer, in_size + vector->buffer_size); + memcpy(&vector->buffer[vector->buffer_size], in_bytes, in_size); + vector->buffer_size += in_size; + } + } + return 1; +}