Fixed signature verification, continuing with swarm connectivity

This commit is contained in:
John Jones 2017-11-02 11:11:50 -05:00
parent 03a4b412dd
commit 746682ebef
6 changed files with 202 additions and 53 deletions

View file

@ -32,6 +32,25 @@ struct PrivateKey* libp2p_crypto_rsa_to_private_key(struct RsaPrivateKey* in) {
return out;
}
/***
* Convert a PrivateKey struct to an RsaPrivateKey struct
* @param in the PrivateKey (NOTE: Must be of type KEYTYPE_RSA
* @returns the RsaPrivateKey or NULL on error
*/
struct RsaPrivateKey* libp2p_crypto_private_key_to_rsa(struct PrivateKey* in) {
struct RsaPrivateKey* out = NULL;
if (in->type == KEYTYPE_RSA) {
out = libp2p_crypto_rsa_rsa_private_key_new();
out->der_length = in->data_size;
out->der = in->data;
if (!libp2p_crypto_rsa_private_key_fill_public_key(out)) {
libp2p_crypto_rsa_rsa_private_key_free(out);
out = NULL;
}
}
return out;
}
/**
* Take an rsa context and turn it into a der formatted byte stream.
* NOTE: the stream starts from the right. So there could be a lot of padding in front.

View file

@ -34,6 +34,13 @@ struct RsaPrivateKey {
*/
struct PrivateKey* libp2p_crypto_rsa_to_private_key(struct RsaPrivateKey* in);
/***
* Convert a PrivateKey struct to an RsaPrivateKey struct
* @param in the PrivateKey (NOTE: Must be of type KEYTYPE_RSA
* @returns the RsaPrivateKey or NULL on error
*/
struct RsaPrivateKey* libp2p_crypto_private_key_to_rsa(struct PrivateKey* in);
/**
* generate a new private key
* @param private_key the new private key

View file

@ -10,3 +10,49 @@
* @returns a Stream
*/
struct Stream* libp2p_net_connection_new(int fd, char* ip, int port);
/***
* These are put here to allow implementations of struct Stream
* to use them. They should not be called by external code
*/
/**
* Close a network connection
* @param stream_context the ConnectionContext
* @returns true(1) on success, false(0) otherwise
*/
int libp2p_net_connection_close(void* stream_context);
/***
* Check and see if there is anything waiting on this network connection
* @param stream_context the ConnectionContext
* @returns number of bytes waiting, or -1 on error
*/
int libp2p_net_connection_peek(void* stream_context);
/**
* Read from the network
* @param stream_context the ConnectionContext
* @param msg where to put the results
* @returns true(1) on success, false(0) otherwise
*/
int libp2p_net_connection_read(void* stream_context, struct StreamMessage** msg, int timeout_secs);
/**
* Reads a certain amount of bytes directly from the stream
* @param stream_context the context
* @param buffer where to put the results
* @param buffer_size the number of bytes to read
* @param timeout_secs number of seconds before a timeout
* @returns number of bytes read, or -1 on error
*/
int libp2p_net_connection_read_raw(void* stream_context, uint8_t* buffer, int buffer_size, int timeout_secs);
/**
* Writes to a stream
* @param stream the stream context (usually a SessionContext pointer)
* @param buffer what to write
* @returns number of bytes written
*/
int libp2p_net_connection_write(void* stream_context, struct StreamMessage* msg);

View file

@ -220,6 +220,7 @@ int libp2p_net_multistream_write(void* stream_context, struct StreamMessage* inc
memcpy(outgoing.data, varint, varint_size);
memcpy(&outgoing.data[varint_size], incoming->data, incoming->data_size);
// now ship it
libp2p_logger_debug("multistream", "Attempting write %d bytes.\n", (int)outgoing.data_size);
num_bytes = parent_stream->write(parent_stream->stream_context, &outgoing);
// subtract the varint if all went well
if (num_bytes == outgoing.data_size)

View file

@ -16,6 +16,7 @@
#include "libp2p/secio/exchange.h"
#include "libp2p/net/multistream.h"
#include "libp2p/net/p2pnet.h"
#include "libp2p/net/connectionstream.h"
#include "libp2p/os/utils.h"
#include "libp2p/crypto/ephemeral.h"
#include "libp2p/crypto/sha1.h"
@ -69,40 +70,6 @@ int libp2p_secio_shutdown(void* context) {
return 1;
}
/***
* Initiates a secio handshake. Use this method when you want to initiate a secio
* session. This should not be used to respond to incoming secio requests
* @param parent_stream the parent stream
* @param remote_peer the remote peer
* @param peerstore the peerstore
* @param rsa_private_key the local private key
* @returns a Secio Stream
*/
struct Stream* libp2p_secio_stream_new(struct Stream* parent_stream, struct Libp2pPeer* remote_peer, struct Peerstore* peerstore, struct RsaPrivateKey* rsa_private_key) {
struct Stream* new_stream = libp2p_stream_new();
if (new_stream != NULL) {
struct SecioContext* ctx = (struct SecioContext*) malloc(sizeof(struct SecioContext));
if (ctx == NULL) {
libp2p_stream_free(new_stream);
new_stream = NULL;
return NULL;
}
new_stream->stream_context = ctx;
ctx->stream = new_stream;
ctx->session_context = remote_peer->sessionContext;
ctx->peer_store = peerstore;
ctx->private_key = rsa_private_key;
new_stream->parent_stream = parent_stream;
if (!libp2p_secio_send_protocol(ctx)
|| !libp2p_secio_receive_protocol(ctx)
|| !libp2p_secio_handshake(ctx)) {
libp2p_stream_free(new_stream);
new_stream = NULL;
}
}
return new_stream;
}
struct Libp2pProtocolHandler* libp2p_secio_build_protocol_handler(struct RsaPrivateKey* private_key, struct Peerstore* peer_store) {
struct Libp2pProtocolHandler* handler = (struct Libp2pProtocolHandler*) malloc(sizeof(struct Libp2pProtocolHandler));
if (handler != NULL) {
@ -309,10 +276,19 @@ int libp2p_secio_select_best(int order, const char* local_list, int local_list_s
* @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) {
// debugging:
if (libp2p_logger_watching_class("secio")) {
fprintf(stdout, "Verifying signature of %d bytes:", (int)in_length);
for(int i = 0; i < 32; i++) {
fprintf(stdout, " %02x", signature[i]);
}
fprintf(stdout, "\n");
}
if (public_key->type == KEYTYPE_RSA) {
struct RsaPublicKey rsa_key = {0};
rsa_key.der = (char*)public_key->data;
@ -333,12 +309,24 @@ int libp2p_secio_verify_signature(struct PublicKey* public_key, const unsigned c
* @returns true(1) on success, otherwise false(0)
*/
int libp2p_secio_sign(struct PrivateKey* private_key, const char* in, size_t in_length, unsigned char** signature, size_t* signature_size) {
if (private_key->type == KEYTYPE_RSA) {
struct RsaPrivateKey rsa_key = {0};
rsa_key.der = (char*)private_key->data;
rsa_key.der_length = private_key->data_size;
return libp2p_crypto_rsa_sign(&rsa_key, in, in_length, signature, signature_size);
int retVal = libp2p_crypto_rsa_sign(&rsa_key, in, in_length, signature, signature_size);
// debugging
if (retVal && libp2p_logger_watching_class("secio")) {
unsigned char* ptr = *signature;
fprintf(stdout, "Signature generated from %d bytes:", (int)in_length);
for(int i = 0; i < *signature_size; i++) {
fprintf(stdout, " %02x", ptr[i]);
}
fprintf(stdout, "\n");
}
return retVal;
}
// TODO: Implement this method for non-RSA
return 0;
}
@ -722,6 +710,7 @@ int libp2p_secio_unencrypted_read(struct Stream* secio_stream, struct StreamMess
return 0;
}
left = left - read_this_time;
read += read_this_time;
} while (left > 0);
m->data_size = buffer_size;
@ -816,6 +805,7 @@ int libp2p_secio_encrypted_write(void* stream_context, struct StreamMessage* byt
return 0;
}
libp2p_logger_debug("secio", "About to write %d bytes.\n", (int)outgoing.data_size);
int retVal = libp2p_secio_unencrypted_write(parent_stream, &outgoing);
if (!retVal) {
libp2p_logger_error("secio", "secio_unencrypted_write returned false\n");
@ -1016,6 +1006,14 @@ int libp2p_secio_handshake(struct SecioContext* secio_context) {
propose_out = libp2p_secio_propose_build(local_session->local_nonce, private_key,
SupportedExchanges, SupportedCiphers, SupportedHashes);
if (libp2p_logger_watching_class("secio")) {
fprintf(stdout, "Our public key: ");
for(int i = 0; i < propose_out->public_key_size; i++) {
fprintf(stdout, " %02x", propose_out->public_key[i]);
}
fprintf(stdout, "\n");
}
// protobuf the proposal
propose_out_size = libp2p_secio_propose_protobuf_encode_size(propose_out);
propose_out_bytes = (unsigned char*)malloc(propose_out_size);
@ -1038,28 +1036,58 @@ int libp2p_secio_handshake(struct SecioContext* secio_context) {
libp2p_logger_error("secio", "Unable to get the remote's Propose struct.\n");
goto exit;
} else {
//libp2p_logger_debug("secio", "Received their propose struct.\n");
libp2p_logger_debug("secio","Received their propose struct.\n");
}
if (!libp2p_secio_propose_protobuf_decode(incoming->data, incoming->data_size, &propose_in)) {
libp2p_logger_error("secio", "Unable to un-protobuf the remote's Propose struct\n");
goto exit;
}
// we need the propose bytes for later, so saving them off
propose_in_bytes = malloc(incoming->data_size);
memcpy(propose_in_bytes, incoming->data, incoming->data_size);
propose_in_size = incoming->data_size;
libp2p_stream_message_free(incoming);
incoming = NULL;
// get their nonce
if (propose_in->rand_size != 16)
if (!libp2p_secio_propose_protobuf_decode(propose_in_bytes, propose_in_size, &propose_in)) {
libp2p_logger_error("secio", "Unable to un-protobuf the remote's Propose struct\n");
goto exit;
}
// get their nonce
if (propose_in->rand_size != 16) {
libp2p_logger_error("secio", "Their nonce is not 16 bytes!\n");
goto exit;
}
memcpy(local_session->remote_nonce, propose_in->rand, 16);
// get public key and put it in a struct PublicKey
// debugging
if (libp2p_logger_watching_class("secio")) {
fprintf(stdout, "Our nonce:");
for (int i = 0; i < 16; i++) {
fprintf(stdout, " %02x", local_session->local_nonce[i]);
}
fprintf(stdout, "\nTheir nonce:");
for (int i = 0; i < 16; i++) {
fprintf(stdout, " %02x", local_session->remote_nonce[i]);
}
fprintf(stdout, "\n");
}
if (libp2p_logger_watching_class("secio")) {
fprintf(stdout, "Their public key (length %d):", (int)propose_in->public_key_size);
for(int i = 0; i < propose_in->public_key_size; i++) {
fprintf(stdout, " %02x", propose_in->public_key[i]);
}
fprintf(stdout, "\n");
}
if (!libp2p_crypto_public_key_protobuf_decode(propose_in->public_key, propose_in->public_key_size, &public_key))
goto exit;
// generate their peer id
libp2p_crypto_public_key_to_peer_id(public_key, &local_session->remote_peer_id);
libp2p_logger_debug("secio", "Their Peer ID: %s.\n", local_session->remote_peer_id);
// pull the peer from the peerstore if it is there
remote_peer = libp2p_secio_get_peer_or_add(peerstore, local_session);
@ -1082,6 +1110,12 @@ int libp2p_secio_handshake(struct SecioContext* secio_context) {
// build buffer to sign
char_buffer_length = propose_in_size + propose_out_size + local_session->ephemeral_private_key->public_key->bytes_size - 1;
if (libp2p_logger_watching_class("secio")) {
fprintf(stdout, "Building buffer to sign.\n");
fprintf(stdout, "Propose in size : %d\n", (int)propose_in_size);
fprintf(stdout, "Propose out size : %d\n", (int)propose_out_size);
fprintf(stdout, "Epemeral key size: %d\n", (int)local_session->ephemeral_private_key->public_key->bytes_size);
}
char_buffer = malloc(char_buffer_length);
if (char_buffer == NULL)
goto exit;
@ -1108,7 +1142,7 @@ int libp2p_secio_handshake(struct SecioContext* secio_context) {
libp2p_logger_error("secio", "Unable to write exchange_out\n");
goto exit;
} else {
//libp2p_logger_debug("secio", "Sent exchange_out.\n");
libp2p_logger_debug("secio", "Sent exchange_out. Size: %d.\n", bytes_written);
}
free(exchange_out_protobuf);
exchange_out_protobuf = NULL;
@ -1122,7 +1156,7 @@ int libp2p_secio_handshake(struct SecioContext* secio_context) {
libp2p_peer_handle_connection_error(remote_peer);
goto exit;
} else {
libp2p_logger_debug("secio", "Read exchange packet.\n");
libp2p_logger_debug("secio", "Read exchange packet. Size: %d.\n", bytes_written);
}
libp2p_secio_exchange_protobuf_decode(incoming->data, incoming->data_size, &exchange_in);
libp2p_stream_message_free(incoming);
@ -1201,7 +1235,7 @@ int libp2p_secio_handshake(struct SecioContext* secio_context) {
// send their nonce to verify encryption works
outgoing.data = (uint8_t*)local_session->remote_nonce;
outgoing.data_size = 16;
if (libp2p_secio_encrypted_write(local_session, &outgoing) <= 0) {
if (libp2p_secio_encrypted_write(secio_context, &outgoing) <= 0) {
libp2p_logger_error("secio", "Encrytped write returned 0 or less.\n");
goto exit;
}
@ -1209,7 +1243,7 @@ int libp2p_secio_handshake(struct SecioContext* secio_context) {
// receive our nonce to verify encryption works
libp2p_logger_log("secio", LOGLEVEL_DEBUG, "Receiving our nonce\n");
results = NULL;
int bytes_read = libp2p_secio_encrypted_read(secio_context->stream->stream_context, &incoming, 10);
int bytes_read = libp2p_secio_encrypted_read(secio_context, &incoming, 10);
if (bytes_read <= 0 || incoming == NULL) {
libp2p_logger_error("secio", "Encrypted read returned %d\n", bytes_read);
goto exit;
@ -1263,3 +1297,43 @@ int libp2p_secio_handshake(struct SecioContext* secio_context) {
}
return retVal;
}
/***
* Initiates a secio handshake. Use this method when you want to initiate a secio
* session. This should not be used to respond to incoming secio requests
* @param parent_stream the parent stream
* @param remote_peer the remote peer
* @param peerstore the peerstore
* @param rsa_private_key the local private key
* @returns a Secio Stream
*/
struct Stream* libp2p_secio_stream_new(struct Stream* parent_stream, struct Libp2pPeer* remote_peer, struct Peerstore* peerstore, struct RsaPrivateKey* rsa_private_key) {
struct Stream* new_stream = libp2p_stream_new();
if (new_stream != NULL) {
struct SecioContext* ctx = (struct SecioContext*) malloc(sizeof(struct SecioContext));
if (ctx == NULL) {
libp2p_stream_free(new_stream);
new_stream = NULL;
return NULL;
}
new_stream->stream_context = ctx;
ctx->stream = new_stream;
ctx->session_context = remote_peer->sessionContext;
ctx->peer_store = peerstore;
ctx->private_key = rsa_private_key;
new_stream->parent_stream = parent_stream;
new_stream->close = libp2p_secio_shutdown;
new_stream->peek = libp2p_net_connection_peek;
new_stream->read = libp2p_secio_encrypted_read;
new_stream->read_raw = libp2p_net_connection_read_raw;
new_stream->write = libp2p_secio_encrypted_write;
if (!libp2p_secio_send_protocol(ctx)
|| !libp2p_secio_receive_protocol(ctx)
|| !libp2p_secio_handshake(ctx)) {
libp2p_stream_free(new_stream);
new_stream = NULL;
}
}
return new_stream;
}

View file

@ -67,6 +67,8 @@ int test_dialer_dial() {
int test_dialer_join_swarm() {
int retVal = 0;
libp2p_logger_add_class("secio");
libp2p_logger_add_class("multistream");
// we need a dialer and a peer
struct Dialer* dialer = NULL;
// this is a base64 encoded private key. It makes it easier to test if it is in base64 form
@ -97,7 +99,7 @@ int test_dialer_join_swarm() {
if (!libp2p_crypto_private_key_protobuf_decode(decode_base64, decode_base64_size, &priv))
goto exit;
//TODO turn PrivateKey into RsaPrivateKey
rsa_private_key = libp2p_crypto_private_key_to_rsa(priv);
// 2) make the local peer
local_peer = libp2p_peer_new();
@ -126,10 +128,10 @@ int test_dialer_join_swarm() {
exit:
if (decode_base64 != NULL)
free(decode_base64);
libp2p_peer_free(local_peer);
libp2p_peerstore_free(peerstore);
libp2p_conn_dialer_free(dialer);
libp2p_crypto_private_key_free(priv);
//libp2p_peer_free(local_peer);
//libp2p_peerstore_free(peerstore);
//libp2p_conn_dialer_free(dialer);
//libp2p_crypto_private_key_free(priv);
return retVal;
}