51 if (public_key == NULL) {
52 log_warn(
"NULL is not a valid public key");
57 pipe_t pipe = ssh_agent_open_pipe();
58 if (pipe == INVALID_PIPE_VALUE) {
63 unsigned char request[5];
71 ssize_t
bytes_written = platform_pipe_write(pipe, request, 5);
73 platform_pipe_close(pipe);
78 unsigned char response[BUFFER_SIZE_XXXLARGE];
79 ssize_t bytes_read = platform_pipe_read(pipe, response,
sizeof(response));
81 platform_pipe_close(pipe);
85 platform_pipe_close(pipe);
88 uint8_t resp_type = response[4];
89 if (resp_type != 12) {
94 uint32_t num_keys = read_u32_be(response + 5);
98 for (uint32_t i = 0; i < num_keys && pos + 4 < (size_t)bytes_read; i++) {
100 uint32_t blob_len = read_u32_be(response + pos);
103 if (pos + blob_len > (
size_t)bytes_read)
107 size_t blob_pos = pos;
109 if (blob_pos + 4 > pos + blob_len) {
113 uint32_t type_len = read_u32_be(response + blob_pos);
114 blob_pos += 4 + type_len;
117 if (blob_pos + 4 > pos + blob_len) {
121 uint32_t pubkey_len = read_u32_be(response + blob_pos);
126 if (pubkey_len == 32 && blob_pos + 32 <= pos + blob_len) {
127 if (sodium_memcmp(response + blob_pos, public_key->key, 32) == 0) {
128 log_debug(
"Found matching key in ssh-agent");
136 if (pos + 4 > (
size_t)bytes_read)
138 uint32_t comment_len = read_u32_be(response + pos);
139 pos += 4 + comment_len;
146 if (private_key == NULL) {
147 return SET_ERRNO(ERROR_INVALID_PARAM,
"Cannot add key to ssh-agent: private_key is NULL");
149 if (private_key->type != KEY_TYPE_ED25519) {
150 return SET_ERRNO(ERROR_INVALID_PARAM,
"Cannot add key to ssh-agent: only Ed25519 keys supported");
153 log_debug(
"Adding key to ssh-agent: %s", key_path ? key_path :
"(memory)");
156 pipe_t pipe = ssh_agent_open_pipe();
157 if (pipe == INVALID_PIPE_VALUE) {
158 return SET_ERRNO(ERROR_CRYPTO,
"Failed to connect to ssh-agent");
170 unsigned char buf[BUFFER_SIZE_XXLARGE];
178 write_u32_be(buf + pos, len);
181 memcpy(buf + pos,
"ssh-ed25519", 11);
186 write_u32_be(buf + pos, len);
188 memcpy(buf + pos, private_key->key.ed25519 + 32, 32);
193 write_u32_be(buf + pos, len);
195 memcpy(buf + pos, private_key->key.ed25519, 64);
199 len = key_path ? strlen(key_path) : 0;
203 size_t max_key_path_len =
sizeof(buf) - pos - 4;
204 if (len > max_key_path_len) {
205 platform_pipe_close(pipe);
206 sodium_memzero(buf,
sizeof(buf));
207 return SET_ERRNO(ERROR_BUFFER_OVERFLOW,
"SSH key path too long: %u bytes (max %zu)", len, max_key_path_len);
210 write_u32_be(buf + pos, len);
213 memcpy(buf + pos, key_path, len);
218 uint32_t msg_len = pos - 4;
219 write_u32_be(buf, msg_len);
224 platform_pipe_close(pipe);
225 sodium_memzero(buf,
sizeof(buf));
226 return SET_ERRNO_SYS(ERROR_CRYPTO,
"Failed to write to ssh-agent pipe");
230 unsigned char response[BUFFER_SIZE_SMALL];
231 ssize_t bytes_read = platform_pipe_read(pipe, response,
sizeof(response));
232 if (bytes_read < 5) {
233 platform_pipe_close(pipe);
234 sodium_memzero(buf,
sizeof(buf));
235 return SET_ERRNO_SYS(ERROR_CRYPTO,
"Failed to read from ssh-agent pipe");
239 platform_pipe_close(pipe);
240 sodium_memzero(buf,
sizeof(buf));
244 uint8_t response_type = response[4];
245 if (response_type == 6) {
246 log_debug(
"Successfully added key to ssh-agent");
248 }
else if (response_type == 5) {
249 return SET_ERRNO(ERROR_CRYPTO,
"ssh-agent rejected key (SSH_AGENT_FAILURE)");
251 return SET_ERRNO(ERROR_CRYPTO,
"ssh-agent returned unexpected response: %d", response_type);
255asciichat_error_t
ssh_agent_sign(
const public_key_t *public_key,
const uint8_t *message,
size_t message_len,
256 uint8_t signature[64]) {
257 if (!public_key || !message || !signature) {
258 return SET_ERRNO(ERROR_INVALID_PARAM,
"Invalid parameters: public_key=%p, message=%p, signature=%p", public_key,
262 if (public_key->type != KEY_TYPE_ED25519) {
263 return SET_ERRNO(ERROR_CRYPTO_KEY,
"Only Ed25519 keys are supported for SSH agent signing");
268 if (message_len > 1024 * 1024) {
269 return SET_ERRNO(ERROR_CRYPTO,
"Message too large for SSH agent (max 1MB)");
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");
282 const char *key_type =
"ssh-ed25519";
283 uint32_t key_type_len = (uint32_t)strlen(key_type);
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;
290 uint8_t *buf = SAFE_MALLOC(total_len + 4, uint8_t *);
292 platform_pipe_close(pipe);
293 return SET_ERRNO(ERROR_CRYPTO,
"Out of memory for SSH agent sign request");
299 write_u32_be(buf + offset, total_len);
306 write_u32_be(buf + offset, key_blob_size);
310 write_u32_be(buf + offset, key_type_len);
312 memcpy(buf + offset, key_type, key_type_len);
313 offset += key_type_len;
316 write_u32_be(buf + offset, 32);
318 memcpy(buf + offset, public_key->key, 32);
322 write_u32_be(buf + offset, (uint32_t)message_len);
324 memcpy(buf + offset, message, message_len);
325 offset += (uint32_t)message_len;
328 write_u32_be(buf + offset, 0);
332 ssize_t written = platform_pipe_write(pipe, buf, total_len + 4);
333 sodium_memzero(buf, total_len + 4);
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");
342 uint8_t response[BUFFER_SIZE_XXLARGE];
343 ssize_t read_bytes = platform_pipe_read(pipe, response,
sizeof(response));
344 platform_pipe_close(pipe);
346 if (read_bytes < 5) {
347 return SET_ERRNO(ERROR_CRYPTO,
"Failed to read SSH agent sign response (read %zd bytes)", read_bytes);
352 (void)read_u32_be(response);
353 uint8_t response_type = response[4];
356 if (response_type != 14) {
357 if (response_type == 5) {
358 return SET_ERRNO(ERROR_CRYPTO,
"ssh-agent refused to sign (SSH_AGENT_FAILURE)");
360 return SET_ERRNO(ERROR_CRYPTO,
"ssh-agent returned unexpected response type: %d (expected 14)", response_type);
365 if (read_bytes < 9) {
366 return SET_ERRNO(ERROR_CRYPTO,
"SSH agent response too short (no signature blob length)");
369 uint32_t sig_blob_len = read_u32_be(response + 5);
370 uint32_t expected_total = 4 + 1 + 4 + sig_blob_len;
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,
378 uint32_t offset_sig = 9;
379 uint32_t sig_type_len = read_u32_be(response + offset_sig);
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");
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");
390 offset_sig += sig_type_len;
393 uint32_t sig_len = read_u32_be(response + offset_sig);
397 return SET_ERRNO(ERROR_CRYPTO,
"SSH agent returned invalid Ed25519 signature length: %u (expected 64)", sig_len);
400 if (offset_sig + 64 > (uint32_t)read_bytes) {
401 return SET_ERRNO(ERROR_CRYPTO,
"SSH agent signature blob truncated at signature bytes");
405 memcpy(signature, response + offset_sig, 64);
407 log_debug(
"SSH agent successfully signed %zu bytes with Ed25519 key", message_len);