2017-02-01 17:14:52 +00:00
|
|
|
#include <stdlib.h>
|
2017-02-02 23:27:57 +00:00
|
|
|
#include <stdio.h> // for debugging, can remove
|
2017-02-01 17:14:52 +00:00
|
|
|
#include <string.h>
|
2017-02-02 19:10:12 +00:00
|
|
|
#include <stdint.h>
|
2017-02-01 17:14:52 +00:00
|
|
|
|
2017-02-01 12:52:09 +00:00
|
|
|
#include "libp2p/secio/secio.h"
|
2017-02-01 17:14:52 +00:00
|
|
|
#include "libp2p/secio/propose.h"
|
2017-02-02 20:43:35 +00:00
|
|
|
//#include "libp2p/net/p2pnet.h"
|
|
|
|
#include "libp2p/net/multistream.h"
|
2017-02-01 12:52:09 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2017-02-02 19:10:12 +00:00
|
|
|
/**
|
|
|
|
* Generate a random nonce
|
|
|
|
* @param results where to put the results
|
|
|
|
* @param length the length of the nonce
|
|
|
|
* @returns true(1) on success, otherwise false(0)
|
|
|
|
*/
|
|
|
|
int libp2p_secio_generate_nonce(char* results, int length) {
|
2017-02-03 00:27:50 +00:00
|
|
|
FILE* fd = fopen("/dev/urandom", "r");
|
|
|
|
fread(results, 1, length, fd);
|
|
|
|
fclose(fd);
|
2017-02-02 19:10:12 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2017-02-01 12:52:09 +00:00
|
|
|
/***
|
|
|
|
* performs initial communication over an insecure channel to share
|
|
|
|
* keys, IDs, and initiate connection. This is a framed messaging system
|
2017-02-03 00:09:20 +00:00
|
|
|
* NOTE: session must contain a valid socket_descriptor that is a multistream.
|
2017-02-01 12:52:09 +00:00
|
|
|
* @param session the secure session to be filled
|
2017-02-03 00:09:20 +00:00
|
|
|
* @param private_key the private key to use
|
2017-02-01 12:52:09 +00:00
|
|
|
* @returns true(1) on success, false(0) otherwise
|
|
|
|
*/
|
2017-02-02 19:10:12 +00:00
|
|
|
int libp2p_secio_handshake(struct SecureSession* session, struct RsaPrivateKey* private_key) {
|
2017-02-02 20:43:35 +00:00
|
|
|
int retVal = 0, bytes_written = 0;
|
|
|
|
size_t protobuf_size = 0, results_size = 0;
|
2017-02-02 19:10:12 +00:00
|
|
|
unsigned char* protobuf = 0;
|
2017-02-02 20:43:35 +00:00
|
|
|
unsigned char* results = NULL;
|
2017-02-02 19:10:12 +00:00
|
|
|
struct Propose* propose_out = NULL;
|
|
|
|
struct Propose* propose_in = NULL;
|
|
|
|
struct PublicKey* public_key = NULL;
|
2017-02-01 12:52:09 +00:00
|
|
|
|
2017-02-02 23:27:57 +00:00
|
|
|
const unsigned char* protocol = (unsigned char*)"/secio/1.0.0\n";
|
2017-02-02 19:10:12 +00:00
|
|
|
|
2017-02-02 23:27:57 +00:00
|
|
|
bytes_written = libp2p_net_multistream_send(session->socket_descriptor, protocol, strlen((char*)protocol));
|
2017-02-02 20:43:35 +00:00
|
|
|
if (bytes_written <= 0)
|
2017-02-01 17:14:52 +00:00
|
|
|
goto exit;
|
|
|
|
|
2017-02-02 23:27:57 +00:00
|
|
|
// we should get back the protocol to signify it was accepted, as well as the protobuf of the Propose struct
|
2017-02-02 20:43:35 +00:00
|
|
|
bytes_written = libp2p_net_multistream_receive(session->socket_descriptor, (char**)&results, &results_size);
|
2017-02-02 23:27:57 +00:00
|
|
|
if (bytes_written < 1 || strstr((char*)results, "secio") == NULL)
|
2017-02-01 17:14:52 +00:00
|
|
|
goto exit;
|
2017-02-02 19:10:12 +00:00
|
|
|
|
2017-02-02 23:27:57 +00:00
|
|
|
// skip to the protobuf section
|
|
|
|
protobuf = (unsigned char*)strchr((char*)results, '\n');
|
|
|
|
if (protobuf == NULL)
|
|
|
|
goto exit;
|
|
|
|
protobuf++;
|
|
|
|
protobuf_size = results_size - (protobuf - results);
|
|
|
|
|
|
|
|
if (!libp2p_secio_propose_protobuf_decode(protobuf, protobuf_size, &propose_in))
|
2017-02-01 17:14:52 +00:00
|
|
|
goto exit;
|
2017-02-01 12:52:09 +00:00
|
|
|
|
2017-02-02 23:27:57 +00:00
|
|
|
// clear results
|
|
|
|
free(results);
|
|
|
|
results = NULL;
|
|
|
|
results_size = 0;
|
|
|
|
|
2017-02-03 00:27:50 +00:00
|
|
|
// get public key and put it in a struct PublicKey
|
2017-02-02 19:10:12 +00:00
|
|
|
if (!libp2p_crypto_public_key_protobuf_decode(propose_in->public_key, propose_in->public_key_size, &public_key))
|
|
|
|
goto exit;
|
2017-02-01 12:52:09 +00:00
|
|
|
// generate their peer id
|
2017-02-02 19:10:12 +00:00
|
|
|
char* remote_peer_id;
|
|
|
|
libp2p_crypto_public_key_to_peer_id(public_key, &remote_peer_id);
|
2017-02-01 12:52:09 +00:00
|
|
|
|
2017-02-02 23:27:57 +00:00
|
|
|
// generate 16 byte nonce
|
|
|
|
char nonceOut[16];
|
|
|
|
if (!libp2p_secio_generate_nonce(&nonceOut[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);
|
|
|
|
|
|
|
|
// we have their information, now we need to gather ours.
|
|
|
|
|
|
|
|
// will need:
|
2017-02-03 00:27:50 +00:00
|
|
|
// public key
|
|
|
|
propose_out->public_key_size = public_key->data_size;
|
|
|
|
propose_out->public_key = (unsigned char*)malloc(public_key->data_size);
|
|
|
|
memcpy(propose_out->public_key, public_key->data, public_key->data_size);
|
2017-02-02 23:27:57 +00:00
|
|
|
// supported exchanges
|
|
|
|
libp2p_secio_propose_set_property((void**)&propose_out->exchanges, &propose_out->exchanges_size, SupportedExchanges, strlen(SupportedExchanges));
|
|
|
|
// 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));
|
2017-02-01 12:52:09 +00:00
|
|
|
// 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
|
|
|
|
|
2017-02-01 17:14:52 +00:00
|
|
|
retVal = 1;
|
|
|
|
|
|
|
|
exit:
|
|
|
|
|
2017-02-02 19:10:12 +00:00
|
|
|
libp2p_secio_propose_free(propose_out);
|
|
|
|
libp2p_secio_propose_free(propose_in);
|
2017-02-01 17:14:52 +00:00
|
|
|
if (protobuf != NULL)
|
|
|
|
free(protobuf);
|
|
|
|
|
|
|
|
return retVal;
|
|
|
|
|
2017-02-01 12:52:09 +00:00
|
|
|
}
|