diff --git a/crypto/ephemeral.c b/crypto/ephemeral.c index f78bd70..6ffecb9 100644 --- a/crypto/ephemeral.c +++ b/crypto/ephemeral.c @@ -7,6 +7,31 @@ #include "mbedtls/ctr_drbg.h" #include "libp2p/crypto/ephemeral.h" +struct StretchedKey* libp2p_crypto_ephemeral_stretched_key_new() { + struct StretchedKey* key = (struct StretchedKey*)malloc(sizeof(struct StretchedKey)); + if (key != NULL) { + key->cipher_key = NULL; + key->cipher_size = 0; + key->iv = NULL; + key->iv_size = 0; + key->mac_key = NULL; + key->mac_size = 0; + } + return key; +} + +void libp2p_crypto_ephemeral_stretched_key_free(struct StretchedKey* key) { + if (key != NULL) { + if (key->cipher_key != NULL) + free(key->cipher_key); + if (key->iv != NULL) + free(key->iv); + if (key->mac_key != NULL) + free(key->mac_key); + free(key); + } +} + struct EphemeralPrivateKey* libp2p_crypto_ephemeral_key_new(uint64_t priv, uint64_t x, uint64_t y, size_t num_bits) { struct EphemeralPrivateKey* results = (struct EphemeralPrivateKey*)malloc(sizeof(struct EphemeralPrivateKey)); if (results != NULL) { @@ -17,9 +42,13 @@ struct EphemeralPrivateKey* libp2p_crypto_ephemeral_key_new(uint64_t priv, uint6 free(results); results = NULL; } else { + results->public_key->num_bits = num_bits; results->public_key->x = x; results->public_key->y = y; - results->public_key->num_bits = num_bits; + results->public_key->bytes = NULL; + results->public_key->bytes_size = 0; + results->public_key->shared_key = NULL; + results->public_key->shared_key_size = 0; } } return results; @@ -27,8 +56,13 @@ struct EphemeralPrivateKey* libp2p_crypto_ephemeral_key_new(uint64_t priv, uint6 void libp2p_crypto_ephemeral_key_free(struct EphemeralPrivateKey* in) { if (in != NULL) { - if (in->public_key != NULL) + if (in->public_key != NULL) { + if (in->public_key->bytes != NULL) + free(in->public_key->bytes); + if (in->public_key->shared_key != NULL) + free(in->public_key->shared_key); free(in->public_key); + } free(in); } } @@ -103,13 +137,16 @@ int libp2p_crypto_ephemeral_point_unmarshal(int bit_size, unsigned char* buffer, * @param private_key the struct to store the generated key * @returns true(1) on success, otherwise false(0) */ -int libp2p_crypto_ephemeral_keypair_generate(char* curve, struct EphemeralPrivateKey** private_key) { +int libp2p_crypto_ephemeral_keypair_generate(char* curve, struct EphemeralPrivateKey** private_key_ptr) { int retVal = 0; mbedtls_ecdsa_context ctx; mbedtls_entropy_context entropy; mbedtls_ctr_drbg_context ctr_drbg; - + struct EphemeralPrivateKey* private_key = NULL; + struct EphemeralPublicKey* public_key = NULL; int selected_curve = 0; + char* pers = "bitShares"; // data for seeding random number generator + if (strcmp(curve, "P-256") == 0) selected_curve = MBEDTLS_ECP_DP_SECP256R1; else if (strcmp(curve, "P-384") == 0) @@ -117,8 +154,6 @@ int libp2p_crypto_ephemeral_keypair_generate(char* curve, struct EphemeralPrivat else selected_curve = MBEDTLS_ECP_DP_SECP521R1; - char* pers = "bitShares"; - mbedtls_ecdsa_init(&ctx); // seed random number generator @@ -127,14 +162,39 @@ int libp2p_crypto_ephemeral_keypair_generate(char* curve, struct EphemeralPrivat if (mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, (const unsigned char*)pers, strlen(pers)) != 0) goto exit; + // generate key 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, ctx.grp.nbits); + *private_key_ptr = libp2p_crypto_ephemeral_key_new(*ctx.d.p, *ctx.Q.X.p, *ctx.Q.Y.p, ctx.grp.nbits); + + private_key = *private_key_ptr; + public_key = private_key->public_key; + + // Fill in more of the public key + libp2p_crypto_ephemeral_point_marshal(public_key->num_bits, public_key->x, public_key->y, &public_key->bytes, &public_key->bytes_size); + + // build shared key, another part of public_key + //mbedtls_ecp_group grp; + mbedtls_ecp_point point; + //mbedtls_ecp_group_init(&grp); + mbedtls_ecp_point_init(&point); + if (mbedtls_ecp_mul(&ctx.grp, &point, &ctx.d, &ctx.Q, mbedtls_ctr_drbg_random, &ctr_drbg) != 0) + goto exit; + public_key->shared_key_size = 8; + public_key->shared_key = (unsigned char*)malloc(8); + serialize_uint64(*point.X.p, public_key->shared_key); + + // ship all this stuff back to the caller + retVal = 1; exit: + mbedtls_ctr_drbg_free(&ctr_drbg); + mbedtls_entropy_free(&entropy); + mbedtls_ecdsa_free(&ctx); + return retVal; } diff --git a/crypto/sha1.c b/crypto/sha1.c index 5bf90ec..332f854 100644 --- a/crypto/sha1.c +++ b/crypto/sha1.c @@ -264,3 +264,19 @@ void SHA1_Final(SHA1_CTX* context, uint8_t digest[SHA1_DIGEST_SIZE]) SHA1_Transform(context->state, context->buffer); #endif } + +/*** + * Hash the input using SHA1 + * @param input the input to hash + * @param input_length the length of the input + * @param output where the output is placed NOTE: mut be preallocated + * @returns the number of bytes written, or 0 on error + */ +int libp2p_crypto_hashing_sha1(const char* input, size_t input_length, unsigned char* output) { + SHA1_CTX ctx; + SHA1_Init(&ctx); + SHA1_Update(&ctx, input, input_length); + SHA1_Final(&ctx, output); + return 40; +} + diff --git a/crypto/sha256.c b/crypto/sha256.c index 0979444..92d745e 100644 --- a/crypto/sha256.c +++ b/crypto/sha256.c @@ -4,10 +4,10 @@ * hash a string using SHA256 * @param input the input string * @param input_length the length of the input string - * @param output where to place the results + * @param output where to place the results, should be 32 bytes * @returns 1 */ -int libp2p_crypto_hashing_sha256(const unsigned char* input, size_t input_length, unsigned char output[32]) { +int libp2p_crypto_hashing_sha256(const char* input, size_t input_length, unsigned char* output) { mbedtls_sha256(input, input_length, output, 0); - return 1; + return 32; } diff --git a/crypto/sha512.c b/crypto/sha512.c index 21ffc32..4301af5 100644 --- a/crypto/sha512.c +++ b/crypto/sha512.c @@ -7,7 +7,7 @@ * @param output where to place the results * @returns 1 */ -int libp2p_crypto_hashing_sha512(const unsigned char* input, size_t input_length, unsigned char output[128]) { +int libp2p_crypto_hashing_sha512(const char* input, size_t input_length, unsigned char* output) { mbedtls_sha512(input, input_length, output, 0); - return 1; + return 64; } diff --git a/include/libp2p/crypto/ephemeral.h b/include/libp2p/crypto/ephemeral.h index 57ed3c5..19f5b7f 100644 --- a/include/libp2p/crypto/ephemeral.h +++ b/include/libp2p/crypto/ephemeral.h @@ -6,10 +6,23 @@ * General helpers for ephemeral keys */ +struct StretchedKey { + char* iv; + size_t iv_size; + char* cipher_key; + size_t cipher_size; + char* mac_key; + size_t mac_size; +}; + struct EphemeralPublicKey { size_t num_bits; uint64_t x; uint64_t y; + unsigned char* bytes; // a public key in bytes (the combination of X and Y) + size_t bytes_size; + unsigned char* shared_key; + size_t shared_key_size; }; struct EphemeralPrivateKey { @@ -25,3 +38,9 @@ struct EphemeralPrivateKey { * @reutrns true(1) on success, otherwise false(0) */ int libp2p_crypto_ephemeral_keypair_generate(char* curve, struct EphemeralPrivateKey** private_key); + +/** + * Routines to help with the StretchedKey struct + */ +struct StretchedKey* libp2p_crypto_ephemeral_stretched_key_new(); +void libp2p_crypto_ephemeral_stretched_key_free(struct StretchedKey* in); diff --git a/include/libp2p/crypto/sha1.h b/include/libp2p/crypto/sha1.h index 4f2a1c7..5565d4a 100644 --- a/include/libp2p/crypto/sha1.h +++ b/include/libp2p/crypto/sha1.h @@ -16,4 +16,13 @@ void SHA1_Init(SHA1_CTX* context); void SHA1_Update(SHA1_CTX* context, const uint8_t* data, const size_t len); void SHA1_Final(SHA1_CTX* context, uint8_t digest[SHA1_DIGEST_SIZE]); +/*** + * Hash the input using SHA1 + * @param input the input to hash + * @param input_length the length of the input + * @param output where the output is placed, should be 40 bytes in width + * @returns the number of bytes written, or 0 on error + */ +int libp2p_crypto_hashing_sha1(const char* input, size_t input_length, unsigned char* output); + #endif /* _SHA1_H */ diff --git a/include/libp2p/crypto/sha256.h b/include/libp2p/crypto/sha256.h index 0d50af5..8a8b5fb 100644 --- a/include/libp2p/crypto/sha256.h +++ b/include/libp2p/crypto/sha256.h @@ -8,6 +8,6 @@ * @param output where to place the results * @returns 1 */ -int libp2p_crypto_hashing_sha256(const unsigned char* input, size_t input_length, unsigned char output[32]); +int libp2p_crypto_hashing_sha256(const char* input, size_t input_length, unsigned char* output); #endif diff --git a/include/libp2p/crypto/sha512.h b/include/libp2p/crypto/sha512.h index c8d4699..f9a8628 100644 --- a/include/libp2p/crypto/sha512.h +++ b/include/libp2p/crypto/sha512.h @@ -4,8 +4,8 @@ * hash a string using SHA512 * @param input the input string * @param input_length the length of the input string - * @param output where to place the results - * @returns 1 + * @param output where to place the results, should be 64 bytes + * @returns number of bytes written, or 0 */ -int libp2p_crypto_hashing_sha512(const unsigned char* input, size_t input_length, unsigned char output[128]); +int libp2p_crypto_hashing_sha512(const char* input, size_t input_length, unsigned char* output); diff --git a/include/libp2p/secio/secio.h b/include/libp2p/secio/secio.h index 015e159..c36e5d4 100644 --- a/include/libp2p/secio/secio.h +++ b/include/libp2p/secio/secio.h @@ -27,6 +27,7 @@ struct SecureSession { unsigned char* shared_key; // a shared key based off of the ephemeral private key size_t shared_key_size; char nonce[16]; + struct StretchedKey* stretched_key; }; /*** diff --git a/secio/secio.c b/secio/secio.c index b1da8be..7d46aa4 100644 --- a/secio/secio.c +++ b/secio/secio.c @@ -7,8 +7,10 @@ #include "libp2p/secio/propose.h" #include "libp2p/secio/exchange.h" #include "libp2p/net/multistream.h" -#include "libp2p/crypto/sha256.h" #include "libp2p/crypto/ephemeral.h" +#include "libp2p/crypto/sha1.h" +#include "libp2p/crypto/sha256.h" +#include "libp2p/crypto/sha512.h" #include "libp2p/utils/string_list.h" #include "libp2p/utils/vector.h" @@ -58,7 +60,7 @@ int libp2p_secio_generate_nonce(char* results, int length) { */ 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]; + 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); @@ -202,30 +204,156 @@ int libp2p_secio_sign(struct PrivateKey* private_key, unsigned char* in, size_t } /** - * 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 + * 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) */ -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) { +int libp2p_secio_stretch_keys(char* cipherType, char* hashType, unsigned char* secret, size_t secret_size, struct StretchedKey** k1_ptr, struct StretchedKey** k2_ptr) { + int retVal = 0, hash_size = 0, num_filled = 0, num_needed = 0, hmac_size = 20; + struct StretchedKey* k1; + struct StretchedKey* k2; + unsigned char* result = NULL;; + size_t result_size = 0; + int (*hash_func)(const char* input, size_t input_length, unsigned char* results); // pointer to hash function + char* first_seed = "key_expansion"; + char* second_seed = NULL; + char* temp = NULL; + int seed_size = strlen(first_seed); + unsigned char* current_hash = NULL; - } else if (strcmp(session->chosen_curve, "P-384") == 0) { - - } else if (strcmp(session->chosen_curve, "P-521") == 0) { + 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; + // 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 + 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; } - // generate priv, x, and y - // marshal x and y into a public key - return 0; -} + result_size = 2 * (k1->iv_size + k1->cipher_size * hmac_size); + result = malloc(result_size); + if (result == NULL) + goto exit; -int libp2p_secio_stretch_keys(struct SecureSession* local_session, struct SecureSession* remote_session, int order_preference) { - // TODO: Implement this method - return 0; + seed_size += secret_size; + second_seed = malloc(seed_size); + memcpy(second_seed, secret, secret_size); + memcpy(&second_seed[secret_size], first_seed, strlen(first_seed)); + + current_hash = malloc(hash_size); + hash_func(second_seed, seed_size, current_hash); + + num_needed = hash_size; + // now we have our first hash. Begin to fill the result buffer + while (num_filled < result_size) { + num_needed = result_size - num_filled; + if (num_needed > hash_size) + num_needed = hash_size; + // combine current_hash with first_seed + if (temp != NULL) + free(temp); + seed_size = secret_size + strlen(first_seed) + hash_size; + temp = malloc(seed_size); + memcpy(temp, secret, secret_size); + memcpy(&temp[secret_size], current_hash, hash_size); + memcpy(&temp[secret_size + hash_size], first_seed, strlen(first_seed)); + // make a new hash + hash_func(temp, seed_size, current_hash); + // copy the hash to results + memcpy(&result[num_filled], current_hash, num_needed); + num_filled += num_needed; + // redo the hashes by adding the secret to the current hash + free(temp); + seed_size = secret_size + hash_size; + temp = malloc(seed_size); + memcpy(temp, secret, secret_size); + memcpy(&temp[secret_size], current_hash, hash_size); + hash_func(temp, seed_size, current_hash); + } + + // now we have a big result. Cut it up into pieces + if (temp != NULL) + free(temp); + temp = (char*)result; + 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; + + 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; + + 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 (current_hash != NULL) + free(current_hash); + if (temp != NULL) + free(temp); + if (second_seed != NULL) + free(second_seed); + if (result != NULL) + free(result); + return retVal; } int libp2p_secio_make_mac_and_cipher(struct SecureSession* session) { @@ -270,6 +398,7 @@ int libp2p_secio_handshake(struct SecureSession* local_session, struct RsaPrivat size_t exchange_out_protobuf_size; struct Exchange* exchange_in; struct Libp2pVector* char_buffer; + struct StretchedKey* k1 = NULL, *k2 = NULL; const unsigned char* protocol = (unsigned char*)"/secio/1.0.0\n"; @@ -346,6 +475,7 @@ int libp2p_secio_handshake(struct SecureSession* local_session, struct RsaPrivat // generate EphemeralPubKey struct EphemeralPrivateKey* e_private_key; libp2p_crypto_ephemeral_keypair_generate(local_session->chosen_curve, &e_private_key); + // build buffer to sign char_buffer = libp2p_utils_vector_new(); if (char_buffer == NULL) @@ -353,6 +483,7 @@ int libp2p_secio_handshake(struct SecureSession* local_session, struct RsaPrivat 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); @@ -393,8 +524,15 @@ int libp2p_secio_handshake(struct SecureSession* local_session, struct RsaPrivat // 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); + libp2p_secio_stretch_keys(local_session->chosen_cipher, local_session->chosen_hash, local_session->shared_key, local_session->shared_key_size, &k1, &k2); + if (order > 1) { + local_session->stretched_key = k1; + remote_session.stretched_key = k2; + } else { + local_session->stretched_key = k2; + remote_session.stretched_key = k1; + } // prepare MAC + cipher libp2p_secio_make_mac_and_cipher(local_session); @@ -403,6 +541,7 @@ int libp2p_secio_handshake(struct SecureSession* local_session, struct RsaPrivat // 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)