Files
n_signer/examples/ssh_sign_example.c

213 lines
6.2 KiB
C

/*
* ssh_sign_example.c — connect to a running n_signer over its abstract UNIX
* socket and demonstrate SSH signing with ed25519.
*
* The example:
* 1. Sends a get_public_key request for an SSH/ed25519 role ("ssh_main").
* 2. Prints the structured public key (algorithm, public_key, key_id).
* 3. Sends an ssh_sign request with a test session ID.
* 4. Prints the signature (hex) and algorithm.
*
* Prerequisites:
* - n_signer must be running with a role configured for purpose=ssh,
* curve=ed25519, named "ssh_main" (or pass the role name as the 2nd arg).
* - A mnemonic must be loaded in the signer.
*
* Usage: ./ssh_sign_example [socket_name] [role_name]
*
* Default socket_name: nsigner
* Default role_name: ssh_main
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "nostr_common.h"
#include "nsigner_transport.h"
#include "nsigner_client.h"
#include "../cjson/cJSON.h"
static int get_structured_pubkey(nsigner_client_t *client, const char *role,
char **out_pub_hex, char **out_key_id) {
cJSON *params = NULL;
cJSON *opts = NULL;
cJSON *result = NULL;
cJSON *parsed = NULL;
int rc = -1;
*out_pub_hex = NULL;
*out_key_id = NULL;
params = cJSON_CreateArray();
if (params == NULL) return -1;
opts = cJSON_CreateObject();
if (opts == NULL) {
cJSON_Delete(params);
return -1;
}
cJSON_AddStringToObject(opts, "role", role);
cJSON_AddItemToArray(params, opts);
opts = NULL;
if (nsigner_client_call(client, "get_public_key", params, &result) != NOSTR_SUCCESS) {
cJSON_Delete(params);
return -1;
}
params = NULL;
if (cJSON_IsString(result)) {
parsed = cJSON_Parse(result->valuestring);
if (parsed != NULL) {
cJSON *pk_item = cJSON_GetObjectItemCaseSensitive(parsed, "public_key");
cJSON *kid_item = cJSON_GetObjectItemCaseSensitive(parsed, "key_id");
if (cJSON_IsString(pk_item)) {
*out_pub_hex = strdup(pk_item->valuestring);
}
if (cJSON_IsString(kid_item)) {
*out_key_id = strdup(kid_item->valuestring);
}
if (*out_pub_hex != NULL) {
rc = 0;
}
}
}
cJSON_Delete(parsed);
cJSON_Delete(result);
cJSON_Delete(params);
return rc;
}
static char *ssh_sign(nsigner_client_t *client, const char *role,
const char *msg_hex) {
cJSON *params = NULL;
cJSON *opts = NULL;
cJSON *result = NULL;
char *sig_hex = NULL;
params = cJSON_CreateArray();
if (params == NULL) return NULL;
cJSON_AddItemToArray(params, cJSON_CreateString(msg_hex));
opts = cJSON_CreateObject();
if (opts == NULL) {
cJSON_Delete(params);
return NULL;
}
cJSON_AddStringToObject(opts, "role", role);
cJSON_AddItemToArray(params, opts);
opts = NULL;
if (nsigner_client_call(client, "ssh_sign", params, &result) != NOSTR_SUCCESS) {
cJSON_Delete(params);
return NULL;
}
params = NULL;
if (cJSON_IsString(result)) {
cJSON *parsed = cJSON_Parse(result->valuestring);
if (parsed != NULL) {
cJSON *sig_item = cJSON_GetObjectItemCaseSensitive(parsed, "signature");
if (cJSON_IsString(sig_item)) {
sig_hex = strdup(sig_item->valuestring);
}
cJSON_Delete(parsed);
}
}
cJSON_Delete(result);
cJSON_Delete(params);
return sig_hex;
}
int main(int argc, char **argv) {
const char *socket_name = "nsigner";
const char *role = "ssh_main";
/* A fake SSH session ID (32 bytes = 64 hex chars) for demonstration. */
const char *session_id_hex =
"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
nsigner_transport_t *transport = NULL;
nsigner_client_t *client = NULL;
char *pub_hex = NULL;
char *key_id = NULL;
char *sig_hex = NULL;
int rc = 1;
if (argc > 1 && argv[1] != NULL && argv[1][0] != '\0') {
socket_name = argv[1];
}
if (argc > 2 && argv[2] != NULL && argv[2][0] != '\0') {
role = argv[2];
}
if (nostr_init() != NOSTR_SUCCESS) {
fprintf(stderr, "failed to initialize crypto subsystem\n");
return 1;
}
transport = nsigner_transport_open_unix(socket_name, 10000);
if (transport == NULL) {
fprintf(stderr, "connect failed: cannot open unix transport @%s\n", socket_name);
goto cleanup;
}
client = nsigner_client_new(transport);
if (client == NULL) {
fprintf(stderr, "connect failed: cannot create nsigner client\n");
transport->close(transport);
goto cleanup;
}
transport = NULL;
printf("=== SSH Sign Example (ed25519) ===\n");
printf("socket: %s\n", socket_name);
printf("role: %s\n", role);
printf("\n");
/* 1. Get the ed25519 public key. */
if (get_structured_pubkey(client, role, &pub_hex, &key_id) != 0 ||
pub_hex == NULL) {
fprintf(stderr, "get_public_key failed: %s\n",
nsigner_client_last_error(client));
goto cleanup;
}
printf("Public Key:\n");
printf(" algorithm: ed25519\n");
printf(" key_id: %s\n", (key_id != NULL) ? key_id : "?");
printf(" pub_len: %zu hex chars (%zu bytes)\n",
strlen(pub_hex), strlen(pub_hex) / 2);
printf(" pubkey: %s\n", pub_hex);
printf("\n");
/* 2. Sign a test SSH session ID. */
printf("Signing SSH session ID (hex): %s\n", session_id_hex);
sig_hex = ssh_sign(client, role, session_id_hex);
if (sig_hex == NULL) {
fprintf(stderr, "ssh_sign failed: %s\n",
nsigner_client_last_error(client));
goto cleanup;
}
printf("Signature:\n");
printf(" sig_len: %zu hex chars (%zu bytes)\n",
strlen(sig_hex), strlen(sig_hex) / 2);
printf(" sig: %s\n", sig_hex);
printf("\n");
printf("This ed25519 signature can be verified with the public key above\n");
printf("using standard ed25519 verify (e.g. libsodium, OpenSSL EVP_DigestVerify).\n");
rc = 0;
cleanup:
free(pub_hex);
free(key_id);
free(sig_hex);
nsigner_client_free(client);
nostr_cleanup();
return rc;
}