#include "keys.h" #include "model/crypto/base64.h" #include "sha256/sha256.h" #include #include #include 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; }