supercop

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

commit 4ecf19685e189ee062c8e20462070541c6b1e390
parent 6e1abcffdecc6029cd9b55c4868ca6bead7017fd
Author: Robin Bron <finwo@pm.me>
Date:   Wed, 15 Sep 2021 23:54:44 +0200

First signature & validation

Diffstat:
M.gitignore | 1+
MMakefile | 11+++++++++--
Asrc/fmt/0x00/common.h | 11+++++++++++
Asrc/fmt/0x00/decode.c | 21+++++++++++++++++++++
Asrc/fmt/0x00/detect.c | 14++++++++++++++
Asrc/fmt/0x00/encode.c | 21+++++++++++++++++++++
Asrc/fmt/0x00/register.c | 25+++++++++++++++++++++++++
Asrc/fmt/common.h | 24++++++++++++++++++++++++
Asrc/keypair/keypair.c | 18++++++++++++++++++
Asrc/keypair/keypair.h | 19+++++++++++++++++++
Msrc/main.c | 306+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
11 files changed, 422 insertions(+), 49 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -1,3 +1,4 @@ *.o +*.asc lib/ /supercop diff --git a/Makefile b/Makefile @@ -1,9 +1,16 @@ +VERSION=v0.0.0 + # Ourselves CFLAGS?= -CFLAGS+=-O2 -mtune=native -march=native -pipe -Wall +CFLAGS+=-D VERSION=\"$(VERSION)\" +CFLAGS+=-O2 -mtune=native -march=native -pipe +CFLAGS+=-Wall INCLUDES?= INCLUDES+=-Isrc -SRC:=$(wildcard src/*.c) +SRC:= +SRC+=$(wildcard src/*.c) +SRC+=$(wildcard src/*/*.c) +SRC+=$(wildcard src/*/*/*.c) # lib/argparse INCLUDES+=-Ilib/argparse diff --git a/src/fmt/0x00/common.h b/src/fmt/0x00/common.h @@ -0,0 +1,11 @@ +#include "../common.h" +#include "../../keypair/keypair.h" + +#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); + +#endif // __SUPERCOP_FMT_0X00_COMMON_H__ diff --git a/src/fmt/0x00/decode.c b/src/fmt/0x00/decode.c @@ -0,0 +1,21 @@ +#include <stdlib.h> +#include <string.h> + +#include "common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct KeyPair * fmt_0x00_decode(unsigned char *cipherdata) { + struct KeyPair *kp = calloc(1, sizeof(struct KeyPair)); + kp->public_key = calloc(1, 32); + kp->private_key = calloc(1, 64); + memcpy(kp->public_key , cipherdata + 1, 32); + memcpy(kp->private_key, cipherdata + 33, 64); + return kp; +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/src/fmt/0x00/detect.c b/src/fmt/0x00/detect.c @@ -0,0 +1,14 @@ +#include "common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +char fmt_0x00_detect(unsigned char *cipherdata) { + if ((*cipherdata) == '\0') return 1; + return 0; +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/src/fmt/0x00/encode.c b/src/fmt/0x00/encode.c @@ -0,0 +1,21 @@ +#include <stdlib.h> +#include <string.h> + +#include "common.h" + +#ifdef __cplusplus +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); + return result; +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/src/fmt/0x00/register.c b/src/fmt/0x00/register.c @@ -0,0 +1,25 @@ +#include <stdlib.h> +#include <string.h> + +#include "common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern struct Format *supercop_formats; + +void __attribute__ ((constructor)) fmt_0x00_register() { + struct Format *fmt = calloc(sizeof(struct Format), 1); + 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); +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/src/fmt/common.h b/src/fmt/common.h @@ -0,0 +1,24 @@ +#ifndef __SUPERCOP_FMT_COMMON_H__ +#define __SUPERCOP_FMT_COMMON_H__ + +#include <stddef.h> + +#include "../keypair/keypair.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct Format { + void *next; + char *name; + char (*detect)(unsigned char *); + char *(*encode)(struct KeyPair *, int*); + struct KeyPair *(*decode)(unsigned char *); +}; + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // __SUPERCOP_FMT_COMMON_H__ diff --git a/src/keypair/keypair.c b/src/keypair/keypair.c @@ -0,0 +1,18 @@ +#include <stdlib.h> +/* #include <string.h> */ + +#include "keypair.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void keypair_free(struct KeyPair *kp) { + free(kp->public_key); + free(kp->private_key); + free(kp); +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/src/keypair/keypair.h b/src/keypair/keypair.h @@ -0,0 +1,19 @@ +#ifndef __SUPERCOP_KEYPAIR_KEYPAIR_H__ +#define __SUPERCOP_KEYPAIR_KEYPAIR_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +struct KeyPair { + unsigned char *public_key; + unsigned char *private_key; +}; + +void keypair_free(struct KeyPair *kp); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // __SUPERCOP_KEYPAIR_KEYPAIR_H__ diff --git a/src/main.c b/src/main.c @@ -1,103 +1,230 @@ +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define COMMAND_GENERATE 1 +#define COMMAND_PRINTKEY 2 +#define COMMAND_SIGN 4 +#define COMMAND_VERIFY 8 +#define COMMAND_VERSION 16 + +#ifndef VERSION +#define VERSION "n/a" +#endif + #ifdef __cplusplus extern "C" { #endif -#define COMMAND_GENERATE 1 -#define COMMAND_PUBKEY 2 -#define COMMAND_SIGN 4 -#define COMMAND_VERIFY 8 +#include "../lib/argparse/argparse.h" +#include "../lib/ed25519/src/ed25519.h" +#include "fmt/common.h" +#include "keypair/keypair.h" -#include <stddef.h> -#include <stdio.h> -#include <stdlib.h> - -#include "argparse.h" -#include "ed25519.h" +struct Format *supercop_formats = NULL; static const char *const usage[] = { "supercop <command> [options]", NULL, }; +long fremaining(FILE *fd) { + long current = ftell(fd); + fseek(fd, 0, SEEK_END); + long end = ftell(fd); + fseek(fd, current, SEEK_SET); + return end - current; +} + +struct KeyPair * readKeyFile(const char *filename) { + struct Format *fmt = supercop_formats; + FILE *fd = fopen(filename, "r"); + unsigned char *buf; + struct KeyPair *kp; + + if (!fd) { + fprintf(stderr, "Could not open key file\n"); + exit(1); + } + + // Read whole file + long fsize = fremaining(fd); + buf = calloc(1, fsize + 1); + fread(buf, 1, fsize, fd); + + // Auto-detect format + while(fmt) { + if (!fmt->detect(buf)) { + fmt = fmt->next; + continue; + } + kp = fmt->decode(buf); + free(buf); + fclose(fd); + return kp; + } + + return NULL; +} + int cmd_generate(FILE *fd) { unsigned char version = 0; unsigned char seed[32]; unsigned char public_key[32]; unsigned char private_key[64]; + // Generate random seed if (ed25519_create_seed(seed)) { fprintf(stderr, "Error while generating seed\n"); - return 1; + exit(1); } + // Generate the actual key ed25519_create_keypair(public_key, private_key, seed); + struct KeyPair kp; + kp.public_key = public_key; + kp.private_key = private_key; - // Hardcoded to format 0x00 - fwrite(&version, 1, 1, fd); - fwrite(public_key, 1, 32, fd); - fwrite(private_key, 1, 64, fd); + // Encode in the last-registered format + // TODO: allow format selection + int encoded_length; + char *encoded = supercop_formats->encode(&kp, &encoded_length); + fwrite(encoded, 1, encoded_length, fd); + + // Clean up + free(encoded); return 0; } -int cmd_pubkey() { +// Prints human-readable version of the keypair +// Should be easy in high-level languages as well +int cmd_printkey(struct KeyPair *kp, FILE *fout) { + int i; + fprintf(fout, "public-key: "); + for(i=0;i<32;i++) fprintf(fout, "%02x", kp->public_key[i]); + fprintf(fout, "\nprivate-key: "); + for(i=0;i<64;i++) fprintf(fout, "%02x", kp->private_key[i]); + fprintf(fout, "\n"); return 0; } -int cmd_sign() { +int cmd_sign(struct KeyPair *kp, FILE *fmessage, FILE *fout) { + int i; + + // Read message + // TODO: make this stdin-compatible + long message_len = fremaining(fmessage); + const unsigned char *message = calloc(1, message_len); + fread(message, 1, message_len, fmessage); + + // Create signature + unsigned char *signature = calloc(1, 64); + ed25519_sign(signature, message, message_len, kp->public_key, kp->private_key); + + // Output signature + for(i=0;i<64;i++) fprintf(fout, "%02x", signature[i]); + fprintf(fout, "\n"); + + // Clean up + free(signature); + free(message); return 0; } -int cmd_verify() { - return 0; + +int cmd_verify(struct KeyPair *kp, FILE *fmessage, FILE *fsignature, FILE *fout) { + int isValid; + + // Read message + // TODO: make this stdin-compatible + long message_len = fremaining(fmessage); + const unsigned char *message = calloc(1, message_len); + fread(message, 1, message_len, fmessage); + + // Read signature + long signature_len = fremaining(fsignature); + const unsigned char *signature = calloc(1, signature_len); + fread(signature, 1, signature_len, fsignature); + if (signature_len != 64) { + fprintf(stderr, "Invalid signature!!\n"); + exit(1); + } + + // Verify the signature + isValid = ed25519_verify(signature, message, message_len, kp->public_key); + + // Clean up + free(signature); + free(message); + + // Handle response + if (isValid) { + fprintf(fout, "OK\n"); + return 0; + } else { + fprintf(fout, "FAIL\n"); + return 1; + } } int main(int argc, const char **argv) { - const char *identityFile = NULL; + struct KeyPair *kp; + const char *keyFile = NULL; const char *verifySignature = NULL; const char *message = NULL; const char *messageFile = NULL; + unsigned char c; int command = 0; int result = 0; + int i; + const char *pos; - FILE *fidentity = NULL; - FILE *fmessage = NULL; - FILE *fout = NULL; + FILE *fmessage = NULL; + FILE *fsignature = NULL; + FILE *fout = NULL; // Setup help & argument parsing struct argparse_option options[] = { OPT_GROUP("Operations"), - OPT_BIT(0, "pubkey" , &command, "Generate new key" , NULL, COMMAND_GENERATE), - OPT_BIT(0, "generate", &command, "Generate new key" , NULL, COMMAND_GENERATE), - OPT_BIT(0, "sign" , &command, "Sign a message" , NULL, COMMAND_SIGN ), - OPT_BIT(0, "verify" , &command, "Verify a message signature", NULL, COMMAND_VERIFY ), + OPT_BIT(0, "printkey", &command, "Print contents of key file" , NULL, COMMAND_PRINTKEY), + OPT_BIT(0, "generate", &command, "Generate new key" , NULL, COMMAND_GENERATE), + OPT_BIT(0, "sign" , &command, "Sign a message" , NULL, COMMAND_SIGN ), + OPT_BIT(0, "verify" , &command, "Verify a message signature" , NULL, COMMAND_VERIFY ), + OPT_BIT(0, "version" , &command, "Show version number and exit", NULL, COMMAND_VERSION ), OPT_GROUP("Basic options"), OPT_HELP(), - OPT_STRING('i', "identity-file", &identityFile , "Select identity file to use for the operation"), - OPT_STRING('m', "message" , &message , "Message to sign or verify (defaults to stdin)"), - OPT_STRING('M', "message-file" , &messageFile , "Message file to sign or 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"), + OPT_STRING('m', "message" , &message , "Message to sign or verify (defaults to stdin)"), + OPT_STRING('M', "message-file", &messageFile , "Message file to sign or verify (defaults to stdin)"), + OPT_STRING('s', "signature" , &verifySignature, "Signature to verify"), OPT_END(), }; // Setup basic information struct argparse argparse; argparse_init(&argparse, options, usage, 0); - argparse_describe(&argparse, "\nMinimalistic program to generate ed25519 keys and verify/sign messages", "\n (c) 2021 finwo"); + argparse_describe(&argparse, "\nMinimalistic program to generate ed25519 keys and verify/sign messages", NULL); // Actually parse the arguments argc = argparse_parse(&argparse, argc, argv); // Our paths separate here switch(command) { + + case COMMAND_VERSION: + fprintf(stdout, "%s\n", VERSION); + return 0; + case COMMAND_GENERATE: // Build output fd fout = stdout; - if (identityFile) { - fout = fopen(identityFile, "w+"); - if (fout < 1) { - fprintf(stderr, "Could not open output file"); - return 1; + if (keyFile) { + fout = fopen(keyFile, "w+"); + if (!fout) { + fprintf(stderr, "Could not open output file\n"); + exit(1); } } @@ -105,25 +232,110 @@ int main(int argc, const char **argv) { result = cmd_generate(fout); // Close output fd if file - if (identityFile) { + if (keyFile) { fclose(fout); } // Done return result; - case COMMAND_PUBKEY: - return cmd_pubkey(); - case COMMAND_VERIFY: - return cmd_verify(); + case COMMAND_PRINTKEY: + + // No key file = error + if (!keyFile) { + fprintf(stderr, "Missing required argument: key-file\n\n"); + argparse_usage(&argparse); + exit(1); + } + + kp = readKeyFile(keyFile); + result = cmd_printkey(kp, stdout); + keypair_free(kp); + return result; + case COMMAND_SIGN: - return cmd_sign(); - default: - argparse_usage(&argparse); - return 1; + + // No key file = error + if (!keyFile) { + fprintf(stderr, "Missing required argument: key-file\n\n"); + argparse_usage(&argparse); + exit(1); + } + + // Fetch message as FILE* + fmessage = stdin; + if (message) { + fmessage = tmpfile(); + fwrite(message, 1, strlen(message), fmessage); + fseek(fmessage, 0, SEEK_SET); + } + if (messageFile) { + if (message) fclose(fmessage); + fmessage = fopen(messageFile, "r"); + if (!fmessage) { + fprintf(stderr, "Could not open message file\n"); + exit(1); + } + } + + // Read the key file we're using + kp = readKeyFile(keyFile); + result = cmd_sign(kp, fmessage, stdout); + keypair_free(kp); + return result; + + case COMMAND_VERIFY: + + // No key file = error + if (!keyFile) { + fprintf(stderr, "Missing required argument: key-file\n\n"); + argparse_usage(&argparse); + exit(1); + } + + // Fetch message as FILE* + fmessage = stdin; + if (message) { + fmessage = tmpfile(); + fwrite(message, 1, strlen(message), fmessage); + fseek(fmessage, 0, SEEK_SET); + } + if (messageFile) { + if (message) fclose(fmessage); + fmessage = fopen(messageFile, "r"); + if (!fmessage) { + fprintf(stderr, "Could not open message file\n"); + exit(1); + } + } + + // Signature = required during verify + if (!verifySignature) { + fprintf(stderr, "Missing required argument: signature\n\n"); + argparse_usage(&argparse); + exit(1); + } + + // Parse signature as hex & keep as FILE* + fsignature = tmpfile(); + pos = verifySignature; + while(*pos) { + sscanf(pos, "%2hhx", &c); + fputc(c, fsignature); + pos += 2; + } + fseek(fsignature, 0, SEEK_SET); + + // Read key & verify signature + kp = readKeyFile(keyFile); + result = cmd_verify(kp, fmessage, fsignature, stdout); + fclose(fsignature); + keypair_free(kp); + return result; } - return 42; + argparse_usage(&argparse); + return 1; } #ifdef __cplusplus