#include #include #include "libp2p/crypto/rsa.h" #include "libp2p/crypto/sha256.h" #include "libp2p/record/record.h" #include "protobuf.h" #include "mh/hashes.h" #include "mh/multihash.h" /** * Create a record with default settings * @returns the newly allocated record struct */ struct Libp2pRecord* libp2p_record_new() { struct Libp2pRecord* out = (struct Libp2pRecord*)malloc(sizeof(struct Libp2pRecord)); if (out != NULL) { out->author = NULL; out->author_size = 0; out->key = NULL; out->key_size = 0; out->signature = NULL; out->signature_size = 0; out->time_received = NULL; out->time_received_size = 0; out->value = NULL; out->value_size = 0; } return out; } /** * Free the resources from a record struct * @param in the struct to free */ void libp2p_record_free(struct Libp2pRecord* in) { if (in != NULL) { if (in->author != NULL) free(in->author); if (in->key != NULL) free(in->key); if (in->signature != NULL) free(in->signature); if (in->time_received != NULL) free(in->time_received); if (in->value != NULL) free(in->value); free(in); } } /** * Protobuf a record, allocating memory * @param in the record to protobuf * @param buffer where to put the results * @param buffer_size the size of the results * @returns true(1) on success, false(0) otherwise */ int libp2p_record_protobuf_allocate_and_encode(const struct Libp2pRecord* in, unsigned char** buffer, size_t* buffer_size) { *buffer_size = libp2p_record_protobuf_encode_size(in); *buffer = malloc(*buffer_size); if (*buffer == NULL) { *buffer_size = 0; return 0; } int retVal = libp2p_record_protobuf_encode(in, *buffer, *buffer_size, buffer_size); if (retVal == 0) { free(*buffer); *buffer = NULL; *buffer_size = 0; } return retVal; } /** * Convert a Libp2pRecord into protobuf format * @param in the Libp2pRecord to convert * @param buffer where to store the protobuf * @param max_buffer_size the size of the allocated buffer * @param bytes_written the size written into buffer * @returns true(1) on success, otherwise false(0) */ int libp2p_record_protobuf_encode(const struct Libp2pRecord* in, unsigned char* buffer, size_t max_buffer_size, size_t* bytes_written) { // data & data_size size_t bytes_used = 0; *bytes_written = 0; int retVal = 0; // field 1 retVal = protobuf_encode_length_delimited(1, WIRETYPE_LENGTH_DELIMITED, in->key, in->key_size, &buffer[*bytes_written], max_buffer_size - *bytes_written, &bytes_used); if (retVal == 0) return 0; *bytes_written += bytes_used; // field 2 retVal = protobuf_encode_length_delimited(2, WIRETYPE_LENGTH_DELIMITED, (char*)in->value, in->value_size, &buffer[*bytes_written], max_buffer_size - *bytes_written, &bytes_used); if (retVal == 0) return 0; *bytes_written += bytes_used; // field 3 retVal = protobuf_encode_length_delimited(3, WIRETYPE_LENGTH_DELIMITED, in->author, in->author_size, &buffer[*bytes_written], max_buffer_size - *bytes_written, &bytes_used); if (retVal == 0) return 0; *bytes_written += bytes_used; // field 4 retVal = protobuf_encode_length_delimited(4, WIRETYPE_LENGTH_DELIMITED, (char*)in->signature, in->signature_size, &buffer[*bytes_written], max_buffer_size - *bytes_written, &bytes_used); if (retVal == 0) return 0; *bytes_written += bytes_used; // field 5 retVal = protobuf_encode_length_delimited(5, WIRETYPE_LENGTH_DELIMITED, in->time_received, in->time_received_size, &buffer[*bytes_written], max_buffer_size - *bytes_written, &bytes_used); if (retVal == 0) return 0; *bytes_written += bytes_used; return 1; } /** * Generates an estimate of the buffer size needed to encode the struct * @param in the Libp2pRecord that you want to encode * @returns the approximate number of bytes required */ size_t libp2p_record_protobuf_encode_size(const struct Libp2pRecord* in) { size_t retVal = 0; if (in != NULL) { retVal = 11 + in->key_size; retVal += 11 + in->value_size; retVal += 11 + in->author_size; retVal += 11 + in->signature_size; retVal += 11 + in->time_received_size; } return retVal; } /** * Convert a protobuf byte array into a Libp2pRecord * @param in the byte array * @param in_size the size of the byte array * @param out a pointer to the new Libp2pRecord * @returns true(1) on success, otherwise false(0) */ int libp2p_record_protobuf_decode(const unsigned char* in, size_t in_size, struct Libp2pRecord** out) { size_t pos = 0; int retVal = 0; if ( (*out = libp2p_record_new()) == NULL) goto exit; while(pos < in_size) { size_t bytes_read = 0; int field_no; enum WireType field_type; if (!protobuf_decode_field_and_type(&in[pos], in_size, &field_no, &field_type, &bytes_read)) { goto exit; } pos += bytes_read; switch(field_no) { case (1): // key if (!protobuf_decode_string(&in[pos], in_size - pos, (char**)&((*out)->key), &bytes_read)) goto exit; (*out)->key_size = strlen((*out)->key); pos += bytes_read; break; case (2): // value if (!protobuf_decode_length_delimited(&in[pos], in_size - pos, (char**)&((*out)->value), &((*out)->value_size), &bytes_read)) goto exit; pos += bytes_read; break; case (3): // author if (!protobuf_decode_length_delimited(&in[pos], in_size - pos, (char**)&((*out)->author), &((*out)->author_size), &bytes_read)) goto exit; pos += bytes_read; break; case (4): // signature if (!protobuf_decode_length_delimited(&in[pos], in_size - pos, (char**)&((*out)->signature), &((*out)->signature_size), &bytes_read)) goto exit; pos += bytes_read; break; case (5): // time if (!protobuf_decode_length_delimited(&in[pos], in_size - pos, (char**)&((*out)->time_received), &((*out)->time_received_size), &bytes_read)) goto exit; pos += bytes_read; break; } } retVal = 1; exit: if (retVal == 0) { free(*out); *out = NULL; } return retVal; } /** * This method does all the hard stuff in one step. It fills a Libp2pRecord struct, and converts it into a protobuf * @param record a pointer to the protobuf results * @param rec_size the number of bytes used in the area pointed to by record * @param sk the private key used to sign * @param key the key in the Libp2pRecord * @param value the value in the Libp2pRecord * @param vlen the length of value * @param sign true(1) if you want to sign the data * @returns 0 on success, otherwise -1 */ int libp2p_record_make_put_record (char** record_buf, size_t *rec_size, const struct RsaPrivateKey* sk, const char* key, const char* value, size_t vlen, int sign) { int retVal = -1; size_t bytes_size = 0; unsigned char* bytes = NULL; unsigned char* sign_buf = NULL; unsigned char hash[32]; // build the struct struct Libp2pRecord record; record.key = (char*)key; record.key_size = strlen(key); record.value = (unsigned char*)value; record.value_size = vlen; // clear the rest of the fields record.signature = NULL; record.signature_size = 0; //TODO: what should we put in the time field? record.time_received = NULL; record.time_received_size = 0; // build a hash of the author's public key libp2p_crypto_hashing_sha256((unsigned char*)sk->public_key_der, sk->public_key_length, &hash[0]); record.author = (char*)&hash[0]; record.author_size = 32; bytes_size = record.key_size + record.value_size + record.author_size; bytes = malloc(bytes_size); if (bytes == NULL) goto exit; // build the signature if (sign) { memcpy(&bytes[0], record.key, record.key_size); memcpy(&bytes[record.key_size], record.value, record.value_size); memcpy(&bytes[record.key_size + record.value_size], record.author, record.author_size); size_t sign_length = 0; if (!libp2p_crypto_rsa_sign ((struct RsaPrivateKey*)sk, (char*)bytes, bytes_size, &sign_buf, &sign_length)) goto exit; record.signature = sign_buf; record.signature_size = sign_length; } // now protobuf the struct size_t protobuf_size = libp2p_record_protobuf_encode_size(&record); *record_buf = malloc(protobuf_size); if (*record_buf == NULL) goto exit; if (!libp2p_record_protobuf_encode(&record, (unsigned char*)*record_buf, protobuf_size, &protobuf_size)) goto exit; *rec_size = protobuf_size; // we're done here. Cleanup time. retVal = 0; exit: if (bytes != NULL) free(bytes); if (sign_buf != NULL) free(sign_buf); if (retVal == -1) { free(*record_buf); *record_buf = NULL; } return retVal; }