Using gpgme in C program to decrypt a PGP file using a private key file and a provided passphrase

78 Views Asked by At

I am trying to implement PGP Decryption in C, in Windows, using gpgme. I did not found a complete, working example.

The error I get occurs at the end of the program after calling gpgme_op_decrypt : Failed to decrypt data. Error code: 32779, which corresponds to "Bad file descriptor"

#include "pch.h"
#include <gpgme.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <io.h>
#include <windows.h> 
#include "pgp.h"
#include <time.h>
#include <stddef.h> 
#include <gpg-error.h>

static gpgme_error_t passphrase_callback(void* hook_value, const char* uid_hint, const char* passphrase_info, int prev_was_bad, int fd) {

const char* passphrase = (const char*)hook_value;

gpgme_io_write(fd, passphrase, strlen(passphrase));
gpgme_io_write(fd, "\n", 1);

return 0;
}

int PGPDecryptFile(const char* fromFilepath, const char* toFilePath, const char* private_key_filepath, const char* logFilePath) {
    gpgme_ctx_t ctx;
    gpgme_data_t in, out;
    const uint8_t private_key_password [13] = { 33, 44, 55, 66, 55, 33, 22, 11, 33, 44, 55, 66, '\0' };
    // Initialize GPGME
    gpgme_check_version(NULL);
    gpgme_set_locale(NULL, 0, NULL);
    gpgme_check_version(NULL);
    // Create and initialize context
    gpgme_new(&ctx);
    gpgme_set_protocol(ctx, GPGME_PROTOCOL_OpenPGP);

    FILE* private_key_file;
    if (fopen_s(&private_key_file, private_key_filepath, "r") != 0) {
        AppendLogToFile(logFilePath, private_key_filepath);
        AppendLogToFile(logFilePath, "Error: Failed to open private key file for reading");
        return 1;
    }

    if (!private_key_file) {
        AppendLogToFile(logFilePath, private_key_filepath);
        AppendLogToFile(logFilePath, "Error: Failed to open private key file for reading");
        return 1;
    }

    gpgme_data_t private_key_data;
    if (gpgme_data_new_from_fd(&private_key_data, _fileno(private_key_file)) != GPG_ERR_NO_ERROR) {
        AppendLogToFile(logFilePath, "Error: Failed to create data buffer for private key");
        fclose(private_key_file);
        return 1;
    }

    gpgme_set_passphrase_cb(ctx, passphrase_callback, (void*)private_key_password);

    // Set the private key in the context
    gpgme_error_t setProtocolResult = gpgme_set_protocol(ctx, GPGME_PROTOCOL_OpenPGP);
    gpgme_error_t setEngineInfoResult = gpgme_ctx_set_engine_info(ctx, GPGME_PROTOCOL_OpenPGP, private_key_filepath, NULL);

    if (setProtocolResult != GPG_ERR_NO_ERROR || setEngineInfoResult != GPG_ERR_NO_ERROR) {
        AppendLogToFile(logFilePath, "Error: Failed to set private key in the context");

        if (setProtocolResult != GPG_ERR_NO_ERROR) {
            AppendLogToFile(logFilePath, "Error setting protocol");
        }

        if (setEngineInfoResult != GPG_ERR_NO_ERROR) {
            AppendLogToFile(logFilePath, "Error setting engine info");
        }

        gpgme_data_release(private_key_data);
        fclose(private_key_file);
        return 1;
    }

    // Prepare data for decryption
    FILE* file;
    if (fopen_s(&file, fromFilepath, "rb") != 0) {
        //fprintf(stderr, "Error: Failed to open file for reading.\n");
        AppendLogToFile(logFilePath, fromFilepath);
        AppendLogToFile(logFilePath, "Error: Failed to open file for reading");
        gpgme_data_release(private_key_data);
        fclose(private_key_file);
        return 1;
    }

    if (!file) {
        AppendLogToFile(logFilePath, fromFilepath);
        AppendLogToFile(logFilePath, "Error: Failed to open file for reading");
        gpgme_data_release(private_key_data);
        fclose(private_key_file);
        return 1;
    }

    gpgme_data_new_from_fd(&in, _fileno(file));
    gpgme_data_new(&out);

    // Decrypt data
    gpgme_error_t decryptResult = gpgme_op_decrypt(ctx,in , out);
    if (decryptResult != GPG_ERR_NO_ERROR) {
        int errCode = gpgme_err_code(decryptResult);
        char errorMessage[256];  
        snprintf(errorMessage, sizeof(errorMessage), "Error: Failed to decrypt data. Error code: %d", errCode);
        AppendLogToFile(logFilePath, errorMessage);
        fclose(file);
        gpgme_data_release(private_key_data);
        fclose(private_key_file);
        return 1;
    }

    char* decrypted_data = gpgme_data_release_and_get_mem(out, NULL);

    FILE* decrypted_file;
    if (fopen_s(&decrypted_file, toFilePath, "wb") != 0) {
        AppendLogToFile(logFilePath, toFilePath);
        AppendLogToFile(logFilePath, "Error: Failed to create decrypted file for writing");
        fclose(file);
        gpgme_data_release(private_key_data);
        fclose(private_key_file);
        free(decrypted_data);
        return 1;
    }

    fwrite(decrypted_data, 1, strlen(decrypted_data), decrypted_file);

    fclose(file);
    fclose(decrypted_file);
    gpgme_data_release(private_key_data);
    free(decrypted_data);
    gpgme_data_release(in);
    gpgme_release(ctx);
    fclose(private_key_file);

    return 0;
}

If you want to test this code you may need to install Gpg4win.

There are three things to do in setting gpgme in Visual Studio:

  • Add "Gpg4win\include" to the "Additional Include Directories" property
  • Add "Gpg4win\bin and Gpg4win\lib" to the "Additional Library Directories" property
  • Add "libgpgme.imp" to the "Additional Dependencies" property

The Additional Library Directories and Additional Dependencies properties are under Linker's General and Input tabs. The Additional Include Directories property is under C/C++ General tab.

0

There are 0 best solutions below