Another night's work - move to ECDH + AES256 from RSA pubkey

This commit is contained in:
Nick Thomas
2013-08-08 00:48:02 +01:00
parent c77557b6ee
commit 118b7b8125
10 changed files with 364 additions and 148 deletions

View File

@@ -17,30 +17,41 @@
#include <openssl/rand.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/ec.h>
#include <openssl/obj_mac.h>
#include <json/json_tokener.h>
#include <json/linkhash.h>
#include <arpa/inet.h>
#include "util.h"
//#include "util.h"
#include "rlocs.h"
#define JSON_TYPE "rloc-registry"
void show_ssl_errors(void)
{
ERR_print_errors_fp(stderr);
return;
}
void rlocs_init(void)
{
uint32_t seed;
SSL_load_error_strings(); /* readable error messages */
SSL_library_init(); /* initialize library */
OpenSSL_add_all_algorithms();
// set the random seed to something good
if ( RAND_bytes( (unsigned char*)&seed, sizeof( uint32_t ) ) != 1 ) {
warn( "Failed to initialize random seed" );
show_ssl_errors();
exit(1);
}
srand( seed );
return;
}
@@ -50,7 +61,8 @@ int rlocs_rloc_from_json(struct rlocs* reg, const char* key, json_object* val )
ssize_t pem_data_size;
const char* pem_data;
struct rloc *rloc;
RSA *rsa_key, *result;
EVP_PKEY *evp_key = NULL;
EC_KEY *ec_key = NULL;
BIO *bp;
if ( !json_object_is_type( val, json_type_string ) ) {
@@ -80,20 +92,47 @@ int rlocs_rloc_from_json(struct rlocs* reg, const char* key, json_object* val )
pem_data = json_object_get_string( val );
pem_data_size = json_object_get_string_len( val );
rsa_key = RSA_new();
bp = BIO_new_mem_buf( (void*)pem_data, pem_data_size );
result = PEM_read_bio_RSA_PUBKEY( bp, &rsa_key, NULL, NULL );
BIO_vfree( bp );
if ( result == NULL ) {
RSA_free( rsa_key );
warn( "Couldn't parse key for %s as an RSA public key", key );
return 0;
if ( ( evp_key = EVP_PKEY_new() ) == NULL ) {
warn( "couldn't allocate new EVP_pkey for rloc %s", key );
goto fail;
}
rloc->key = rsa_key;
if ( !( bp = BIO_new_mem_buf( (void*)pem_data, pem_data_size ) ) ) {
warn( "couldn't allocate new BIO to interpret key for rloc %s", key );
goto fail;
}
ec_key = PEM_read_bio_EC_PUBKEY( bp, &ec_key, NULL, NULL );
BIO_vfree( bp );
if ( ec_key == NULL ) {
warn( "Couldn't parse key for rloc %s as an EC secp160r2 public key", key );
goto fail;
}
// assign so we don't have to free the EC_KEY object any more
if ( !EVP_PKEY_assign_EC_KEY( evp_key, ec_key ) ) {
warn( "couldn't assign key for rloc %s to container", key );
goto fail;
}
rloc->key = evp_key;
rloc->context_id = reg->num_entries;
reg->num_entries++;
return 1;
fail:
show_ssl_errors();
if ( evp_key != NULL ) {
EVP_PKEY_free( evp_key );
}
if ( ec_key != NULL ) {
EC_KEY_free( ec_key );
}
return 0;
}
int rlocs_eid_from_json( struct rlocs *reg, json_object *map)
@@ -410,53 +449,182 @@ struct rloc *rloc_find_by_address( struct rlocs *reg, struct in_addr *ipv4, stru
*/
int rloc_add_private_key( struct rloc *rloc, char *filename )
{
BIO *key_data = BIO_new_file( filename, "r" );
RSA* key = PEM_read_bio_RSAPrivateKey( key_data, NULL, NULL, NULL );
BIO *key_data = BIO_new_file( filename, "r" );
EC_KEY* key = PEM_read_bio_ECPrivateKey( key_data, NULL, NULL, NULL );
if ( key == NULL ) {
warn( "Failed to add private key %s", filename );
return 0;
goto fail;
}
RSA_free( rloc->key );
rloc->key = key;
if ( !EVP_PKEY_assign_EC_KEY( rloc->key, key ) ) {
warn( "Failed to assign private key in %s to rloc", filename );
goto fail;
}
return 1;
fail:
show_ssl_errors();
if ( key != NULL ) {
EC_KEY_free( key );
}
return 0;
}
void show_ssl_errors(void)
int rlocs_update_key_context(struct rlocs *reg, struct rloc *x, struct rloc *y)
{
long err;
char msg[256];
struct key_context *entry = &reg->key_contexts[x->context_id][y->context_id];
unsigned char secret[1024]; // Should be enough buffer space
size_t secret_len = 1024;
while ( ( err = ERR_get_error() ) ) {
ERR_error_string( err, &msg[0] );
warn( "Error %lu in crypto: %s", err, &msg[0] );
unsigned char secret_hash[SHA256_DIGEST_LENGTH];
EVP_PKEY_CTX *ctx = NULL;
int result;
/* Create the context for the shared secret derivation */
if( ( ctx = EVP_PKEY_CTX_new( x->key, NULL ) ) == NULL) {
warn( "EVP_PKEY_CTX_new failed" );
goto fail;
}
/* Initialise */
if( ( result = EVP_PKEY_derive_init( ctx ) ) != 1 ) {
warn( "EVP_PKEY_derive_init failed: %i", result );
goto fail;
}
/* Provide the peer public key */
if( ( result = EVP_PKEY_derive_set_peer( ctx, y->key ) ) != 1 ) {
warn( "EV_PKEY_derive_set_peer failed: %i", result );
goto fail;
}
/* Derive the shared secret */
if( ( result = EVP_PKEY_derive( ctx, &secret[0], &secret_len ) ) != 1 ) {
warn( "EVP_PKEY_derive failed: %i", result );
goto fail;
}
/* SHA256 hash of the shared secret. TODO: Add a salt if we want one... */
if ( !sha256sum( &secret[0], secret_len, secret_hash ) ) {
warn( "SHA256( secret ) failed!" );
goto fail;
}
memcpy( &entry->secret[0], &secret_hash[0], SHA256_DIGEST_LENGTH );
EVP_PKEY_CTX_free( ctx );
/* Now we finally have our secret key, we can set up an AES256
* symmetric key cipher with it, which is what will be used to encrypt /
* decrypt packet data. */
EVP_CIPHER_CTX_init( &entry->ctx );
entry->in_use = 1;
return 1;
fail:
show_ssl_errors();
if ( ctx != NULL) {
EVP_PKEY_CTX_free( ctx );
}
return 0;
}
ssize_t rloc_encrypt( struct rloc *rloc, unsigned char *data, size_t data_len, unsigned char *dest, size_t dest_len )
{
ssize_t result = RSA_public_encrypt( data_len, data, dest, rloc->key, RSA_PKCS1_OAEP_PADDING );
if ( result < 0 ) {
show_ssl_errors();
ssize_t rlocs_encrypt( struct rlocs *reg, struct rloc *x, struct rloc *y, unsigned char *data, size_t data_len, unsigned char *dest, size_t dest_len )
{
struct key_context *entry = &reg->key_contexts[x->context_id][y->context_id];
if ( !entry->in_use && !rlocs_update_key_context( reg, x, y ) ) {
warn( "Couldn't build an encryption context for these rlocs" );
return -1;
}
return result;
// FIXME: I don't know that this is good. It could be terrible.
// We use a PRNG to generate an IV per message, and put it at the start of
// the encrypted blob. Since we're using aes256 (hardcoded), IV is 128 bits
if ( dest_len < 48 ) {
warn( "dest_len must be at least 48 bytes, maybe more");
goto fail;
}
unsigned char *iv = dest;
unsigned char *secret = (unsigned char *)&entry->secret[0];
size_t written = 16;
int outl = dest_len - written;
RAND_pseudo_bytes( iv, 16 );
if ( !EVP_EncryptInit_ex( &entry->ctx, EVP_aes_256_cbc(), NULL, secret, iv ) ) {
warn( "EVP_EncryptInit_ex() failed" );
goto fail;
}
if ( !EVP_EncryptUpdate( &entry->ctx, dest + written, &outl, data, data_len ) ) {
warn( "EVP_EncryptUpdate() failed" );
goto fail;
}
written += outl;
outl = dest_len - written;
if ( !EVP_EncryptFinal_ex( &entry->ctx, dest + written, &outl ) ) {
warn( "EVP_EncryptFinal_ex() failed" );
goto fail;
}
return written + outl;
fail:
show_ssl_errors();
return -1;
}
ssize_t rloc_decrypt( struct rloc *rloc, unsigned char *data, size_t data_len, unsigned char *dest, size_t dest_len )
ssize_t rlocs_decrypt( struct rlocs *reg, struct rloc *x, struct rloc *y, unsigned char *data, size_t data_len, unsigned char *dest, size_t dest_len )
{
ssize_t result = RSA_private_decrypt( data_len, data, dest, rloc->key, RSA_PKCS1_OAEP_PADDING );
if ( result < 0 ) {
show_ssl_errors();
struct key_context *entry = &reg->key_contexts[x->context_id][y->context_id];
if ( !entry->in_use && !rlocs_update_key_context( reg, x, y ) ) {
warn( "Couldn't build a decryption context for these rlocs" );
return -1;
}
return result;
unsigned char *iv = data;
unsigned char *secret = (unsigned char *)&entry->secret[0];
size_t written = 0;
int outl = dest_len;
if ( !EVP_DecryptInit_ex( &entry->ctx, EVP_aes_256_cbc(), NULL, secret, iv ) ) {
warn( "EVP_DecrypttInit_ex() failed" );
goto fail;
}
if ( !EVP_DecryptUpdate( &entry->ctx, dest, &outl, data, data_len ) ) {
warn( "EVP_DecryptUpdate() failed" );
goto fail;
}
written += outl;
outl = dest_len - written;
if ( !EVP_DecryptFinal_ex( &entry->ctx, dest + written, &outl ) ) {
warn( "EVP_DecryptFinal_ex() failed" );
goto fail;
}
return written + outl;
fail:
show_ssl_errors();
return -1;
}
@@ -497,7 +665,7 @@ void rlocs_free( struct rlocs* registry )
for( i = 0; i < registry->num_entries; i++ ) {
if ( registry->entries[i].key != NULL ) {
RSA_free( registry->entries[i].key );
EVP_PKEY_free( registry->entries[i].key );
}
}
// No need to do json_object_put() here.