33 const uint8_t signature[64]) {
38 log_debug(
"gpg_verify_detached_ed25519: Verifying signature with key ID %s using gpg --verify", key_id);
47 uint8_t openpgp_signature[512];
48 size_t openpgp_len = 0;
50 int sign_result =
gpg_sign_with_key(key_id, message, message_len, openpgp_signature, &openpgp_len);
51 if (sign_result != 0) {
52 log_error(
"Failed to create reference signature for verification");
62 if (platform_create_temp_file(msg_path,
sizeof(msg_path),
"gpg_verify_msg", &msg_fd) != 0) {
63 log_error(
"Failed to create temporary message file");
67 if (platform_create_temp_file(sig_path,
sizeof(sig_path),
"gpg_verify_sig", &sig_fd) != 0) {
68 platform_delete_temp_file(msg_path);
69 log_error(
"Failed to create temporary signature file");
74 if (write(msg_fd, message, message_len) != (ssize_t)message_len) {
75 log_error(
"Failed to write message to temp file");
78 platform_delete_temp_file(msg_path);
79 platform_delete_temp_file(sig_path);
85 if (write(sig_fd, openpgp_signature, openpgp_len) != (ssize_t)openpgp_len) {
86 log_error(
"Failed to write signature to temp file");
88 platform_delete_temp_file(msg_path);
89 platform_delete_temp_file(sig_path);
96 safe_snprintf(cmd,
sizeof(cmd),
"gpg --verify '%s' '%s' 2>&1", sig_path, msg_path);
97 log_debug(
"Running: %s", cmd);
100 platform_popen(cmd,
"r", &fp);
102 log_error(
"Failed to run gpg --verify");
103 platform_unlink(msg_path);
104 platform_unlink(sig_path);
108 char output[4096] = {0};
109 size_t output_len = fread(output, 1,
sizeof(output) - 1, fp);
110 int exit_code = platform_pclose(&fp);
113 platform_delete_temp_file(msg_path);
114 platform_delete_temp_file(sig_path);
116 if (exit_code == 0) {
117 log_debug(
"GPG signature verification PASSED");
120 log_error(
"GPG signature verification FAILED (exit code %d)", exit_code);
121 if (output_len > 0) {
122 log_debug(
"GPG output: %s", output);
129 const uint8_t *signature) {
132 gcry_sexp_t s_pubkey = NULL;
133 gcry_sexp_t s_sig = NULL;
134 gcry_sexp_t s_data = NULL;
137 if (!gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P)) {
138 gcry_check_version(NULL);
139 gcry_control(GCRYCTL_DISABLE_SECMEM, 0);
140 gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
146 err = gcry_sexp_build(&s_pubkey, NULL,
"(public-key (ecc (curve Ed25519) (flags eddsa) (q %b)))", 32, public_key);
148 log_error(
"gpg_verify_signature: Failed to build public key S-expression: %s", gcry_strerror(err));
154 err = gcry_sexp_build(&s_sig, NULL,
"(sig-val (eddsa (r %b) (s %b)))", 32, signature, 32, signature + 32);
156 log_error(
"gpg_verify_signature: Failed to build signature S-expression: %s", gcry_strerror(err));
157 gcry_sexp_release(s_pubkey);
166 err = gcry_sexp_build(&s_data, NULL,
"(data (value %b))", message_len, message);
168 log_error(
"gpg_verify_signature: Failed to build data S-expression: %s", gcry_strerror(err));
169 gcry_sexp_release(s_pubkey);
170 gcry_sexp_release(s_sig);
180 for (
int i = 0; i < 32; i++) {
185 for (
size_t i = 0; i < (message_len < 32 ? message_len : 32); i++) {
189 log_debug(
"gpg_verify_signature: pubkey=%s", pubkey_hex);
190 log_debug(
"gpg_verify_signature: R=%s", r_hex);
191 log_debug(
"gpg_verify_signature: S=%s", s_hex);
192 log_debug(
"gpg_verify_signature: msg=%s (len=%zu)", msg_hex, message_len);
195 err = gcry_pk_verify(s_sig, s_data, s_pubkey);
198 gcry_sexp_release(s_pubkey);
199 gcry_sexp_release(s_sig);
200 gcry_sexp_release(s_data);
203 log_debug(
"gpg_verify_signature: Signature verification failed: %s", gcry_strerror(err));
207 log_debug(
"gpg_verify_signature: Signature verified successfully");
215 log_error(
"gpg_verify_signature: libgcrypt not available");
221 size_t message_len,
const char *expected_key_id) {
223 if (!signature || signature_len == 0 || signature_len > 512) {
224 log_error(
"gpg_verify_signature_with_binary: Invalid signature (expected 1-512 bytes, got %zu)", signature_len);
227 if (!message || message_len == 0) {
228 log_error(
"gpg_verify_signature_with_binary: Invalid message");
240 if (platform_create_temp_file(sig_path,
sizeof(sig_path),
"asciichat_sig", &sig_fd) != 0) {
241 log_error(
"Failed to create signature temp file");
245 if (platform_create_temp_file(msg_path,
sizeof(msg_path),
"asciichat_msg", &msg_fd) != 0) {
246 log_error(
"Failed to create message temp file");
247 platform_delete_temp_file(sig_path);
253 HANDLE sig_handle = CreateFileA(sig_path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, NULL);
254 if (sig_handle == INVALID_HANDLE_VALUE) {
255 log_error(
"Failed to open signature temp file: %lu", GetLastError());
256 platform_delete_temp_file(sig_path);
257 platform_delete_temp_file(msg_path);
263 log_error(
"Failed to write signature to temp file: %lu", GetLastError());
264 CloseHandle(sig_handle);
265 platform_delete_temp_file(sig_path);
266 platform_delete_temp_file(msg_path);
269 CloseHandle(sig_handle);
271 HANDLE msg_handle = CreateFileA(msg_path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, NULL);
272 if (msg_handle == INVALID_HANDLE_VALUE) {
273 log_error(
"Failed to open message temp file: %lu", GetLastError());
274 platform_delete_temp_file(sig_path);
275 platform_delete_temp_file(msg_path);
280 log_error(
"Failed to write message to temp file: %lu", GetLastError());
281 CloseHandle(msg_handle);
282 platform_delete_temp_file(sig_path);
283 platform_delete_temp_file(msg_path);
286 CloseHandle(msg_handle);
290 ssize_t sig_written = write(sig_fd, signature, signature_len);
291 if (sig_written != (ssize_t)signature_len) {
292 log_error(
"Failed to write signature to temp file: %s", SAFE_STRERROR(errno));
295 platform_delete_temp_file(sig_path);
296 platform_delete_temp_file(msg_path);
301 ssize_t msg_written = write(msg_fd, message, message_len);
302 if (msg_written != (ssize_t)message_len) {
303 log_error(
"Failed to write message to temp file: %s", SAFE_STRERROR(errno));
305 platform_delete_temp_file(sig_path);
306 platform_delete_temp_file(msg_path);
313 char cmd[BUFFER_SIZE_LARGE];
315 safe_snprintf(cmd,
sizeof(cmd),
"gpg --verify \"%s\" \"%s\" 2>&1", sig_path, msg_path);
317 safe_snprintf(cmd,
sizeof(cmd),
"gpg --verify '%s' '%s' 2>&1", sig_path, msg_path);
320 log_debug(
"Running GPG verify command: %s", cmd);
324 platform_popen(cmd,
"r", &fp);
326 log_error(
"Failed to execute gpg --verify command");
331 char line[BUFFER_SIZE_MEDIUM];
332 bool found_good_sig =
false;
333 bool found_key_id =
false;
335 while (fgets(line,
sizeof(line), fp)) {
336 log_debug(
"GPG output: %s", line);
339 if (strstr(line,
"Good signature")) {
340 found_good_sig =
true;
344 if (expected_key_id && strlen(expected_key_id) > 0) {
345 if (strstr(line, expected_key_id)) {
347 log_debug(
"Found expected key ID in GPG output: %s", expected_key_id);
352 if (strstr(line,
"BAD signature")) {
353 log_error(
"GPG reports BAD signature");
354 platform_pclose(&fp);
361 int status = platform_pclose(&fp);
365 int exit_code = status;
367 int exit_code = WEXITSTATUS(status);
370 if (exit_code != 0) {
371 log_error(
"GPG verify failed with exit code: %d", exit_code);
375 if (!found_good_sig) {
376 log_error(
"GPG verify did not report 'Good signature'");
381 if (expected_key_id && strlen(expected_key_id) > 0) {
383 log_error(
"GPG signature key ID does not match expected key ID: %s", expected_key_id);
388 log_debug(
"GPG signature verified successfully via gpg --verify binary");
393 platform_delete_temp_file(sig_path);
394 platform_delete_temp_file(msg_path);
397 platform_pclose(&fp);
int gpg_sign_with_key(const char *key_id, const uint8_t *message, size_t message_len, uint8_t *signature_out, size_t *signature_len_out)
Sign a message using GPG key (via gpg –detach-sign)