ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
verification.c File Reference

GPG signature verification implementation. More...

Go to the source code of this file.

Functions

int gpg_verify_detached_ed25519 (const char *key_id, const uint8_t *message, size_t message_len, const uint8_t signature[64])
 
int gpg_verify_signature (const uint8_t *public_key, const uint8_t *message, size_t message_len, const uint8_t *signature)
 
int gpg_verify_signature_with_binary (const uint8_t *signature, size_t signature_len, const uint8_t *message, size_t message_len, const char *expected_key_id)
 

Detailed Description

GPG signature verification implementation.

Definition in file verification.c.

Function Documentation

◆ gpg_verify_detached_ed25519()

int gpg_verify_detached_ed25519 ( const char *  key_id,
const uint8_t *  message,
size_t  message_len,
const uint8_t  signature[64] 
)

Definition at line 32 of file verification.c.

33 {
34 // Note: We don't use the raw signature parameter directly.
35 // Instead, we regenerate the OpenPGP signature using GPG (Ed25519 is deterministic).
36 (void)signature;
37
38 log_debug("gpg_verify_detached_ed25519: Verifying signature with key ID %s using gpg --verify", key_id);
39
40 // To verify with GPG, we need to:
41 // 1. Reconstruct the OpenPGP signature packet from the raw R||S signature
42 // 2. Write message and signature to temp files
43 // 3. Call gpg --verify
44
45 // First, reconstruct OpenPGP signature by signing the same message
46 // Since Ed25519 is deterministic, we should get the same OpenPGP packet
47 uint8_t openpgp_signature[512];
48 size_t openpgp_len = 0;
49
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");
53 return -1;
54 }
55
56 // Now verify using gpg --verify
57 char msg_path[PLATFORM_MAX_PATH_LENGTH];
58 char sig_path[PLATFORM_MAX_PATH_LENGTH];
59 int msg_fd = -1;
60 int sig_fd = -1;
61
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");
64 return -1;
65 }
66
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");
70 return -1;
71 }
72
73 // Write message
74 if (write(msg_fd, message, message_len) != (ssize_t)message_len) {
75 log_error("Failed to write message to temp file");
76 close(msg_fd);
77 close(sig_fd);
78 platform_delete_temp_file(msg_path);
79 platform_delete_temp_file(sig_path);
80 return -1;
81 }
82 close(msg_fd);
83
84 // Write OpenPGP signature
85 if (write(sig_fd, openpgp_signature, openpgp_len) != (ssize_t)openpgp_len) {
86 log_error("Failed to write signature to temp file");
87 close(sig_fd);
88 platform_delete_temp_file(msg_path);
89 platform_delete_temp_file(sig_path);
90 return -1;
91 }
92 close(sig_fd);
93
94 // Call gpg --verify
95 char cmd[1024];
96 safe_snprintf(cmd, sizeof(cmd), "gpg --verify '%s' '%s' 2>&1", sig_path, msg_path);
97 log_debug("Running: %s", cmd);
98
99 FILE *fp;
100 platform_popen(cmd, "r", &fp);
101 if (!fp) {
102 log_error("Failed to run gpg --verify");
103 platform_unlink(msg_path);
104 platform_unlink(sig_path);
105 return -1;
106 }
107
108 char output[4096] = {0};
109 size_t output_len = fread(output, 1, sizeof(output) - 1, fp);
110 int exit_code = platform_pclose(&fp);
111
112 // Cleanup temp files
113 platform_delete_temp_file(msg_path);
114 platform_delete_temp_file(sig_path);
115
116 if (exit_code == 0) {
117 log_debug("GPG signature verification PASSED");
118 return 0;
119 } else {
120 log_error("GPG signature verification FAILED (exit code %d)", exit_code);
121 if (output_len > 0) {
122 log_debug("GPG output: %s", output);
123 }
124 return -1;
125 }
126}
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)
Definition signing.c:41
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Safe formatted string printing to buffer.
Definition system.c:456
#define PLATFORM_MAX_PATH_LENGTH
Definition system.c:64

References gpg_sign_with_key(), PLATFORM_MAX_PATH_LENGTH, and safe_snprintf().

Referenced by ed25519_verify_signature().

◆ gpg_verify_signature()

int gpg_verify_signature ( const uint8_t *  public_key,
const uint8_t *  message,
size_t  message_len,
const uint8_t *  signature 
)

Definition at line 128 of file verification.c.

129 {
130#ifdef HAVE_LIBGCRYPT
131 gcry_error_t err;
132 gcry_sexp_t s_pubkey = NULL;
133 gcry_sexp_t s_sig = NULL;
134 gcry_sexp_t s_data = NULL;
135
136 // Initialize libgcrypt if not already done
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);
141 }
142
143 // Build public key S-expression: (public-key (ecc (curve Ed25519) (flags eddsa) (q %b)))
144 // Must include (flags eddsa) to match libgcrypt's Ed25519 test suite.
145 // See libgcrypt/tests/t-ed25519.c line 246-251.
146 err = gcry_sexp_build(&s_pubkey, NULL, "(public-key (ecc (curve Ed25519) (flags eddsa) (q %b)))", 32, public_key);
147 if (err) {
148 log_error("gpg_verify_signature: Failed to build public key S-expression: %s", gcry_strerror(err));
149 return -1;
150 }
151
152 // Build signature S-expression: (sig-val (eddsa (r %b) (s %b)))
153 // Signature is 64 bytes: first 32 bytes are R, last 32 bytes are S
154 err = gcry_sexp_build(&s_sig, NULL, "(sig-val (eddsa (r %b) (s %b)))", 32, signature, 32, signature + 32);
155 if (err) {
156 log_error("gpg_verify_signature: Failed to build signature S-expression: %s", gcry_strerror(err));
157 gcry_sexp_release(s_pubkey);
158 return -1;
159 }
160
161 // Build data S-expression with raw message
162 // According to libgcrypt's test suite (t-ed25519.c line 273),
163 // Ed25519 data should be: (data (value %b)) with no flags.
164 // The (flags eddsa) belongs in the key S-expression above, not in the data.
165 // GPG agent's internal format is different - this is the correct libgcrypt API usage.
166 err = gcry_sexp_build(&s_data, NULL, "(data (value %b))", message_len, message);
167 if (err) {
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);
171 return -1;
172 }
173
174 // Debug logging
175 char pubkey_hex[65];
176 char r_hex[65];
177 char s_hex[65];
178 char msg_hex[128];
179
180 for (int i = 0; i < 32; i++) {
181 safe_snprintf(pubkey_hex + i * 2, 3, "%02x", public_key[i]);
182 safe_snprintf(r_hex + i * 2, 3, "%02x", signature[i]);
183 safe_snprintf(s_hex + i * 2, 3, "%02x", signature[32 + i]);
184 }
185 for (size_t i = 0; i < (message_len < 32 ? message_len : 32); i++) {
186 safe_snprintf(msg_hex + i * 2, 3, "%02x", message[i]);
187 }
188
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);
193
194 // Verify the signature
195 err = gcry_pk_verify(s_sig, s_data, s_pubkey);
196
197 // Clean up S-expressions
198 gcry_sexp_release(s_pubkey);
199 gcry_sexp_release(s_sig);
200 gcry_sexp_release(s_data);
201
202 if (err) {
203 log_debug("gpg_verify_signature: Signature verification failed: %s", gcry_strerror(err));
204 return -1;
205 }
206
207 log_debug("gpg_verify_signature: Signature verified successfully");
208 return 0;
209#else
210 // Explicitly mark parameters as unused when libgcrypt is not available
211 (void)public_key;
212 (void)message;
213 (void)message_len;
214 (void)signature;
215 log_error("gpg_verify_signature: libgcrypt not available");
216 return -1;
217#endif
218}

References safe_snprintf().

◆ gpg_verify_signature_with_binary()

int gpg_verify_signature_with_binary ( const uint8_t *  signature,
size_t  signature_len,
const uint8_t *  message,
size_t  message_len,
const char *  expected_key_id 
)

Definition at line 220 of file verification.c.

221 {
222 // Validate inputs
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 // Create temporary files for signature and message
233 char sig_path[PLATFORM_MAX_PATH_LENGTH];
234 char msg_path[PLATFORM_MAX_PATH_LENGTH];
235 int sig_fd = -1;
236 int msg_fd = -1;
237 int result = -1;
238
239 // Create temp files using platform abstraction
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 // Windows: Write to already-created files using CreateFileA
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
261 DWORD bytes_written;
262 if (!WriteFile(sig_handle, signature, (DWORD)signature_len, &bytes_written, NULL) || bytes_written != signature_len) {
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
279 if (!WriteFile(msg_handle, message, (DWORD)message_len, &bytes_written, NULL) || bytes_written != message_len) {
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 // Unix: Write to open file descriptors returned by platform_create_temp_file
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 // Build gpg --verify command
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 // Execute gpg --verify command
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 // Parse output for "Good signature" and verify key ID
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 // Check for "Good signature"
339 if (strstr(line, "Good signature")) {
340 found_good_sig = true;
341 }
342
343 // Check if this line contains the expected key ID (GPG outputs key ID on separate line)
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 // Check for signature errors
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 // Check exit code
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 // If expected_key_id was provided, verify we found it in the output
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 // Clean up temp files
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
Definition mmap.c:42

References bytes_written, PLATFORM_MAX_PATH_LENGTH, and safe_snprintf().