commit 4ecf19685e189ee062c8e20462070541c6b1e390
parent 6e1abcffdecc6029cd9b55c4868ca6bead7017fd
Author: Robin Bron <finwo@pm.me>
Date: Wed, 15 Sep 2021 23:54:44 +0200
First signature & validation
Diffstat:
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