/* * gsign: make signify-compatible Ed25519 keys and signatures * (REPO_DESIGN.txt section 2). A build/maintainer tool, like * gsha256sum. * * gsign -G -p KEY.pub -s KEY.sec [-c COMMENT] * gsign -S -s KEY.sec -m FILE [-x SIGFILE] * gsign -V -p KEY.pub -m FILE [-x SIGFILE] * * SIGFILE defaults to FILE.sig. Exit 0 on success, 1 otherwise. */ #define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include "log.h" #include "sign.h" #define USAGE \ "usage: gsign [-hv]\n" \ " gsign -G -p KEY.pub -s KEY.sec [-c COMMENT]\n" \ " gsign -S -s KEY.sec -m FILE [-x SIGFILE]\n" \ " gsign -V -p KEY.pub -m FILE [-x SIGFILE]\n" static const struct option longopts[] = { { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'v' }, { "generate", no_argument, NULL, 'G' }, { "sign", no_argument, NULL, 'S' }, { "verify", no_argument, NULL, 'V' }, { "pubkey", required_argument, NULL, 'p' }, { "seckey", required_argument, NULL, 's' }, { "message", required_argument, NULL, 'm' }, { "sig", required_argument, NULL, 'x' }, { "comment", required_argument, NULL, 'c' }, { NULL, 0, NULL, 0 } }; static void usage(void) { fprintf(stderr, USAGE); } static void help(void) { printf("gsign: Make and check signify-compatible signatures\n"); printf(USAGE); printf("\n"); printf("%-25s %s\n", "{-h --help}", "Show this message"); printf("%-25s %s\n", "{-v --version}", "Show the current version"); printf("%-25s %s\n", "{-G --generate}", "Generate a new key pair"); printf("%-25s %s\n", "{-S --sign}", "Sign a file"); printf("%-25s %s\n", "{-V --verify}", "Verify a file's signature"); printf("%-25s %s\n", "{-p --pubkey} KEY.pub", "Public key file"); printf("%-25s %s\n", "{-s --seckey} KEY.sec", "Secret key file"); printf("%-25s %s\n", "{-m --message} FILE", "File to sign or verify"); printf("%-25s %s\n", "{-x --sig} SIGFILE", "Signature file (default: FILE.sig)"); printf("%-25s %s\n", "{-c --comment} COMMENT", "Comment for a new key pair (default: gsign)"); printf("\n"); printf("This program is free software.\n"); printf("See the GNU GPL version 3 for details.\n"); } /* " ." about one file. */ static int fail(const char *what, const char *path, int st) { const char *problem; switch (st) { case GL_SIGN_ERR_FORMAT: problem = "is malformed"; break; case GL_SIGN_ERR_ENCRYPTED: problem = "is encrypted: sign with signify"; break; case GL_SIGN_ERR_TOOBIG: problem = "is too large"; break; case GL_SIGN_ERR_EXISTS: problem = "already exists"; break; case GL_SIGN_ERR_WRITE: problem = "cannot be written"; break; default: problem = "cannot be read"; break; } lg_printf(LG_ERR, "%s %s %s.", what, path, problem); return 1; } int main(int argc, char *argv[]) { char mode = 0; const char *pub = NULL, *sec = NULL, *msg = NULL, *sig = NULL; const char *comment = "gsign"; int c; while ((c = getopt_long(argc, argv, "hvGSVp:s:m:x:c:", longopts, NULL)) != -1) { switch (c) { case 'h': help(); return 0; case 'v': printf("v4.0.0\n"); return 0; case 'G': case 'S': case 'V': if (mode && mode != c) { usage(); return 1; } mode = (char)c; break; case 'p': pub = optarg; break; case 's': sec = optarg; break; case 'm': msg = optarg; break; case 'x': sig = optarg; break; case 'c': comment = optarg; break; default: usage(); return 1; } } if (optind != argc) { usage(); return 1; } char sigbuf[PATH_MAX]; if (msg && !sig) { if ((size_t)snprintf(sigbuf, sizeof sigbuf, "%s.sig", msg) >= sizeof sigbuf) { lg_printf(LG_ERR, "Path too long: %s", msg); return 1; } sig = sigbuf; } int st; switch (mode) { case 'G': if (!pub || !sec || msg) { usage(); return 1; } /* The library refuses these too (O_EXCL); this names which. */ struct stat sb; if (lstat(sec, &sb) == 0) return fail("Secret key", sec, GL_SIGN_ERR_EXISTS); if (lstat(pub, &sb) == 0) return fail("Public key", pub, GL_SIGN_ERR_EXISTS); st = gl_sign_generate(pub, sec, comment); if (st == GL_SIGN_ERR_FORMAT) { lg_printf(LG_ERR, "Comment must be one line, at most %d characters.", GL_SIGN_MAX_COMMENT - 11); return 1; } if (st == GL_SIGN_ERR_RANDOM) { lg_printf(LG_ERR, "Cannot get random bytes for a new key."); return 1; } if (st != GL_SIGN_OK) { lg_printf(LG_ERR, "Cannot write %s and %s.", pub, sec); return 1; } lg_printf(LG_SUCCESS, "Wrote %s and %s.", pub, sec); return 0; case 'S': if (!sec || !msg || pub) { usage(); return 1; } if ((st = gl_sign_check_seckey(sec)) != GL_SIGN_OK) return fail("Secret key", sec, st); st = gl_sign_file(sec, msg, sig); if (st == GL_SIGN_ERR_WRITE) return fail("Signature", sig, st); if (st != GL_SIGN_OK) return fail("File", msg, st); lg_printf(LG_SUCCESS, "Signed %s: %s.", msg, sig); return 0; case 'V': { if (!pub || !msg || sec) { usage(); return 1; } gl_sign_pubkey_t pk; gl_sign_sig_t sg; if ((st = gl_sign_read_pubkey(pub, &pk)) != GL_SIGN_OK) return fail("Public key", pub, st); if ((st = gl_sign_read_sig(sig, &sg)) != GL_SIGN_OK) return fail("Signature", sig, st); st = gl_sign_verify_file(pub, msg, sig); switch (st) { case GL_SIGN_OK: lg_printf(LG_SUCCESS, "Signature verified: %s.", msg); return 0; case GL_SIGN_ERR_KEYNUM: lg_printf(LG_ERR, "%s was signed by a different key than %s.", msg, pub); return 1; case GL_SIGN_ERR_BAD: lg_printf(LG_ERR, "Signature does not verify: %s.", msg); return 1; default: return fail("File", msg, st); } } default: usage(); return 1; } }