about summary refs log tree commit diff
path: root/src/usr.bin/signify/signify.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/usr.bin/signify/signify.c')
-rw-r--r--src/usr.bin/signify/signify.c266
1 files changed, 174 insertions, 92 deletions
diff --git a/src/usr.bin/signify/signify.c b/src/usr.bin/signify/signify.c
index a4cb84a..dd9a0dc 100644
--- a/src/usr.bin/signify/signify.c
+++ b/src/usr.bin/signify/signify.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: signify.c,v 1.105 2015/12/04 11:05:22 tedu Exp $ */
+/* $OpenBSD: signify.c,v 1.117 2016/09/03 12:21:38 espie Exp $ */
 /*
  * Copyright (c) 2013 Ted Unangst <tedu@openbsd.org>
  *
@@ -34,6 +34,7 @@
 #include <sha2.h>
 
 #include "crypto_api.h"
+#include "signify.h"
 
 #define SIGBYTES crypto_sign_ed25519_BYTES
 #define SECRETBYTES crypto_sign_ed25519_SECRETKEYBYTES
@@ -81,14 +82,14 @@ usage(const char *error)
 #ifndef VERIFYONLY
 	    "\t%1$s -C [-q] -p pubkey -x sigfile [file ...]\n"
 	    "\t%1$s -G [-n] [-c comment] -p pubkey -s seckey\n"
-	    "\t%1$s -S [-e] [-x sigfile] -s seckey -m message\n"
+	    "\t%1$s -S [-ez] [-x sigfile] -s seckey -m message\n"
 #endif
-	    "\t%1$s -V [-eq] [-x sigfile] -p pubkey -m message\n",
+	    "\t%1$s -V [-eqz] [-p pubkey] [-t keytype] [-x sigfile] -m message\n",
 	    __progname);
 	exit(1);
 }
 
-static int
+int
 xopen(const char *fname, int oflags, mode_t mode)
 {
 	struct stat sb;
@@ -112,7 +113,7 @@ xopen(const char *fname, int oflags, mode_t mode)
 	return fd;
 }
 
-static void *
+void *
 xmalloc(size_t len)
 {
 	void *p;
@@ -190,7 +191,7 @@ readmsg(const char *filename, unsigned long long *msglenp)
 				errx(1, "msg too large in %s", filename);
 			space = msglen;
 			if (!(msg = realloc(msg, msglen + space + 1)))
-				errx(1, "realloc");
+				err(1, "realloc");
 		}
 		if ((x = read(fd, msg + msglen, space)) == -1)
 			err(1, "read from %s", filename);
@@ -207,7 +208,7 @@ readmsg(const char *filename, unsigned long long *msglenp)
 	return msg;
 }
 
-static void
+void
 writeall(int fd, const void *buf, size_t buflen, const char *filename)
 {
 	ssize_t x;
@@ -221,26 +222,32 @@ writeall(int fd, const void *buf, size_t buflen, const char *filename)
 }
 
 #ifndef VERIFYONLY
-static void
-writeb64file(const char *filename, const char *comment, const void *buf,
-    size_t buflen, const void *msg, size_t msglen, int oflags, mode_t mode)
+static char *
+createheader(const char *comment, const void *buf, size_t buflen)
 {
-	char header[1024];
+	char *header;
 	char b64[1024];
-	int fd, rv, nr;
 
-	fd = xopen(filename, O_CREAT|oflags|O_NOFOLLOW|O_WRONLY, mode);
-	if ((nr = snprintf(header, sizeof(header), "%s%s\n",
-	    COMMENTHDR, comment)) == -1 || nr >= sizeof(header))
-		errx(1, "comment too long");
-	writeall(fd, header, strlen(header), filename);
-	if ((rv = b64_ntop(buf, buflen, b64, sizeof(b64))) == -1)
+	if (b64_ntop(buf, buflen, b64, sizeof(b64)) == -1)
 		errx(1, "base64 encode failed");
-	b64[rv++] = '\n';
-	writeall(fd, b64, rv, filename);
+	if (asprintf(&header, "%s%s\n%s\n", COMMENTHDR, comment, b64) == -1)
+		err(1, "asprintf failed");
 	explicit_bzero(b64, sizeof(b64));
-	if (msg)
-		writeall(fd, msg, msglen, filename);
+	return header;
+}
+
+static void
+writekeyfile(const char *filename, const char *comment, const void *buf,
+    size_t buflen, int oflags, mode_t mode)
+{
+	char *header;
+	int fd;
+
+	fd = xopen(filename, O_CREAT|oflags|O_NOFOLLOW|O_WRONLY, mode);
+	header = createheader(comment, buf, buflen);
+	writeall(fd, header, strlen(header), filename);
+	explicit_bzero(header, strlen(header));
+	free(header);
 	close(fd);
 }
 
@@ -325,8 +332,8 @@ generate(const char *pubkeyfile, const char *seckeyfile, int rounds,
 	if ((nr = snprintf(commentbuf, sizeof(commentbuf), "%s secret key",
 	    comment)) == -1 || nr >= sizeof(commentbuf))
 		errx(1, "comment too long");
-	writeb64file(seckeyfile, commentbuf, &enckey,
-	    sizeof(enckey), NULL, 0, O_EXCL, 0600);
+	writekeyfile(seckeyfile, commentbuf, &enckey,
+	    sizeof(enckey), O_EXCL, 0600);
 	explicit_bzero(&enckey, sizeof(enckey));
 
 	memcpy(pubkey.pkalg, PKALG, 2);
@@ -334,27 +341,37 @@ generate(const char *pubkeyfile, const char *seckeyfile, int rounds,
 	if ((nr = snprintf(commentbuf, sizeof(commentbuf), "%s public key",
 	    comment)) == -1 || nr >= sizeof(commentbuf))
 		errx(1, "comment too long");
-	writeb64file(pubkeyfile, commentbuf, &pubkey,
-	    sizeof(pubkey), NULL, 0, O_EXCL, 0666);
+	writekeyfile(pubkeyfile, commentbuf, &pubkey,
+	    sizeof(pubkey), O_EXCL, 0666);
 }
 
-static void
-sign(const char *seckeyfile, const char *msgfile, const char *sigfile,
-    int embedded)
+uint8_t *
+createsig(const char *seckeyfile, const char *msgfile, uint8_t *msg,
+    unsigned long long msglen)
 {
-	struct sig sig;
-	uint8_t digest[SHA512_DIGEST_LENGTH];
 	struct enckey enckey;
 	uint8_t xorkey[sizeof(enckey.seckey)];
-	uint8_t *msg;
-	char comment[COMMENTMAXLEN], sigcomment[COMMENTMAXLEN];
+	struct sig sig;
+	char *sighdr;
 	char *secname;
-	unsigned long long msglen;
-	int i, rounds, nr;
+	uint8_t digest[SHA512_DIGEST_LENGTH];
+	int i, nr, rounds;
 	SHA2_CTX ctx;
+	char comment[COMMENTMAXLEN], sigcomment[COMMENTMAXLEN];
 
 	readb64file(seckeyfile, &enckey, sizeof(enckey), comment);
 
+	secname = strstr(seckeyfile, ".sec");
+	if (secname && strlen(secname) == 4) {
+		if ((nr = snprintf(sigcomment, sizeof(sigcomment), VERIFYWITH "%.*s.pub",
+		    (int)strlen(seckeyfile) - 4, seckeyfile)) == -1 || nr >= sizeof(sigcomment))
+			errx(1, "comment too long");
+	} else {
+		if ((nr = snprintf(sigcomment, sizeof(sigcomment), "signature from %s",
+		    comment)) == -1 || nr >= sizeof(sigcomment))
+			errx(1, "comment too long");
+	}
+
 	if (memcmp(enckey.kdfalg, KDFALG, 2) != 0)
 		errx(1, "unsupported KDF");
 	rounds = ntohl(enckey.kdfrounds);
@@ -370,29 +387,35 @@ sign(const char *seckeyfile, const char *msgfile, const char *sigfile,
 		errx(1, "incorrect passphrase");
 	explicit_bzero(digest, sizeof(digest));
 
-	msg = readmsg(msgfile, &msglen);
-
 	signmsg(enckey.seckey, msg, msglen, sig.sig);
 	memcpy(sig.keynum, enckey.keynum, KEYNUMLEN);
 	explicit_bzero(&enckey, sizeof(enckey));
 
 	memcpy(sig.pkalg, PKALG, 2);
-	secname = strstr(seckeyfile, ".sec");
-	if (secname && strlen(secname) == 4) {
-		if ((nr = snprintf(sigcomment, sizeof(sigcomment), VERIFYWITH "%.*s.pub",
-		    (int)strlen(seckeyfile) - 4, seckeyfile)) == -1 || nr >= sizeof(sigcomment))
-			errx(1, "comment too long");
-	} else {
-		if ((nr = snprintf(sigcomment, sizeof(sigcomment), "signature from %s",
-		    comment)) == -1 || nr >= sizeof(sigcomment))
-			errx(1, "comment too long");
-	}
+
+	sighdr = createheader(sigcomment, &sig, sizeof(sig));
+	return sighdr;
+}
+
+static void
+sign(const char *seckeyfile, const char *msgfile, const char *sigfile,
+    int embedded)
+{
+	uint8_t *msg;
+	char *sighdr;
+	int fd;
+	unsigned long long msglen;
+
+	msg = readmsg(msgfile, &msglen);
+
+	sighdr = createsig(seckeyfile, msgfile, msg, msglen);
+
+	fd = xopen(sigfile, O_CREAT|O_TRUNC|O_NOFOLLOW|O_WRONLY, 0666);
+	writeall(fd, sighdr, strlen(sighdr), sigfile);
+	free(sighdr);
 	if (embedded)
-		writeb64file(sigfile, sigcomment, &sig, sizeof(sig), msg,
-		    msglen, O_TRUNC, 0666);
-	else
-		writeb64file(sigfile, sigcomment, &sig, sizeof(sig), NULL,
-		    0, O_TRUNC, 0666);
+		writeall(fd, msg, msglen, sigfile);
+	close(fd);
 
 	free(msg);
 }
@@ -422,9 +445,30 @@ verifymsg(struct pubkey *pubkey, uint8_t *msg, unsigned long long msglen,
 	free(dummybuf);
 }
 
+#ifndef VERIFYONLY
+static void
+check_keytype(const char *pubkeyfile, const char *keytype)
+{
+	size_t len;
+	char *cmp;
+	int slen;
+	
+	len = strlen(pubkeyfile);
+	slen = asprintf(&cmp, "-%s.pub", keytype);
+	if (slen < 0)
+		err(1, "asprintf error");
+	if (len < slen)
+		errx(1, "too short");
+
+	if (strcmp(pubkeyfile + len - slen, cmp) != 0)
+		errx(1, "wrong keytype");
+	free(cmp);
+}
+#endif
+
 static void
 readpubkey(const char *pubkeyfile, struct pubkey *pubkey,
-    const char *sigcomment)
+    const char *sigcomment, const char *keytype)
 {
 	const char *safepath = "/etc/signify/";
 
@@ -435,6 +479,10 @@ readpubkey(const char *pubkeyfile, struct pubkey *pubkey,
 			if (strncmp(pubkeyfile, safepath, strlen(safepath)) != 0 ||
 			    strstr(pubkeyfile, "/../") != NULL)
 				errx(1, "untrusted path %s", pubkeyfile);
+#ifndef VERIFYONLY
+			if (keytype)
+				check_keytype(pubkeyfile, keytype);
+#endif
 		} else
 			usage("must specify pubkey");
 	}
@@ -443,7 +491,7 @@ readpubkey(const char *pubkeyfile, struct pubkey *pubkey,
 
 static void
 verifysimple(const char *pubkeyfile, const char *msgfile, const char *sigfile,
-    int quiet)
+    int quiet, const char *keytype)
 {
 	char sigcomment[COMMENTMAXLEN];
 	struct sig sig;
@@ -454,7 +502,7 @@ verifysimple(const char *pubkeyfile, const char *msgfile, const char *sigfile,
 	msg = readmsg(msgfile, &msglen);
 
 	readb64file(sigfile, &sig, sizeof(sig), sigcomment);
-	readpubkey(pubkeyfile, &pubkey, sigcomment);
+	readpubkey(pubkeyfile, &pubkey, sigcomment, keytype);
 
 	verifymsg(&pubkey, msg, msglen, &sig, quiet);
 
@@ -463,7 +511,7 @@ verifysimple(const char *pubkeyfile, const char *msgfile, const char *sigfile,
 
 static uint8_t *
 verifyembedded(const char *pubkeyfile, const char *sigfile,
-    int quiet, unsigned long long *msglenp)
+    int quiet, unsigned long long *msglenp, const char *keytype)
 {
 	char sigcomment[COMMENTMAXLEN];
 	struct sig sig;
@@ -474,7 +522,7 @@ verifyembedded(const char *pubkeyfile, const char *sigfile,
 	msg = readmsg(sigfile, &msglen);
 
 	siglen = parseb64file(sigfile, msg, &sig, sizeof(sig), sigcomment);
-	readpubkey(pubkeyfile, &pubkey, sigcomment);
+	readpubkey(pubkeyfile, &pubkey, sigcomment, keytype);
 
 	msglen -= siglen;
 	memmove(msg, msg + siglen, msglen);
@@ -488,20 +536,21 @@ verifyembedded(const char *pubkeyfile, const char *sigfile,
 
 static void
 verify(const char *pubkeyfile, const char *msgfile, const char *sigfile,
-    int embedded, int quiet)
+    int embedded, int quiet, const char *keytype)
 {
 	unsigned long long msglen;
 	uint8_t *msg;
 	int fd;
 
 	if (embedded) {
-		msg = verifyembedded(pubkeyfile, sigfile, quiet, &msglen);
+		msg = verifyembedded(pubkeyfile, sigfile, quiet, &msglen,
+		    keytype);
 		fd = xopen(msgfile, O_CREAT|O_TRUNC|O_NOFOLLOW|O_WRONLY, 0666);
 		writeall(fd, msg, msglen, msgfile);
 		free(msg);
 		close(fd);
 	} else {
-		verifysimple(pubkeyfile, msgfile, sigfile, quiet);
+		verifysimple(pubkeyfile, msgfile, sigfile, quiet, keytype);
 	}
 }
 
@@ -637,11 +686,31 @@ check(const char *pubkeyfile, const char *sigfile, int quiet, int argc,
 	unsigned long long msglen;
 	uint8_t *msg;
 
-	msg = verifyembedded(pubkeyfile, sigfile, quiet, &msglen);
+	msg = verifyembedded(pubkeyfile, sigfile, quiet, &msglen, NULL);
 	verifychecksums((char *)msg, argc, argv, quiet);
 
 	free(msg);
 }
+
+void *
+verifyzdata(uint8_t *zdata, unsigned long long zdatalen, 
+    const char *filename, const char *pubkeyfile, const char *keytype)
+{
+	struct sig sig;
+	char sigcomment[COMMENTMAXLEN];
+	unsigned long long siglen;
+	struct pubkey pubkey;
+
+	if (zdatalen < sizeof(sig))
+		errx(1, "signature too short in %s", filename);
+	siglen = parseb64file(filename, zdata, &sig, sizeof(sig), 
+	    sigcomment);
+	readpubkey(pubkeyfile, &pubkey, sigcomment, keytype);
+	zdata += siglen;
+	zdatalen -= siglen;
+	verifymsg(&pubkey, zdata, zdatalen, &sig, 1);
+	return zdata;
+}
 #endif
 
 int
@@ -651,9 +720,11 @@ main(int argc, char **argv)
 	    *sigfile = NULL;
 	char sigfilebuf[PATH_MAX];
 	const char *comment = "signify";
+	char *keytype = NULL;
 	int ch, rounds;
 	int embedded = 0;
 	int quiet = 0;
+	int gzip = 0;
 	enum {
 		NONE,
 		CHECK,
@@ -667,7 +738,7 @@ main(int argc, char **argv)
 
 	rounds = 42;
 
-	while ((ch = getopt(argc, argv, "CGSVc:em:np:qs:x:")) != -1) {
+	while ((ch = getopt(argc, argv, "CGSVzc:em:np:qs:t:x:")) != -1) {
 		switch (ch) {
 #ifndef VERIFYONLY
 		case 'C':
@@ -685,6 +756,9 @@ main(int argc, char **argv)
 				usage(NULL);
 			verb = SIGN;
 			break;
+		case 'z':
+			gzip = 1;
+			break;
 #endif
 		case 'V':
 			if (verb)
@@ -712,6 +786,9 @@ main(int argc, char **argv)
 		case 's':
 			seckeyfile = optarg;
 			break;
+		case 't':
+			keytype = optarg;
+			break;
 		case 'x':
 			sigfile = optarg;
 			break;
@@ -723,35 +800,16 @@ main(int argc, char **argv)
 	argc -= optind;
 	argv += optind;
 
+	if (embedded && gzip)
+		errx(1, "can't combine -e and -z options");
+
 	if (setvbuf(stdout, NULL, _IOLBF, 0) != 0)
 		err(1, "setvbuf");
 
-	switch (verb) {
-	case GENERATE:
-	case SIGN:
-		/* keep it all */
-		break;
-	case CHECK:
-		if (pledge("stdio rpath", NULL) == -1)
-			err(1, "pledge");
-		break;
-	case VERIFY:
-		if (embedded && (!msgfile || strcmp(msgfile, "-") != 0)) {
-			if (pledge("stdio rpath wpath cpath", NULL) == -1)
-				err(1, "pledge");
-		} else {
-			if (pledge("stdio rpath", NULL) == -1)
-				err(1, "pledge");
-		}
-		break;
-	default:
-		if (pledge("stdio", NULL) == -1)
-			err(1, "pledge");
-		break;
-	}
-
 #ifndef VERIFYONLY
 	if (verb == CHECK) {
+		if (pledge("stdio rpath", NULL) == -1)
+			err(1, "pledge");
 		if (!sigfile)
 			usage("must specify sigfile");
 		check(pubkeyfile, sigfile, quiet, argc, argv);
@@ -775,22 +833,46 @@ main(int argc, char **argv)
 	switch (verb) {
 #ifndef VERIFYONLY
 	case GENERATE:
+		/* no pledge */
 		if (!pubkeyfile || !seckeyfile)
 			usage("must specify pubkey and seckey");
 		generate(pubkeyfile, seckeyfile, rounds, comment);
 		break;
 	case SIGN:
-		if (!msgfile || !seckeyfile)
-			usage("must specify message and seckey");
-		sign(seckeyfile, msgfile, sigfile, embedded);
+		/* no pledge */
+		if (gzip) {
+			if (!msgfile || !seckeyfile || !sigfile)
+				usage("must specify message sigfile seckey");
+			zsign(seckeyfile, msgfile, sigfile);
+		} else {
+			if (!msgfile || !seckeyfile)
+				usage("must specify message and seckey");
+			sign(seckeyfile, msgfile, sigfile, embedded);
+		}
 		break;
 #endif
 	case VERIFY:
-		if (!msgfile)
-			usage("must specify message");
-		verify(pubkeyfile, msgfile, sigfile, embedded, quiet);
+		if ((embedded || gzip) &&
+		    (msgfile && strcmp(msgfile, "-") != 0)) {
+			/* will need to create output file */
+			if (pledge("stdio rpath wpath cpath", NULL) == -1)
+				err(1, "pledge");
+		} else {
+			if (pledge("stdio rpath", NULL) == -1)
+				err(1, "pledge");
+		}
+		if (gzip) {
+			zverify(pubkeyfile, msgfile, sigfile, keytype);
+		} else {
+			if (!msgfile)
+				usage("must specify message");
+			verify(pubkeyfile, msgfile, sigfile, embedded, 
+			    quiet, keytype);
+		}
 		break;
 	default:
+		if (pledge("stdio", NULL) == -1)
+			err(1, "pledge");
 		usage(NULL);
 		break;
 	}