summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Makefile4
-rw-r--r--src/liboutils/include/sha2.h22
-rw-r--r--src/liboutils/outils.h6
-rw-r--r--src/liboutils/sha2.c96
-rw-r--r--src/usr.bin/signify/signify.h33
-rw-r--r--src/usr.bin/signify/zsig.c322
6 files changed, 468 insertions, 15 deletions
diff --git a/Makefile b/Makefile
index d3e13d9..47abb0b 100644
--- a/Makefile
+++ b/Makefile
@@ -21,7 +21,7 @@ src/bin/md5/md5: src/bin/md5/md5.o src/bin/md5/crc.o src/liboutils/sha512.o src/
 
 src/usr.bin/rs/rs: src/usr.bin/rs/rs.o src/usr.bin/rs/utf8.o src/liboutils/pledge.o src/liboutils/strtonum.o src/liboutils/reallocarray.o
 
-src/usr.bin/signify/signify: src/usr.bin/signify/signify.o src/usr.bin/signify/crypto_api.o src/usr.bin/signify/fe25519.o src/usr.bin/signify/mod_ed25519.o src/usr.bin/signify/mod_ge25519.o src/usr.bin/signify/sc25519.o src/usr.bin/signify/smult_curve25519_ref.o src/usr.bin/signify/zsig.o src/liboutils/pledge.o src/liboutils/strlcpy.o src/liboutils/base64.o src/liboutils/explicit_bzero.o src/liboutils/ohash.o src/liboutils/arc4random.o src/liboutils/getentropy_linux.o src/liboutils/readpassphrase.o src/liboutils/sha2.o src/liboutils/sha256hl.o src/liboutils/sha384hl.o src/liboutils/sha512hl.o src/liboutils/timingsafe_bcmp.o src/liboutils/bcrypt_pbkdf.o src/liboutils/blowfish.o
+src/usr.bin/signify/signify: src/usr.bin/signify/signify.o src/usr.bin/signify/crypto_api.o src/usr.bin/signify/fe25519.o src/usr.bin/signify/mod_ed25519.o src/usr.bin/signify/mod_ge25519.o src/usr.bin/signify/sc25519.o src/usr.bin/signify/smult_curve25519_ref.o src/usr.bin/signify/zsig.o src/liboutils/pledge.o src/liboutils/strlcpy.o src/liboutils/base64.o src/liboutils/explicit_bzero.o src/liboutils/ohash.o src/liboutils/arc4random.o src/liboutils/getentropy_linux.o src/liboutils/readpassphrase.o src/liboutils/sha2.o src/liboutils/sha256hl.o src/liboutils/sha512_256hl.o src/liboutils/sha512hl.o src/liboutils/timingsafe_bcmp.o src/liboutils/bcrypt_pbkdf.o src/liboutils/blowfish.o
 
 src/usr.bin/calendar/calendar: src/usr.bin/calendar/calendar.o src/usr.bin/calendar/day.o src/usr.bin/calendar/io.o src/usr.bin/calendar/ostern.o src/usr.bin/calendar/paskha.o src/usr.bin/calendar/pesach.o src/liboutils/arc4random_uniform.o src/liboutils/arc4random.o src/liboutils/getentropy_linux.o src/liboutils/explicit_bzero.o src/liboutils/pledge.o src/liboutils/sha2.o src/liboutils/strtonum.o
 
@@ -34,7 +34,7 @@ src/usr.bin/what/what: src/usr.bin/what/what.o src/liboutils/pledge.o src/libout
 src/usr.sbin/rdate/rdate: src/usr.sbin/rdate/ntp.o src/usr.sbin/rdate/rfc868time.o src/usr.sbin/rdate/rdate.o src/usr.sbin/rdate/ntpleaps.o src/liboutils/arc4random.o src/liboutils/reallocarray.o src/liboutils/getentropy_linux.o src/liboutils/explicit_bzero.o src/liboutils/sha2.o
 
 clean:
-	rm -f src/*/*/*.o
+	rm -f src/*/*.o src/*/*/*.o
 
 DESTDIR=
 PREFIX=/usr
diff --git a/src/liboutils/include/sha2.h b/src/liboutils/include/sha2.h
index 065c30d..52ddb3f 100644
--- a/src/liboutils/include/sha2.h
+++ b/src/liboutils/include/sha2.h
@@ -1,4 +1,4 @@
-/*	$OpenBSD: sha2.h,v 1.9 2013/04/15 15:54:17 millert Exp $	*/
+/*	$OpenBSD: sha2.h,v 1.10 2016/09/03 17:00:29 tedu Exp $	*/
 
 /*
  * FILE:	sha2.h
@@ -51,6 +51,9 @@
 #define SHA512_BLOCK_LENGTH		128
 #define SHA512_DIGEST_LENGTH		64
 #define SHA512_DIGEST_STRING_LENGTH	(SHA512_DIGEST_LENGTH * 2 + 1)
+#define SHA512_256_BLOCK_LENGTH		128
+#define SHA512_256_DIGEST_LENGTH	32
+#define SHA512_256_DIGEST_STRING_LENGTH	(SHA512_256_DIGEST_LENGTH * 2 + 1)
 
 
 /*** SHA-224/256/384/512 Context Structure *******************************/
@@ -131,6 +134,23 @@ char *SHA512FileChunk(const char *, char *, off_t, off_t)
 char *SHA512Data(const u_int8_t *, size_t, char *)
 	__attribute__((__bounded__(__string__,1,2)))
 	__attribute__((__bounded__(__minbytes__,3,SHA512_DIGEST_STRING_LENGTH)));
+
+void SHA512_256Init(SHA2_CTX *);
+void SHA512_256Transform(u_int64_t state[8], const u_int8_t [SHA512_256_BLOCK_LENGTH]);
+void SHA512_256Update(SHA2_CTX *, const u_int8_t *, size_t)
+	__attribute__((__bounded__(__string__,2,3)));
+void SHA512_256Pad(SHA2_CTX *);
+void SHA512_256Final(u_int8_t [SHA512_256_DIGEST_LENGTH], SHA2_CTX *)
+	__attribute__((__bounded__(__minbytes__,1,SHA512_256_DIGEST_LENGTH)));
+char *SHA512_256End(SHA2_CTX *, char *)
+	__attribute__((__bounded__(__minbytes__,2,SHA512_256_DIGEST_STRING_LENGTH)));
+char *SHA512_256File(const char *, char *)
+	__attribute__((__bounded__(__minbytes__,2,SHA512_256_DIGEST_STRING_LENGTH)));
+char *SHA512_256FileChunk(const char *, char *, off_t, off_t)
+	__attribute__((__bounded__(__minbytes__,2,SHA512_256_DIGEST_STRING_LENGTH)));
+char *SHA512_256Data(const u_int8_t *, size_t, char *)
+	__attribute__((__bounded__(__string__,1,2)))
+	__attribute__((__bounded__(__minbytes__,3,SHA512_256_DIGEST_STRING_LENGTH)));
 __END_DECLS
 
 #endif /* _SHA2_H */
diff --git a/src/liboutils/outils.h b/src/liboutils/outils.h
index 6e516b7..7686d60 100644
--- a/src/liboutils/outils.h
+++ b/src/liboutils/outils.h
@@ -1,5 +1,9 @@
+#define DEF_WEAK(x)
+
 #define __weak_alias(new, old) \
         extern __typeof(old) new __attribute__((weak, alias(#old)))
+#define MAKE_CLONE(new, old) \
+        extern __typeof(old) new __attribute__((weak, alias(#old)))
 #define __dead __attribute__((__noreturn__))
 #define __BEGIN_DECLS
 #define __END_DECLS
@@ -33,4 +37,4 @@ int timingsafe_bcmp(const void *, const void *, size_t);
 void *reallocarray(void *, size_t, size_t);
 int pledge(const char *, const char **);
 void explicit_bzero(void *buf, size_t len);
-
+int getentropy(void *buf, size_t len);
diff --git a/src/liboutils/sha2.c b/src/liboutils/sha2.c
index e8216df..ec13e44 100644
--- a/src/liboutils/sha2.c
+++ b/src/liboutils/sha2.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: sha2.c,v 1.18 2014/07/20 04:22:34 guenther Exp $	*/
+/*	$OpenBSD: sha2.c,v 1.25 2016/09/03 16:25:03 tedu Exp $	*/
 
 /*
  * FILE:	sha2.c
@@ -52,6 +52,11 @@
  *   #define SHA2_UNROLL_TRANSFORM
  *
  */
+#ifndef SHA2_SMALL
+#if defined(__amd64__) || defined(__i386__)
+#define SHA2_UNROLL_TRANSFORM
+#endif
+#endif
 
 /*** SHA-224/256/384/512 Machine Architecture Definitions *****************/
 /*
@@ -283,6 +288,18 @@ static const u_int64_t sha384_initial_hash_value[8] = {
 	0x47b5481dbefa4fa4ULL
 };
 
+/* Initial hash value H for SHA-512-256 */
+static const u_int64_t sha512_256_initial_hash_value[8] = {
+	0x22312194fc2bf72cULL,
+	0x9f555fa3c84c64c2ULL,
+	0x2393b86b6f53b151ULL,
+	0x963877195940eabdULL,
+	0x96283ee2a88effe3ULL,
+	0xbe5e1e2553863992ULL,
+	0x2b0199fc2c85b8aaULL,
+	0x0eb72ddc81c52ca2ULL
+};
+
 /*** SHA-224: *********************************************************/
 void
 SHA224Init(SHA2_CTX *context)
@@ -292,10 +309,14 @@ SHA224Init(SHA2_CTX *context)
 	memset(context->buffer, 0, sizeof(context->buffer));
 	context->bitcount[0] = 0;
 }
+DEF_WEAK(SHA224Init);
 
-__weak_alias(SHA224Transform, SHA256Transform);
-__weak_alias(SHA224Update, SHA256Update);
-__weak_alias(SHA224Pad, SHA256Pad);
+MAKE_CLONE(SHA224Transform, SHA256Transform);
+MAKE_CLONE(SHA224Update, SHA256Update);
+MAKE_CLONE(SHA224Pad, SHA256Pad);
+DEF_WEAK(SHA224Transform);
+DEF_WEAK(SHA224Update);
+DEF_WEAK(SHA224Pad);
 
 void
 SHA224Final(u_int8_t digest[SHA224_DIGEST_LENGTH], SHA2_CTX *context)
@@ -311,8 +332,9 @@ SHA224Final(u_int8_t digest[SHA224_DIGEST_LENGTH], SHA2_CTX *context)
 #else
 	memcpy(digest, context->state.st32, SHA224_DIGEST_LENGTH);
 #endif
-	memset(context, 0, sizeof(*context));
+	explicit_bzero(context, sizeof(*context));
 }
+DEF_WEAK(SHA224Final);
 #endif /* !defined(SHA2_SMALL) */
 
 /*** SHA-256: *********************************************************/
@@ -324,6 +346,7 @@ SHA256Init(SHA2_CTX *context)
 	memset(context->buffer, 0, sizeof(context->buffer));
 	context->bitcount[0] = 0;
 }
+DEF_WEAK(SHA256Init);
 
 #ifdef SHA2_UNROLL_TRANSFORM
 
@@ -482,6 +505,7 @@ SHA256Transform(u_int32_t state[8], const u_int8_t data[SHA256_BLOCK_LENGTH])
 }
 
 #endif /* SHA2_UNROLL_TRANSFORM */
+DEF_WEAK(SHA256Transform);
 
 void
 SHA256Update(SHA2_CTX *context, const u_int8_t *data, size_t len)
@@ -528,6 +552,7 @@ SHA256Update(SHA2_CTX *context, const u_int8_t *data, size_t len)
 	/* Clean up: */
 	usedspace = freespace = 0;
 }
+DEF_WEAK(SHA256Update);
 
 void
 SHA256Pad(SHA2_CTX *context)
@@ -571,6 +596,7 @@ SHA256Pad(SHA2_CTX *context)
 	/* Clean up: */
 	usedspace = 0;
 }
+DEF_WEAK(SHA256Pad);
 
 void
 SHA256Final(u_int8_t digest[SHA256_DIGEST_LENGTH], SHA2_CTX *context)
@@ -586,8 +612,9 @@ SHA256Final(u_int8_t digest[SHA256_DIGEST_LENGTH], SHA2_CTX *context)
 #else
 	memcpy(digest, context->state.st32, SHA256_DIGEST_LENGTH);
 #endif
-	memset(context, 0, sizeof(*context));
+	explicit_bzero(context, sizeof(*context));
 }
+DEF_WEAK(SHA256Final);
 
 
 /*** SHA-512: *********************************************************/
@@ -599,6 +626,7 @@ SHA512Init(SHA2_CTX *context)
 	memset(context->buffer, 0, sizeof(context->buffer));
 	context->bitcount[0] = context->bitcount[1] =  0;
 }
+DEF_WEAK(SHA512Init);
 
 #ifdef SHA2_UNROLL_TRANSFORM
 
@@ -758,6 +786,7 @@ SHA512Transform(u_int64_t state[8], const u_int8_t data[SHA512_BLOCK_LENGTH])
 }
 
 #endif /* SHA2_UNROLL_TRANSFORM */
+DEF_WEAK(SHA512Transform);
 
 void
 SHA512Update(SHA2_CTX *context, const u_int8_t *data, size_t len)
@@ -804,6 +833,7 @@ SHA512Update(SHA2_CTX *context, const u_int8_t *data, size_t len)
 	/* Clean up: */
 	usedspace = freespace = 0;
 }
+DEF_WEAK(SHA512Update);
 
 void
 SHA512Pad(SHA2_CTX *context)
@@ -847,6 +877,7 @@ SHA512Pad(SHA2_CTX *context)
 	/* Clean up: */
 	usedspace = 0;
 }
+DEF_WEAK(SHA512Pad);
 
 void
 SHA512Final(u_int8_t digest[SHA512_DIGEST_LENGTH], SHA2_CTX *context)
@@ -862,8 +893,9 @@ SHA512Final(u_int8_t digest[SHA512_DIGEST_LENGTH], SHA2_CTX *context)
 #else
 	memcpy(digest, context->state.st64, SHA512_DIGEST_LENGTH);
 #endif
-	memset(context, 0, sizeof(*context));
+	explicit_bzero(context, sizeof(*context));
 }
+DEF_WEAK(SHA512Final);
 
 #if !defined(SHA2_SMALL)
 
@@ -876,10 +908,14 @@ SHA384Init(SHA2_CTX *context)
 	memset(context->buffer, 0, sizeof(context->buffer));
 	context->bitcount[0] = context->bitcount[1] = 0;
 }
+DEF_WEAK(SHA384Init);
 
-__weak_alias(SHA384Transform, SHA512Transform);
-__weak_alias(SHA384Update, SHA512Update);
-__weak_alias(SHA384Pad, SHA512Pad);
+MAKE_CLONE(SHA384Transform, SHA512Transform);
+MAKE_CLONE(SHA384Update, SHA512Update);
+MAKE_CLONE(SHA384Pad, SHA512Pad);
+DEF_WEAK(SHA384Transform);
+DEF_WEAK(SHA384Update);
+DEF_WEAK(SHA384Pad);
 
 void
 SHA384Final(u_int8_t digest[SHA384_DIGEST_LENGTH], SHA2_CTX *context)
@@ -896,6 +932,44 @@ SHA384Final(u_int8_t digest[SHA384_DIGEST_LENGTH], SHA2_CTX *context)
 	memcpy(digest, context->state.st64, SHA384_DIGEST_LENGTH);
 #endif
 	/* Zero out state data */
-	memset(context, 0, sizeof(*context));
+	explicit_bzero(context, sizeof(*context));
+}
+DEF_WEAK(SHA384Final);
+
+/*** SHA-512/256: *********************************************************/
+void
+SHA512_256Init(SHA2_CTX *context)
+{
+	memcpy(context->state.st64, sha512_256_initial_hash_value,
+	    sizeof(sha512_256_initial_hash_value));
+	memset(context->buffer, 0, sizeof(context->buffer));
+	context->bitcount[0] = context->bitcount[1] = 0;
+}
+DEF_WEAK(SHA512_256Init);
+
+MAKE_CLONE(SHA512_256Transform, SHA512Transform);
+MAKE_CLONE(SHA512_256Update, SHA512Update);
+MAKE_CLONE(SHA512_256Pad, SHA512Pad);
+DEF_WEAK(SHA512_256Transform);
+DEF_WEAK(SHA512_256Update);
+DEF_WEAK(SHA512_256Pad);
+
+void
+SHA512_256Final(u_int8_t digest[SHA512_256_DIGEST_LENGTH], SHA2_CTX *context)
+{
+	SHA512_256Pad(context);
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+	int	i;
+
+	/* Convert TO host byte order */
+	for (i = 0; i < 4; i++)
+		BE_64_TO_8(digest + i * 8, context->state.st64[i]);
+#else
+	memcpy(digest, context->state.st64, SHA512_256_DIGEST_LENGTH);
+#endif
+	/* Zero out state data */
+	explicit_bzero(context, sizeof(*context));
 }
+DEF_WEAK(SHA512_256Final);
 #endif /* !defined(SHA2_SMALL) */
diff --git a/src/usr.bin/signify/signify.h b/src/usr.bin/signify/signify.h
new file mode 100644
index 0000000..6edb6a4
--- /dev/null
+++ b/src/usr.bin/signify/signify.h
@@ -0,0 +1,33 @@
+/* $OpenBSD: signify.h,v 1.1 2016/09/02 16:10:56 espie Exp $ */
+/*
+ * Copyright (c) 2016 Marc Espie <espie@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * 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.
+ */
+
+/* common interface to signify.c/zsig.c */
+#ifndef signify_h
+#define signify_h
+extern void zverify(const char *, const char *, const char *, const char *);
+extern void zsign(const char *, const char *, const char *);
+
+extern void *xmalloc(size_t);
+extern void writeall(int, const void *, size_t, const char *);
+extern int xopen(const char *, int, mode_t);
+extern void *verifyzdata(uint8_t *, unsigned long long,
+    const char *, const char *, const char *);
+extern uint8_t *createsig(const char *, const char *, uint8_t *,
+    unsigned long long);
+
+
+#endif
diff --git a/src/usr.bin/signify/zsig.c b/src/usr.bin/signify/zsig.c
new file mode 100644
index 0000000..3611f28
--- /dev/null
+++ b/src/usr.bin/signify/zsig.c
@@ -0,0 +1,322 @@
+/* $OpenBSD: zsig.c,v 1.10 2016/09/04 17:00:22 espie Exp $ */
+/*
+ * Copyright (c) 2016 Marc Espie <espie@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * 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.
+ */
+
+#ifndef VERIFYONLY
+#include <stdint.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sha2.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <fcntl.h>
+#include "signify.h"
+
+struct gzheader {
+	uint8_t flg;
+	uint32_t mtime;
+	uint8_t xflg;
+	uint8_t os;
+	uint8_t *name;
+	uint8_t *comment;
+	uint8_t *endcomment;
+	unsigned long long headerlength;
+	uint8_t *buffer;
+};
+
+#define FTEXT_FLAG 1
+#define FHCRC_FLAG 2
+#define FEXTRA_FLAG 4
+#define FNAME_FLAG 8
+#define FCOMMENT_FLAG 16
+
+#define GZHEADERLENGTH 10
+#define MYBUFSIZE 65536LU
+
+
+static uint8_t fake[10] = { 0x1f, 0x8b, 8, FCOMMENT_FLAG, 0, 0, 0, 0, 0, 3 };
+
+/* XXX no static there, confuses the hell out of gcc which displays
+ * non-existent warnings.
+ */
+uint8_t *
+readgz_header(struct gzheader *h, int fd)
+{
+	size_t sz = 1024;
+	uint8_t *p;
+	size_t pos = 0;
+	size_t len = 0;
+	int state = 0;
+	ssize_t n;
+	uint8_t *buf;
+
+	buf = xmalloc(sz);
+
+	while (1) {
+		if (len == sz) {
+			sz *= 2;
+			buf = realloc(buf, sz);
+			if (!buf)
+				err(1, "realloc");
+		}
+		n = read(fd, buf+len, sz-len);
+		if (n == -1)
+			err(1, "read");
+		/* incomplete info */
+		if (n == 0)
+			errx(1, "gzheader truncated");
+		len += n;
+		h->comment = NULL;
+		h->name = NULL;
+
+		switch(state) {
+		case 0: /* check header proper */
+			/* need ten bytes */
+			if (len < GZHEADERLENGTH)
+				continue;
+			h->flg = buf[3];
+			h->mtime = buf[4] | (buf[5] << 8U) | (buf[6] << 16U) |
+			    (buf[7] << 24U);
+			h->xflg = buf[8];
+			h->os = buf[9];
+			/* magic gzip header */
+			if (buf[0] != 0x1f || buf[1] != 0x8b || buf[2] != 8)
+				err(1, "invalud magic in gzheader");
+			/* XXX special code that only caters to our needs */
+			if (h->flg & ~ (FCOMMENT_FLAG | FNAME_FLAG))
+				err(1, "invalid flags in gzheader");
+			pos = GZHEADERLENGTH;
+			state++;
+			/*FALLTHRU*/
+		case 1:
+			if (h->flg & FNAME_FLAG) {
+				p = memchr(buf+pos, 0, len - pos);
+				if (!p)
+					continue;
+				pos = (p - buf) + 1;
+			}
+			state++;
+			/*FALLTHRU*/
+		case 2:
+			if (h->flg & FCOMMENT_FLAG) {
+				p = memchr(buf+pos, 0, len - pos);
+				if (!p)
+					continue;
+				h->comment = buf + pos;
+				h->endcomment = p;
+				pos = (p - buf) + 1;
+			}
+			if (h->flg & FNAME_FLAG)
+				h->name = buf + GZHEADERLENGTH;
+			h->headerlength = pos;
+			h->buffer = buf;
+			return buf + len;
+		}
+
+	}
+}
+
+static void
+copy_blocks(int fdout, int fdin, const char *sha, const char *endsha,
+    size_t bufsize, uint8_t *bufend)
+{
+	uint8_t *buffer;
+	uint8_t *residual;
+	uint8_t output[SHA512_256_DIGEST_STRING_LENGTH];
+
+	buffer = xmalloc(bufsize);
+	residual = (uint8_t *)endsha + 1;
+
+	while (1) {
+		/* get the next block */
+		size_t n = 0;
+		/* if we have residual data, we use it */
+		if (residual != bufend) {
+			/* how much can we copy */
+			size_t len = bufend - residual;
+			if (len >= bufsize) {
+				memcpy(buffer, residual, bufsize);
+				n = bufsize;
+				residual += bufsize;
+			} else {
+				memcpy(buffer, residual, len);
+				residual += len;
+				n = len;
+			}
+		}
+		/* if we're not done yet, try to obtain more until EOF */
+		while (n != bufsize) {
+			ssize_t more = read(fdin, buffer+n, bufsize-n);
+			if (more == -1)
+				err(1, "read");
+			n += more;
+			if (more == 0)
+				break;
+		}
+		SHA512_256Data(buffer, n, output);
+		if (endsha - sha < SHA512_256_DIGEST_STRING_LENGTH-1)
+			errx(4, "signature truncated");
+		if (memcmp(output, sha, SHA512_256_DIGEST_STRING_LENGTH-1) != 0)
+			errx(4, "signature mismatch");
+		if (sha[SHA512_256_DIGEST_STRING_LENGTH-1] != '\n')
+			errx(4, "signature mismatch");
+		sha += SHA512_256_DIGEST_STRING_LENGTH;
+		writeall(fdout, buffer, n, "stdout");
+		if (n != bufsize)
+			break;
+	}
+	free(buffer);
+}
+
+void
+zverify(const char *pubkeyfile, const char *msgfile, const char *sigfile,
+    const char *keytype)
+{
+	struct gzheader h;
+	size_t bufsize;
+	char *p, *meta;
+	uint8_t *bufend;
+	int fdin, fdout;
+
+	/* by default, verification will love pipes */
+	if (!sigfile)
+		sigfile = "-";
+	if (!msgfile)
+		msgfile = "-";
+
+	fdin = xopen(sigfile, O_RDONLY | O_NOFOLLOW, 0);
+
+	bufend = readgz_header(&h, fdin);
+	if (!(h.flg & FCOMMENT_FLAG))
+		errx(1, "unsigned gzip archive");
+	fake[8] = h.xflg;
+
+	p = verifyzdata(h.comment, h.endcomment-h.comment, sigfile,
+	    pubkeyfile, keytype);
+
+	bufsize = MYBUFSIZE;
+
+	meta = p;
+#define BEGINS_WITH(x, y) memcmp((x), (y), sizeof(y)-1) == 0
+
+	while (BEGINS_WITH(p, "algorithm=SHA512/256") ||
+	    BEGINS_WITH(p, "date=") ||
+	    BEGINS_WITH(p, "key=") ||
+	    sscanf(p, "blocksize=%zu\n", &bufsize) > 0) {
+		while (*(p++) != '\n')
+			continue;
+	}
+
+	if (*p != '\n')
+		errx(1, "invalid signature");
+	*(p++) = 0;
+
+	fdout = xopen(msgfile, O_CREAT|O_TRUNC|O_NOFOLLOW|O_WRONLY, 0666);
+	/* we don't actually copy the header, but put in a fake one with about
+	 * zero useful information.
+	 */
+	writeall(fdout, fake, sizeof fake, msgfile);
+	writeall(fdout, meta, p - meta, msgfile);
+	copy_blocks(fdout, fdin, p, h.endcomment, bufsize, bufend);
+	free(h.buffer);
+	close(fdout);
+	close(fdin);
+}
+
+void
+zsign(const char *seckeyfile, const char *msgfile, const char *sigfile)
+{
+	size_t bufsize = MYBUFSIZE;
+	int fdin, fdout;
+	struct gzheader h;
+	struct stat sb;
+	size_t space;
+	char *msg;
+	char *p;
+	uint8_t *buffer;
+	uint8_t *sighdr;
+	char date[80];
+	time_t clock;
+
+	fdin = xopen(msgfile, O_RDONLY, 0);
+	if (fstat(fdin, &sb) == -1 || !S_ISREG(sb.st_mode))
+		errx(1, "Sorry can only sign regular files");
+
+	readgz_header(&h, fdin);
+	/* we don't care about the header, actually */
+	free(h.buffer);
+
+	if (lseek(fdin, h.headerlength, SEEK_SET) == -1)
+		err(1, "seek in %s", msgfile);
+
+	space = (sb.st_size / MYBUFSIZE+1) * SHA512_256_DIGEST_STRING_LENGTH +
+		1024; /* long enough for extra header information */
+
+	msg = xmalloc(space);
+	buffer = xmalloc(bufsize);
+	time(&clock);
+	strftime(date, sizeof date, "%Y-%m-%dT%H:%M:%SZ", gmtime(&clock));
+	snprintf(msg, space, 
+	    "date=%s\n"
+	    "key=%s\n"
+	    "algorithm=SHA512/256\n"
+	    "blocksize=%zu\n\n",
+	    date, seckeyfile, bufsize);
+	p = strchr(msg, 0);
+
+	while (1) {
+		size_t n = read(fdin, buffer, bufsize);
+		if (n == -1)
+			err(1, "read from %s", msgfile);
+		if (n == 0)
+			break;
+		SHA512_256Data(buffer, n, p);
+		p += SHA512_256_DIGEST_STRING_LENGTH;
+		p[-1] = '\n';
+		if (msg + space < p)
+			errx(1, "file too long %s", msgfile);
+	}
+	*p = 0;
+
+	fdout = xopen(sigfile, O_CREAT|O_TRUNC|O_NOFOLLOW|O_WRONLY, 0666);
+	sighdr = createsig(seckeyfile, msgfile, msg, p-msg);
+	fake[8] = h.xflg;
+
+	writeall(fdout, fake, sizeof fake, sigfile);
+	writeall(fdout, sighdr, strlen(sighdr), sigfile);
+	free(sighdr);
+	/* need the 0 ! */
+	writeall(fdout, msg, p - msg + 1, sigfile);
+	free(msg);
+
+	if (lseek(fdin, h.headerlength, SEEK_SET) == -1)
+		err(1, "seek in %s", msgfile);
+
+	while (1) {
+		size_t n = read(fdin, buffer, bufsize);
+		if (n == -1)
+			err(1, "read from %s", msgfile);
+		if (n == 0)
+			break;
+		writeall(fdout, buffer, n, sigfile);
+	}
+	free(buffer);
+	close(fdout);
+}
+#endif