20#define SAFE_CLOSE _close
21#define SAFE_UNLINK _unlink
22#define SAFE_POPEN _popen
23#define SAFE_PCLOSE _pclose
28#define SAFE_CLOSE close
29#define SAFE_UNLINK unlink
30#define SAFE_POPEN popen
31#define SAFE_PCLOSE pclose
35static pipe_t ssh_agent_open_pipe(
void) {
36 const char *auth_sock =
SAFE_GETENV(
"SSH_AUTH_SOCK");
40 const char *pipe_path = (auth_sock && strlen(auth_sock) > 0) ? auth_sock :
"\\\\.\\pipe\\openssh-ssh-agent";
44 if (!auth_sock || strlen(auth_sock) == 0) {
45 log_debug(
"SSH_AUTH_SOCK not set, cannot connect to ssh-agent");
54 const char *auth_sock =
SAFE_GETENV(
"SSH_AUTH_SOCK");
58 if (!auth_sock || strlen(auth_sock) == 0) {
59 pipe_t pipe = ssh_agent_open_pipe();
62 log_debug(
"ssh-agent is available via Windows named pipe (SSH_AUTH_SOCK not set)");
65 log_debug(
"ssh-agent not available: SSH_AUTH_SOCK not set and Windows named pipe not accessible");
71 log_debug(
"ssh-agent appears available (SSH_AUTH_SOCK=%s)", auth_sock);
75 if (!auth_sock || strlen(auth_sock) == 0) {
76 log_debug(
"ssh-agent not available: SSH_AUTH_SOCK not set");
81 if (access(auth_sock, W_OK) != 0) {
82 log_debug(
"ssh-agent not available: cannot access socket at %s", auth_sock);
85 log_debug(
"ssh-agent is available at %s", auth_sock);
91 if (public_key == NULL) {
92 log_warn(
"NULL is not a valid public key");
97 pipe_t pipe = ssh_agent_open_pipe();
103 unsigned char request[5];
120 if (bytes_read < 9) {
128 uint8_t resp_type = response[4];
129 if (resp_type != 12) {
134 uint32_t num_keys = read_u32_be(response + 5);
138 for (
uint32_t i = 0; i < num_keys && pos + 4 < (size_t)bytes_read; i++) {
140 uint32_t blob_len = read_u32_be(response + pos);
143 if (pos + blob_len > (
size_t)bytes_read)
147 size_t blob_pos = pos;
149 if (blob_pos + 4 > pos + blob_len) {
153 uint32_t type_len = read_u32_be(response + blob_pos);
154 blob_pos += 4 + type_len;
157 if (blob_pos + 4 > pos + blob_len) {
161 uint32_t pubkey_len = read_u32_be(response + blob_pos);
166 if (pubkey_len == 32 && blob_pos + 32 <= pos + blob_len) {
167 if (sodium_memcmp(response + blob_pos, public_key->
key, 32) == 0) {
168 log_debug(
"Found matching key in ssh-agent");
176 if (pos + 4 > (
size_t)bytes_read)
178 uint32_t comment_len = read_u32_be(response + pos);
179 pos += 4 + comment_len;
186 if (private_key == NULL) {
193 log_info(
"Adding key to ssh-agent: %s", key_path ? key_path :
"(memory)");
196 pipe_t pipe = ssh_agent_open_pipe();
218 write_u32_be(buf + pos, len);
221 memcpy(buf + pos,
"ssh-ed25519", 11);
226 write_u32_be(buf + pos, len);
228 memcpy(buf + pos, private_key->
key.
ed25519 + 32, 32);
233 write_u32_be(buf + pos, len);
235 memcpy(buf + pos, private_key->
key.
ed25519, 64);
239 len = key_path ? strlen(key_path) : 0;
243 size_t max_key_path_len =
sizeof(buf) - pos - 4;
244 if (len > max_key_path_len) {
246 sodium_memzero(buf,
sizeof(buf));
250 write_u32_be(buf + pos, len);
253 memcpy(buf + pos, key_path, len);
259 write_u32_be(buf, msg_len);
265 sodium_memzero(buf,
sizeof(buf));
272 if (bytes_read < 5) {
274 sodium_memzero(buf,
sizeof(buf));
280 sodium_memzero(buf,
sizeof(buf));
284 uint8_t response_type = response[4];
285 if (response_type == 6) {
286 log_info(
"Successfully added key to ssh-agent");
288 }
else if (response_type == 5) {
297 if (!public_key || !message || !signature) {
307 pipe_t pipe = ssh_agent_open_pipe();
316 const char *key_type =
"ssh-ed25519";
321 uint32_t key_blob_size = 4 + key_type_len + 4 + 32;
322 uint32_t total_len = 1 + 4 + key_blob_size + 4 + message_len + 4;
333 write_u32_be(buf + offset, total_len);
340 write_u32_be(buf + offset, key_blob_size);
344 write_u32_be(buf + offset, key_type_len);
346 memcpy(buf + offset, key_type, key_type_len);
347 offset += key_type_len;
350 write_u32_be(buf + offset, 32);
352 memcpy(buf + offset, public_key->
key, 32);
356 write_u32_be(buf + offset, (
uint32_t)message_len);
358 memcpy(buf + offset, message, message_len);
362 write_u32_be(buf + offset, 0);
367 sodium_memzero(buf, total_len + 4);
370 if (written < 0 || (
size_t)written != total_len + 4) {
380 if (read_bytes < 5) {
381 return SET_ERRNO(
ERROR_CRYPTO,
"Failed to read SSH agent sign response (read %zd bytes)", read_bytes);
386 (void)read_u32_be(response);
387 uint8_t response_type = response[4];
390 if (response_type != 14) {
391 if (response_type == 5) {
394 return SET_ERRNO(
ERROR_CRYPTO,
"ssh-agent returned unexpected response type: %d (expected 14)", response_type);
399 if (read_bytes < 9) {
403 uint32_t sig_blob_len = read_u32_be(response + 5);
404 uint32_t expected_total = 4 + 1 + 4 + sig_blob_len;
406 if ((
size_t)read_bytes < expected_total) {
407 return SET_ERRNO(
ERROR_CRYPTO,
"SSH agent response truncated (expected %u bytes, got %zd)", expected_total,
413 uint32_t sig_type_len = read_u32_be(response + offset_sig);
416 if (offset_sig + sig_type_len + 4 > (
uint32_t)read_bytes) {
421 if (sig_type_len != 11 || memcmp(response + offset_sig,
"ssh-ed25519", 11) != 0) {
424 offset_sig += sig_type_len;
427 uint32_t sig_len = read_u32_be(response + offset_sig);
431 return SET_ERRNO(
ERROR_CRYPTO,
"SSH agent returned invalid Ed25519 signature length: %u (expected 64)", sig_len);
434 if (offset_sig + 64 > (
uint32_t)read_bytes) {
439 memcpy(signature, response + offset_sig, 64);
441 log_debug(
"SSH agent successfully signed %zu bytes with Ed25519 key", message_len);
🔢 Byte-Level Access and Arithmetic Utilities
#define BUFFER_SIZE_XXXLARGE
Extra extra extra large buffer size (8192 bytes)
#define BUFFER_SIZE_XXLARGE
Extra extra large buffer size (4096 bytes)
#define BUFFER_SIZE_SMALL
Small buffer size (256 bytes)
#define SAFE_MALLOC(size, cast)
#define SAFE_GETENV(name)
bool ssh_agent_has_key(const public_key_t *public_key)
Check if a public key is already in ssh-agent.
bool ssh_agent_is_available(void)
Check if ssh-agent is running and available.
asciichat_error_t ssh_agent_add_key(const private_key_t *private_key, const char *key_path)
Add a private key to ssh-agent.
asciichat_error_t ssh_agent_sign(const public_key_t *public_key, const uint8_t *message, size_t message_len, uint8_t signature[64])
Sign data using SSH agent with the specified public key.
#define SET_ERRNO_SYS(code, context_msg,...)
Set error code with custom message and system error context.
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it.
asciichat_error_t
Error and exit codes - unified status values (0-255)
union private_key_t::@1 key
#define log_warn(...)
Log a WARN message.
#define log_info(...)
Log an INFO message.
#define log_debug(...)
Log a DEBUG message.
_Atomic uint64_t bytes_written
Cross-platform pipe/agent socket interface for ascii-chat.
Private key structure (for server –ssh-key)