Another night's work - move to ECDH + AES256 from RSA pubkey
This commit is contained in:
246
pass-1/rlocs.c
246
pass-1/rlocs.c
@@ -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 = ®->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 = ®->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 = ®->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.
|
||||
|
Reference in New Issue
Block a user