256 {
257 if (!public_key || !message || !signature) {
258 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters: public_key=%p, message=%p, signature=%p", public_key,
259 message, signature);
260 }
261
262 if (public_key->type != KEY_TYPE_ED25519) {
263 return SET_ERRNO(ERROR_CRYPTO_KEY, "Only Ed25519 keys are supported for SSH agent signing");
264 }
265
266
267
268 if (message_len > 1024 * 1024) {
269 return SET_ERRNO(ERROR_CRYPTO, "Message too large for SSH agent (max 1MB)");
270 }
271
272
273 pipe_t pipe = ssh_agent_open_pipe();
274 if (pipe == INVALID_PIPE_VALUE) {
275 return SET_ERRNO(ERROR_CRYPTO, "Cannot connect to ssh-agent");
276 }
277
278
279
280
281
282 const char *key_type = "ssh-ed25519";
283 uint32_t key_type_len = (uint32_t)strlen(key_type);
284
285
286
287 uint32_t key_blob_size = 4 + key_type_len + 4 + 32;
288 uint32_t total_len = 1 + 4 + key_blob_size + 4 + message_len + 4;
289
290 uint8_t *buf = SAFE_MALLOC(total_len + 4, uint8_t *);
291 if (!buf) {
292 platform_pipe_close(pipe);
293 return SET_ERRNO(ERROR_CRYPTO, "Out of memory for SSH agent sign request");
294 }
295
296 uint32_t offset = 0;
297
298
299 write_u32_be(buf + offset, total_len);
300 offset += 4;
301
302
303 buf[offset++] = 13;
304
305
306 write_u32_be(buf + offset, key_blob_size);
307 offset += 4;
308
309
310 write_u32_be(buf + offset, key_type_len);
311 offset += 4;
312 memcpy(buf + offset, key_type, key_type_len);
313 offset += key_type_len;
314
315
316 write_u32_be(buf + offset, 32);
317 offset += 4;
318 memcpy(buf + offset, public_key->key, 32);
319 offset += 32;
320
321
322 write_u32_be(buf + offset, (uint32_t)message_len);
323 offset += 4;
324 memcpy(buf + offset, message, message_len);
325 offset += (uint32_t)message_len;
326
327
328 write_u32_be(buf + offset, 0);
329 offset += 4;
330
331
332 ssize_t written = platform_pipe_write(pipe, buf, total_len + 4);
333 sodium_memzero(buf, total_len + 4);
334 SAFE_FREE(buf);
335
336 if (written < 0 || (size_t)written != total_len + 4) {
337 platform_pipe_close(pipe);
338 return SET_ERRNO(ERROR_CRYPTO, "Failed to write SSH agent sign request");
339 }
340
341
342 uint8_t response[BUFFER_SIZE_XXLARGE];
343 ssize_t read_bytes = platform_pipe_read(pipe, response, sizeof(response));
344 platform_pipe_close(pipe);
345
346 if (read_bytes < 5) {
347 return SET_ERRNO(ERROR_CRYPTO, "Failed to read SSH agent sign response (read %zd bytes)", read_bytes);
348 }
349
350
351
352 (void)read_u32_be(response);
353 uint8_t response_type = response[4];
354
355
356 if (response_type != 14) {
357 if (response_type == 5) {
358 return SET_ERRNO(ERROR_CRYPTO, "ssh-agent refused to sign (SSH_AGENT_FAILURE)");
359 }
360 return SET_ERRNO(ERROR_CRYPTO, "ssh-agent returned unexpected response type: %d (expected 14)", response_type);
361 }
362
363
364
365 if (read_bytes < 9) {
366 return SET_ERRNO(ERROR_CRYPTO, "SSH agent response too short (no signature blob length)");
367 }
368
369 uint32_t sig_blob_len = read_u32_be(response + 5);
370 uint32_t expected_total = 4 + 1 + 4 + sig_blob_len;
371
372 if ((size_t)read_bytes < expected_total) {
373 return SET_ERRNO(ERROR_CRYPTO, "SSH agent response truncated (expected %u bytes, got %zd)", expected_total,
374 read_bytes);
375 }
376
377
378 uint32_t offset_sig = 9;
379 uint32_t sig_type_len = read_u32_be(response + offset_sig);
380 offset_sig += 4;
381
382 if (offset_sig + sig_type_len + 4 > (uint32_t)read_bytes) {
383 return SET_ERRNO(ERROR_CRYPTO, "SSH agent signature blob truncated at signature type");
384 }
385
386
387 if (sig_type_len != 11 || memcmp(response + offset_sig, "ssh-ed25519", 11) != 0) {
388 return SET_ERRNO(ERROR_CRYPTO, "SSH agent returned non-Ed25519 signature");
389 }
390 offset_sig += sig_type_len;
391
392
393 uint32_t sig_len = read_u32_be(response + offset_sig);
394 offset_sig += 4;
395
396 if (sig_len != 64) {
397 return SET_ERRNO(ERROR_CRYPTO, "SSH agent returned invalid Ed25519 signature length: %u (expected 64)", sig_len);
398 }
399
400 if (offset_sig + 64 > (uint32_t)read_bytes) {
401 return SET_ERRNO(ERROR_CRYPTO, "SSH agent signature blob truncated at signature bytes");
402 }
403
404
405 memcpy(signature, response + offset_sig, 64);
406
407 log_debug("SSH agent successfully signed %zu bytes with Ed25519 key", message_len);
408 return ASCIICHAT_OK;
409}