25 if (!input || !key_out) {
33 if (strncmp(input,
"ssh-ed25519", 11) == 0) {
43 if (strncmp(input,
"gpg:", 4) == 0) {
48 if (strncmp(input,
"github:", 7) == 0 || strncmp(input,
"gitlab:", 7) == 0) {
59 if (strlen(input) == 64) {
61 bool is_valid_hex =
true;
62 for (
int i = 0; i < 64; i++) {
63 if (!((input[i] >=
'0' && input[i] <=
'9') || (input[i] >=
'a' && input[i] <=
'f') ||
64 (input[i] >=
'A' && input[i] <=
'F'))) {
83 char *normalized_path = NULL;
93 if (fgets(line,
sizeof(line), f)) {
97 line[strcspn(line,
"\r\n")] = 0;
109 if (!key_path || !key_out) {
120 if (strncmp(key_path,
"gpg:", 4) == 0) {
121 const char *key_id = key_path + 4;
124 size_t key_id_len = strlen(key_id);
125 if (key_id_len != 8 && key_id_len != 16 && key_id_len != 40) {
131 for (
size_t i = 0; i < key_id_len; i++) {
133 if (!((c >=
'0' && c <=
'9') || (c >=
'a' && c <=
'f') || (c >=
'A' && c <=
'F'))) {
165 memcpy(key_out->
key.
ed25519 + 32, public_key, 32);
167 log_info(
"Loaded GPG key %s (keygrip: %.40s) for agent signing", key_id, keygrip);
172 char *normalized_path = NULL;
188 if (!input || !keys_out || !num_keys || max_keys == 0) {
196 if (strncmp(input,
"ssh-ed25519", 11) == 0) {
205 if (strncmp(input,
"github:", 7) == 0 || strncmp(input,
"gitlab:", 7) == 0) {
206 const char *username = input + 7;
207 bool is_github = (strncmp(input,
"github:", 7) == 0);
208 bool is_gpg = (strstr(username,
".gpg") != NULL);
211 size_t num_fetched_keys = 0;
234 for (
size_t i = 0; i < num_fetched_keys && *num_keys < max_keys; i++) {
242 for (
size_t i = 0; i < num_fetched_keys; i++) {
247 if (*num_keys == 0) {
252 log_info(
"Parsed %zu Ed25519 key(s) from %s user: %s", *num_keys, is_github ?
"GitHub" :
"GitLab", username);
274 if (!key || !x25519_pk) {
280 memcpy(x25519_pk, key->
key, 32);
293 if (!key || !x25519_sk) {
316 if (!username || !keys_out || !num_keys) {
328 if (!username || !keys_out || !num_keys) {
344 if (!path || !keys || !num_keys) {
354 char *normalized_path = NULL;
368 while (fgets(line,
sizeof(line), f) && *num_keys < max_keys) {
370 line[strcspn(line,
"\r\n")] = 0;
373 if (strlen(line) == 0 || line[0] ==
'#') {
388 if (!key || !output || output_size == 0) {
397 for (
size_t i = 0; i < 32; i++) {
400 hex_key[i * 2] = hex_byte[0];
401 hex_key[i * 2 + 1] = hex_byte[1];
408 for (
size_t i = 0; i < 32; i++) {
411 hex_key[i * 2] = hex_byte[0];
412 hex_key[i * 2 + 1] = hex_byte[1];
426 if (!hex || !output) {
430 size_t hex_len = strlen(hex);
431 size_t expected_hex_len = output_len * 2;
433 if (hex_len != expected_hex_len) {
435 "Hex string length (%zu) doesn't match expected output length (%zu * 2 = %zu)", hex_len,
436 output_len, expected_hex_len);
440 for (
size_t i = 0; i < output_len; i++) {
441 char hex_byte[3] = {hex[i * 2], hex[i * 2 + 1],
'\0'};
443 unsigned long byte = strtoul(hex_byte, &endptr, 16);
446 if (*endptr !=
'\0' ||
byte > 255) {
⚠️‼️ Error and/or exit() when things go bad.
GPG public key export interface.
#define BUFFER_SIZE_LARGE
Large buffer size (1024 bytes)
int gpg_get_public_key(const char *key_id, uint8_t *public_key_out, char *keygrip_out)
Get public key from GPG keyring by key ID.
#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)
asciichat_error_t public_key_to_x25519(const public_key_t *key, uint8_t x25519_pk[32])
Convert public key to X25519 for Diffie-Hellman key exchange.
asciichat_error_t parse_keys_from_file(const char *path, public_key_t *keys, size_t *num_keys, size_t max_keys)
Parse SSH keys from file (supports authorized_keys and known_hosts formats)
asciichat_error_t parse_private_key(const char *key_path, private_key_t *key_out)
Parse SSH private key from file.
asciichat_error_t fetch_gitlab_gpg_keys(const char *username, char ***keys_out, size_t *num_keys)
Fetch GPG keys from GitLab using HTTPS.
asciichat_error_t ed25519_to_x25519_private(const uint8_t ed25519_sk[64], uint8_t x25519_sk[32])
Convert Ed25519 private key to X25519 for key exchange.
asciichat_error_t private_key_to_x25519(const private_key_t *key, uint8_t x25519_sk[32])
Convert private key to X25519 for Diffie-Hellman key exchange.
asciichat_error_t fetch_github_ssh_keys(const char *username, char ***keys_out, size_t *num_keys)
Fetch SSH keys from GitHub using HTTPS.
asciichat_error_t ed25519_to_x25519_public(const uint8_t ed25519_pk[32], uint8_t x25519_pk[32])
Convert Ed25519 public key to X25519 for key exchange.
asciichat_error_t parse_ssh_ed25519_line(const char *line, uint8_t ed25519_pk[32])
Parse SSH Ed25519 public key from "ssh-ed25519 AAAAC3..." format.
asciichat_error_t fetch_github_gpg_keys(const char *username, char ***keys_out, size_t *num_keys)
Fetch GPG keys from GitHub using HTTPS.
asciichat_error_t parse_public_keys(const char *input, public_key_t *keys_out, size_t *num_keys, size_t max_keys)
Parse all SSH/GPG public keys from any format (returns all keys)
asciichat_error_t fetch_gitlab_ssh_keys(const char *username, char ***keys_out, size_t *num_keys)
Fetch SSH keys from GitLab using HTTPS.
asciichat_error_t parse_gpg_key(const char *gpg_key_id, public_key_t *key_out)
Parse GPG key from armored text format.
asciichat_error_t parse_public_key(const char *input, public_key_t *key_out)
Parse SSH/GPG public key from any format (returns first key only)
asciichat_error_t hex_decode(const char *hex, uint8_t *output, size_t output_len)
Decode hex string to binary (utility function for testing)
asciichat_error_t fetch_gitlab_keys(const char *username, char ***keys_out, size_t *num_keys, bool use_gpg)
Fetch SSH/GPG keys from GitLab using BearSSL.
asciichat_error_t parse_ssh_private_key(const char *key_path, private_key_t *key_out)
Parse SSH Ed25519 private key from file.
union private_key_t::@1 key
void format_public_key(const public_key_t *key, char *output, size_t output_size)
Convert public key to display format (ssh-ed25519 or x25519 hex)
asciichat_error_t fetch_github_keys(const char *username, char ***keys_out, size_t *num_keys, bool use_gpg)
Fetch SSH/GPG keys from GitHub using BearSSL.
#define log_info(...)
Log an INFO message.
bool path_looks_like_path(const char *value)
Determine if a string is likely intended to reference the filesystem.
asciichat_error_t path_validate_user_path(const char *input, path_role_t role, char **normalized_out)
Validate and canonicalize a user-supplied filesystem path.
📂 Path Manipulation Utilities
Private key structure (for server –ssh-key)