|
|
|
#include "keys.h"
|
|
|
|
|
|
|
|
#include "model/crypto/base64.h"
|
|
|
|
#include "sha256/sha256.h"
|
|
|
|
|
|
|
|
#include <openssl/pem.h>
|
|
|
|
#include <openssl/evp.h>
|
|
|
|
#include <openssl/err.h>
|
|
|
|
|
|
|
|
struct crypto_keys
|
|
|
|
{
|
|
|
|
EVP_PKEY* pubkey;
|
|
|
|
EVP_PKEY* privkey;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct crypto_keys* crypto_keys_new()
|
|
|
|
{
|
|
|
|
struct crypto_keys* k;
|
|
|
|
|
|
|
|
k = malloc(sizeof(*k));
|
|
|
|
memset(k,0,sizeof(*k));
|
|
|
|
|
|
|
|
return k;
|
|
|
|
}
|
|
|
|
|
|
|
|
void crypto_keys_free( struct crypto_keys* keys )
|
|
|
|
{
|
|
|
|
if( !keys ) { return; }
|
|
|
|
|
|
|
|
if( keys->pubkey ) {
|
|
|
|
EVP_PKEY_free(keys->pubkey);
|
|
|
|
}
|
|
|
|
if( keys->privkey ) {
|
|
|
|
EVP_PKEY_free(keys->privkey);
|
|
|
|
}
|
|
|
|
|
|
|
|
free(keys);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool crypto_keys_load_private( struct crypto_keys* keys, const char* filename )
|
|
|
|
{
|
|
|
|
if( keys->privkey ) {
|
|
|
|
EVP_PKEY_free( keys->privkey );
|
|
|
|
keys->privkey = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
FILE* f = fopen(filename,"r");
|
|
|
|
if( !f ) { return false; }
|
|
|
|
|
|
|
|
keys->privkey = PEM_read_PrivateKey( f, NULL, NULL, NULL );
|
|
|
|
fclose(f);
|
|
|
|
|
|
|
|
return !!keys->privkey;
|
|
|
|
}
|
|
|
|
bool crypto_keys_load_public( struct crypto_keys* keys, const char* filename )
|
|
|
|
{
|
|
|
|
if( keys->pubkey ) {
|
|
|
|
EVP_PKEY_free( keys->pubkey );
|
|
|
|
keys->pubkey = NULL;
|
|
|
|
}
|
|
|
|
FILE* f = fopen(filename,"r");
|
|
|
|
if( !f ) { return false; }
|
|
|
|
|
|
|
|
keys->pubkey = PEM_read_PUBKEY( f, NULL, NULL, NULL );
|
|
|
|
fclose(f);
|
|
|
|
|
|
|
|
return !!keys->pubkey;
|
|
|
|
}
|
|
|
|
|
|
|
|
char* crypto_keys_sign( struct crypto_keys* keys, void* data, unsigned int size )
|
|
|
|
{
|
|
|
|
char* result = NULL;
|
|
|
|
unsigned char* sign_binary = NULL;
|
|
|
|
EVP_PKEY_CTX* ctx = NULL;
|
|
|
|
|
|
|
|
if( !keys->privkey ) { return NULL; }
|
|
|
|
|
|
|
|
// hash data with SHA-256
|
|
|
|
unsigned char hash[32];
|
|
|
|
sha256_easy_hash( data, size, hash );
|
|
|
|
|
|
|
|
// Setup for signature
|
|
|
|
// Code based on https://www.openssl.org/docs/man3.1/man3/EVP_PKEY_sign.html
|
|
|
|
ctx = EVP_PKEY_CTX_new(keys->privkey, NULL /* no engine */);
|
|
|
|
if( !ctx ) { goto failed; }
|
|
|
|
|
|
|
|
if( EVP_PKEY_sign_init(ctx) <= 0 ) { goto failed; }
|
|
|
|
if( EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0 ) { goto failed; }
|
|
|
|
if( EVP_PKEY_CTX_set_signature_md(ctx, EVP_sha256()) <= 0 ) { goto failed; }
|
|
|
|
|
|
|
|
size_t siglen;
|
|
|
|
if( EVP_PKEY_sign(ctx, NULL, &siglen, data, size ) <= 0 ) { goto failed; }
|
|
|
|
|
|
|
|
sign_binary = malloc(siglen);
|
|
|
|
if( !sign_binary ) { goto failed; }
|
|
|
|
|
|
|
|
int retcode = EVP_PKEY_sign(ctx, sign_binary, &siglen, hash, 32);
|
|
|
|
if( retcode <= 0 ) {
|
|
|
|
char buffer[512];
|
|
|
|
ERR_error_string_n( retcode, buffer, sizeof(buffer) );
|
|
|
|
printf( "! Failed to create signature: %s\n", buffer );
|
|
|
|
goto failed;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = base64_strict_encode( sign_binary, siglen );
|
|
|
|
cleanup:
|
|
|
|
free(sign_binary);
|
|
|
|
EVP_PKEY_CTX_free(ctx);
|
|
|
|
return result;
|
|
|
|
failed:
|
|
|
|
free(result); result = NULL;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool crypto_keys_verify( struct crypto_keys* keys, void* data, unsigned int size, char* signature )
|
|
|
|
{
|
|
|
|
bool result = false;
|
|
|
|
EVP_PKEY_CTX* ctx = NULL;
|
|
|
|
unsigned char* signature_bin = NULL;
|
|
|
|
|
|
|
|
if( !keys->pubkey ) { goto failed; }
|
|
|
|
|
|
|
|
// hash data with SHA-256
|
|
|
|
unsigned char hash[32];
|
|
|
|
sha256_easy_hash( data, size, hash );
|
|
|
|
|
|
|
|
// Decode the signature
|
|
|
|
size_t signature_len = 0;
|
|
|
|
if( !base64_decode( signature, (void**)&signature_bin, &signature_len ) ) {
|
|
|
|
printf( "! Failed to decode base64 signature\n" );
|
|
|
|
goto failed;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Setup for signature
|
|
|
|
// Code based on https://www.openssl.org/docs/man3.1/man3/EVP_PKEY_verify.html
|
|
|
|
ctx = EVP_PKEY_CTX_new(keys->pubkey, NULL /* no engine */);
|
|
|
|
if( !ctx ) { goto failed; }
|
|
|
|
|
|
|
|
if( EVP_PKEY_verify_init(ctx) <= 0 ) { goto failed; }
|
|
|
|
if( EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0 ) { goto failed; }
|
|
|
|
if( EVP_PKEY_CTX_set_signature_md(ctx, EVP_sha256()) <= 0 ) { goto failed; }
|
|
|
|
|
|
|
|
int retcode = EVP_PKEY_verify(ctx, signature_bin, signature_len, hash, 32 );
|
|
|
|
if( retcode < 0 ) {
|
|
|
|
char buffer[512];
|
|
|
|
ERR_error_string_n( retcode, buffer, sizeof(buffer) );
|
|
|
|
printf( "! Failed to verify signature: %s\n", buffer );
|
|
|
|
goto failed;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( retcode == 1 ) {
|
|
|
|
result = true;
|
|
|
|
}
|
|
|
|
cleanup:
|
|
|
|
free(signature_bin);
|
|
|
|
EVP_PKEY_CTX_free(ctx);
|
|
|
|
return result;
|
|
|
|
failed:
|
|
|
|
result = false;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|