supercop

Basic CLI for supercop
git clone git://git.finwo.net/app/supercop
Log | Files | Refs | README | LICENSE

commit 0cd54e2ee4f7fc860defffcbc7cd93652051bb12
parent 2999de5cd79be7e539e16ec91764678740c94c51
Author: Robin Bron <robin.bron@yourhosting.nl>
Date:   Sun, 13 Sep 2026 19:48:31 +0200

Fix compiler warnings and add pub-only key support

Diffstat:
Msrc/cli/command/generate.c | 9+++++++--
Msrc/cli/command/printkey.c | 15++++++++++++---
Msrc/cli/command/sign.c | 16+++++++++++++---
Msrc/cli/command/verify.c | 16++++++++++++----
Msrc/cli/common.c | 10++++++++--
Msrc/fmt/0x00/common.h | 6+++---
Msrc/fmt/0x00/decode.c | 23+++++++++++++++++++----
Msrc/fmt/0x00/detect.c | 7++++++-
Msrc/fmt/0x00/encode.c | 18++++++++++++------
Msrc/fmt/0x00/register.c | 4++--
Msrc/fmt/common.h | 6+++---
11 files changed, 97 insertions(+), 33 deletions(-)

diff --git a/src/cli/command/generate.c b/src/cli/command/generate.c @@ -17,7 +17,7 @@ static int cmd_generate(int argc, const char **argv) { }; struct argparse_option options[] = { OPT_HELP(), - OPT_STRING('k', "key-file", &keyFile, "Key file to write (defaults to stdout)"), + OPT_STRING('k', "key-file", &keyFile, "Key file to write (defaults to stdout)", NULL, 0, 0), OPT_END(), }; @@ -51,8 +51,13 @@ static int cmd_generate(int argc, const char **argv) { // Encode in the last-registered format // TODO: allow format selection - int encoded_length; + size_t encoded_length; char *encoded = supercop_formats->encode(&kp, &encoded_length); + if (!encoded) { + fprintf(stderr, "Error while encoding key\n"); + if (keyFile) fclose(fout); + return 1; + } fwrite(encoded, 1, encoded_length, fout); free(encoded); diff --git a/src/cli/command/printkey.c b/src/cli/command/printkey.c @@ -16,7 +16,7 @@ static int cmd_printkey(int argc, const char **argv) { }; struct argparse_option options[] = { OPT_HELP(), - OPT_STRING('k', "key-file", &keyFile, "Select key file to use for the operation"), + OPT_STRING('k', "key-file", &keyFile, "Select key file to use for the operation", NULL, 0, 0), OPT_END(), }; @@ -32,11 +32,20 @@ static int cmd_printkey(int argc, const char **argv) { } kp = readKeyFile(keyFile); + if (!kp || !kp->public_key) { + fprintf(stderr, "Could not decode key file: unknown or invalid format\n"); + if (kp) keypair_free(kp); + return 1; + } fprintf(stdout, "public-key: "); for(i=0;i<32;i++) fprintf(stdout, "%02x", kp->public_key[i]); - fprintf(stdout, "\nprivate-key: "); - for(i=0;i<64;i++) fprintf(stdout, "%02x", kp->private_key[i]); + if (kp->private_key) { + fprintf(stdout, "\nprivate-key: "); + for(i=0;i<64;i++) fprintf(stdout, "%02x", kp->private_key[i]); + } else { + fprintf(stdout, "\nprivate-key: (no private key in file)"); + } fprintf(stdout, "\n"); result = 0; diff --git a/src/cli/command/sign.c b/src/cli/command/sign.c @@ -20,9 +20,9 @@ static int cmd_sign(int argc, const char **argv) { }; struct argparse_option options[] = { OPT_HELP(), - OPT_STRING('k', "key-file", &keyFile, "Select key file to use for the operation"), - OPT_STRING('m', "message", &message, "Message to sign (defaults to stdin)"), - OPT_STRING('M', "message-file", &messageFile, "Message file to sign (defaults to stdin)"), + OPT_STRING('k', "key-file", &keyFile, "Select key file to use for the operation", NULL, 0, 0), + OPT_STRING('m', "message", &message, "Message to sign (defaults to stdin)", NULL, 0, 0), + OPT_STRING('M', "message-file", &messageFile, "Message file to sign (defaults to stdin)", NULL, 0, 0), OPT_END(), }; @@ -39,6 +39,16 @@ static int cmd_sign(int argc, const char **argv) { fmessage = supercop_open_message(message, messageFile); kp = readKeyFile(keyFile); + if (!kp || !kp->public_key) { + fprintf(stderr, "Could not decode key file: unknown or invalid format\n"); + if (kp) keypair_free(kp); + return 1; + } + if (!kp->private_key) { + fprintf(stderr, "Key file contains no private key: signing requires a full key\n"); + keypair_free(kp); + return 1; + } long message_len = fremaining(fmessage); const unsigned char *msg = calloc(1, message_len); diff --git a/src/cli/command/verify.c b/src/cli/command/verify.c @@ -22,10 +22,10 @@ static int cmd_verify(int argc, const char **argv) { }; struct argparse_option options[] = { OPT_HELP(), - OPT_STRING('k', "key-file", &keyFile, "Select key file to use for the operation"), - OPT_STRING('m', "message", &message, "Message to verify (defaults to stdin)"), - OPT_STRING('M', "message-file", &messageFile, "Message file to verify (defaults to stdin)"), - OPT_STRING('s', "signature", &verifySignature, "Signature to verify"), + OPT_STRING('k', "key-file", &keyFile, "Select key file to use for the operation", NULL, 0, 0), + OPT_STRING('m', "message", &message, "Message to verify (defaults to stdin)", NULL, 0, 0), + OPT_STRING('M', "message-file", &messageFile, "Message file to verify (defaults to stdin)", NULL, 0, 0), + OPT_STRING('s', "signature", &verifySignature, "Signature to verify", NULL, 0, 0), OPT_END(), }; @@ -65,6 +65,14 @@ static int cmd_verify(int argc, const char **argv) { } kp = readKeyFile(keyFile); + if (!kp || !kp->public_key) { + fprintf(stderr, "Could not decode key file: unknown or invalid format\n"); + free((void *)sig); + free((void *)msg); + fclose(fsignature); + if (kp) keypair_free(kp); + return 1; + } isValid = ed25519_verify(sig, msg, message_len, kp->public_key); free((void *)sig); diff --git a/src/cli/common.c b/src/cli/common.c @@ -34,16 +34,22 @@ struct KeyPair *readKeyFile(const char *filename) { // Auto-detect format while(fmt) { - if (!fmt->detect(buf)) { + if (!fmt->detect(buf, fsize)) { + fmt = fmt->next; + continue; + } + kp = fmt->decode(buf, fsize); + if (!kp) { fmt = fmt->next; continue; } - kp = fmt->decode(buf); free(buf); fclose(fd); return kp; } + free(buf); + fclose(fd); return NULL; } diff --git a/src/fmt/0x00/common.h b/src/fmt/0x00/common.h @@ -4,8 +4,8 @@ #ifndef __SUPERCOP_FMT_0X00_COMMON_H__ #define __SUPERCOP_FMT_0X00_COMMON_H__ -char fmt_0x00_detect(unsigned char *cipherdata); -char * fmt_0x00_encode(struct KeyPair *keypair, int *len); -struct KeyPair * fmt_0x00_decode(unsigned char *cipherdata); +char fmt_0x00_detect(unsigned char *cipherdata, size_t len); +char * fmt_0x00_encode(struct KeyPair *keypair, size_t *len); +struct KeyPair * fmt_0x00_decode(unsigned char *cipherdata, size_t len); #endif // __SUPERCOP_FMT_0X00_COMMON_H__ diff --git a/src/fmt/0x00/decode.c b/src/fmt/0x00/decode.c @@ -7,12 +7,27 @@ extern "C" { #endif -struct KeyPair * fmt_0x00_decode(unsigned char *cipherdata) { - struct KeyPair *kp = calloc(1, sizeof(struct KeyPair)); +struct KeyPair * fmt_0x00_decode(unsigned char *cipherdata, size_t len) { + struct KeyPair *kp; + // Pub-only (33 bytes) or full (97 bytes), anything else is invalid + if (len != 33 && len != 97) return NULL; + kp = calloc(1, sizeof(struct KeyPair)); + if (!kp) return NULL; kp->public_key = calloc(1, 32); - kp->private_key = calloc(1, 64); + if (!kp->public_key) { + free(kp); + return NULL; + } memcpy(kp->public_key , cipherdata + 1, 32); - memcpy(kp->private_key, cipherdata + 33, 64); + if (len == 97) { + kp->private_key = calloc(1, 64); + if (!kp->private_key) { + free(kp->public_key); + free(kp); + return NULL; + } + memcpy(kp->private_key, cipherdata + 33, 64); + } return kp; } diff --git a/src/fmt/0x00/detect.c b/src/fmt/0x00/detect.c @@ -1,10 +1,15 @@ +#include <stddef.h> + #include "common.h" #ifdef __cplusplus extern "C" { #endif -char fmt_0x00_detect(unsigned char *cipherdata) { +char fmt_0x00_detect(unsigned char *cipherdata, size_t len) { + // Only claim 0x00-prefixed payloads of a known size, so a future format + // sharing the leading null-byte but with a different length won't collide + if (len != 33 && len != 97) return 0; if ((*cipherdata) == '\0') return 1; return 0; } diff --git a/src/fmt/0x00/encode.c b/src/fmt/0x00/encode.c @@ -7,12 +7,18 @@ extern "C" { #endif -char * fmt_0x00_encode(struct KeyPair *kp, int *len) { - *len = 1 + 32 + 64; - char *result = malloc(*len); - result[0] = 0; - memcpy(result + 1, kp->public_key , 32); - memcpy(result + 33, kp->private_key, 64); +char * fmt_0x00_encode(struct KeyPair *kp, size_t *len) { + char *result; + if (!kp || !kp->public_key || !len) return NULL; + result = malloc(1 + 32 + 64); + if (!result) return NULL; + result[0] = 0; + memcpy(result + 1, kp->public_key, 32); + *len = 1 + 32; + if (kp->private_key) { + memcpy(result + 33, kp->private_key, 64); + *len = 1 + 32 + 64; + } return result; } diff --git a/src/fmt/0x00/register.c b/src/fmt/0x00/register.c @@ -10,14 +10,14 @@ extern "C" { extern struct Format *supercop_formats; void __attribute__ ((constructor)) fmt_0x00_register() { - struct Format *fmt = calloc(sizeof(struct Format), 1); + struct Format *fmt = calloc(1, sizeof(struct Format)); fmt->next = supercop_formats; fmt->name = calloc(1,5); fmt->detect = fmt_0x00_detect; fmt->encode = fmt_0x00_encode; fmt->decode = fmt_0x00_decode; supercop_formats = fmt; - strncpy(fmt->name, "0x00", 4); + strcpy(fmt->name, "0x00"); } #ifdef __cplusplus diff --git a/src/fmt/common.h b/src/fmt/common.h @@ -12,9 +12,9 @@ extern "C" { struct Format { void *next; char *name; - char (*detect)(unsigned char *); - char *(*encode)(struct KeyPair *, int*); - struct KeyPair *(*decode)(unsigned char *); + char (*detect)(unsigned char *, size_t); + char *(*encode)(struct KeyPair *, size_t*); + struct KeyPair *(*decode)(unsigned char *, size_t); }; #ifdef __cplusplus