221 {
222
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);
225 return -1;
226 }
227 if (!message || message_len == 0) {
228 log_error("gpg_verify_signature_with_binary: Invalid message");
229 return -1;
230 }
231
232
235 int sig_fd = -1;
236 int msg_fd = -1;
237 int result = -1;
238
239
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");
242 return -1;
243 }
244
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);
248 return -1;
249 }
250
251#ifdef _WIN32
252
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);
258 return -1;
259 }
260
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);
267 return -1;
268 }
269 CloseHandle(sig_handle);
270
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);
276 return -1;
277 }
278
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);
284 return -1;
285 }
286 CloseHandle(msg_handle);
287
288#else
289
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));
293 close(sig_fd);
294 close(msg_fd);
295 platform_delete_temp_file(sig_path);
296 platform_delete_temp_file(msg_path);
297 return -1;
298 }
299 close(sig_fd);
300
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));
304 close(msg_fd);
305 platform_delete_temp_file(sig_path);
306 platform_delete_temp_file(msg_path);
307 return -1;
308 }
309 close(msg_fd);
310#endif
311
312
313 char cmd[BUFFER_SIZE_LARGE];
314#ifdef _WIN32
315 safe_snprintf(cmd,
sizeof(cmd),
"gpg --verify \"%s\" \"%s\" 2>&1", sig_path, msg_path);
316#else
317 safe_snprintf(cmd,
sizeof(cmd),
"gpg --verify '%s' '%s' 2>&1", sig_path, msg_path);
318#endif
319
320 log_debug("Running GPG verify command: %s", cmd);
321
322
323 FILE *fp;
324 platform_popen(cmd, "r", &fp);
325 if (!fp) {
326 log_error("Failed to execute gpg --verify command");
327 goto cleanup;
328 }
329
330
331 char line[BUFFER_SIZE_MEDIUM];
332 bool found_good_sig = false;
333 bool found_key_id = false;
334
335 while (fgets(line, sizeof(line), fp)) {
336 log_debug("GPG output: %s", line);
337
338
339 if (strstr(line, "Good signature")) {
340 found_good_sig = true;
341 }
342
343
344 if (expected_key_id && strlen(expected_key_id) > 0) {
345 if (strstr(line, expected_key_id)) {
346 found_key_id = true;
347 log_debug("Found expected key ID in GPG output: %s", expected_key_id);
348 }
349 }
350
351
352 if (strstr(line, "BAD signature")) {
353 log_error("GPG reports BAD signature");
354 platform_pclose(&fp);
355 fp = NULL;
356 goto cleanup;
357 }
358 }
359
360
361 int status = platform_pclose(&fp);
362 fp = NULL;
363
364#ifdef _WIN32
365 int exit_code = status;
366#else
367 int exit_code = WEXITSTATUS(status);
368#endif
369
370 if (exit_code != 0) {
371 log_error("GPG verify failed with exit code: %d", exit_code);
372 goto cleanup;
373 }
374
375 if (!found_good_sig) {
376 log_error("GPG verify did not report 'Good signature'");
377 goto cleanup;
378 }
379
380
381 if (expected_key_id && strlen(expected_key_id) > 0) {
382 if (!found_key_id) {
383 log_error("GPG signature key ID does not match expected key ID: %s", expected_key_id);
384 goto cleanup;
385 }
386 }
387
388 log_debug("GPG signature verified successfully via gpg --verify binary");
389 result = 0;
390
391cleanup:
392
393 platform_delete_temp_file(sig_path);
394 platform_delete_temp_file(msg_path);
395
396 if (fp) {
397 platform_pclose(&fp);
398 }
399
400 return result;
401}
_Atomic uint64_t bytes_written