c-libp2p/crypto/key.c
2017-02-09 03:34:12 -05:00

232 lines
6.8 KiB
C

#include <stdlib.h>
#include <string.h>
#include "libp2p/crypto/key.h"
#include "libp2p/crypto/sha256.h"
#include "libp2p/crypto/peerutils.h"
#include "protobuf.h"
/**
* Utilities for public and private keys
*/
struct PublicKey* libp2p_crypto_public_key_new() {
struct PublicKey* retVal = malloc(sizeof(struct PublicKey));
if (retVal == NULL)
return NULL;
retVal->type = KEYTYPE_INVALID;
retVal->data = NULL;
retVal->data_size = 0;
return retVal;
}
void libp2p_crypto_public_key_free(struct PublicKey* in) {
if (in != NULL) {
if (in->data != NULL)
free(in->data);
free(in);
in = NULL;
}
}
/***
* Calculates an approximate required size of a buffer for protobuf encoding a public key
* @param in the public key to examine
* @returns the size in bytes
*/
size_t libp2p_crypto_public_key_protobuf_encode_size(const struct PublicKey* in) {
return 11 + 11 + in->data_size;
}
/***
* Encode a PublicKey into a protobuf
* @param in the struct PublicKey
* @param buffer where to put the results
* @param max_buffer_length the size of the buffer
* @param bytes_written how many bytes were used in the buffer
* @returns true(1) on success, otherwise false(0)
*/
int libp2p_crypto_public_key_protobuf_encode(const struct PublicKey* in, unsigned char* buffer, size_t max_buffer_length, size_t* bytes_written) {
// data & data_size
size_t bytes_used = 0;
*bytes_written = 0;
int retVal = 0;
// key type (RSA vs ...)
retVal = protobuf_encode_varint(1, WIRETYPE_VARINT, in->type, &buffer[*bytes_written], max_buffer_length - *bytes_written, &bytes_used);
*bytes_written += bytes_used;
// public key
retVal = protobuf_encode_length_delimited(2, WIRETYPE_LENGTH_DELIMITED, in->data, in->data_size, &buffer[*bytes_written], max_buffer_length - *bytes_written, &bytes_used);
if (retVal == 0)
return 0;
*bytes_written += bytes_used;
return 1;
}
/**
* Unmarshal a public key from a protobuf
* @param buffer the protobuf
* @param buffer_length the length of the protobuf
* @param out the pointer to the struct PublicKey that will be allocated
* @returns true(1) on success, otherwise false(0)
*/
int libp2p_crypto_public_key_protobuf_decode(unsigned char* buffer, size_t buffer_length, struct PublicKey** out) {
// first field is type (RSA vs ED25519)
// second field is the public key
size_t pos = 0;
int retVal = 0;
if ( (*out = libp2p_crypto_public_key_new()) == NULL)
goto exit;
while(pos < buffer_length) {
size_t bytes_read = 0;
int field_no;
enum WireType field_type;
if (protobuf_decode_field_and_type(&buffer[pos], buffer_length, &field_no, &field_type, &bytes_read) == 0) {
goto exit;
}
pos += bytes_read;
switch(field_no) {
case (1): // type
if (protobuf_decode_varint(&buffer[pos], buffer_length - pos, (long long unsigned int*)&((*out)->type), &bytes_read) == 0)
goto exit;
pos += bytes_read;
break;
case (2): // key
if (protobuf_decode_length_delimited(&buffer[pos], buffer_length - pos, (char**)&((*out)->data), &((*out)->data_size), &bytes_read) == 0)
goto exit;
pos += bytes_read;
break;
}
}
retVal = 1;
exit:
if (retVal == 0) {
libp2p_crypto_public_key_free(*out);
}
return retVal;
}
struct PrivateKey* libp2p_crypto_private_key_new() {
struct PrivateKey* retVal = malloc(sizeof(struct PrivateKey));
if (retVal == NULL)
return NULL;
retVal->type = KEYTYPE_INVALID;
retVal->data = NULL;
retVal->data_size = 0;
return retVal;
}
void libp2p_crypto_private_key_free(struct PrivateKey* in) {
if (in != NULL) {
if (in->data != NULL)
free(in->data);
free(in);
in = NULL;
}
}
int libp2p_crypto_private_key_protobuf_encode_size(struct PrivateKey* in) {
return 22 + in->data_size;
}
int libp2p_crypto_private_key_protobuf_encode(struct PrivateKey* in, unsigned char* buffer, size_t max_buffer_length, size_t* bytes_written) {
*bytes_written = 0;
size_t bytes_used;
// type (RSA vs ED25519)
if (!protobuf_encode_varint(1, WIRETYPE_VARINT, in->type, &buffer[*bytes_written], max_buffer_length - *bytes_written, &bytes_used))
return 0;
*bytes_written += bytes_used;
// private key
if (!protobuf_encode_length_delimited(2, WIRETYPE_LENGTH_DELIMITED, in->data, in->data_size, &buffer[*bytes_written], max_buffer_length - *bytes_written, &bytes_used))
return 0;
*bytes_written += bytes_used;
return 1;
}
/**
* Unmarshal a private key from a protobuf
* @param buffer the protobuf
* @param buffer_length the length of the protobuf
* @param out the pointer to the struct PrivateKey that will be allocated
* @returns true(1) on success, otherwise false(0)
*/
int libp2p_crypto_private_key_protobuf_decode(unsigned char* buffer, size_t buffer_length, struct PrivateKey** out) {
// first field is type (RSA vs ED25519)
// second field is the public key
size_t pos = 0;
int retVal = 0;
if ( (*out = libp2p_crypto_private_key_new()) == NULL)
goto exit;
while(pos < buffer_length) {
size_t bytes_read = 0;
int field_no;
enum WireType field_type;
if (protobuf_decode_field_and_type(&buffer[pos], buffer_length, &field_no, &field_type, &bytes_read) == 0) {
goto exit;
}
pos += bytes_read;
switch(field_no) {
case (1): // type
if (protobuf_decode_varint(&buffer[pos], buffer_length - pos, (long long unsigned int*)&((*out)->type), &bytes_read) == 0)
goto exit;
pos += bytes_read;
break;
case (2): // key
if (protobuf_decode_length_delimited(&buffer[pos], buffer_length - pos, (char**)&((*out)->data), &((*out)->data_size), &bytes_read) == 0)
goto exit;
pos += bytes_read;
break;
}
}
retVal = 1;
exit:
if (retVal == 0) {
libp2p_crypto_private_key_free(*out);
}
return retVal;
}
/**
* convert a public key into a peer id
* @param public_key the public key struct
* @param peer_id the results, in a null-terminated string
* @returns true(1) on success, otherwise false(0)
*/
int libp2p_crypto_public_key_to_peer_id(struct PublicKey* public_key, char** peer_id) {
/**
* Converting to a peer id involves protobufing the struct PublicKey, SHA256 it, turn it into a MultiHash and base58 it
*/
size_t protobuf_len = libp2p_crypto_public_key_protobuf_encode_size(public_key);
unsigned char protobuf[protobuf_len];
libp2p_crypto_public_key_protobuf_encode(public_key, protobuf, protobuf_len, &protobuf_len);
unsigned char hashed[32];
//libp2p_crypto_hashing_sha256(public_key->data, public_key->data_size, hashed);
libp2p_crypto_hashing_sha256(protobuf, protobuf_len, hashed);
size_t final_id_size = 100;
unsigned char final_id[final_id_size];
memset(final_id, 0, final_id_size);
// turn it into a multihash and base58 it
if (!PrettyID(final_id, &final_id_size, hashed, 32))
return 0;
*peer_id = (char*)malloc(final_id_size + 1);
if (*peer_id == NULL)
return 0;
memset(*peer_id, 0, final_id_size + 1);
memcpy(*peer_id, final_id, final_id_size);
return 1;
}