about summary refs log tree commit diff
path: root/src/bin
diff options
context:
space:
mode:
authorChristian Neukirchen <chneukirchen@gmail.com>2014-07-30 18:25:14 +0200
committerChristian Neukirchen <chneukirchen@gmail.com>2014-07-31 16:00:16 +0200
commitd6872925c8b6b13b59a1ece32416aa8ef9c12c70 (patch)
treef9d4d6c4152241476dbd1eb3c8765908c065b147 /src/bin
downloadoutils-d6872925c8b6b13b59a1ece32416aa8ef9c12c70.tar.gz
outils-d6872925c8b6b13b59a1ece32416aa8ef9c12c70.tar.xz
outils-d6872925c8b6b13b59a1ece32416aa8ef9c12c70.zip
initial import of OpenBSD tools
Diffstat (limited to 'src/bin')
-rw-r--r--src/bin/md5/Makefile17
-rw-r--r--src/bin/md5/cksum.1191
-rw-r--r--src/bin/md5/crc.c137
-rw-r--r--src/bin/md5/crc.h31
-rw-r--r--src/bin/md5/md5.1147
-rw-r--r--src/bin/md5/md5.c815
6 files changed, 1338 insertions, 0 deletions
diff --git a/src/bin/md5/Makefile b/src/bin/md5/Makefile
new file mode 100644
index 0000000..9760377
--- /dev/null
+++ b/src/bin/md5/Makefile
@@ -0,0 +1,17 @@
+#	$OpenBSD: Makefile,v 1.14 2014/03/20 22:03:56 tedu Exp $
+
+PROG=	md5
+SRCS=	crc.c md5.c
+MAN=	cksum.1 md5.1
+LINKS=	${BINDIR}/md5 ${BINDIR}/sha1 \
+	${BINDIR}/md5 ${BINDIR}/sha256 \
+	${BINDIR}/md5 ${BINDIR}/sha512 \
+	${BINDIR}/md5 ${BINDIR}/cksum
+MLINKS= md5.1 sha1.1
+MLINKS+= md5.1 sha256.1
+MLINKS+= md5.1 sha512.1
+
+CPPFLAGS+= -I${.CURDIR}
+COPTS+=	-Wall -Wconversion -Wmissing-prototypes
+
+.include <bsd.prog.mk>
diff --git a/src/bin/md5/cksum.1 b/src/bin/md5/cksum.1
new file mode 100644
index 0000000..452007a
--- /dev/null
+++ b/src/bin/md5/cksum.1
@@ -0,0 +1,191 @@
+.\"	$OpenBSD: cksum.1,v 1.38 2014/03/20 22:06:21 tedu Exp $
+.\"
+.\" Copyright (c) 1991, 1993
+.\"	The Regents of the University of California.  All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"	@(#)cksum.1	8.2 (Berkeley) 4/28/95
+.\"
+.Dd $Mdocdate: March 20 2014 $
+.Dt CKSUM 1
+.Os
+.Sh NAME
+.Nm cksum
+.Nd display file checksums and block counts
+.Sh SYNOPSIS
+.Nm cksum
+.Bk -words
+.Op Fl bcpqrtx
+.Op Fl a Ar algorithms
+.Op Fl C Ar checklist
+.Op Fl h Ar hashfile
+.Op Fl s Ar string
+.Op Ar
+.Ek
+.Sh DESCRIPTION
+The
+.Nm cksum
+utility writes to the standard output a single line for each input file.
+The format of this line varies with the algorithm being used as follows:
+.Bl -tag -width allxothers
+.It cksum
+The output line consists of three whitespace separated fields:
+a CRC checksum, the number of octets in the input,
+and name of the file or string.
+If no file name is specified, the standard input is used and no file name
+is written.
+.It all others
+The output line consists of four whitespace separated fields:
+the name of the algorithm used, the name of the file or string in
+parentheses, an equals sign, and the cryptographic hash of the input.
+If no file name is specified, the standard input is used and only
+the cryptographic hash is output.
+.El
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl a Ar algorithms
+Use the specified algorithm(s) instead of the default (cksum).
+Supported algorithms include cksum, md5, rmd160, sha1,
+sha224, sha256, sha384, and sha512.
+Multiple algorithms may be specified, separated by a comma or whitespace.
+Additionally, multiple
+.Fl a
+options may be specified on the command line.
+Case is ignored when matching algorithms.
+The output format may be specified on a per-algorithm basis
+by using a single-character suffix, e.g.\&
+.Dq sha256b .
+If the algorithm has a
+.Sq b
+suffix, the checksum will be output in base64 format.
+If the algorithm has an
+.Sq x
+suffix, the checksum will be output in hex format.
+If an algorithm with the same output format is repeated,
+only the first instance is used.
+Note that output format suffixes are not supported
+for the cksum algorithm.
+.It Fl b
+Output checksums in base64 notation, not hexadecimal by
+default.
+A
+.Sq b
+or
+.Sq x
+suffix on the algorithm will override this default.
+This option is ignored for the cksum algorithm.
+.It Fl C Ar checklist
+Compare the checksum of each
+.Ar file
+against the checksums in the
+.Ar checklist .
+Any specified
+.Ar file
+that is not listed in the
+.Ar checklist
+will generate an error.
+.It Fl c
+If this option is specified, the
+.Ar file
+options become checklists.
+Each checklist should contain hash results in the normal format,
+which will be verified against the specified paths.
+Output consists of the digest used, the file name,
+and an OK, FAILED, or MISSING for the result of the comparison.
+This will validate any of the supported checksums.
+If no file is given, stdin is used.
+The
+.Fl c
+option may not be used in conjunction with more than a single
+.Fl a
+option.
+.It Fl h Ar hashfile
+Place the checksum into
+.Ar hashfile
+instead of stdout.
+.It Fl p
+Echoes stdin to stdout and appends the
+checksum to stdout.
+.It Fl q
+Only print the checksum (quiet mode) or if used in conjunction with the
+.Fl c
+flag, only print the failed cases.
+.It Fl r
+Reverse the format of the hash algorithm output, making
+it match the checksum output format.
+.It Fl s Ar string
+Prints a checksum of the given
+.Ar string .
+.It Fl t
+Runs a built-in time trial.
+Specifying
+.Fl t
+multiple times results in the number of rounds being multiplied
+by 10 for each additional flag.
+.It Fl x
+Runs a built-in test script.
+.El
+.Pp
+The default CRC used is based on the polynomial used for CRC error checking
+in the networking standard
+ISO/IEC 8802-3:1996.
+The other available algorithms are described in their respective
+man pages in section 3 of the manual.
+.Sh EXIT STATUS
+.Ex -std cksum
+.Sh SEE ALSO
+.Xr md5 1
+.Pp
+The default calculation is identical to that given in pseudo-code
+in the following ACM article:
+.Rs
+.%T "Computation of Cyclic Redundancy Checks Via Table Lookup"
+.%A Dilip V. Sarwate
+.%J "Communications of the ACM"
+.%D "August 1988"
+.Re
+.Sh STANDARDS
+The
+.Nm
+utility is compliant with the
+.St -p1003.1-2008
+specification.
+.Pp
+All the flags are extensions to that specification.
+.Sh HISTORY
+The
+.Nm cksum
+utility appeared in
+.Bx 4.4 .
+.Sh CAVEATS
+Do not use the cksum or md5 algorithms to verify file integrity.
+An attacker can trivially produce modified payload that
+has the same checksum as the original version.
+Use a cryptographic checksum instead.
diff --git a/src/bin/md5/crc.c b/src/bin/md5/crc.c
new file mode 100644
index 0000000..05a253f
--- /dev/null
+++ b/src/bin/md5/crc.c
@@ -0,0 +1,137 @@
+/*	$OpenBSD: crc.c,v 1.4 2014/03/20 22:03:56 tedu Exp $	*/
+
+/*
+ * Copyright (c) 2004 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * 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.
+ */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "crc.h"
+
+/*
+ * Table-driven version of the following polynomial from POSIX 1003.2:
+ *  G(x) = x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 +
+ *	   x^7 + x^5 + x^4 + x^2 + x + 1
+ */
+static const u_int32_t crc32tab[] = {
+	0x00000000U,
+	0x04c11db7U, 0x09823b6eU, 0x0d4326d9U, 0x130476dcU, 0x17c56b6bU,
+	0x1a864db2U, 0x1e475005U, 0x2608edb8U, 0x22c9f00fU, 0x2f8ad6d6U,
+	0x2b4bcb61U, 0x350c9b64U, 0x31cd86d3U, 0x3c8ea00aU, 0x384fbdbdU,
+	0x4c11db70U, 0x48d0c6c7U, 0x4593e01eU, 0x4152fda9U, 0x5f15adacU,
+	0x5bd4b01bU, 0x569796c2U, 0x52568b75U, 0x6a1936c8U, 0x6ed82b7fU,
+	0x639b0da6U, 0x675a1011U, 0x791d4014U, 0x7ddc5da3U, 0x709f7b7aU,
+	0x745e66cdU, 0x9823b6e0U, 0x9ce2ab57U, 0x91a18d8eU, 0x95609039U,
+	0x8b27c03cU, 0x8fe6dd8bU, 0x82a5fb52U, 0x8664e6e5U, 0xbe2b5b58U,
+	0xbaea46efU, 0xb7a96036U, 0xb3687d81U, 0xad2f2d84U, 0xa9ee3033U,
+	0xa4ad16eaU, 0xa06c0b5dU, 0xd4326d90U, 0xd0f37027U, 0xddb056feU,
+	0xd9714b49U, 0xc7361b4cU, 0xc3f706fbU, 0xceb42022U, 0xca753d95U,
+	0xf23a8028U, 0xf6fb9d9fU, 0xfbb8bb46U, 0xff79a6f1U, 0xe13ef6f4U,
+	0xe5ffeb43U, 0xe8bccd9aU, 0xec7dd02dU, 0x34867077U, 0x30476dc0U,
+	0x3d044b19U, 0x39c556aeU, 0x278206abU, 0x23431b1cU, 0x2e003dc5U,
+	0x2ac12072U, 0x128e9dcfU, 0x164f8078U, 0x1b0ca6a1U, 0x1fcdbb16U,
+	0x018aeb13U, 0x054bf6a4U, 0x0808d07dU, 0x0cc9cdcaU, 0x7897ab07U,
+	0x7c56b6b0U, 0x71159069U, 0x75d48ddeU, 0x6b93dddbU, 0x6f52c06cU,
+	0x6211e6b5U, 0x66d0fb02U, 0x5e9f46bfU, 0x5a5e5b08U, 0x571d7dd1U,
+	0x53dc6066U, 0x4d9b3063U, 0x495a2dd4U, 0x44190b0dU, 0x40d816baU,
+	0xaca5c697U, 0xa864db20U, 0xa527fdf9U, 0xa1e6e04eU, 0xbfa1b04bU,
+	0xbb60adfcU, 0xb6238b25U, 0xb2e29692U, 0x8aad2b2fU, 0x8e6c3698U,
+	0x832f1041U, 0x87ee0df6U, 0x99a95df3U, 0x9d684044U, 0x902b669dU,
+	0x94ea7b2aU, 0xe0b41de7U, 0xe4750050U, 0xe9362689U, 0xedf73b3eU,
+	0xf3b06b3bU, 0xf771768cU, 0xfa325055U, 0xfef34de2U, 0xc6bcf05fU,
+	0xc27dede8U, 0xcf3ecb31U, 0xcbffd686U, 0xd5b88683U, 0xd1799b34U,
+	0xdc3abdedU, 0xd8fba05aU, 0x690ce0eeU, 0x6dcdfd59U, 0x608edb80U,
+	0x644fc637U, 0x7a089632U, 0x7ec98b85U, 0x738aad5cU, 0x774bb0ebU,
+	0x4f040d56U, 0x4bc510e1U, 0x46863638U, 0x42472b8fU, 0x5c007b8aU,
+	0x58c1663dU, 0x558240e4U, 0x51435d53U, 0x251d3b9eU, 0x21dc2629U,
+	0x2c9f00f0U, 0x285e1d47U, 0x36194d42U, 0x32d850f5U, 0x3f9b762cU,
+	0x3b5a6b9bU, 0x0315d626U, 0x07d4cb91U, 0x0a97ed48U, 0x0e56f0ffU,
+	0x1011a0faU, 0x14d0bd4dU, 0x19939b94U, 0x1d528623U, 0xf12f560eU,
+	0xf5ee4bb9U, 0xf8ad6d60U, 0xfc6c70d7U, 0xe22b20d2U, 0xe6ea3d65U,
+	0xeba91bbcU, 0xef68060bU, 0xd727bbb6U, 0xd3e6a601U, 0xdea580d8U,
+	0xda649d6fU, 0xc423cd6aU, 0xc0e2d0ddU, 0xcda1f604U, 0xc960ebb3U,
+	0xbd3e8d7eU, 0xb9ff90c9U, 0xb4bcb610U, 0xb07daba7U, 0xae3afba2U,
+	0xaafbe615U, 0xa7b8c0ccU, 0xa379dd7bU, 0x9b3660c6U, 0x9ff77d71U,
+	0x92b45ba8U, 0x9675461fU, 0x8832161aU, 0x8cf30badU, 0x81b02d74U,
+	0x857130c3U, 0x5d8a9099U, 0x594b8d2eU, 0x5408abf7U, 0x50c9b640U,
+	0x4e8ee645U, 0x4a4ffbf2U, 0x470cdd2bU, 0x43cdc09cU, 0x7b827d21U,
+	0x7f436096U, 0x7200464fU, 0x76c15bf8U, 0x68860bfdU, 0x6c47164aU,
+	0x61043093U, 0x65c52d24U, 0x119b4be9U, 0x155a565eU, 0x18197087U,
+	0x1cd86d30U, 0x029f3d35U, 0x065e2082U, 0x0b1d065bU, 0x0fdc1becU,
+	0x3793a651U, 0x3352bbe6U, 0x3e119d3fU, 0x3ad08088U, 0x2497d08dU,
+	0x2056cd3aU, 0x2d15ebe3U, 0x29d4f654U, 0xc5a92679U, 0xc1683bceU,
+	0xcc2b1d17U, 0xc8ea00a0U, 0xd6ad50a5U, 0xd26c4d12U, 0xdf2f6bcbU,
+	0xdbee767cU, 0xe3a1cbc1U, 0xe760d676U, 0xea23f0afU, 0xeee2ed18U,
+	0xf0a5bd1dU, 0xf464a0aaU, 0xf9278673U, 0xfde69bc4U, 0x89b8fd09U,
+	0x8d79e0beU, 0x803ac667U, 0x84fbdbd0U, 0x9abc8bd5U, 0x9e7d9662U,
+	0x933eb0bbU, 0x97ffad0cU, 0xafb010b1U, 0xab710d06U, 0xa6322bdfU,
+	0xa2f33668U, 0xbcb4666dU, 0xb8757bdaU, 0xb5365d03U, 0xb1f740b4U
+};
+
+void
+CKSUM_Init(CKSUM_CTX *ctx)
+{
+	ctx->crc = 0;
+	ctx->len = 0;
+}
+
+#define	UPDATE(crc, byte) do						\
+	(crc) = ((crc) << 8) ^ crc32tab[((crc) >> 24) ^ (byte)];	\
+while(0)
+
+void
+CKSUM_Update(CKSUM_CTX *ctx, const unsigned char *buf, size_t len)
+{
+	size_t i;
+
+	for (i = 0; i < len; i++)
+		UPDATE(ctx->crc, buf[i]);
+	ctx->len += len;
+}
+
+void
+CKSUM_Final(CKSUM_CTX *ctx)
+{
+	off_t len = ctx->len;
+
+	/* add in number of bytes read and finish */
+	while (len != 0) {
+		UPDATE(ctx->crc, len & 0xff);
+		len >>= 8;
+	}
+	ctx->crc = ~ctx->crc;
+}
+
+char *
+CKSUM_End(CKSUM_CTX *ctx, char *outstr)
+{
+	CKSUM_Final(ctx);
+
+	if (outstr == NULL) {
+		if (asprintf(&outstr, "%u %lld", ctx->crc, ctx->len) == -1)
+			return (NULL);
+	} else {
+		(void)snprintf(outstr, (size_t)CKSUM_DIGEST_STRING_LENGTH,
+		    "%u %lld", ctx->crc, ctx->len);
+	}
+
+	return (outstr);
+}
diff --git a/src/bin/md5/crc.h b/src/bin/md5/crc.h
new file mode 100644
index 0000000..d47eceb
--- /dev/null
+++ b/src/bin/md5/crc.h
@@ -0,0 +1,31 @@
+/*	$OpenBSD: crc.h,v 1.2 2014/03/20 22:03:56 tedu Exp $	*/
+
+/*
+ * Copyright (c) 2004 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * 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.
+ */
+
+#define	CKSUM_DIGEST_LENGTH		4
+#define	CKSUM_DIGEST_STRING_LENGTH	(10 + 1 + 19)
+
+typedef struct CKSUMContext {
+	u_int32_t crc;
+	off_t len;
+} CKSUM_CTX;
+
+void	 CKSUM_Init(CKSUM_CTX *);
+void	 CKSUM_Update(CKSUM_CTX *, const u_int8_t *, size_t);
+void	 CKSUM_Final(CKSUM_CTX *);
+char    *CKSUM_End(CKSUM_CTX *, char *);
+char    *CKSUM_Data(const u_int8_t *, size_t, char *);
diff --git a/src/bin/md5/md5.1 b/src/bin/md5/md5.1
new file mode 100644
index 0000000..77ab3f8
--- /dev/null
+++ b/src/bin/md5/md5.1
@@ -0,0 +1,147 @@
+.\"	$OpenBSD: md5.1,v 1.43 2014/03/07 04:10:55 lteo Exp $
+.\"
+.\" Copyright (c) 2003, 2004, 2006 Todd C. Miller <Todd.Miller@courtesan.com>
+.\"
+.\" 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.
+.\"
+.\" Sponsored in part by the Defense Advanced Research Projects
+.\" Agency (DARPA) and Air Force Research Laboratory, Air Force
+.\" Materiel Command, USAF, under agreement number F39502-99-1-0512.
+.\"
+.Dd $Mdocdate: March 7 2014 $
+.Dt MD5 1
+.Os
+.Sh NAME
+.Nm md5
+.Nd calculate a message digest (checksum) for a file
+.Sh SYNOPSIS
+.Nm md5
+.Op Fl bcpqrtx
+.Op Fl C Ar checklist
+.Op Fl h Ar hashfile
+.Op Fl s Ar string
+.Op Ar
+.Nm sha1
+.Op Fl bcpqrtx
+.Op Fl C Ar checklist
+.Op Fl h Ar hashfile
+.Op Fl s Ar string
+.Op Ar
+.Nm sha256
+.Op Fl bcpqrtx
+.Op Fl C Ar checklist
+.Op Fl h Ar hashfile
+.Op Fl s Ar string
+.Op Ar
+.Nm sha512
+.Op Fl bcpqrtx
+.Op Fl C Ar checklist
+.Op Fl h Ar hashfile
+.Op Fl s Ar string
+.Op Ar
+.Sh DESCRIPTION
+These utilities take as input a message of arbitrary length and produce
+as output a message digest (checksum) of the input.
+It is conjectured that it is computationally infeasible to produce
+two messages having the same message digest (a collision),
+or to produce any message having a given prespecified target message digest.
+The algorithm (MD5, SHA-1, SHA-256, SHA-512) is determined by the
+name used to invoke the utility.
+.Pp
+Collisions have now been produced for MD5, and are theorized for SHA-1,
+so their use is deprecated.
+.Pp
+The options for use with each command are as follows:
+.Bl -tag -width Ds
+.It Fl b
+Output checksums in base64 notation, not hexadecimal.
+.It Fl C Ar checklist
+Compare the checksum of each
+.Ar file
+against the checksums in the
+.Ar checklist .
+Any specified
+.Ar file
+that is not listed in the
+.Ar checklist
+will generate an error.
+.It Fl c
+If this option is specified, the
+.Ar file
+options become checklists.
+Each checklist should contain hash results in the normal format,
+which will be verified against the specified paths.
+Output consists of the digest used, the file name,
+and an OK, FAILED, or MISSING for the result of the comparison.
+This will validate any of the supported checksums (see
+.Xr cksum 1 ) .
+If no file is given, stdin is used.
+.It Fl h Ar hashfile
+Place the checksum into
+.Ar hashfile
+instead of stdout.
+.It Fl p
+Echoes stdin to stdout and appends the
+checksum to stdout.
+.It Fl q
+Only print the checksum (quiet mode).
+.It Fl r
+Reverse the format of the hash algorithm output, making
+it match the output format used by
+.Xr cksum 1 .
+.It Fl s Ar string
+Prints a checksum of the given
+.Ar string .
+.It Fl t
+Runs a built-in time trial.
+Specifying
+.Fl t
+multiple times results in the number of rounds being multiplied
+by 10 for each additional flag.
+.It Fl x
+Runs a built-in test script.
+.El
+.Sh EXIT STATUS
+These utilities exit 0 on success,
+and \*(Gt0 if an error occurs.
+.Sh SEE ALSO
+.Xr cksum 1
+.Sh STANDARDS
+.Rs
+.%A R. Rivest
+.%D April 1992
+.%R RFC 1321
+.%T The MD5 Message-Digest Algorithm
+.Re
+.Rs
+.%A J. Burrows
+.%O FIPS PUB 180-1
+.%T The Secure Hash Standard
+.Re
+.Pp
+.Rs
+.%A D. Eastlake
+.%A P. Jones
+.%D September 2001
+.%R RFC 3174
+.%T US Secure Hash Algorithm 1 (SHA1)
+.Re
+.Rs
+.%T Secure Hash Standard
+.%O FIPS PUB 180-2
+.Re
+.Sh CAVEATS
+Since collisions have been found for MD5,
+the use of
+.Nm sha256
+is recommended instead.
diff --git a/src/bin/md5/md5.c b/src/bin/md5/md5.c
new file mode 100644
index 0000000..9112868
--- /dev/null
+++ b/src/bin/md5/md5.c
@@ -0,0 +1,815 @@
+/*	$OpenBSD: md5.c,v 1.76 2014/06/19 15:30:49 millert Exp $	*/
+
+/*
+ * Copyright (c) 2001,2003,2005-2007,2010,2013,2014
+ *	Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * 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.
+ *
+ * Sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F39502-99-1-0512.
+ */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <netinet/in.h>
+#include <ctype.h>
+#include <err.h>
+#include <fcntl.h>
+#include <resolv.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <time.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <md5.h>
+#include <rmd160.h>
+#include <sha1.h>
+#include <sha2.h>
+#include <crc.h>
+
+#define STYLE_MD5	0
+#define STYLE_CKSUM	1
+#define STYLE_TERSE	2
+
+#define MAX_DIGEST_LEN	128
+
+#define MIN(a,b) (((a)<(b))?(a):(b))
+#define MAX(a,b) (((a)>(b))?(a):(b))
+
+union ANY_CTX {
+#if !defined(SHA2_ONLY)
+	CKSUM_CTX cksum;
+	MD5_CTX md5;
+	RMD160_CTX rmd160;
+	SHA1_CTX sha1;
+#endif /* !defined(SHA2_ONLY) */
+	SHA2_CTX sha2;
+};
+
+struct hash_function {
+	const char *name;
+	size_t digestlen;
+	int style;
+	int base64;
+	void *ctx;	/* XXX - only used by digest_file() */
+	void (*init)(void *);
+	void (*update)(void *, const unsigned char *, unsigned int);
+	void (*final)(unsigned char *, void *);
+	char * (*end)(void *, char *);
+	TAILQ_ENTRY(hash_function) tailq;
+} functions[] = {
+#if !defined(SHA2_ONLY)
+	{
+		"CKSUM",
+		CKSUM_DIGEST_LENGTH,
+		STYLE_CKSUM,
+		-1,
+		NULL,
+		(void (*)(void *))CKSUM_Init,
+		(void (*)(void *, const unsigned char *, unsigned int))CKSUM_Update,
+		(void (*)(unsigned char *, void *))CKSUM_Final,
+		(char *(*)(void *, char *))CKSUM_End
+	},
+	{
+		"MD5",
+		MD5_DIGEST_LENGTH,
+		STYLE_MD5,
+		0,
+		NULL,
+		(void (*)(void *))MD5Init,
+		(void (*)(void *, const unsigned char *, unsigned int))MD5Update,
+		(void (*)(unsigned char *, void *))MD5Final,
+		(char *(*)(void *, char *))MD5End
+	},
+	{
+		"RMD160",
+		RMD160_DIGEST_LENGTH,
+		STYLE_MD5,
+		0,
+		NULL,
+		(void (*)(void *))RMD160Init,
+		(void (*)(void *, const unsigned char *, unsigned int))RMD160Update,
+		(void (*)(unsigned char *, void *))RMD160Final,
+		(char *(*)(void *, char *))RMD160End
+	},
+	{
+		"SHA1",
+		SHA1_DIGEST_LENGTH,
+		STYLE_MD5,
+		0,
+		NULL,
+		(void (*)(void *))SHA1Init,
+		(void (*)(void *, const unsigned char *, unsigned int))SHA1Update,
+		(void (*)(unsigned char *, void *))SHA1Final,
+		(char *(*)(void *, char *))SHA1End
+	},
+	{
+		"SHA224",
+		SHA224_DIGEST_LENGTH,
+		STYLE_MD5,
+		0,
+		NULL,
+		(void (*)(void *))SHA224Init,
+		(void (*)(void *, const unsigned char *, unsigned int))SHA224Update,
+		(void (*)(unsigned char *, void *))SHA224Final,
+		(char *(*)(void *, char *))SHA224End
+	},
+#endif /* !defined(SHA2_ONLY) */
+	{
+		"SHA256",
+		SHA256_DIGEST_LENGTH,
+		STYLE_MD5,
+		0,
+		NULL,
+		(void (*)(void *))SHA256Init,
+		(void (*)(void *, const unsigned char *, unsigned int))SHA256Update,
+		(void (*)(unsigned char *, void *))SHA256Final,
+		(char *(*)(void *, char *))SHA256End
+	},
+#if !defined(SHA2_ONLY)
+	{
+		"SHA384",
+		SHA384_DIGEST_LENGTH,
+		STYLE_MD5,
+		0,
+		NULL,
+		(void (*)(void *))SHA384Init,
+		(void (*)(void *, const unsigned char *, unsigned int))SHA384Update,
+		(void (*)(unsigned char *, void *))SHA384Final,
+		(char *(*)(void *, char *))SHA384End
+	},
+#endif /* !defined(SHA2_ONLY) */
+	{
+		"SHA512",
+		SHA512_DIGEST_LENGTH,
+		STYLE_MD5,
+		0,
+		NULL,
+		(void (*)(void *))SHA512Init,
+		(void (*)(void *, const unsigned char *, unsigned int))SHA512Update,
+		(void (*)(unsigned char *, void *))SHA512Final,
+		(char *(*)(void *, char *))SHA512End
+	},
+	{
+		NULL,
+	}
+};
+
+TAILQ_HEAD(hash_list, hash_function);
+
+void digest_end(const struct hash_function *, void *, char *, size_t, int);
+int  digest_file(const char *, struct hash_list *, int);
+int  digest_filelist(const char *, struct hash_function *, int, char **);
+void digest_print(const struct hash_function *, const char *, const char *);
+#if !defined(SHA2_ONLY)
+void digest_printstr(const struct hash_function *, const char *, const char *);
+void digest_string(char *, struct hash_list *);
+void digest_test(struct hash_list *);
+void digest_time(struct hash_list *, int);
+#endif /* !defined(SHA2_ONLY) */
+void hash_insert(struct hash_list *, struct hash_function *, int);
+void usage(void) __attribute__((__noreturn__));
+
+extern char *__progname;
+int qflag = 0;
+FILE *ofile = NULL;
+
+int
+main(int argc, char **argv)
+{
+	struct hash_function *hf, *hftmp;
+	struct hash_list hl;
+	size_t len;
+	char *cp, *input_string, *selective_checklist;
+	const char *optstr;
+	int fl, error, base64, i;
+	int bflag, cflag, pflag, rflag, tflag, xflag;
+
+	TAILQ_INIT(&hl);
+	input_string = NULL;
+	selective_checklist = NULL;
+	error = bflag = cflag = pflag = qflag = rflag = tflag = xflag = 0;
+
+#if !defined(SHA2_ONLY)
+	if (strcmp(__progname, "cksum") == 0)
+		optstr = "a:bC:ch:pqrs:tx";
+	else
+#endif /* !defined(SHA2_ONLY) */
+		optstr = "bC:ch:pqrs:tx";
+
+	/* Check for -b option early since it changes behavior. */
+	while ((fl = getopt(argc, argv, optstr)) != -1) {
+		switch (fl) {
+		case 'b':
+			bflag = 1;
+			break;
+		case '?':
+			usage();
+		}
+	}
+	optind = 1;
+	optreset = 1;
+	while ((fl = getopt(argc, argv, optstr)) != -1) {
+		switch (fl) {
+		case 'a':
+			while ((cp = strsep(&optarg, " \t,")) != NULL) {
+				if (*cp == '\0')
+					continue;
+				base64 = -1;
+				for (hf = functions; hf->name != NULL; hf++) {
+					len = strlen(hf->name);
+					if (strncasecmp(cp, hf->name, len) != 0)
+						continue;
+					if (cp[len] == '\0') {
+						if (hf->base64 != -1)
+							base64 = bflag;
+						break;	/* exact match */
+					}
+					if (cp[len + 1] == '\0' &&
+					    (cp[len] == 'b' || cp[len] == 'x')) {
+						base64 =
+						    cp[len] == 'b' ?  1 : 0;
+						break;	/* match w/ suffix */
+					}
+				}
+				if (hf->name == NULL) {
+					warnx("unknown algorithm \"%s\"", cp);
+					usage();
+				}
+				if (hf->base64 == -1 && base64 != -1) {
+					warnx("%s doesn't support %s",
+					    hf->name,
+					    base64 ? "base64" : "hex");
+					usage();
+				}
+				/* Check for dupes. */
+				TAILQ_FOREACH(hftmp, &hl, tailq) {
+					if (hftmp->base64 == base64 &&
+					    strcmp(hf->name, hftmp->name) == 0)
+						break;
+				}
+				if (hftmp == TAILQ_END(&hl))
+					hash_insert(&hl, hf, base64);
+			}
+			break;
+		case 'b':
+			/* has already been parsed */
+			break;
+		case 'h':
+			ofile = fopen(optarg, "w");
+			if (ofile == NULL)
+				err(1, "%s", optarg);
+			break;
+#if !defined(SHA2_ONLY)
+		case 'C':
+			selective_checklist = optarg;
+			break;
+		case 'c':
+			cflag = 1;
+			break;
+#endif /* !defined(SHA2_ONLY) */
+		case 'p':
+			pflag = 1;
+			break;
+		case 'q':
+			qflag = 1;
+			break;
+		case 'r':
+			rflag = 1;
+			break;
+		case 's':
+			input_string = optarg;
+			break;
+		case 't':
+			tflag++;
+			break;
+		case 'x':
+			xflag = 1;
+			break;
+		default:
+			usage();
+		}
+	}
+	argc -= optind;
+	argv += optind;
+
+	if (ofile == NULL)
+		ofile = stdout;
+
+	/* Most arguments are mutually exclusive */
+	fl = pflag + (tflag ? 1 : 0) + xflag + cflag + (input_string != NULL);
+	if (fl > 1 || (fl && argc && cflag == 0) || (rflag && qflag) ||
+	    (selective_checklist != NULL && argc == 0))
+		usage();
+	if (selective_checklist || cflag) {
+		if (TAILQ_FIRST(&hl) != TAILQ_LAST(&hl, hash_list))
+			errx(1, "only a single algorithm may be specified "
+			    "in -C or -c mode");
+	}
+
+	/* No algorithm specified, check the name we were called as. */
+	if (TAILQ_EMPTY(&hl)) {
+		for (hf = functions; hf->name != NULL; hf++) {
+			if (strcasecmp(hf->name, __progname) == 0)
+				break;
+		}
+		if (hf->name == NULL)
+			hf = &functions[0];	/* default to cksum */
+		hash_insert(&hl, hf, (hf->base64 == -1 ? 0 : bflag));
+	}
+
+	if (rflag || qflag) {
+		const int new_style = rflag ? STYLE_CKSUM : STYLE_TERSE;
+		TAILQ_FOREACH(hf, &hl, tailq) {
+			hf->style = new_style;
+		}
+	}
+
+#if !defined(SHA2_ONLY)
+	if (tflag)
+		digest_time(&hl, tflag);
+	else if (xflag)
+		digest_test(&hl);
+	else if (input_string)
+		digest_string(input_string, &hl);
+	else if (selective_checklist) {
+		error = digest_filelist(selective_checklist, TAILQ_FIRST(&hl),
+		    argc, argv);
+		for (i = 0; i < argc; i++) {
+			if (argv[i] != NULL) {
+				warnx("%s does not exist in %s", argv[i],
+				    selective_checklist);
+				error++;
+			}
+		}
+	} else if (cflag) {
+		if (argc == 0)
+			error = digest_filelist("-", TAILQ_FIRST(&hl), 0, NULL);
+		else
+			while (argc--)
+				error += digest_filelist(*argv++,
+				    TAILQ_FIRST(&hl), 0, NULL);
+	} else
+#endif /* !defined(SHA2_ONLY) */
+	if (pflag || argc == 0)
+		error = digest_file("-", &hl, pflag);
+	else
+		while (argc--)
+			error += digest_file(*argv++, &hl, 0);
+
+	return(error ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+void
+hash_insert(struct hash_list *hl, struct hash_function *hf, int base64)
+{
+	struct hash_function *hftmp;
+
+	hftmp = malloc(sizeof(*hftmp));
+	if (hftmp == NULL)
+		err(1, NULL);
+	*hftmp = *hf;
+	hftmp->base64 = base64;
+	TAILQ_INSERT_TAIL(hl, hftmp, tailq);
+}
+
+void
+digest_end(const struct hash_function *hf, void *ctx, char *buf, size_t bsize,
+    int base64)
+{
+	u_char *digest;
+
+	if (base64 == 1) {
+		if ((digest = malloc(hf->digestlen)) == NULL)
+			err(1, NULL);
+		hf->final(digest, ctx);
+		if (b64_ntop(digest, hf->digestlen, buf, bsize) == -1)
+			errx(1, "error encoding base64");
+		memset(digest, 0, hf->digestlen);
+		free(digest);
+	} else {
+		hf->end(ctx, buf);
+	}
+}
+
+#if !defined(SHA2_ONLY)
+void
+digest_string(char *string, struct hash_list *hl)
+{
+	struct hash_function *hf;
+	char digest[MAX_DIGEST_LEN + 1];
+	union ANY_CTX context;
+
+	TAILQ_FOREACH(hf, hl, tailq) {
+		hf->init(&context);
+		hf->update(&context, string, (unsigned int)strlen(string));
+		digest_end(hf, &context, digest, sizeof(digest),
+		    hf->base64);
+		digest_printstr(hf, string, digest);
+	}
+}
+#endif /* !defined(SHA2_ONLY) */
+
+void
+digest_print(const struct hash_function *hf, const char *what,
+    const char *digest)
+{
+	switch (hf->style) {
+	case STYLE_MD5:
+		(void)fprintf(ofile, "%s (%s) = %s\n", hf->name, what, digest);
+		break;
+	case STYLE_CKSUM:
+		(void)fprintf(ofile, "%s %s\n", digest, what);
+		break;
+	case STYLE_TERSE:
+		(void)fprintf(ofile, "%s\n", digest);
+		break;
+	}
+}
+
+void
+digest_printstr(const struct hash_function *hf, const char *what,
+    const char *digest)
+{
+	switch (hf->style) {
+	case STYLE_MD5:
+		(void)fprintf(ofile, "%s (\"%s\") = %s\n", hf->name, what, digest);
+		break;
+	case STYLE_CKSUM:
+		(void)fprintf(ofile, "%s %s\n", digest, what);
+		break;
+	case STYLE_TERSE:
+		(void)fprintf(ofile, "%s\n", digest);
+		break;
+	}
+}
+
+int
+digest_file(const char *file, struct hash_list *hl, int echo)
+{
+	struct hash_function *hf;
+	FILE *fp;
+	size_t nread;
+	u_char data[32 * 1024];
+	char digest[MAX_DIGEST_LEN + 1];
+
+	if (strcmp(file, "-") == 0)
+		fp = stdin;
+	else if ((fp = fopen(file, "r")) == NULL) {
+		warn("cannot open %s", file);
+		return(1);
+	}
+
+	TAILQ_FOREACH(hf, hl, tailq) {
+		if ((hf->ctx = malloc(sizeof(union ANY_CTX))) == NULL)
+			err(1, NULL);
+		hf->init(hf->ctx);
+	}
+	while ((nread = fread(data, 1UL, sizeof(data), fp)) != 0) {
+		if (echo) {
+			(void)fwrite(data, nread, 1UL, stdout);
+			if (fflush(stdout) != 0)
+				err(1, "stdout: write error");
+		}
+		TAILQ_FOREACH(hf, hl, tailq)
+			hf->update(hf->ctx, data, (unsigned int)nread);
+	}
+	if (ferror(fp)) {
+		warn("%s: read error", file);
+		if (fp != stdin)
+			fclose(fp);
+		TAILQ_FOREACH(hf, hl, tailq) {
+			free(hf->ctx);
+			hf->ctx = NULL;
+		}
+		return(1);
+	}
+	if (fp != stdin)
+		fclose(fp);
+	TAILQ_FOREACH(hf, hl, tailq) {
+		digest_end(hf, hf->ctx, digest, sizeof(digest), hf->base64);
+		free(hf->ctx);
+		hf->ctx = NULL;
+		if (fp == stdin)
+			fprintf(ofile, "%s\n", digest);
+		else
+			digest_print(hf, file, digest);
+	}
+	return(0);
+}
+
+#if !defined(SHA2_ONLY)
+/*
+ * Parse through the input file looking for valid lines.
+ * If one is found, use this checksum and file as a reference and
+ * generate a new checksum against the file on the filesystem.
+ * Print out the result of each comparison.
+ */
+int
+digest_filelist(const char *file, struct hash_function *defhash, int selcount,
+    char **sel)
+{
+	int found, base64, error, cmp, i;
+	size_t algorithm_max, algorithm_min;
+	const char *algorithm;
+	char *filename, *checksum, *buf, *p;
+	char digest[MAX_DIGEST_LEN + 1];
+	char *lbuf = NULL;
+	FILE *listfp, *fp;
+	size_t len, nread;
+	u_char data[32 * 1024];
+	union ANY_CTX context;
+	struct hash_function *hf;
+
+	if (strcmp(file, "-") == 0) {
+		listfp = stdin;
+	} else if ((listfp = fopen(file, "r")) == NULL) {
+		warn("cannot open %s", file);
+		return(1);
+	}
+
+	algorithm_max = algorithm_min = strlen(functions[0].name);
+	for (hf = &functions[1]; hf->name != NULL; hf++) {
+		len = strlen(hf->name);
+		algorithm_max = MAX(algorithm_max, len);
+		algorithm_min = MIN(algorithm_min, len);
+	}
+
+	error = found = 0;
+	while ((buf = fgetln(listfp, &len))) {
+		base64 = 0;
+		if (buf[len - 1] == '\n')
+			buf[len - 1] = '\0';
+		else {
+			if ((lbuf = malloc(len + 1)) == NULL)
+				err(1, NULL);
+
+			(void)memcpy(lbuf, buf, len);
+			lbuf[len] = '\0';
+			buf = lbuf;
+		}
+		while (isspace((unsigned char)*buf))
+			buf++;
+
+		/*
+		 * Crack the line into an algorithm, filename, and checksum.
+		 * Lines are of the form:
+		 *  ALGORITHM (FILENAME) = CHECKSUM
+		 *
+		 * Fallback on GNU form:
+		 *  CHECKSUM  FILENAME
+		 */
+		p = strchr(buf, ' ');
+		if (p != NULL && *(p + 1) == '(') {
+			/* BSD form */
+			*p = '\0';
+			algorithm = buf;
+			len = strlen(algorithm);
+			if (len > algorithm_max || len < algorithm_min)
+				continue;
+
+			filename = p + 2;
+			p = strrchr(filename, ')');
+			if (p == NULL || strncmp(p + 1, " = ", (size_t)3) != 0)
+				continue;
+			*p = '\0';
+
+			checksum = p + 4;
+			p = strpbrk(checksum, " \t\r");
+			if (p != NULL)
+				*p = '\0';
+
+			/*
+			 * Check that the algorithm is one we recognize.
+			 */
+			for (hf = functions; hf->name != NULL; hf++) {
+				if (strcasecmp(algorithm, hf->name) == 0)
+					break;
+			}
+			if (hf->name == NULL || *checksum == '\0')
+				continue;
+			/*
+			 * Check the length to see if this could be
+			 * a valid checksum.  If hex, it will be 2x the
+			 * size of the binary data.  For base64, we have
+			 * to check both with and without the '=' padding.
+			 */
+			len = strlen(checksum);
+			if (len != hf->digestlen * 2) {
+				size_t len2;
+
+				if (checksum[len - 1] == '=') {
+					/* use padding */
+					len2 = 4 * ((hf->digestlen + 2) / 3);
+				} else {
+					/* no padding */
+					len2 = (4 * hf->digestlen + 2) / 3;
+				}
+				if (len != len2)
+					continue;
+				base64 = 1;
+			}
+		} else {
+			/* could be GNU form */
+			if ((hf = defhash) == NULL)
+				continue;
+			algorithm = hf->name;
+			checksum = buf;
+			if ((p = strchr(checksum, ' ')) == NULL)
+				continue;
+			if (hf->style == STYLE_CKSUM) {
+				if ((p = strchr(p + 1, ' ')) == NULL)
+					continue;
+			}
+			*p++ = '\0';
+			while (isspace((unsigned char)*p))
+				p++;
+			if (*p == '\0')
+				continue;
+			filename = p;
+			p = strpbrk(filename, "\t\r");
+			if (p != NULL)
+				*p = '\0';
+		}
+		found = 1;
+
+		/*
+		 * If only a selection of files is wanted, proceed only
+		 * if the filename matches one of those in the selection.
+		 * Mark found files by setting them to NULL so that we can
+		 * detect files that are missing from the checklist later.
+		 */
+		if (sel) {
+			for (i = 0; i < selcount; i++) {
+				if (sel[i] && strcmp(sel[i], filename) == 0) {
+					sel[i] = NULL;
+					break;
+				}
+			}
+			if (i == selcount)
+				continue;
+		}
+
+		if ((fp = fopen(filename, "r")) == NULL) {
+			warn("cannot open %s", filename);
+			(void)printf("(%s) %s: %s\n", algorithm, filename,
+			    (errno == ENOENT ? "MISSING" : "FAILED"));
+			error = 1;
+			continue;
+		}
+
+		hf->init(&context);
+		while ((nread = fread(data, 1UL, sizeof(data), fp)) > 0)
+			hf->update(&context, data, (unsigned int)nread);
+		if (ferror(fp)) {
+			warn("%s: read error", file);
+			error = 1;
+			fclose(fp);
+			continue;
+		}
+		fclose(fp);
+		digest_end(hf, &context, digest, sizeof(digest), base64);
+
+		if (base64)
+			cmp = strncmp(checksum, digest, len);
+		else
+			cmp = strcasecmp(checksum, digest);
+		if (cmp == 0) {
+			if (qflag == 0)
+				(void)printf("(%s) %s: OK\n", algorithm,
+				    filename);
+		} else {
+			(void)printf("(%s) %s: FAILED\n", algorithm, filename);
+			error = 1;
+		}
+	}
+	if (listfp != stdin)
+		fclose(listfp);
+	if (!found)
+		warnx("%s: no properly formatted checksum lines found", file);
+	if (lbuf != NULL)
+		free(lbuf);
+	return(error || !found);
+}
+
+#define TEST_BLOCK_LEN 10000
+#define TEST_BLOCK_COUNT 10000
+
+void
+digest_time(struct hash_list *hl, int times)
+{
+	struct hash_function *hf;
+	struct timeval start, stop, res;
+	union ANY_CTX context;
+	u_int i;
+	u_char data[TEST_BLOCK_LEN];
+	char digest[MAX_DIGEST_LEN + 1];
+	double elapsed;
+	int count = TEST_BLOCK_COUNT;
+	while (--times > 0 && count < INT_MAX / 10)
+		count *= 10;
+
+	TAILQ_FOREACH(hf, hl, tailq) {
+		(void)printf("%s time trial.  Processing %d %d-byte blocks...",
+		    hf->name, count, TEST_BLOCK_LEN);
+		fflush(stdout);
+
+		/* Initialize data based on block number. */
+		for (i = 0; i < TEST_BLOCK_LEN; i++)
+			data[i] = (u_char)(i & 0xff);
+
+		gettimeofday(&start, NULL);
+		hf->init(&context);
+		for (i = 0; i < count; i++)
+			hf->update(&context, data, TEST_BLOCK_LEN);
+		digest_end(hf, &context, digest, sizeof(digest), hf->base64);
+		gettimeofday(&stop, NULL);
+		timersub(&stop, &start, &res);
+		elapsed = res.tv_sec + res.tv_usec / 1000000.0;
+
+		(void)printf("\nDigest = %s\n", digest);
+		(void)printf("Time   = %f seconds\n", elapsed);
+		(void)printf("Speed  = %f bytes/second\n",
+		    (double)TEST_BLOCK_LEN * count / elapsed);
+	}
+}
+
+void
+digest_test(struct hash_list *hl)
+{
+	struct hash_function *hf;
+	union ANY_CTX context;
+	int i;
+	char digest[MAX_DIGEST_LEN + 1];
+	unsigned char buf[1000];
+	unsigned const char *test_strings[] = {
+		"",
+		"a",
+		"abc",
+		"message digest",
+		"abcdefghijklmnopqrstuvwxyz",
+		"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+		"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+		    "0123456789",
+		"12345678901234567890123456789012345678901234567890123456789"
+		    "012345678901234567890",
+	};
+
+	TAILQ_FOREACH(hf, hl, tailq) {
+		(void)printf("%s test suite:\n", hf->name);
+
+		for (i = 0; i < 8; i++) {
+			hf->init(&context);
+			hf->update((void *)&context, test_strings[i],
+			    (unsigned int)strlen(test_strings[i]));
+			digest_end(hf, &context, digest, sizeof(digest),
+			    hf->base64);
+			digest_printstr(hf, test_strings[i], digest);
+		}
+
+		/* Now simulate a string of a million 'a' characters. */
+		memset(buf, 'a', sizeof(buf));
+		hf->init(&context);
+		for (i = 0; i < 1000; i++)
+			hf->update(&context, buf,
+			    (unsigned int)sizeof(buf));
+		digest_end(hf, &context, digest, sizeof(digest), hf->base64);
+		digest_print(hf, "one million 'a' characters",
+		    digest);
+	}
+}
+#endif /* !defined(SHA2_ONLY) */
+
+void
+usage(void)
+{
+#if !defined(SHA2_ONLY)
+	if (strcmp(__progname, "cksum") == 0)
+		fprintf(stderr, "usage: %s [-bcpqrtx] [-a algorithms] [-C checklist] "
+		    "[-h hashfile]\n"
+		    "	[-s string] [file ...]\n",
+		    __progname);
+	else
+#endif /* !defined(SHA2_ONLY) */
+		fprintf(stderr, "usage:"
+		    "\t%s [-bcpqrtx] [-C checklist] [-h hashfile] [-s string] "
+		    "[file ...]\n",
+		    __progname);
+
+	exit(EXIT_FAILURE);
+}