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.