about summary refs log tree commit diff
path: root/src/usr.bin/signify
diff options
context:
space:
mode:
Diffstat (limited to 'src/usr.bin/signify')
-rw-r--r--src/usr.bin/signify/Makefile3
-rw-r--r--src/usr.bin/signify/signify.136
-rw-r--r--src/usr.bin/signify/signify.c266
3 files changed, 207 insertions, 98 deletions
diff --git a/src/usr.bin/signify/Makefile b/src/usr.bin/signify/Makefile
index 09c3075..161c43d 100644
--- a/src/usr.bin/signify/Makefile
+++ b/src/usr.bin/signify/Makefile
@@ -1,6 +1,7 @@
-#	$OpenBSD: Makefile,v 1.10 2014/07/22 00:41:19 deraadt Exp $
+#	$OpenBSD: Makefile,v 1.11 2016/09/02 16:10:56 espie Exp $
 
 SRCS=	signify.c
+SRCS+=	zsig.c
 SRCS+=	fe25519.c sc25519.c smult_curve25519_ref.c
 SRCS+=	mod_ed25519.c mod_ge25519.c
 SRCS+=	crypto_api.c
diff --git a/src/usr.bin/signify/signify.1 b/src/usr.bin/signify/signify.1
index 349c02f..92b13f9 100644
--- a/src/usr.bin/signify/signify.1
+++ b/src/usr.bin/signify/signify.1
@@ -1,4 +1,4 @@
-.\" $OpenBSD: signify.1,v 1.34 2016/05/11 18:07:28 deraadt Exp $
+.\" $OpenBSD: signify.1,v 1.38 2016/09/02 21:04:26 tedu Exp $
 .\"
 .\"Copyright (c) 2013 Marc Espie <espie@openbsd.org>
 .\"Copyright (c) 2013 Ted Unangst <tedu@openbsd.org>
@@ -14,7 +14,7 @@
 .\"WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 .\"ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\"OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-.Dd $Mdocdate: May 11 2016 $
+.Dd $Mdocdate: September 2 2016 $
 .Dt SIGNIFY 1
 .Os
 .Sh NAME
@@ -35,15 +35,16 @@
 .Fl s Ar seckey
 .Nm signify
 .Fl S
-.Op Fl e
+.Op Fl ez
 .Op Fl x Ar sigfile
 .Fl s Ar seckey
 .Fl m Ar message
 .Nm signify
 .Fl V
-.Op Fl eq
+.Op Fl eqz
+.Op Fl p Ar pubkey
+.Op Fl t Ar keytype
 .Op Fl x Ar sigfile
-.Fl p Ar pubkey
 .Fl m Ar message
 .Sh DESCRIPTION
 The
@@ -104,10 +105,21 @@ Secret (private) key produced by
 and used by
 .Fl S
 to sign a message.
+.It Fl t Ar keytype
+When deducing the correct key to check a signature, make sure
+the actual key matches
+.Pa /etc/signify/<somekey>-keytype.pub .
 .It Fl x Ar sigfile
 The signature file to create or verify.
 The default is
 .Ar message Ns .sig .
+.It Fl z
+Sign and verify
+.Xr gzip 1
+archives, where the signing data
+is embedded in the
+.Xr gzip 1
+header.
 .El
 .Pp
 The key and signature files created by
@@ -155,8 +167,19 @@ Verify a bsd.rd before an upgrade:
 .Bd -literal -offset indent -compact
 $ signify -C -p /etc/signify/openbsd-61-base.pub -x SHA256.sig bsd.rd
 .Ed
+.Pp
+Sign a gzip archive:
+.Bd -literal -offset indent -compact
+$ signify -Sz -s key-arc.sec -m in.tgz -x out.tgz
+.Ed
+.Pp
+Verify a gzip pipeline:
+.Bd -literal -offset indent -compact
+$ ftp url | signify -Vz -t arc | tar ztf -
+.Ed
 .Sh SEE ALSO
 .Xr fw_update 1 ,
+.Xr gzip 1 ,
 .Xr pkg_add 1 ,
 .Xr sha256 1
 .Sh HISTORY
@@ -165,4 +188,7 @@ The
 command first appeared in
 .Ox 5.5 .
 .Sh AUTHORS
+.An -nosplit
 .An Ted Unangst Aq Mt tedu@openbsd.org
+and
+.An Marc Espie Aq Mt espie@openbsd.org .
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;
 	}