From d03ab969116fe715d569304c1c474749b2f64529 Mon Sep 17 00:00:00 2001
From: mdw <mdw>
Date: Fri, 3 Sep 1999 08:41:14 +0000
Subject: [PATCH] Initial import.

---
 .cvsignore        |   57 +++
 .links            |    6 +
 .skelrc           |    9 +
 Makefile.m4       |  173 ++++++++
 acconfig.h        |   68 +++
 bf_ikey.h         |  324 ++++++++++++++
 blkc.h            |  281 ++++++++++++
 blowfish.c        |  208 +++++++++
 blowfish.h        |  113 +++++
 cbc.h             |  521 ++++++++++++++++++++++
 cfb.h             |  477 ++++++++++++++++++++
 configure.in      |   95 ++++
 daftstory.h       |   86 ++++
 des-base.c        |   49 +++
 des-base.h        |  166 +++++++
 des-mktab.c       |  279 ++++++++++++
 des.c             |  263 +++++++++++
 des.h             |  116 +++++
 des3.c            |  123 ++++++
 des3.h            |  116 +++++
 ecb.h             |  440 +++++++++++++++++++
 genmodes          |   50 +++
 hash.h            |  234 ++++++++++
 hmac.h            |  321 ++++++++++++++
 idea.c            |  276 ++++++++++++
 idea.h            |  119 +++++
 key.1             |  245 +++++++++++
 key.c             | 1077 +++++++++++++++++++++++++++++++++++++++++++++
 key.h             |  462 ++++++++++++++++++++
 keyring.5         |   54 +++
 keyutil.c         |  722 ++++++++++++++++++++++++++++++
 md4.c             |  260 +++++++++++
 md4.h             |  158 +++++++
 md5.c             |  279 ++++++++++++
 md5.h             |  159 +++++++
 mp.c              | 1252 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 mp.h              |  449 +++++++++++++++++++
 mpscan.c          |   89 ++++
 mpscan.h          |  125 ++++++
 mptypes.h         |  206 +++++++++
 mpx.c             |  644 +++++++++++++++++++++++++++
 mpx.h             |  440 +++++++++++++++++++
 noise.c           |  396 +++++++++++++++++
 noise.h           |  145 +++++++
 ofb.h             |  406 +++++++++++++++++
 papers/.cvsignore |    4 +
 papers/crypto.sty |    1 +
 papers/rand.tex   |  179 ++++++++
 paranoia.h        |   59 +++
 rand.c            |  392 +++++++++++++++++
 rand.h            |  270 ++++++++++++
 rc4.c             |  197 +++++++++
 rc4.h             |  160 +++++++
 rc5.c             |  235 ++++++++++
 rc5.h             |   99 +++++
 rmd160.c          |  402 +++++++++++++++++
 rmd160.h          |  160 +++++++
 setup             |   11 +
 sha.c             |  307 +++++++++++++
 sha.h             |  157 +++++++
 tests/blowfish    |  124 ++++++
 tests/des         |   19 +
 tests/des3        |   43 ++
 tests/idea        |    9 +
 tests/md4         |   60 +++
 tests/md5         |   84 ++++
 tests/rc4         |   28 ++
 tests/rc5         |   17 +
 tests/rmd160      |   59 +++
 tests/sha         |   83 ++++
 70 files changed, 15697 insertions(+)
 create mode 100644 .cvsignore
 create mode 100644 .links
 create mode 100644 .skelrc
 create mode 100644 Makefile.m4
 create mode 100644 acconfig.h
 create mode 100644 bf_ikey.h
 create mode 100644 blkc.h
 create mode 100644 blowfish.c
 create mode 100644 blowfish.h
 create mode 100644 cbc.h
 create mode 100644 cfb.h
 create mode 100644 configure.in
 create mode 100644 daftstory.h
 create mode 100644 des-base.c
 create mode 100644 des-base.h
 create mode 100644 des-mktab.c
 create mode 100644 des.c
 create mode 100644 des.h
 create mode 100644 des3.c
 create mode 100644 des3.h
 create mode 100644 ecb.h
 create mode 100755 genmodes
 create mode 100644 hash.h
 create mode 100644 hmac.h
 create mode 100644 idea.c
 create mode 100644 idea.h
 create mode 100644 key.1
 create mode 100644 key.c
 create mode 100644 key.h
 create mode 100644 keyring.5
 create mode 100644 keyutil.c
 create mode 100644 md4.c
 create mode 100644 md4.h
 create mode 100644 md5.c
 create mode 100644 md5.h
 create mode 100644 mp.c
 create mode 100644 mp.h
 create mode 100644 mpscan.c
 create mode 100644 mpscan.h
 create mode 100644 mptypes.h
 create mode 100644 mpx.c
 create mode 100644 mpx.h
 create mode 100644 noise.c
 create mode 100644 noise.h
 create mode 100644 ofb.h
 create mode 100644 papers/.cvsignore
 create mode 100644 papers/crypto.sty
 create mode 100644 papers/rand.tex
 create mode 100644 paranoia.h
 create mode 100644 rand.c
 create mode 100644 rand.h
 create mode 100644 rc4.c
 create mode 100644 rc4.h
 create mode 100644 rc5.c
 create mode 100644 rc5.h
 create mode 100644 rmd160.c
 create mode 100644 rmd160.h
 create mode 100755 setup
 create mode 100644 sha.c
 create mode 100644 sha.h
 create mode 100644 tests/blowfish
 create mode 100644 tests/des
 create mode 100644 tests/des3
 create mode 100644 tests/idea
 create mode 100644 tests/md4
 create mode 100644 tests/md5
 create mode 100644 tests/rc4
 create mode 100644 tests/rc5
 create mode 100644 tests/rmd160
 create mode 100644 tests/sha

diff --git a/.cvsignore b/.cvsignore
new file mode 100644
index 0000000..ed9b81e
--- /dev/null
+++ b/.cvsignore
@@ -0,0 +1,57 @@
+Makefile.am
+Makefile.in
+_mpdiv.c
+aclocal.m4
+blowfish-cbc.c
+blowfish-cbc.h
+blowfish-cfb.c
+blowfish-cfb.h
+blowfish-ecb.c
+blowfish-ecb.h
+blowfish-ofb.c
+blowfish-ofb.h
+build
+config.h.in
+configure
+des-cbc.c
+des-cbc.h
+des-cfb.c
+des-cfb.h
+des-ecb.c
+des-ecb.h
+des-ofb.c
+des-ofb.h
+des3-cbc.c
+des3-cbc.h
+des3-cfb.c
+des3-cfb.h
+des3-ecb.c
+des3-ecb.h
+des3-ofb.c
+des3-ofb.h
+des_sp.h
+idea-cbc.c
+idea-cbc.h
+idea-cfb.c
+idea-cfb.h
+idea-ecb.c
+idea-ecb.h
+idea-ofb.c
+idea-ofb.h
+md4-hmac.c
+md4-hmac.h
+md5-hmac.c
+md5-hmac.h
+rc5-cbc.c
+rc5-cbc.h
+rc5-cfb.c
+rc5-cfb.h
+rc5-ecb.c
+rc5-ecb.h
+rc5-ofb.c
+rc5-ofb.h
+rmd160-hmac.c
+rmd160-hmac.h
+sha-hmac.c
+sha-hmac.h
+stamp-h.in
diff --git a/.links b/.links
new file mode 100644
index 0000000..a4f5a17
--- /dev/null
+++ b/.links
@@ -0,0 +1,6 @@
+COPYING.LIB
+install-sh
+missing
+mkinstalldirs
+getdate.y
+getdate.h
diff --git a/.skelrc b/.skelrc
new file mode 100644
index 0000000..4847795
--- /dev/null
+++ b/.skelrc
@@ -0,0 +1,9 @@
+;;; -*-emacs-lisp-*-
+
+(setq skel-alist
+      (append
+       '((author . "Straylight/Edgeware")
+	 (licence-text . "[[lgpl]]")
+	 (full-title . "Catacomb")
+	 (library . "Catacomb"))
+       skel-alist))
diff --git a/Makefile.m4 b/Makefile.m4
new file mode 100644
index 0000000..06e1b7d
--- /dev/null
+++ b/Makefile.m4
@@ -0,0 +1,173 @@
+## -*-makefile-*-
+##
+## $Id: Makefile.m4,v 1.1 1999/09/03 08:41:11 mdw Exp $
+##
+## Makefile for Catacomb
+##
+## (c) 1999 Straylight/Edgeware
+##
+
+##----- Licensing notice ----------------------------------------------------
+##
+## This file is part of Catacomb.
+##
+## Catacomb is free software; you can redistribute it and/or modify
+## it under the terms of the GNU Library General Public License as
+## published by the Free Software Foundation; either version 2 of the
+## License, or (at your option) any later version.
+## 
+## Catacomb is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU Library General Public License for more details.
+## 
+## You should have received a copy of the GNU Library General Public
+## License along with Catacomb; if not, write to the Free
+## Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+## MA 02111-1307, USA.
+
+##----- Revision history ----------------------------------------------------
+##
+## $Log: Makefile.m4,v $
+## Revision 1.1  1999/09/03 08:41:11  mdw
+## Initial import.
+##
+## The `CVS' directory can't be hardlinked.  Don't worry about this
+## overmuch.
+##
+##
+
+AUTOMAKE_OPTIONS = foreign
+
+archincludedir = $(libdir)/catacomb/include
+
+## --- List handling macros ---
+##
+## List handling is nice, usually.  Unfortunately, I based this design on
+## TeX list macros rather than anything sensible...
+
+define(`_', `define(`_item', $1)define(`_item_2', $2)emit`'')
+define(`adorn', `pushdef(`emit', `$1`'_item`'$3')$2`'popdef(`emit')')
+define(`join',
+`pushdef(`emit', `adorn(``_(''_item`$2', `$3', ``)'')`'')$1`'popdef(`emit')')
+define(`addsuffix', `adorn(`', `$1', `$2')')
+define(`lit', `adorn(`', `$1', `')')
+define(`nl', `
+')
+
+define(`allwithsuffix',
+`addsuffix(`$1', `$3') addsuffix(`$2', `$3') dnl
+addsuffix(join(`$1', `-', `$2'), `$3')')
+
+## --- Autogenerated source files ---
+
+define(`ciphers', `_(des) _(des3) _(blowfish) _(idea) _(rc5)')
+define(`cipher_modes', `_(ecb) _(cbc) _(cfb) _(ofb)')
+
+define(`hashes', `_(md5) _(md4) _(rmd160) _(sha)')
+define(`hash_modes', `_(hmac)')
+
+MODES = \
+	adorn(`$(srcdir)/', join(`ciphers', `-', `cipher_modes'), `.c') \
+	adorn(`$(srcdir)/', join(`ciphers', `-', `cipher_modes'), `.h') \
+	adorn(`$(srcdir)/', join(`hashes', `-', `hash_modes'), `.c') \
+	adorn(`$(srcdir)/', join(`hashes', `-', `hash_modes'), `.h')
+
+$(MODES): $(srcdir)/genmodes
+
+archinclude_HEADERS = mptypes.h
+
+mptypes.h: mptypes
+	des_sp.h \
+	./mptypes >mptypes.h
+
+BUILT_SOURCES = \
+	getdate.c \
+	addsuffix(join(`ciphers', `-', `cipher_modes'), `.c') \
+	addsuffix(join(`ciphers', `-', `cipher_modes'), `.h') \
+INCLUDES = -I$(srcdir)/..
+	addsuffix(join(`hashes', `-', `hash_modes'), `.c') \
+	addsuffix(join(`hashes', `-', `hash_modes'), `.h')
+include_HEADERS = \
+libcatacomb_la_LDFLAGS = -version-info 0:4:0
+## Middle number is the patchlevel.  Final number is the minor version.  The
+## difference between the first and last numbers is major version.
+
+define(`emit', `$1.h ')
+pkginclude_HEADERS = \
+	paranoia.h \
+	blkc.h hash.h \
+	mpx.h mpw.h mpscan.h mparena.h mp.h mptext.h mpmont.h \
+	ptab.h pgen.h rabin.h \
+	dsa.h dh.h \
+	allwithsuffix(`ciphers', `cipher_modes', `.h') \
+libcatacomb_a_SOURCES = \
+
+define(`emit', `$1.c ')
+libcatacomb_la_SOURCES = \
+	des-base.c des-base.h des_sp.h bf_ikey.h daftstory.h \
+	ptab.c pgen.c rabin.c \
+	dsa-sign.c dsa-verify.c dsa-gen.c \
+	dh-prime.c \
+	addsuffix(join(`ciphers', `-', `cipher_modes'), `.c') \
+noinst_PROGRAMS = des-mktab
+LDADD = libcatacomb.a ../mLib/libmLib.a
+## --- Utility programs ---
+noinst_PROGRAMS = des-mktab mptypes
+LDADD = libcatacomb.a
+bin_SCRIPTS = catacomb-config
+noinst_PROGRAMS = des-mktab genprimes mptypes
+LDADD = libcatacomb.la
+genprimes_SOURCES = genprimes.c
+genprimes_LDADD =
+
+mptypes_SOURCES = mptypes.c
+mptypes_LDADD =
+
+## --- Documentation ---
+
+man_MANS = key.1 keyring.5
+
+## --- Other handy definitions ---
+	@ln $(srcdir)/tests/* $(distdir)/tests
+EXTRA_DIST = Makefile.m4 genmodes $(man_MANS)
+
+dist-hook:
+	@ln getdate.c $(distdir) || ln $(srcdir)/getdate.c $(distdir) || true
+	@mkdir $(distdir)/tests
+	@ln $(srcdir)/tests/* $(distdir)/tests || true
+	@rm -f $(distdir)/tests/*~
+TESTS = \
+	rc4.t \
+	addsuffix(join(`ciphers', `-', `cipher_modes'), `.t') \
+	addsuffix(join(`hashes', `-', `hash_modes'), `.t') \
+	addsuffix(`ciphers', `.t') addsuffix(`hashes', `.t')
+	$(COMPILE) -DTEST_RIG -DSRCDIR=\"$(srcdir)\" \
+	  $(srcdir)/$1.c libcatacomb.a ../mLib/libmLib.a -o $1.t')
+
+`$1.t: $1.c libcatacomb.a
+	$(COMPILE) -DTEST_RIG -DSRCDIR=\"$(srcdir)\" $(srcdir)/$1.c libcatacomb.a $(LIBS) -o $1.t')
+	$1.t)dnl
+$1.t: $1.c libcatacomb.la
+
+adorn(`nl`'CTESTRIG(', `ciphers', `)')
+adorn(`nl`'CTESTRIG(', `hashes', `)')
+CLEANFILES = *.t
+adorn(`nl`'CTESTRIG(', join(`hashes', `-', `hash_modes'), `)')
+CTESTRIG(dsa-verify)
+CLEANFILES = *.t mptypes.h
+TESTS = testprogs
+
+CLEANFILES = *.t mptypes.h des_sp.h ptab.c ptab.h
+
+## --- Makefile building (haha!) ---
+
+$(srcdir)/Makefile.am: $(srcdir)/Makefile.m4
+	m4 $(srcdir)/Makefile.m4 >$(srcdir)/Makefile.am
+	$(MODES) $(srcdir)/des_sp.h \
+	$(srcdir)/getdate.c getdate.c
+	$(srcdir)/Makefile.am \
+	$(srcdir)/getdate.c getdate.c \
+	$(MODES)
+
+##----- That's all, folks ---------------------------------------------------
diff --git a/acconfig.h b/acconfig.h
new file mode 100644
index 0000000..b8e2eb5
--- /dev/null
+++ b/acconfig.h
@@ -0,0 +1,68 @@
+/* -*-c-*-
+ *
+ * $Id: acconfig.h,v 1.1 1999/09/03 08:41:11 mdw Exp $
+ *
+ * Configuration header for Catacomb
+ *
+ * (c) 1999 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: acconfig.h,v $
+ * Revision 1.1  1999/09/03 08:41:11  mdw
+ * Initial import.
+ *
+ */
+
+#ifndef ACCONFIG_H
+#define ACCONFIG_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Autoconfiguration data --------------------------------------------*/
+@TOP@
+
+/* Package and version number.  */
+#define PACKAGE "catacomb"
+#define VERSION "1.0.0"
+
+/* If it's not provided already, define to be a signed integer type capable
+ * of representing any object size.  (If in doubt, make it an `int'.)  */
+#undef ssize_t
+
+/* If not provided already, define to be whatever your system uses for
+ * representing time.  (Probably `long'.)  */
+#undef time_t
+
+@BOTTOM@
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/bf_ikey.h b/bf_ikey.h
new file mode 100644
index 0000000..9d602fb
--- /dev/null
+++ b/bf_ikey.h
@@ -0,0 +1,324 @@
+/* -*-c-*-
+ *
+ * $Id: bf_ikey.h,v 1.1 1999/09/03 08:41:11 mdw Exp $
+ *
+ * Blowfish initial key data
+ *
+ * (c) 1998 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: bf_ikey.h,v $
+ * Revision 1.1  1999/09/03 08:41:11  mdw
+ * Initial import.
+ *
+ */
+
+#ifndef BF_IKEY_H
+#define BF_IKEY_H
+
+/*----- Macros ------------------------------------------------------------*/
+
+#define BLOWFISH_IKEY {							\
+									\
+  /* --- P-array of round-specific subkeys --- */			\
+									\
+  { 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,			\
+    0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89,			\
+    0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,			\
+    0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917,			\
+    0x9216d5d9, 0x8979fb1b },						\
+									\
+  /* --- First S-box --- */						\
+									\
+  { 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7,			\
+    0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99,			\
+    0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,			\
+    0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e,			\
+    0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee,			\
+    0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,			\
+    0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef,			\
+    0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e,			\
+    0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60,			\
+    0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440,			\
+    0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce,			\
+    0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,			\
+    0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e,			\
+    0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677,			\
+    0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,			\
+    0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032,			\
+    0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88,			\
+    0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,			\
+    0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e,			\
+    0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0,			\
+    0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,			\
+    0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98,			\
+    0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88,			\
+    0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,			\
+    0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6,			\
+    0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d,			\
+    0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b,			\
+    0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7,			\
+    0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba,			\
+    0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,			\
+    0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f,			\
+    0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09,			\
+    0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,			\
+    0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb,			\
+    0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279,			\
+    0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,			\
+    0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab,			\
+    0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82,			\
+    0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db,			\
+    0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573,			\
+    0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0,			\
+    0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,			\
+    0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790,			\
+    0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8,			\
+    0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,			\
+    0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0,			\
+    0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7,			\
+    0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,			\
+    0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad,			\
+    0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1,			\
+    0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,			\
+    0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9,			\
+    0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477,			\
+    0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,			\
+    0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49,			\
+    0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af,			\
+    0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa,			\
+    0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5,			\
+    0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41,			\
+    0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,			\
+    0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400,			\
+    0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915,			\
+    0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,			\
+    0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a },			\
+									\
+  /* --- Second S-box --- */						\
+									\
+  { 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623,			\
+    0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266,			\
+    0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,			\
+    0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e,			\
+    0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6,			\
+    0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,			\
+    0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e,			\
+    0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1,			\
+    0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,			\
+    0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8,			\
+    0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff,			\
+    0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,			\
+    0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701,			\
+    0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7,			\
+    0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,			\
+    0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331,			\
+    0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf,			\
+    0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,			\
+    0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e,			\
+    0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87,			\
+    0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,			\
+    0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2,			\
+    0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16,			\
+    0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,			\
+    0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b,			\
+    0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509,			\
+    0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,			\
+    0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3,			\
+    0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f,			\
+    0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,			\
+    0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4,			\
+    0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960,			\
+    0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,			\
+    0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28,			\
+    0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802,			\
+    0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,			\
+    0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510,			\
+    0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf,			\
+    0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,			\
+    0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e,			\
+    0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50,			\
+    0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,			\
+    0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8,			\
+    0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281,			\
+    0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,			\
+    0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696,			\
+    0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128,			\
+    0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,			\
+    0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0,			\
+    0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0,			\
+    0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,			\
+    0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250,			\
+    0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3,			\
+    0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,			\
+    0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00,			\
+    0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061,			\
+    0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,			\
+    0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e,			\
+    0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735,			\
+    0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,			\
+    0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9,			\
+    0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340,			\
+    0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,			\
+    0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7 },			\
+									\
+  /* --- Third S-box --- */						\
+									\
+  { 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934,			\
+    0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068,			\
+    0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af,			\
+    0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840,			\
+    0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45,			\
+    0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,			\
+    0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a,			\
+    0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb,			\
+    0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee,			\
+    0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6,			\
+    0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42,			\
+    0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,			\
+    0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2,			\
+    0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb,			\
+    0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527,			\
+    0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b,			\
+    0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33,			\
+    0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,			\
+    0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3,			\
+    0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc,			\
+    0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,			\
+    0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564,			\
+    0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b,			\
+    0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,			\
+    0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922,			\
+    0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728,			\
+    0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,			\
+    0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e,			\
+    0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37,			\
+    0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,			\
+    0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804,			\
+    0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b,			\
+    0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3,			\
+    0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb,			\
+    0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d,			\
+    0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,			\
+    0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350,			\
+    0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9,			\
+    0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a,			\
+    0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe,			\
+    0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d,			\
+    0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,			\
+    0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f,			\
+    0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61,			\
+    0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2,			\
+    0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9,			\
+    0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2,			\
+    0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,			\
+    0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e,			\
+    0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633,			\
+    0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10,			\
+    0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169,			\
+    0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52,			\
+    0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,			\
+    0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5,			\
+    0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62,			\
+    0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,			\
+    0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76,			\
+    0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24,			\
+    0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,			\
+    0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4,			\
+    0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,			\
+    0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,			\
+    0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0 },			\
+									\
+  /* --- Fourth and final S-box --- */					\
+  									\
+  { 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b,			\
+    0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe,			\
+    0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,			\
+    0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4,			\
+    0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8,			\
+    0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,			\
+    0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304,			\
+    0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22,			\
+    0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4,			\
+    0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6,			\
+    0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9,			\
+    0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,			\
+    0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593,			\
+    0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51,			\
+    0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28,			\
+    0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c,			\
+    0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b,			\
+    0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,			\
+    0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c,			\
+    0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd,			\
+    0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,			\
+    0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319,			\
+    0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb,			\
+    0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,			\
+    0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991,			\
+    0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32,			\
+    0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680,			\
+    0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166,			\
+    0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae,			\
+    0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,			\
+    0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5,			\
+    0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47,			\
+    0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,			\
+    0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d,			\
+    0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84,			\
+    0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,			\
+    0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8,			\
+    0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd,			\
+    0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,			\
+    0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7,			\
+    0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38,			\
+    0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,			\
+    0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c,			\
+    0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525,			\
+    0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1,			\
+    0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442,			\
+    0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964,			\
+    0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,			\
+    0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8,			\
+    0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d,			\
+    0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f,			\
+    0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299,			\
+    0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02,			\
+    0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,			\
+    0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614,			\
+    0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a,			\
+    0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,			\
+    0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b,			\
+    0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0,			\
+    0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,			\
+    0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e,			\
+    0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9,			\
+    0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,			\
+    0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6 }			\
+}
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#endif
diff --git a/blkc.h b/blkc.h
new file mode 100644
index 0000000..e7f5518
--- /dev/null
+++ b/blkc.h
@@ -0,0 +1,281 @@
+/* -*-c-*-
+ *
+ * $Id: blkc.h,v 1.1 1999/09/03 08:41:11 mdw Exp $
+ *
+ * Common definitions for block ciphers
+ *
+ * (c) 1999 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: blkc.h,v $
+ * Revision 1.1  1999/09/03 08:41:11  mdw
+ * Initial import.
+ *
+ */
+
+#ifndef BLKC_H
+#define BLKC_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <mLib/bits.h>
+
+/*----- Theory of operation -----------------------------------------------*
+ *
+ * A block cipher has associated with it a triple, called PRE_CLASS, of the
+ * form `(TYPE, ENDIAN, BITS)', where TYPE is either `N' (representing an
+ * implemented bit size) or `X' (representing an unimplemented bit size,
+ * causing loops to be compiled rather than unrolled code), ENDIAN is `B'
+ * (big) or `L' (little), and BITS is the block size of the cipher in bits.
+ */
+
+/*----- Data movement macros ----------------------------------------------*/
+
+/*
+ * `The C preprocessor.  You will never find a more wretched hive of bogus
+ * hackery.  We must be cautious.'
+ */
+
+/* --- General dispatch macros --- */
+
+#define BLKC_DOGLUE(x, y) x ## y
+#define BLKC_GLUE(x, y) BLKC_DOGLUE(x, y)
+#define BLKC_APPLY(f, x) f x
+#define BLKC_FIRST(x, y, z) x
+#define BLKC_SECOND(x, y, z) y
+#define BLKC_THIRD(x, y, z) z
+#define BLKC_TYPE(PRE) BLKC_APPLY(BLKC_FIRST, PRE ## _CLASS)
+#define BLKC_ENDIAN(PRE) BLKC_APPLY(BLKC_SECOND, PRE ## _CLASS)
+#define BLKC_BITS(PRE) BLKC_APPLY(BLKC_THIRD, PRE ## _CLASS)
+
+#define BLKC_STORE_E(PRE) BLKC_GLUE(STORE32_, BLKC_ENDIAN(PRE))
+#define BLKC_LOAD_E(PRE) BLKC_GLUE(LOAD32_, BLKC_ENDIAN(PRE))
+
+/* --- Interface macros --- */
+
+#define BLKC_STORE(PRE, b, w)						\
+  BLKC_GLUE(BLKC_STORE_, BLKC_TYPE(PRE))				\
+    (PRE, b, w, BLKC_STORE_E(PRE), BLKC_BITS(PRE))
+
+#define BLKC_XSTORE(PRE, b, w, wx)					\
+  BLKC_GLUE(BLKC_XSTORE_, BLKC_TYPE(PRE))				\
+    (PRE, b, w, wx, BLKC_STORE_E(PRE), BLKC_BITS(PRE))
+
+#define BLKC_LOAD(PRE, w, b)						\
+  BLKC_GLUE(BLKC_LOAD_, BLKC_TYPE(PRE))					\
+    (PRE, w, b, BLKC_LOAD_E(PRE), BLKC_BITS(PRE))
+
+#define BLKC_XLOAD(PRE, w, b)						\
+  BLKC_GLUE(BLKC_XLOAD_, BLKC_TYPE(PRE))				\
+    (PRE, w, b, BLKC_LOAD_E(PRE), BLKC_BITS(PRE))
+
+#define BLKC_MOVE(PRE, w, wx)						\
+  BLKC_GLUE(BLKC_MOVE_, BLKC_TYPE(PRE))					\
+    (PRE, w, wx, BLKC_BITS(PRE))
+
+#define BLKC_XMOVE(PRE, w, wx)						\
+  BLKC_GLUE(BLKC_XMOVE_, BLKC_TYPE(PRE))				\
+    (PRE, w, wx, BLKC_BITS(PRE))
+
+/* --- General implementation skeleton --- */
+
+#define BLKC_SKEL(PRE, decl, guts) do {					\
+  decl									\
+  guts									\
+} while (0)
+
+#define BLKC_P(p) register octet *_p = (octet *)(p)
+#define BLKC_W(w) register uint32 *_w = (w)
+#define BLKC_WX(wx) register uint32 *_wx = (wx);
+
+/* --- Implementation for unusual block sizes --- */
+
+#define BLKC_SKEL_X(PRE, decl, guts)					\
+  BLKC_SKEL(PRE, int _i; decl,						\
+	    for (_i = 0; _i < PRE ## _BLKSZ / 4; _i++) {	       	\
+	      guts							\
+	    })
+
+#define BLKC_STORE_X(PRE, b, w, op, n)					\
+  BLKC_SKEL_X(PRE, BLKC_P(b); const BLKC_W(w);,				\
+	      op(_p, *_w); _p += 4; _w++; )
+
+#define BLKC_XSTORE_X(PRE, b, w, wx, op, n)				\
+  BLKC_SKEL_X(PRE, BLKC_P(b); const BLKC_W(w); const BLKC_WX(wx);,	\
+	      op(_p, *_w ^ *_wx); _p += 4; _w++; _wx++; )
+
+#define BLKC_LOAD_X(PRE, w, b, op, n)					\
+  BLKC_SKEL_X(PRE, const BLKC_P(b); BLKC_W(w);,				\
+	      *_w = op(_p); _p += 4; _w++; )
+
+#define BLKC_XLOAD_X(PRE, w, b, op, n)					\
+  BLKC_SKEL_X(PRE, const BLKC_P(b); BLKC_W(w);,				\
+	      *_w ^= op(_p); _p += 4; _w++; )
+
+#define BLKC_MOVE_X(PRE, w, wx, n)					\
+  BLKC_SKEL_X(PRE, BLKC_W(w); const BLKC_WX(wx);,			\
+	      *_w = *_wx; _w++; _wx++; )				\
+
+#define BLKC_XMOVE_X(PRE, w, wx, n)					\
+  BLKC_SKEL_X(PRE, BLKC_W(w); const BLKC_WX(wx);,			\
+	      *_w ^= *_wx; _w++; _wx++; )				\
+
+/* --- Implementation for known block sizes --- */
+
+#define BLKC_SKEL_64(PRE, decl, op, guts)				\
+  BLKC_SKEL(PRE, decl, guts(op, 0); guts(op, 1);)
+
+#define BLKC_SKEL_128(PRE, decl, op, guts)				\
+  BLKC_SKEL(PRE, decl, guts(op, 0); guts(op, 1); guts(op, 2); guts(op, 3);)
+
+#define BLKC_STORE_GUTS(op, i) op(_p + 4 * i, _w[i])
+#define BLKC_XSTORE_GUTS(op, i) op(_p + 4 * i, _w[i] ^ _wx[i])
+#define BLKC_LOAD_GUTS(op, i) _w[i] = op(_p + 4 * i)
+#define BLKC_XLOAD_GUTS(op, i) _w[i] ^= op(_p + 4 * i)
+#define BLKC_MOVE_GUTS(op, i) _w[i] = _wx[i]
+#define BLKC_XMOVE_GUTS(op, i) _w[i] ^= _wx[i]
+
+#define BLKC_STORE_N(PRE, b, w, op, n)					\
+  BLKC_GLUE(BLKC_SKEL_, n)						\
+    (PRE, BLKC_P(b); const BLKC_W(w);, op, BLKC_STORE_GUTS)
+
+#define BLKC_XSTORE_N(PRE, b, w, wx, op, n)				\
+  BLKC_GLUE(BLKC_SKEL_, n)						\
+    (PRE, BLKC_P(b); const BLKC_W(w); const BLKC_WX(wx);,		\
+     op, BLKC_XSTORE_GUTS)
+
+#define BLKC_LOAD_N(PRE, w, b, op, n)					\
+  BLKC_GLUE(BLKC_SKEL_, n)						\
+    (PRE, const BLKC_P(b); BLKC_W(w);, op, BLKC_LOAD_GUTS)
+
+#define BLKC_XLOAD_N(PRE, w, b, op, n)					\
+  BLKC_GLUE(BLKC_SKEL_, n)						\
+    (PRE, const BLKC_P(b); BLKC_W(w);, op, BLKC_XLOAD_GUTS)
+
+#define BLKC_MOVE_N(PRE, w, wx, n)					\
+  BLKC_GLUE(BLKC_SKEL_, n)						\
+    (PRE, BLKC_W(w); const BLKC_WX(wx);, op, BLKC_MOVE_GUTS)
+
+#define BLKC_XMOVE_N(PRE, w, wx, n)					\
+  BLKC_GLUE(BLKC_SKEL_, n)						\
+    (PRE, BLKC_W(w); const BLKC_WX(wx);, op, BLKC_XMOVE_GUTS)
+
+/*----- Test rig for block ciphers ----------------------------------------*/
+
+/* --- @BLKC_TEST@ --- *
+ *
+ * Arguments:	@PRE@, @pre@ = prefixes for cipher-specific definitions
+ *
+ * Use:		Standard test rig for block ciphers.
+ */
+
+#ifdef TEST_RIG
+
+#include <mLib/quis.h>
+#include <mLib/testrig.h>
+
+#define BLKC_TEST(PRE, pre)						\
+									\
+static int verify(dstr *v)						\
+{									\
+  pre ## _ctx k;							\
+  uint32 p[PRE ## _BLKSZ / 4];						\
+  uint32 c[PRE ## _BLKSZ / 4];						\
+  uint32 d[PRE ## _BLKSZ / 4];						\
+  dstr b = DSTR_INIT;							\
+  int ok = 1;								\
+									\
+  /* --- Initialize the key buffer --- */				\
+									\
+  dstr_ensure(&b, PRE ## _BLKSZ);					\
+  b.len = PRE ## _BLKSZ;						\
+  pre ## _init(&k, v[0].buf, v[0].len);					\
+  BLKC_LOAD(PRE, p, v[1].buf);						\
+  BLKC_LOAD(PRE, c, v[2].buf);						\
+									\
+  /* --- Test encryption --- */						\
+									\
+  BLKC_MOVE(PRE, d, p);							\
+  pre ## _eblk(&k, d, d);						\
+  BLKC_STORE(PRE, b.buf, d);						\
+  if (memcmp(b.buf, v[2].buf, PRE ## _BLKSZ)) {				\
+    ok = 0;								\
+    printf("\nfail encryption:"						\
+           "\n\tkey        = ");					\
+    type_hex.dump(&v[0], stdout);					\
+    printf("\n\tplaintext  = "); type_hex.dump(&v[1], stdout);		\
+    printf("\n\texpected   = "); type_hex.dump(&v[2], stdout);		\
+    printf("\n\tcalculated = "); type_hex.dump(&b, stdout);		\
+    putchar('\n');							\
+  }									\
+									\
+  /* --- Test decryption --- */						\
+									\
+  BLKC_MOVE(PRE, d, c);							\
+  pre ## _dblk(&k, d, d);						\
+  BLKC_STORE(PRE, b.buf, d);						\
+  if (memcmp(b.buf, v[1].buf, PRE ## _BLKSZ)) {				\
+    ok = 0;								\
+    printf("\nfail decryption:"						\
+           "\n\tkey        = ");					\
+    type_hex.dump(&v[0], stdout);					\
+    printf("\n\tciphertext = "); type_hex.dump(&v[2], stdout);		\
+    printf("\n\texpected   = "); type_hex.dump(&v[1], stdout);		\
+    printf("\n\tcalculated = "); type_hex.dump(&b, stdout);		\
+    putchar('\n');							\
+  }									\
+									\
+  /* --- Return --- */							\
+									\
+  return (ok);								\
+}									\
+									\
+static test_chunk defs[] = {						\
+  { #pre, verify, { &type_hex, &type_hex, &type_hex, 0 } },		\
+  { #pre "-sched", verify, { &type_hex, &type_hex, &type_hex, 0 } },	\
+  { 0, 0, { 0 } }							\
+};									\
+									\
+int main(int argc, char *argv[])					\
+{									\
+  test_run(argc, argv, defs, SRCDIR"/tests/" #pre);			\
+  return (0);								\
+}
+
+#else
+#  define BLKC_TEST(PRE, pre)
+#endif
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/blowfish.c b/blowfish.c
new file mode 100644
index 0000000..16c862e
--- /dev/null
+++ b/blowfish.c
@@ -0,0 +1,208 @@
+/* -*-c-*-
+ *
+ * $Id: blowfish.c,v 1.1 1999/09/03 08:41:11 mdw Exp $
+ *
+ * The Blowfish block cipher
+ *
+ * (c) 1998 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: blowfish.c,v $
+ * Revision 1.1  1999/09/03 08:41:11  mdw
+ * Initial import.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <mLib/bits.h>
+
+#include "blowfish.h"
+#include "bf_ikey.h"
+#include "blkc.h"
+#include "paranoia.h"
+
+/*----- Global variables --------------------------------------------------*/
+
+static blowfish_ctx ikey = BLOWFISH_IKEY;
+
+/*----- Macros ------------------------------------------------------------*/
+
+#define ROUND(k, x, y, r)						\
+  ((x) ^= (k)->p[(r)],							\
+   (y) ^= (((((k)->s0[((x) >> 24) & MASK8]) +				\
+	     ((k)->s1[((x) >> 16) & MASK8])) ^				\
+	    ((k)->s2[((x) >>  8) & MASK8])) +				\
+	   ((k)->s3[((x) >>  0) & MASK8])))
+
+#define EBLK(k, a, b, c, d) do {					\
+  uint32 _x = (a);							\
+  uint32 _y = (b);							\
+  ROUND((k), _x, _y,  0);						\
+  ROUND((k), _y, _x,  1);						\
+  ROUND((k), _x, _y,  2);						\
+  ROUND((k), _y, _x,  3);						\
+  ROUND((k), _x, _y,  4);						\
+  ROUND((k), _y, _x,  5);						\
+  ROUND((k), _x, _y,  6);						\
+  ROUND((k), _y, _x,  7);						\
+  ROUND((k), _x, _y,  8);						\
+  ROUND((k), _y, _x,  9);						\
+  ROUND((k), _x, _y, 10);						\
+  ROUND((k), _y, _x, 11);						\
+  ROUND((k), _x, _y, 12);						\
+  ROUND((k), _y, _x, 13);						\
+  ROUND((k), _x, _y, 14);						\
+  ROUND((k), _y, _x, 15);						\
+  (c) = _y ^ (k)->p[17];						\
+  (d) = _x ^ (k)->p[16];						\
+} while (0)
+
+#define DBLK(k, a, b, c, d) do {					\
+  uint32 _x = (a);							\
+  uint32 _y = (b);							\
+  ROUND((k), _x, _y, 17);						\
+  ROUND((k), _y, _x, 16);						\
+  ROUND((k), _x, _y, 15);						\
+  ROUND((k), _y, _x, 14);						\
+  ROUND((k), _x, _y, 13);						\
+  ROUND((k), _y, _x, 12);						\
+  ROUND((k), _x, _y, 11);						\
+  ROUND((k), _y, _x, 10);						\
+  ROUND((k), _x, _y,  9);						\
+  ROUND((k), _y, _x,  8);						\
+  ROUND((k), _x, _y,  7);						\
+  ROUND((k), _y, _x,  6);						\
+  ROUND((k), _x, _y,  5);						\
+  ROUND((k), _y, _x,  4);						\
+  ROUND((k), _x, _y,  3);						\
+  ROUND((k), _y, _x,  2);						\
+  (c) = _y ^ (k)->p[0];							\
+  (d) = _x ^ (k)->p[1];							\
+} while (0)
+
+/*----- Low-level encryption interface ------------------------------------*/
+
+/* --- @blowfish_init@ --- *
+ *
+ * Arguments:	@blowfish_ctx *k@ = pointer to key block to fill in
+ *		@const void *buf@ = pointer to buffer of key material
+ *		@size_t sz@ = size of key material
+ *
+ * Returns:	---
+ *
+ * Use:		Initializes a Blowfish key buffer.  Blowfish accepts
+ *		a more-or-less arbitrary size key.
+ */
+
+void blowfish_init(blowfish_ctx *k, const void *buf, size_t sz)
+{
+  /* --- Copy the initial value over --- */
+
+  memcpy(k, &ikey, sizeof(ikey));
+
+  /* --- Initialize the %$P$% array --- */
+
+  {
+    const octet *p = buf;
+    const octet *q = p + sz;
+    int i = 0, j = 0;
+    uint32 x = 0;
+
+    while (i < 18) {
+      x = (x << 8) | U8(*p++);
+      if (p >= q)
+	p = buf;
+      if (++j >= 4) {
+	k->p[i++] ^= x;
+	x = 0;
+	j = 0;
+      }
+    }
+
+    x = 0;
+  }
+
+  /* --- Now mangle the complete array of keys --- */
+
+  {
+    uint32 b[2];
+    int i;
+
+    b[0] = b[1] = 0;
+
+    for (i = 0; i < 18; i += 2) {
+      blowfish_eblk(k, b, b);
+      k->p[i] = b[0]; k->p[i + 1] = b[1];
+    }
+
+    for (i = 0; i < 256; i += 2) {
+      blowfish_eblk(k, b, b);
+      k->s0[i] = b[0]; k->s0[i + 1] = b[1];
+    }
+
+    for (i = 0; i < 256; i += 2) {
+      blowfish_eblk(k, b, b);
+      k->s1[i] = b[0]; k->s1[i + 1] = b[1];
+    }
+
+    for (i = 0; i < 256; i += 2) {
+      blowfish_eblk(k, b, b);
+      k->s2[i] = b[0]; k->s2[i + 1] = b[1];
+    }
+
+    for (i = 0; i < 256; i += 2) {
+      blowfish_eblk(k, b, b);
+      k->s3[i] = b[0]; k->s3[i + 1] = b[1];
+    }
+
+    BURN(b);
+  }
+}
+
+/* --- @blowfish_eblk@, @blowfish_dblk@ --- *
+ *
+ * Arguments:	@const blowfish_ctx *k@ = pointer to key block
+ *		@const uint32 s[2]@ = pointer to source block
+ *		@uint32 d[2]@ = pointer to destination block
+ *
+ * Returns:	---
+ *
+ * Use:		Low-level block encryption and decryption.
+ */
+
+void blowfish_eblk(const blowfish_ctx *k, const uint32 *s, uint32 *d)
+{
+  EBLK(k, s[0], s[1], d[0], d[1]);
+}
+
+void blowfish_dblk(const blowfish_ctx *k, const uint32 *s, uint32 *d)
+{
+  DBLK(k, s[0], s[1], d[0], d[1]);
+}
+
+BLKC_TEST(BLOWFISH, blowfish)
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/blowfish.h b/blowfish.h
new file mode 100644
index 0000000..9f4fb56
--- /dev/null
+++ b/blowfish.h
@@ -0,0 +1,113 @@
+/* -*-c-*-
+ *
+ * $Id: blowfish.h,v 1.1 1999/09/03 08:41:11 mdw Exp $
+ *
+ * The Blowfish block cipher
+ *
+ * (c) 1998 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: blowfish.h,v $
+ * Revision 1.1  1999/09/03 08:41:11  mdw
+ * Initial import.
+ *
+ */
+
+/*----- Notes on the Blowfish block cipher --------------------------------*
+ *
+ * Blowfish was invented by Bruce Schneier.  The algorithm is unpatented and
+ * free for anyone to use.  It's fast, simple, offers a big key, and is
+ * looking relatively bulletproof.  It's also this author's block cipher of
+ * choice, for what little that's worth.  The disadvantage is that Blowfish
+ * has a particularly heavyweight key schedule.
+ */
+
+#ifndef BLOWFISH_H
+#define BLOWFISH_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <stddef.h>
+
+#include <mLib/bits.h>
+
+/*----- Magical numbers ---------------------------------------------------*/
+
+#define BLOWFISH_BLKSZ 8
+#define BLOWFISH_KEYSZ 0
+#define BLOWFISH_CLASS (N, B, 64)
+
+/*----- Data structures ---------------------------------------------------*/
+
+typedef struct blowfish_ctx {
+  uint32 p[18];
+  uint32 s0[256], s1[256], s2[256], s3[256];
+} blowfish_ctx;
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @blowfish_init@ --- *
+ *
+ * Arguments:	@blowfish_ctx *k@ = pointer to key block to fill in
+ *		@const void *buf@ = pointer to buffer of key material
+ *		@size_t sz@ = size of key material
+ *
+ * Returns:	---
+ *
+ * Use:		Initializes a Blowfish key buffer.  Blowfish accepts
+ *		a more-or-less arbitrary size key.
+ */
+
+extern void blowfish_init(blowfish_ctx */*k*/,
+			  const void */*buf*/, size_t /*sz*/);
+
+/* --- @blowfish_eblk@, @blowfish_dblk@ --- *
+ *
+ * Arguments:	@const blowfish_ctx *k@ = pointer to key block
+ *		@const uint32 s[2]@ = pointer to source block
+ *		@uint32 d[2]@ = pointer to destination block
+ *
+ * Returns:	---
+ *
+ * Use:		Low-level block encryption and decryption.
+ */
+
+extern void blowfish_eblk(const blowfish_ctx */*k*/,
+			  const uint32 */*s*/, uint32 */*d*/);
+
+extern void blowfish_dblk(const blowfish_ctx */*k*/,
+			  const uint32 */*s*/, uint32 */*d*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/cbc.h b/cbc.h
new file mode 100644
index 0000000..66979de
--- /dev/null
+++ b/cbc.h
@@ -0,0 +1,521 @@
+/* -*-c-*-
+ *
+ * $Id: cbc.h,v 1.1 1999/09/03 08:41:11 mdw Exp $
+ *
+ * Ciphertext block chaining for block ciphers
+ *
+ * (c) 1999 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: cbc.h,v $
+ * Revision 1.1  1999/09/03 08:41:11  mdw
+ * Initial import.
+ *
+ */
+
+#ifndef CBC_H
+#define CBC_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <string.h>
+
+#include <mLib/bits.h>
+
+#ifndef BLKC_H
+#  include "blkc.h"
+#endif
+
+/*----- Macros ------------------------------------------------------------*/
+
+/* --- @CBC_DECL@ --- *
+ *
+ * Arguments:	@PRE@, @pre@ = prefixes for the underlying block cipher
+ *
+ * Use:		Creates declarations for CBC stealing mode.
+ */
+
+#define CBC_DECL(PRE, pre)						\
+									\
+typedef struct pre ## _cbcctx {						\
+  pre ## _ctx ctx;			/* Underlying cipher context */	\
+  uint32 iv[PRE ## _BLKSZ / 4];		/* Previous ciphertext or IV */	\
+} pre ## _cbcctx;							\
+									\
+extern void pre ## _cbcgetiv(const pre ## _cbcctx */*ctx*/,		\
+			     void */*iv*/);				\
+									\
+extern void pre ## _cbcsetiv(pre ## _cbcctx */*ctx*/,			\
+			     const void */*iv*/);			\
+									\
+extern void pre ## _cbcsetkey(pre ## _cbcctx */*ctx*/,			\
+			      const pre ## _ctx */*k*/);		\
+									\
+extern void pre ## _cbcinit(pre ## _cbcctx */*ctx*/,			\
+			    const void */*key*/, size_t /*sz*/,		\
+			    const void */*iv*/);			\
+									\
+extern void pre ## _cbcencrypt(pre ## _cbcctx */*ctx*/,			\
+			       const void */*src*/, void */*dest*/,	\
+			       size_t /*sz*/);				\
+									\
+extern void pre ## _cbcdecrypt(pre ## _cbcctx */*ctx*/,			\
+			       const void */*src*/, void */*dest*/,	\
+			       size_t /*sz*/);				\
+
+/* --- @CBC_DEF@ --- *
+ *
+ * Arguments:	@PRE@, @pre@ = prefixes for the underlying block cipher
+ *
+ * Use:		Creates an implementation for CBC stealing mode.
+ */
+
+#define CBC_DEF(PRE, pre)						\
+									\
+/* --- @pre_cbcgetiv@ --- *						\
+ *									\
+ * Arguments:	@const pre_cbcctx *ctx@ = pointer to CBC context block	\
+ *		@void *iv#@ = pointer to output data block		\
+ *									\
+ * Returns:	---							\
+ *									\
+ * Use:		Reads the currently set IV.  Reading and setting an IV	\
+ *		is transparent to the CBC encryption or decryption	\
+ *		process.						\
+ */									\
+									\
+void pre ## _cbcgetiv(const pre ## _cbcctx *ctx, void *iv)		\
+{									\
+  BLKC_STORE(PRE, iv, ctx->iv);						\
+}									\
+									\
+/* --- @pre_cbcsetiv@ --- *						\
+ *									\
+ * Arguments:	@pre_cbcctx *ctx@ = pointer to CBC context block	\
+ *		@cnost void *iv@ = pointer to IV to set			\
+ *									\
+ * Returns:	---							\
+ *									\
+ * Use:		Sets the IV to use for subsequent encryption.		\
+ */									\
+									\
+void pre ## _cbcsetiv(pre ## _cbcctx *ctx, const void *iv)		\
+{									\
+  BLKC_LOAD(PRE, ctx->iv, iv);						\
+}									\
+									\
+/* --- @pre_cbcsetkey@ --- *						\
+ *									\
+ * Arguments:	@pre_cbcctx *ctx@ = pointer to CBC context block	\
+ *		@const pre_ctx *k@ = pointer to cipher context		\
+ *									\
+ * Returns:	---							\
+ *									\
+ * Use:		Sets the CBC context to use a different cipher key.	\
+ */									\
+									\
+void pre ## _cbcsetkey(pre ## _cbcctx *ctx, const pre ## _ctx *k)	\
+{									\
+  ctx->ctx = *k;							\
+}									\
+									\
+/* --- @pre_cbcinit@ --- *						\
+ *									\
+ * Arguments:	@pre_cbcctx *ctx@ = pointer to cipher context		\
+ *		@const void *key@ = pointer to the key buffer		\
+ *		@size_t sz@ = size of the key				\
+ *		@const void *iv@ = pointer to initialization vector	\
+ *									\
+ * Returns:	---							\
+ *									\
+ * Use:		Initializes a CBC context ready for use.  The @iv@	\
+ *		argument may be passed as a null pointer to set a zero	\
+ *		IV.  Apart from that, this call is equivalent to calls	\
+ *		to @pre_init@, @pre_cbcsetkey@ and @pre_cbcsetiv@.	\
+ */									\
+									\
+void pre ## _cbcinit(pre ## _cbcctx *ctx,				\
+		     const void *key, size_t sz,			\
+		     const void *iv)					\
+{									\
+  static octet zero[PRE ## _BLKSZ] = { 0 };				\
+  pre ## _init(&ctx->ctx, key, sz);					\
+  BLKC_LOAD(PRE, ctx->iv, iv ? iv : zero);				\
+}									\
+									\
+/* --- @pre_cbcencrypt@ --- *						\
+ *									\
+ * Arguments:	@pre_cbcctx *ctx@ = pointer to CBC context block	\
+ *		@const void *src@ = pointer to source data		\
+ *		@void *dest@ = pointer to destination data		\
+ *		@size_t sz@ = size of block to be encrypted		\
+ *									\
+ * Returns:	---							\
+ *									\
+ * Use:		Encrypts a block with a block cipher in CBC mode, with	\
+ *		ciphertext stealing and other clever tricks.		\
+ *		Essentially, data can be encrypted in arbitrary sized	\
+ *		chunks, although decryption must use the same chunks.	\
+ */									\
+									\
+void pre ## _cbcencrypt(pre ## _cbcctx *ctx,				\
+			const void *src, void *dest,			\
+			size_t sz)					\
+{									\
+  const octet *s = src;							\
+  octet *d = dest;							\
+									\
+  /* --- Empty blocks are trivial --- */				\
+									\
+  if (!sz)								\
+    return;								\
+									\
+  /* --- Extra magical case for a short block --- *			\
+   *									\
+   * Encrypt the IV, then exclusive-or the plaintext with the octets	\
+   * of the encrypted IV, shifting ciphertext octets in instead.  This	\
+   * basically switches over to CFB.					\
+   */									\
+									\
+  if (sz < PRE ## _BLKSZ) {						\
+    octet b[PRE ## _BLKSZ];						\
+    unsigned i;								\
+									\
+    pre ## _eblk(&ctx->ctx, ctx->iv, ctx->iv);				\
+    BLKC_STORE(PRE, b, ctx->iv);					\
+    for (i = 0; i < sz; i++)						\
+      d[i] = b[i] ^ s[i];						\
+    memmove(b, b + sz, PRE ## _BLKSZ - sz);				\
+    memcpy(b + PRE ## _BLKSZ - sz, d, sz);				\
+    BLKC_LOAD(PRE, ctx->iv, b);						\
+    return;								\
+  }									\
+									\
+  /* --- Do the main chunk of encryption --- *				\
+   *									\
+   * This will do the whole lot if it's a whole number of blocks.  For	\
+   * each block, XOR it with the previous ciphertext in @iv@, encrypt,	\
+   * and keep a copy of the ciphertext for the next block.		\
+   */									\
+									\
+  while (sz >= 2 * PRE ## _BLKSZ || sz == PRE ## _BLKSZ) {		\
+    BLKC_XLOAD(PRE, ctx->iv, s);					\
+    pre ## _eblk(&ctx->ctx, ctx->iv, ctx->iv);				\
+    BLKC_STORE(PRE, d, ctx->iv);					\
+    s += PRE ## _BLKSZ;							\
+    d += PRE ## _BLKSZ;							\
+    sz -= PRE ## _BLKSZ;						\
+  }									\
+									\
+  /* --- Do the tail-end block and bit-left-over --- *			\
+   *									\
+   * This isn't very efficient.  That shouldn't matter much.		\
+   */									\
+									\
+  if (sz) {								\
+    octet b[PRE ## _BLKSZ];						\
+    unsigned i;								\
+									\
+    /* --- Let @sz@ be the size of the partial block --- */		\
+									\
+    sz -= PRE ## _BLKSZ;						\
+									\
+    /* --- First stage --- *						\
+     *									\
+     * XOR the complete block with the current IV, and encrypt it.  The	\
+     * first part of the result is the partial ciphertext block.  Don't	\
+     * write that out yet, because I've not read the partial plaintext	\
+     * block.								\
+     */									\
+									\
+    BLKC_XLOAD(PRE, ctx->iv, s);					\
+    pre ## _eblk(&ctx->ctx, ctx->iv, ctx->iv);				\
+    BLKC_STORE(PRE, b, ctx->iv);					\
+									\
+    /* --- Second stage --- *						\
+     *									\
+     * Now XOR in the partial plaintext block, writing out the		\
+     * ciphertext as I go.  Then encrypt, and write the complete	\
+     * ciphertext block.						\
+     */									\
+									\
+    s += PRE ## _BLKSZ;							\
+    d += PRE ## _BLKSZ;							\
+    for (i = 0; i < sz; i++) {						\
+      register octet x = b[i];						\
+      b[i] ^= s[i];							\
+      d[i] = x;								\
+    }									\
+    BLKC_LOAD(PRE, ctx->iv, b);						\
+    pre ## _eblk(&ctx->ctx, ctx->iv, ctx->iv);				\
+    BLKC_STORE(PRE, d - PRE ## _BLKSZ, ctx->iv);			\
+  }									\
+									\
+  /* --- Done --- */							\
+									\
+  return;								\
+}									\
+									\
+/* --- @pre_cbcdecrypt@ --- *						\
+ *									\
+ * Arguments:	@pre_cbcctx *ctx@ = pointer to CBC context block	\
+ *		@const void *src@ = pointer to source data		\
+ *		@void *dest@ = pointer to destination data		\
+ *		@size_t sz@ = size of block to be encrypted		\
+ *									\
+ * Returns:	---							\
+ *									\
+ * Use:		Encrypts a block with a block cipher in CBC mode, with	\
+ *		ciphertext stealing and other clever tricks.		\
+ *		Essentially, data can be encrypted in arbitrary sized	\
+ *		chunks, although decryption must use the same chunks.	\
+ */									\
+									\
+void pre ## _cbcdecrypt(pre ## _cbcctx *ctx,				\
+			const void *src, void *dest,			\
+			size_t sz)					\
+{									\
+  const octet *s = src;							\
+  octet *d = dest;							\
+									\
+  /* --- Empty blocks are trivial --- */				\
+									\
+  if (!sz)								\
+    return;								\
+									\
+  /* --- Extra magical case for a short block --- *			\
+   *									\
+   * Encrypt the IV, then exclusive-or the ciphertext with the octets	\
+   * of the encrypted IV, shifting ciphertext octets in instead.  This	\
+   * basically switches over to CFB.					\
+   */									\
+									\
+  if (sz < PRE ## _BLKSZ) {						\
+    octet b[PRE ## _BLKSZ], c[PRE ## _BLKSZ];				\
+    unsigned i;								\
+									\
+    pre ## _eblk(&ctx->ctx, ctx->iv, ctx->iv);				\
+    BLKC_STORE(PRE, b, ctx->iv);					\
+    for (i = 0; i < sz; i++) {						\
+      register octet x = s[i];						\
+      d[i] = b[i] ^ x;							\
+      c[i] = x;								\
+    }									\
+    memmove(b, b + sz, PRE ## _BLKSZ - sz);				\
+    memcpy(b + PRE ## _BLKSZ - sz, c, sz);				\
+    BLKC_LOAD(PRE, ctx->iv, b);						\
+    return;								\
+  }									\
+									\
+  /* --- Do the main chunk of decryption --- *				\
+   *									\
+   * This will do the whole lot if it's a whole number of blocks.  For	\
+   * each block, decrypt, XOR it with the previous ciphertext in @iv@,	\
+   * and keep a copy of the ciphertext for the next block.		\
+   */									\
+									\
+  while (sz >= 2 * PRE ## _BLKSZ || sz == PRE ## _BLKSZ) {		\
+    uint32 b[PRE ## _BLKSZ / 4], niv[PRE ## _BLKSZ / 4];		\
+    BLKC_LOAD(PRE, niv, s);						\
+    pre ## _dblk(&ctx->ctx, niv, b);					\
+    BLKC_XSTORE(PRE, d, b, ctx->iv);					\
+    BLKC_MOVE(PRE, ctx->iv, niv);					\
+    s += PRE ## _BLKSZ;							\
+    d += PRE ## _BLKSZ;							\
+    sz -= PRE ## _BLKSZ;						\
+  }									\
+									\
+  /* --- Do the tail-end block and bit-left-over --- *			\
+   *									\
+   * This isn't very efficient.  That shouldn't matter much.		\
+   */									\
+									\
+  if (sz) {								\
+    octet b[PRE ## _BLKSZ];						\
+    uint32 bk[PRE ## _BLKSZ / 4], niv[PRE ## _BLKSZ / 4];		\
+    unsigned i;								\
+									\
+    /* --- Let @sz@ be the size of the partial block --- */		\
+									\
+    sz -= PRE ## _BLKSZ;						\
+									\
+    /* --- First stage --- *						\
+     *									\
+     * Take the complete ciphertext block, and decrypt it.  This block	\
+     * is carried over for the next encryption operation.		\
+     */									\
+									\
+    BLKC_LOAD(PRE, niv, s);						\
+    pre ## _dblk(&ctx->ctx, niv, bk);					\
+									\
+    /* --- Second stage --- *						\
+     *									\
+     * XORing the first few bytes of this with the partial ciphertext	\
+     * block recovers the partial plaintext block.  At the same time,	\
+     * write the partial ciphertext block's contents in ready for stage	\
+     * three.								\
+     */									\
+									\
+    BLKC_STORE(PRE, b, bk);						\
+    s += PRE ## _BLKSZ;							\
+    d += PRE ## _BLKSZ;							\
+    for (i = 0; i < sz; i++) {						\
+      register octet x = s[i];						\
+      d[i] = b[i] ^ x;							\
+      b[i] = x;								\
+    }									\
+									\
+    /* --- Third stage --- *						\
+     *									\
+     * Decrypt the block we've got left, and XOR with the initial IV to	\
+     * recover the complete plaintext block.				\
+     */									\
+									\
+    BLKC_LOAD(PRE, bk, b);						\
+    pre ## _dblk(&ctx->ctx, bk, bk);					\
+    BLKC_XSTORE(PRE, d - PRE ## _BLKSZ, bk, ctx->iv);			\
+    BLKC_MOVE(PRE, ctx->iv, niv);					\
+  }									\
+									\
+  /* --- Done --- */							\
+									\
+  return;								\
+}									\
+									\
+CBC_TEST(PRE, pre)
+
+/*----- Test rig ----------------------------------------------------------*/
+
+#ifdef TEST_RIG
+
+#include <stdio.h>
+
+#include "daftstory.h"
+
+/* --- @CBC_TEST@ --- *
+ *
+ * Arguments:	@PRE@, @pre@ = prefixes for block cipher definitions
+ *
+ * Use:		Standard test rig for CBC functions.
+ */
+
+#define CBC_TEST(PRE, pre)						\
+									\
+/* --- Initial plaintext for the test --- */				\
+									\
+static const octet text[] = TEXT;					\
+									\
+/* --- Key and IV to use --- */						\
+									\
+static const octet key[] = KEY;						\
+static const octet iv[] = IV;						\
+									\
+/* --- Buffers for encryption and decryption output --- */		\
+									\
+static octet ct[sizeof(text)];						\
+static octet pt[sizeof(text)];						\
+									\
+static void hexdump(const octet *p, size_t sz)				\
+{									\
+  const octet *q = p + sz;						\
+  for (sz = 0; p < q; p++, sz++) {					\
+    printf("%02x", *p);							\
+    if ((sz + 1) % PRE ## _BLKSZ == 0)					\
+      putchar(':');							\
+  }									\
+}									\
+									\
+int main(void)								\
+{									\
+  size_t sz = 0, rest;							\
+  pre ## _cbcctx ctx;							\
+  pre ## _ctx k;							\
+  int status = 0;							\
+  int done = 0;								\
+									\
+  size_t keysz = PRE ## _KEYSZ ?					\
+    PRE ## _KEYSZ : strlen((const char *)key);				\
+									\
+  fputs(#pre "-cbc: ", stdout);						\
+									\
+  pre ## _init(&k, key, keysz);						\
+  pre ## _cbcsetkey(&ctx, &k);						\
+									\
+  while (sz <= sizeof(text)) {						\
+    rest = sizeof(text) - sz;						\
+    memcpy(ct, text, sizeof(text));					\
+    pre ## _cbcsetiv(&ctx, iv);						\
+    pre ## _cbcencrypt(&ctx, ct, ct, sz);				\
+    pre ## _cbcencrypt(&ctx, ct + sz, ct + sz, rest);			\
+    memcpy(pt, ct, sizeof(text));					\
+    pre ## _cbcsetiv(&ctx, iv);						\
+    pre ## _cbcdecrypt(&ctx, pt, pt, sz);				\
+    pre ## _cbcdecrypt(&ctx, pt + sz, pt + sz, rest);			\
+    if (memcmp(pt, text, sizeof(text)) == 0) {				\
+      done++;								\
+      if (sizeof(text) < 40 || done % 8 == 0)				\
+        fputc('.', stdout);						\
+      if (done % 480 == 0)						\
+        fputs("\n\t", stdout);						\
+      fflush(stdout);							\
+    } else {								\
+      printf("\nError (sz = %lu)\n", (unsigned long)sz);		\
+      status = 1;							\
+      printf("\tplaintext      = "); hexdump(text, sz);			\
+        printf(", "); hexdump(text + sz, rest);				\
+        fputc('\n', stdout);						\
+      printf("\tciphertext     = "); hexdump(ct, sz);			\
+        printf(", "); hexdump(ct + sz, rest);				\
+        fputc('\n', stdout);						\
+      printf("\trecovered text = "); hexdump(pt, sz);			\
+        printf(", "); hexdump(pt + sz, rest);				\
+        fputc('\n', stdout);						\
+      fputc('\n', stdout);						\
+    }									\
+    if (sz < 63)							\
+      sz++;								\
+    else								\
+      sz += 9;								\
+  }									\
+									\
+  fputs(status ? " failed\n" : " ok\n", stdout);			\
+  return (status);							\
+}
+
+#else
+#  define CBC_TEST(PRE, pre)
+#endif
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/cfb.h b/cfb.h
new file mode 100644
index 0000000..0719934
--- /dev/null
+++ b/cfb.h
@@ -0,0 +1,477 @@
+/* -*-c-*-
+ *
+ * $Id: cfb.h,v 1.1 1999/09/03 08:41:11 mdw Exp $
+ *
+ * Ciphertext feedback for block ciphers
+ *
+ * (c) 1999 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: cfb.h,v $
+ * Revision 1.1  1999/09/03 08:41:11  mdw
+ * Initial import.
+ *
+ */
+
+#ifndef CFB_H
+#define CFB_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <string.h>
+
+#include <mLib/bits.h>
+
+#ifndef BLKC_H
+#  include "blkc.h"
+#endif
+
+#ifndef PARANOIA_H
+#  include "paranoia.h"
+#endif
+
+/*----- Data structures ---------------------------------------------------*/
+
+/* --- @CFB_DECL@ --- *
+ *
+ * Arguments:	@PRE@, @pre@ = prefixes for the underlying block cipher
+ *
+ * Use:		Creates declarations for CFB mode.
+ */
+
+#define CFB_DECL(PRE, pre)						\
+									\
+typedef struct pre ## _cfbctx {						\
+  pre ## _ctx ctx;			/* Underlying cipher context */	\
+  int off;				/* Offset into @iv@ buffer */	\
+  octet iv[PRE ## _BLKSZ];		/* Previous ciphertext or IV */	\
+} pre ## _cfbctx;							\
+									\
+extern void pre ## _cfbgetiv(const pre ## _cfbctx */*ctx*/,		\
+			     void */*iv*/);				\
+									\
+extern void pre ## _cfbsetiv(pre ## _cfbctx */*ctx*/,			\
+			     const void */*iv*/);			\
+									\
+extern void pre ## _cfbbdry(pre ## _cfbctx */*ctx*/);			\
+									\
+extern void pre ## _cfbsetkey(pre ## _cfbctx */*ctx*/,			\
+			      const pre ## _ctx */*k*/);		\
+									\
+extern void pre ## _cfbinit(pre ## _cfbctx */*ctx*/,			\
+			    const void */*key*/, size_t /*sz*/,		\
+			    const void */*iv*/);			\
+									\
+extern void pre ## _cfbencrypt(pre ## _cfbctx */*ctx*/,			\
+			       const void */*src*/, void */*dest*/,	\
+			       size_t /*sz*/);				\
+									\
+extern void pre ## _cfbdecrypt(pre ## _cfbctx */*ctx*/,			\
+			       const void */*src*/, void */*dest*/,	\
+			       size_t /*sz*/);				\
+
+
+/* --- @CFB_DEF@ --- *
+ *
+ * Arguments:	@PRE@, @pre@ = prefixes for the underlying block cipher
+ *
+ * Use:		Creates an implementation for CFB mode.
+ */
+
+#define CFB_DEF(PRE, pre)						\
+									\
+/* --- @pre_cfbgetiv@ --- *						\
+ *									\
+ * Arguments:	@const pre_cfbctx *ctx@ = pointer to CFB context block	\
+ *		@void *iv#@ = pointer to output data block		\
+ *									\
+ * Returns:	---							\
+ *									\
+ * Use:		Reads the currently set IV.  Reading and setting an IV	\
+ *		is not transparent to the cipher.  It will add a `step'	\
+ *		which must be matched by a similar operation during	\
+ *		decryption.						\
+ */									\
+									\
+void pre ## _cfbgetiv(const pre ## _cfbctx *ctx, void *iv)		\
+{									\
+  octet *p = iv;							\
+  int off = ctx->off;							\
+  int rest = PRE ## _BLKSZ - off;					\
+  memcpy(p, ctx->iv + off, rest);					\
+  memcpy(p + rest, ctx->iv, off);					\
+}									\
+									\
+/* --- @pre_cfbsetiv@ --- *						\
+ *									\
+ * Arguments:	@pre_cfbctx *ctx@ = pointer to CFB context block	\
+ *		@cnost void *iv@ = pointer to IV to set			\
+ *									\
+ * Returns:	---							\
+ *									\
+ * Use:		Sets the IV to use for subsequent encryption.		\
+ */									\
+									\
+void pre ## _cfbsetiv(pre ## _cfbctx *ctx, const void *iv)		\
+{									\
+  uint32 niv[PRE ## _BLKSZ / 4];					\
+  BLKC_LOAD(PRE, niv, iv);						\
+  pre ## _eblk(&ctx->ctx, niv, niv);					\
+  BLKC_STORE(PRE, ctx->iv, niv);					\
+  ctx->off = 0;								\
+}									\
+									\
+/* --- @pre_cfbbdry@ --- *						\
+ *									\
+ * Arguments:	@pre_cfbctx *ctx@ = pointer to CFB context block	\
+ *									\
+ * Returns:	---							\
+ *									\
+ * Use:		Inserts a boundary during encryption.  Successful	\
+ *		decryption must place a similar boundary.		\
+ */									\
+									\
+void pre ## _cfbbdry(pre ## _cfbctx *ctx)				\
+{									\
+  octet iv[PRE ## _BLKSZ];						\
+  pre ## _cfbgetiv(ctx, iv);						\
+  pre ## _cfbsetiv(ctx, iv);						\
+  BURN(iv);								\
+}									\
+									\
+/* --- @pre_cfbsetkey@ --- *						\
+ *									\
+ * Arguments:	@pre_cfbctx *ctx@ = pointer to CFB context block	\
+ *		@const pre_ctx *k@ = pointer to cipher context		\
+ *									\
+ * Returns:	---							\
+ *									\
+ * Use:		Sets the CFB context to use a different cipher key.	\
+ */									\
+									\
+void pre ## _cfbsetkey(pre ## _cfbctx *ctx, const pre ## _ctx *k)	\
+{									\
+  ctx->ctx = *k;							\
+}									\
+									\
+/* --- @pre_cfbinit@ --- *						\
+ *									\
+ * Arguments:	@pre_cfbctx *ctx@ = pointer to cipher context		\
+ *		@const void *key@ = pointer to the key buffer		\
+ *		@size_t sz@ = size of the key				\
+ *		@const void *iv@ = pointer to initialization vector	\
+ *									\
+ * Returns:	---							\
+ *									\
+ * Use:		Initializes a CFB context ready for use.  You should	\
+ *		ensure that the IV chosen is unique: reusing an IV will	\
+ *		compromise the security of at least the first block	\
+ *		encrypted.  This is equivalent to calls to @pre_init@,	\
+ *		@pre_cfbsetkey@ and @pre_cfbsetiv@.			\
+ */									\
+									\
+void pre ## _cfbinit(pre ## _cfbctx *ctx,				\
+		     const void *key, size_t sz,			\
+		     const void *iv)					\
+{									\
+  static octet zero[PRE ## _BLKSZ] = { 0 };				\
+  pre ## _init(&ctx->ctx, key, sz);					\
+  pre ## _cfbsetiv(ctx, iv ? iv : zero);				\
+}									\
+									\
+/* --- @pre_cfbencrypt@ --- *						\
+ *									\
+ * Arguments:	@pre_cfbctx *ctx@ = pointer to CFB context block	\
+ *		@const void *src@ = pointer to source data		\
+ *		@void *dest@ = pointer to destination data		\
+ *		@size_t sz@ = size of block to be encrypted		\
+ *									\
+ * Returns:	---							\
+ *									\
+ * Use:		Encrypts a block with a block cipher in CFB mode.  The	\
+ *		input block may be arbitrary in size.  CFB mode is not	\
+ *		sensitive to block boundaries.				\
+ */									\
+									\
+void pre ## _cfbencrypt(pre ## _cfbctx *ctx,				\
+			const void *src, void *dest,			\
+			size_t sz)					\
+{									\
+  const octet *s = src;							\
+  octet *d = dest;							\
+  int off = ctx->off;							\
+									\
+  /* --- Empty blocks are trivial --- */				\
+									\
+  if (!sz)								\
+    return;								\
+									\
+  /* --- If I can deal with the block from my buffer, do that --- */	\
+									\
+  if (sz < PRE ## _BLKSZ - off)						\
+    goto small;								\
+									\
+  /* --- Finish off what's left in my buffer --- */			\
+									\
+  while (off < PRE ## _BLKSZ) {						\
+    register octet x = *s++;						\
+    *d++ = ctx->iv[off++] ^= x;						\
+    sz--;								\
+  }									\
+									\
+  /* --- Main encryption loop --- */					\
+									\
+  {									\
+    uint32 iv[PRE ## _BLKSZ / 4];					\
+    BLKC_LOAD(PRE, iv, ctx->iv);					\
+									\
+    for (;;) {								\
+      pre ## _eblk(&ctx->ctx, iv, iv);					\
+      if (sz < PRE ## _BLKSZ)						\
+	break;								\
+      BLKC_XLOAD(PRE, iv, s);						\
+      BLKC_STORE(PRE, d, iv);						\
+      s += PRE ## _BLKSZ;						\
+      d += PRE ## _BLKSZ;						\
+      sz -= PRE ## _BLKSZ;						\
+    }									\
+    off = 0;								\
+    BLKC_STORE(PRE, ctx->iv, iv);					\
+  }									\
+									\
+  /* --- Tidying up the tail end --- */					\
+									\
+  if (sz) {								\
+  small:								\
+    do {								\
+      register octet x = *s++;						\
+      *d++ = ctx->iv[off++] ^= x;					\
+      sz--;								\
+    } while (sz);							\
+  }									\
+									\
+  /* --- Done --- */							\
+									\
+  ctx->off = off;							\
+  return;								\
+}									\
+									\
+/* --- @pre_cfbdecrypt@ --- *						\
+ *									\
+ * Arguments:	@pre_cfbctx *ctx@ = pointer to CFB context block	\
+ *		@const void *src@ = pointer to source data		\
+ *		@void *dest@ = pointer to destination data		\
+ *		@size_t sz@ = size of block to be encrypted		\
+ *									\
+ * Returns:	---							\
+ *									\
+ * Use:		Encrypts a block with a block cipher in CFB mode, with	\
+ *		ciphertext stealing and other clever tricks.		\
+ *		Essentially, data can be encrypted in arbitrary sized	\
+ *		chunks, although decryption must use the same chunks.	\
+ */									\
+									\
+void pre ## _cfbdecrypt(pre ## _cfbctx *ctx,				\
+			const void *src, void *dest,			\
+			size_t sz)					\
+{									\
+  const octet *s = src;							\
+  octet *d = dest;							\
+  int off = ctx->off;							\
+									\
+  /* --- Empty blocks are trivial --- */				\
+									\
+  if (!sz)								\
+    return;								\
+									\
+  /* --- If I can deal with the block from my buffer, do that --- */	\
+									\
+  if (sz < PRE ## _BLKSZ - off)						\
+    goto small;								\
+									\
+  /* --- Finish off what's left in my buffer --- */			\
+									\
+  while (off < PRE ## _BLKSZ) {						\
+    register octet x = *s++;						\
+    *d++ = ctx->iv[off] ^ x;						\
+    ctx->iv[off++] = x;							\
+    sz--;								\
+  }									\
+									\
+  /* --- Main encryption loop --- */					\
+									\
+  {									\
+    uint32 iv[PRE ## _BLKSZ / 4];					\
+    BLKC_LOAD(PRE, iv, ctx->iv);					\
+									\
+    for (;;) {								\
+      uint32 x[PRE ## _BLKSZ / 4];					\
+      pre ## _eblk(&ctx->ctx, iv, iv);					\
+      if (sz < PRE ## _BLKSZ)						\
+	break;								\
+      BLKC_LOAD(PRE, x, s);						\
+      BLKC_XSTORE(PRE, d, iv, x);					\
+      BLKC_MOVE(PRE, iv, x);						\
+      s += PRE ## _BLKSZ;						\
+      d += PRE ## _BLKSZ;						\
+      sz -= PRE ## _BLKSZ;						\
+    }									\
+    off = 0;								\
+    BLKC_STORE(PRE, ctx->iv, iv);					\
+  }									\
+									\
+  /* --- Tidying up the tail end --- */					\
+									\
+  if (sz) {								\
+  small:								\
+    do {								\
+      register octet x = *s++;						\
+      *d++ = ctx->iv[off] ^ x;						\
+      ctx->iv[off++] = x;						\
+      sz--;								\
+    } while (sz);							\
+  }									\
+									\
+  /* --- Done --- */							\
+									\
+  ctx->off = off;							\
+  return;								\
+}									\
+									\
+CFB_TEST(PRE, pre)
+
+/*----- Test rig ----------------------------------------------------------*/
+
+#ifdef TEST_RIG
+
+#include <stdio.h>
+
+#include "daftstory.h"
+
+/* --- @CFB_TEST@ --- *
+ *
+ * Arguments:	@PRE@, @pre@ = prefixes for block cipher definitions
+ *
+ * Use:		Standard test rig for CFB functions.
+ */
+
+#define CFB_TEST(PRE, pre)						\
+									\
+/* --- Initial plaintext for the test --- */				\
+									\
+static const octet text[] = TEXT;					\
+									\
+/* --- Key and IV to use --- */						\
+									\
+static const octet key[] = KEY;						\
+static const octet iv[] = IV;						\
+									\
+/* --- Buffers for encryption and decryption output --- */		\
+									\
+static octet ct[sizeof(text)];						\
+static octet pt[sizeof(text)];						\
+									\
+static void hexdump(const octet *p, size_t sz)				\
+{									\
+  const octet *q = p + sz;						\
+  for (sz = 0; p < q; p++, sz++) {					\
+    printf("%02x", *p);							\
+    if ((sz + 1) % PRE ## _BLKSZ == 0)					\
+      putchar(':');							\
+  }									\
+}									\
+									\
+int main(void)								\
+{									\
+  size_t sz = 0, rest;							\
+  pre ## _cfbctx ctx;							\
+  int status = 0;							\
+  int done = 0;								\
+  pre ## _ctx k;							\
+									\
+  size_t keysz = PRE ## _KEYSZ ?					\
+    PRE ## _KEYSZ : strlen((const char *)key);				\
+									\
+  fputs(#pre "-cfb: ", stdout);						\
+									\
+  pre ## _init(&k, key, keysz);						\
+  pre ## _cfbsetkey(&ctx, &k);						\
+									\
+  while (sz <= sizeof(text)) {						\
+    rest = sizeof(text) - sz;						\
+    memcpy(ct, text, sizeof(text));					\
+    pre ## _cfbsetiv(&ctx, iv);						\
+    pre ## _cfbencrypt(&ctx, ct, ct, sz);				\
+    pre ## _cfbencrypt(&ctx, ct + sz, ct + sz, rest);			\
+    memcpy(pt, ct, sizeof(text));					\
+    pre ## _cfbsetiv(&ctx, iv);						\
+    pre ## _cfbdecrypt(&ctx, pt, pt, rest);				\
+    pre ## _cfbdecrypt(&ctx, pt + rest, pt + rest, sz);			\
+    if (memcmp(pt, text, sizeof(text)) == 0) {				\
+      done++;								\
+      if (sizeof(text) < 40 || done % 8 == 0)				\
+        fputc('.', stdout);						\
+      if (done % 480 == 0)						\
+        fputs("\n\t", stdout);						\
+      fflush(stdout);							\
+    } else {								\
+      printf("\nError (sz = %lu)\n", (unsigned long)sz);		\
+      status = 1;							\
+      printf("\tplaintext      = "); hexdump(text, sz);			\
+        printf(", "); hexdump(text + sz, rest);				\
+        fputc('\n', stdout);						\
+      printf("\tciphertext     = "); hexdump(ct, sz);			\
+        printf(", "); hexdump(ct + sz, rest);				\
+        fputc('\n', stdout);						\
+      printf("\trecovered text = "); hexdump(pt, sz);			\
+        printf(", "); hexdump(pt + sz, rest);				\
+        fputc('\n', stdout);						\
+      fputc('\n', stdout);						\
+    }									\
+    if (sz < 63)							\
+      sz++;								\
+    else								\
+      sz += 9;								\
+  }									\
+									\
+  fputs(status ? " failed\n" : " ok\n", stdout);			\
+  return (status);							\
+}
+
+#else
+#  define CFB_TEST(PRE, pre)
+#endif
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/configure.in b/configure.in
new file mode 100644
index 0000000..4893b92
--- /dev/null
+++ b/configure.in
@@ -0,0 +1,95 @@
+dnl -*-fundamental-*-
+dnl
+dnl $Id: configure.in,v 1.1 1999/09/03 08:41:11 mdw Exp $
+dnl
+dnl Autoconfiguration for Catacomb
+dnl
+dnl (c) 1999 Straylight/Edgeware
+dnl
+
+dnl ----- Licensing notice --------------------------------------------------
+dnl
+dnl This file is part of Catacomb.
+dnl
+dnl Catacomb is free software; you can redistribute it and/or modify
+dnl it under the terms of the GNU Library General Public License as
+dnl published by the Free Software Foundation; either version 2 of the
+dnl License, or (at your option) any later version.
+dnl 
+dnl Catacomb is distributed in the hope that it will be useful,
+dnl but WITHOUT ANY WARRANTY; without even the implied warranty of
+dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+dnl GNU Library General Public License for more details.
+dnl 
+dnl You should have received a copy of the GNU Library General Public
+dnl License along with Catacomb; if not, write to the Free
+dnl Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+dnl MA 02111-1307, USA.
+
+dnl ----- Revision history --------------------------------------------------
+dnl
+dnl $Log: configure.in,v $
+dnl Revision 1.1  1999/09/03 08:41:11  mdw
+dnl Initial import.
+dnl
+dnl Revision 1.1  1999/09/03 08:41:11  mdw
+dnl Initial import.
+dnl
+
+AM_INIT_AUTOMAKE(catacomb, 1.0.0pre1)
+
+AC_INIT(blkc.h)
+AM_INIT_AUTOMAKE(catacomb, 1.0.0pre2)
+AM_CONFIG_HEADER(config.h)
+
+dnl --- Make sure I can compile and build libraries ---
+
+AC_PROG_CC
+AC_CHECK_PROG(AR, ar, ar)
+AC_PROG_RANLIB
+mdw_MLIB
+
+AC_PROG_YACC
+
+dnl --- Actually, I assume these exist anyway ---
+
+AC_CHECK_HEADERS(unistd.h)
+AC_HEADER_STDC
+
+dnl --- Check for various important system types ---
+
+AC_TYPE_PID_T
+
+dnl --- Tedious check for ssize_t ---
+dnl
+dnl glibc-2 puts ssize_t in a strange place.
+
+AC_CACHE_CHECK(for ssize_t, cat_cv_type_ssize_t,
+[AC_EGREP_CPP(ssize_t,
+[#include <sys/types.h>
+#if HAVE_UNISTD_H
+#inlcude <unistd.h>
+#endif
+#if STDC_HEADERS
+#include <stddef.h>
+#include <stdlib.h>
+#endif],
+cat_cv_type_ssize_t=yes, cat_cv_type_ssize_t=no)])
+if test $cat_cv_type_ssize_t = no; then
+  AC_DEFINE(ssize_t, int)
+fi
+AC_TYPE_UID_T
+AC_CHECK_TYPE(time_t, long)
+mdw_TYPE_SSIZE_T
+
+dnl --- Can I call `initgroups'? ---
+dnl
+dnl This is used in noise-gathering.
+
+AC_CHECK_FUNCS(setgroups)
+AC_OUTPUT(Makefile)
+dnl --- Done ---
+
+AC_OUTPUT(Makefile catacomb-config)
+
+dnl ----- That's all, folks -------------------------------------------------
diff --git a/daftstory.h b/daftstory.h
new file mode 100644
index 0000000..8715cdc
--- /dev/null
+++ b/daftstory.h
@@ -0,0 +1,86 @@
+/* -*-c-*-
+ *
+ * $Id: daftstory.h,v 1.1 1999/09/03 08:41:11 mdw Exp $
+ *
+ * Daft story for use in test encryptions
+ *
+ * (c) 1999 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: daftstory.h,v $
+ * Revision 1.1  1999/09/03 08:41:11  mdw
+ * Initial import.
+ *
+ */
+
+#ifndef DAFTSTORY_H
+#define DAFTSTORY_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Macros ------------------------------------------------------------*/
+
+/* --- Don't ask --- */
+
+#ifdef SMALL_TEST
+#  define TEXT "A small piece of text for testing encryption."
+#else
+#  define STORY "\
+Once upon a time there were a beautiful princess, a slightly nutty wizard,\n\
+and a watermelon.  Now, the watermelon had decided that it probably wasn't\n\
+going to get very far with the princess unless it did something pretty\n\
+drastic.  So it asked the wizard to turn it into a handsome prince.\n\
+\n\
+At least, this is the way that the wizard viewed the situation.  He might\n\
+have just hallucinated it all; those mushrooms had looked ever so nice.\n\
+\n\
+Back to the point.  The watermelon had expressed its desire not to be a\n\
+watermelon any more.  And the wizard was probably tripping something quite\n\
+powerful.  He hunted around a bit for his staff, and mumbled something\n\
+that film directors would think of as sounding appropriately arcane and\n\
+mystical (but was, in fact, just the ingredients list for an ancient\n\
+remedy for athlete's foot) and *pop*.  Cooked watermelon.  Yuk.\n\
+\n\
+Later in the year, the princess tripped over the hem of her dress, fell\n\
+down a spiral staircase, and died.  The king ordered dressmakers to attach\n\
+safety warnings to long dresses.\n\
+\n\
+And the wizard?  Who cares?\n\
+"
+#  define TEXT STORY STORY STORY
+#endif
+
+#define KEY "Penguins rule OK, rhubarb cauliflower"
+#define IV "EdgewareCatacomb, parsley, sage, rosemary and thyme"
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/des-base.c b/des-base.c
new file mode 100644
index 0000000..73246b6
--- /dev/null
+++ b/des-base.c
@@ -0,0 +1,49 @@
+/* -*-c-*-
+ *
+ * $Id: des-base.c,v 1.1 1999/09/03 08:41:11 mdw Exp $
+ *
+ * Common features for DES implementation
+ *
+ * (c) 1999 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: des-base.c,v $
+ * Revision 1.1  1999/09/03 08:41:11  mdw
+ * Initial import.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <mLib/bits.h>
+
+#include "des-base.h"
+#include "des_sp.h"
+
+/*----- Global variables --------------------------------------------------*/
+
+uint32 des_sp[8][64] = DES_SP;
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/des-base.h b/des-base.h
new file mode 100644
index 0000000..dd4fdb7
--- /dev/null
+++ b/des-base.h
@@ -0,0 +1,166 @@
+/* -*-c-*-
+ *
+ * $Id: des-base.h,v 1.1 1999/09/03 08:41:11 mdw Exp $
+ *
+ * Common features for DES implementation
+ *
+ * (c) 1999 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: des-base.h,v $
+ * Revision 1.1  1999/09/03 08:41:11  mdw
+ * Initial import.
+ *
+ */
+
+#ifndef DES_BASE_H
+#define DES_BASE_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <mLib/bits.h>
+
+/*----- External data -----------------------------------------------------*/
+
+extern uint32 des_sp[8][64];
+
+/*----- Macros ------------------------------------------------------------*/
+
+/* --- @DES_ROUND@ --- *
+ *
+ * This is the basic DES round function.  The inputs are the two subkey
+ * halves, and the left and right block halves.  Note that the block halves
+ * are rotated left one place at this point.  This wraps what's meant to be
+ * the top bit around to the bottom, so I get a clear run at the S-boxes.
+ */
+
+#define DES_ROUND(ka, kb, x, y) do {					\
+  uint32 _t = (y) ^ (ka);						\
+  (x) ^= des_sp[7][(_t >>  0) & 0x3f] ^					\
+	 des_sp[5][(_t >>  8) & 0x3f] ^					\
+	 des_sp[3][(_t >> 16) & 0x3f] ^					\
+	 des_sp[1][(_t >> 24) & 0x3f];					\
+  _t = ROR32((y), 4) ^ (kb);						\
+  (x) ^= des_sp[6][(_t >>  0) & 0x3f] ^					\
+	 des_sp[4][(_t >>  8) & 0x3f] ^					\
+	 des_sp[2][(_t >> 16) & 0x3f] ^					\
+	 des_sp[0][(_t >> 24) & 0x3f];					\
+} while (0)
+
+/* --- @DES_IP@, @DES_IPINV@ --- *
+ *
+ * The cryptographically useless initial and final permutations.  The initial
+ * permutation also rotates the two block halves left by one place.  This is
+ * undone by the inverse permutation at the end.
+ */
+
+#define DES_IP(x, y) do {						\
+  uint32 _t;								\
+  _t = (y ^ (x >> 4)) & 0x0f0f0f0f; y ^= _t; x ^= _t << 4;		\
+  _t = (x ^ (x >> 18)) & 0x00003333; x ^= _t; x ^= _t << 18;		\
+  _t = (y ^ (y >> 18)) & 0x00003333; y ^= _t; y ^= _t << 18;		\
+  _t = (x ^ (x >> 9)) & 0x00550055; x ^= _t; x ^= _t << 9;		\
+  _t = (y ^ (y >> 9)) & 0x00550055; y ^= _t; y ^= _t << 9;		\
+  _t = (x ^ (x >> 24)) & 0x000000ff; x ^= _t; x ^= _t << 24;		\
+  _t = (y ^ (y >> 24)) & 0x000000ff; y ^= _t; y ^= _t << 24;		\
+  _t = (y ^ (x >> 16)) & 0x0000ffff; y ^= _t; x ^= _t << 16;		\
+  x = ROL32(x, 1); y = ROL32(y, 1);					\
+} while (0)
+
+#define DES_IPINV(x, y) do {						\
+  uint32 _t;								\
+  x = ROR32(x, 1); y = ROR32(y, 1);					\
+  _t = (y ^ (x >> 16)) & 0x0000ffff; y ^= _t; x ^= _t << 16;		\
+  _t = (x ^ (x >> 24)) & 0x000000ff; x ^= _t; x ^= _t << 24;		\
+  _t = (y ^ (y >> 24)) & 0x000000ff; y ^= _t; y ^= _t << 24;		\
+  _t = (y ^ (x >> 4)) & 0x0f0f0f0f; y ^= _t; x ^= _t << 4;		\
+  _t = (x ^ (x >> 18)) & 0x00003333; x ^= _t; x ^= _t << 18;		\
+  _t = (y ^ (y >> 18)) & 0x00003333; y ^= _t; y ^= _t << 18;		\
+  _t = (x ^ (x >> 9)) & 0x00550055; x ^= _t; x ^= _t << 9;		\
+  _t = (y ^ (y >> 9)) & 0x00550055; y ^= _t; y ^= _t << 9;		\
+} while (0)
+
+/* --- @DES_EBLK@, @DES_DBLK@ --- *
+ *
+ * Whole block encryption and decryption.
+ */
+
+#define DES_EBLK(k, a, b, c, d) do {					\
+  const uint32 *_k = (k);						\
+  uint32 _x = (a), _y = (b);						\
+  DES_ROUND(_k[0], _k[1], _x, _y); _k += 2;				\
+  DES_ROUND(_k[0], _k[1], _y, _x); _k += 2;				\
+  DES_ROUND(_k[0], _k[1], _x, _y); _k += 2;				\
+  DES_ROUND(_k[0], _k[1], _y, _x); _k += 2;				\
+  DES_ROUND(_k[0], _k[1], _x, _y); _k += 2;				\
+  DES_ROUND(_k[0], _k[1], _y, _x); _k += 2;				\
+  DES_ROUND(_k[0], _k[1], _x, _y); _k += 2;				\
+  DES_ROUND(_k[0], _k[1], _y, _x); _k += 2;				\
+  DES_ROUND(_k[0], _k[1], _x, _y); _k += 2;				\
+  DES_ROUND(_k[0], _k[1], _y, _x); _k += 2;				\
+  DES_ROUND(_k[0], _k[1], _x, _y); _k += 2;				\
+  DES_ROUND(_k[0], _k[1], _y, _x); _k += 2;				\
+  DES_ROUND(_k[0], _k[1], _x, _y); _k += 2;				\
+  DES_ROUND(_k[0], _k[1], _y, _x); _k += 2;				\
+  DES_ROUND(_k[0], _k[1], _x, _y); _k += 2;				\
+  DES_ROUND(_k[0], _k[1], _y, _x); _k += 2;				\
+  (c) = _y;								\
+  (d) = _x;								\
+} while (0)
+
+#define DES_DBLK(k, a, b, c, d) do {					\
+  const uint32 *_k = (k) + 32;						\
+  uint32 _x = (a), _y = (b);						\
+  _k -= 2; DES_ROUND(_k[0], _k[1], _x, _y);				\
+  _k -= 2; DES_ROUND(_k[0], _k[1], _y, _x);				\
+  _k -= 2; DES_ROUND(_k[0], _k[1], _x, _y);				\
+  _k -= 2; DES_ROUND(_k[0], _k[1], _y, _x);				\
+  _k -= 2; DES_ROUND(_k[0], _k[1], _x, _y);				\
+  _k -= 2; DES_ROUND(_k[0], _k[1], _y, _x);				\
+  _k -= 2; DES_ROUND(_k[0], _k[1], _x, _y);				\
+  _k -= 2; DES_ROUND(_k[0], _k[1], _y, _x);				\
+  _k -= 2; DES_ROUND(_k[0], _k[1], _x, _y);				\
+  _k -= 2; DES_ROUND(_k[0], _k[1], _y, _x);				\
+  _k -= 2; DES_ROUND(_k[0], _k[1], _x, _y);				\
+  _k -= 2; DES_ROUND(_k[0], _k[1], _y, _x);				\
+  _k -= 2; DES_ROUND(_k[0], _k[1], _x, _y);				\
+  _k -= 2; DES_ROUND(_k[0], _k[1], _y, _x);				\
+  _k -= 2; DES_ROUND(_k[0], _k[1], _x, _y);				\
+  _k -= 2; DES_ROUND(_k[0], _k[1], _y, _x);				\
+  (c) = _y;								\
+  (d) = _x;								\
+} while (0)
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/des-mktab.c b/des-mktab.c
new file mode 100644
index 0000000..07cff39
--- /dev/null
+++ b/des-mktab.c
@@ -0,0 +1,279 @@
+/* -*-c-*-
+ *
+ * $Id: des-mktab.c,v 1.1 1999/09/03 08:41:11 mdw Exp $
+ *
+ * Build combined S-P tables for DES
+ *
+ * (c) 1999 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: des-mktab.c,v $
+ * Revision 1.1  1999/09/03 08:41:11  mdw
+ * Initial import.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <mLib/bits.h>
+
+/*----- Static variables --------------------------------------------------*/
+
+/* --- S boxes --- */
+
+static char s[8][4][16] = {
+
+  /* --- S1 --- */
+
+  { { 14,  4, 13,  1,  2, 15, 11,  8,  3, 10,  6, 12,  5,  9,  0,  7 },
+    {  0, 15,  7,  4, 14,  2, 13,  1, 10,  6, 12, 11,  9,  5,  3,  8 },
+    {  4,  1, 14,  8, 13,  6,  2, 11, 15, 12,  9,  7,  3, 10,  5,  0 },
+    { 15, 12,  8,  2,  4,  9,  1,  7,  5, 11,  3, 14, 10,  0,  6, 13 } },
+
+  /* --- S2 --- */
+
+  { { 15,  1,  8, 14,  6, 11,  3,  4,  9,  7,  2, 13, 12,  0,  5, 10 },
+    {  3, 13,  4,  7, 15,  2,  8, 14, 12,  0,  1, 10,  6,  9, 11,  5 },
+    {  0, 14,  7, 11, 10,  4, 13,  1,  5,  8, 12,  6,  9,  3,  2, 15 },
+    { 13,  8, 10,  1,  3, 15,  4,  2, 11,  6,  7, 12,  0,  5, 14,  9 } },
+
+  /* --- S3 --- */
+
+  { { 10,  0,  9, 14,  6,  3, 15,  5,  1, 13, 12,  7, 11,  4,  2,  8 },
+    { 13,  7,  0,  9,  3,  4,  6, 10,  2,  8,  5, 14, 12, 11, 15,  1 },
+    { 13,  6,  4,  9,  8, 15,  3,  0, 11,  1,  2, 12,  5, 10, 14,  7 },
+    {  1, 10, 13,  0,  6,  9,  8,  7,  4, 15, 14,  3, 11,  5,  2, 12 } },
+
+  /* --- S4 --- */
+
+  { {  7, 13, 14,  3,  0,  6,  9, 10,  1,  2,  8,  5, 11, 12,  4, 15 },
+    { 13,  8, 11,  5,  6, 15,  0,  3,  4,  7,  2, 12,  1, 10, 14,  9 },
+    { 10,  6,  9,  0, 12, 11,  7, 13, 15,  1,  3, 14,  5,  2,  8,  4 },
+    {  3, 15,  0,  6, 10,  1, 13,  8,  9,  4,  5, 11, 12,  7,  2, 14 } },
+
+  /* --- S5 --- */
+
+  { {  2, 12,  4,  1,  7, 10, 11,  6,  8,  5,  3, 15, 13,  0, 14,  9 },
+    { 14, 11,  2, 12,  4,  7, 13,  1,  5,  0, 15, 10,  3,  9,  8,  6 },
+    {  4,  2,  1, 11, 10, 13,  7,  8, 15,  9, 12,  5,  6,  3,  0, 14 },
+    { 11,  8, 12,  7,  1, 14,  2, 13,  6, 15,  0,  9, 10,  4,  5,  3 } },
+
+  /* --- S6 --- */
+
+  { { 12,  1, 10, 15,  9,  2,  6,  8,  0, 13,  3,  4, 14,  7,  5, 11 },
+    { 10, 15,  4,  2,  7, 12,  9,  5,  6,  1, 13, 14,  0, 11,  3,  8 },
+    {  9, 14, 15,  5,  2,  8, 12,  3,  7,  0,  4, 10,  1, 13, 11,  6 },
+    {  4,  3,  2, 12,  9,  5, 15, 10, 11, 14,  1,  7,  6,  0,  8, 13 } },
+
+  /* --- S7 --- */
+
+  { {  4, 11,  2, 14, 15,  0,  8, 13,  3, 12,  9,  7,  5, 10,  6,  1 },
+    { 13,  0, 11,  7,  4,  9,  1, 10, 14,  3,  5, 12,  2, 15,  8,  6 },
+    {  1,  4, 11, 13, 12,  3,  7, 14, 10, 15,  6,  8,  0,  5,  9,  2 },
+    {  6, 11, 13,  8,  1,  4, 10,  7,  9,  5,  0, 15, 14,  2,  3, 12 } },
+
+  /* --- S8 --- */
+
+  { { 13,  2,  8,  4,  6, 15, 11,  1, 10,  9,  3, 14,  5,  0, 12,  7 },
+    {  1, 15, 13,  8, 10,  3,  7,  4, 12,  5,  6, 11,  0, 14,  9,  2 },
+    {  7, 11,  4,  1,  9, 12, 14,  2,  0,  6, 10, 13, 15,  3,  5,  8 },
+    {  2,  1, 14,  7,  4, 10,  8, 13, 15, 12,  9,  0,  3,  5,  6, 11 } }
+};
+
+/* --- P table --- */
+
+static char p[32] = {
+  16,  7, 20, 21, 29, 12, 28, 17,  1, 15, 23, 26,  5, 18, 31, 10,
+   2,  8, 24, 14, 32, 27,  3,  9, 19, 13, 30,  6, 22, 11,  4, 25
+};
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @unique@ --- *
+ *
+ * Arguments:	@const char *t@ = pointer to table
+ *		@int base@ = base of the data
+ *		@int sz@ = number of elements
+ *		@const char *name@ = name of this table
+ *		@...@ = things to fill in
+ *
+ * Returns:	Zero if it failed, nonzero if it didn't.
+ *
+ * Use:		Validates a table.  All the elements must be in range and
+ *		unique.
+ */
+
+static int unique(char *t, int base, int sz, const char *name, ...)
+{
+  char u[32];
+  char nbuf[128];
+  int i;
+  int ok = 1;
+
+  {
+    va_list ap;
+    va_start(ap, name);
+    vsprintf(nbuf, name, ap);
+    va_end(ap);
+  }
+
+  if (sz > sizeof(u)) {
+    fprintf(stderr, "internal error: table `%s' too large\n", nbuf);
+    exit(EXIT_FAILURE);
+  }
+  memset(u, 0, sizeof(u));
+  for (i = 0; i < sz; i++) {
+    int x = t[i] - base;
+    if (x >= sz) {
+      fprintf(stderr, "validation error: %i too big (index %i) in %s\n",
+	      x + base, i, nbuf);
+      ok = 0;
+    } else if (u[x]) {
+      fprintf(stderr, "validation error: duplicate %i (index %i) in %s\n",
+	      x + base, i, nbuf);
+      ok = 0;
+    }
+    u[x] = 1;
+  }
+  for (i = 0; i < sz; i++) {
+    if (!u[i]) {
+      fprintf(stderr, "validation error: missing %i in %s\n",
+	      i + base, nbuf);
+      ok = 0;
+    }
+  }
+
+  return (ok);
+}
+
+/* --- @validate@ --- *
+ *
+ * Arguments:	---
+ *
+ * Returns:	Only if everything's OK.
+ *
+ * Use:		Validates the tables.  A bit.  Not much at all...
+ */
+
+static void validate(void)
+{
+  int i, j;
+  int ok = 1;
+
+  for (i = 0; i < 8; i++) for (j = 0; j < 4; j++)
+    if (!unique(s[i][j], 0, 16, "sbox %i, row %i", i, j)) ok = 0;
+  if (!unique(p, 1, 32, "p")) ok = 0;
+  if (!ok)
+    exit(EXIT_FAILURE);
+}
+
+/* --- @permute@ --- *
+ *
+ * Arguments:	@unsigned long x@ = value to permute
+ *
+ * Returns:	Permuted version of @x@.
+ *
+ * Use:		Permutes a number.  The result is the input value after
+ *		having been spewed through the @P@ permutation, and then
+ *		(and this is important) rotated left one place.
+ */
+
+static unsigned long permute(unsigned long x)
+{
+  unsigned long y = 0;
+  unsigned i;
+
+  for (i = 0; i < 32; i++) {
+    if (x & (1 << (32 - p[i])))
+      y |= (1 << (31 - i));
+  }
+  return (ROL32(y, 1));
+}
+
+/* --- @mangle@ --- *
+ *
+ * Arguments:	@const char s[4][16]@ = an s-box
+ *		@unsigned long ss[64]@ = output buffer
+ *		@int bitoff@ = bit offset to use
+ *
+ * Returns:	---
+ *
+ * Use:		Mangles the s-box.  Specifically, the bizarre indexing is
+ *		transformed into something sensible, and the result is
+ *		permuted according to the @p@ table.
+ */
+
+static void mangle(const char s[4][16], unsigned long *ss, int bitoff)
+{
+  unsigned i;
+  for (i = 0; i < 64; i++) {
+    unsigned row = ((i & 0x20) >> 4) | (i & 0x01);
+    unsigned col = (i & 0x1e) >> 1;
+    ss[i] = permute(s[row][col] << bitoff);
+  }
+}
+
+/* --- @main@ --- */
+
+int main(void)
+{
+  int i, j;
+  unsigned long ss[64];
+  const char *sep;
+
+  validate();
+
+  fputs("\
+/* DES SP table (generated) */\n\
+\n\
+#define DES_SP {							\\\n\
+", stdout);
+  for (i = 0; i < 8; i++) {
+    mangle(s[i], ss, 28 - 4 * i);
+    printf("\
+									\\\n\
+  /* --- SP[%i] --- */							\\\n\
+									\\\n\
+", i);
+    sep = "  { ";
+    for (j = 0; j < 64; j++) {
+      printf("%s0x%08lx", sep, ss[j]);
+      if (j % 4 == 3)
+	sep = ",			\\\n    ";
+      else
+	sep = ", ";
+    }
+    printf(" }%s	       		\\\n", i == 7 ? "" : ",");
+  }
+  fputs("}\n", stdout);
+  return (0);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/des.c b/des.c
new file mode 100644
index 0000000..cb73a8e
--- /dev/null
+++ b/des.c
@@ -0,0 +1,263 @@
+/* -*-c-*-
+ *
+ * $Id: des.c,v 1.1 1999/09/03 08:41:11 mdw Exp $
+ *
+ * The Data Encryption Standard
+ *
+ * (c) 1999 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: des.c,v $
+ * Revision 1.1  1999/09/03 08:41:11  mdw
+ * Initial import.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <mLib/bits.h>
+
+#include "blkc.h"
+#include "des-base.h"
+#include "des.h"
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @permute@ --- *
+ *
+ * Arguments:	@const char *p@ = pointer to permutation table
+ *		@uint32 a, b@ = source value to permute
+ *		@uint32 *d@ = destination for value
+ *
+ * Returns:	---
+ *
+ * Use:		Performs a 64-bit permutation.  The table is given in the
+ *		normal (but bizarre) DES bit numbering system.  That's not to
+ *		say that the tables in this source file are like the normal
+ *		DES tables, because they're not.
+ */
+
+static void permute(const char *p, uint32 a, uint32 b, uint32 *d)
+{
+  uint32 x = 0, y = 0;
+  int i;
+
+  for (i = 0; i < 32; i++) {
+    int q = p[i];
+    uint32 t;
+    if (!q)
+      continue;
+    else if (q <= 32)
+      t = a;
+    else {
+      t = b;
+      q -= 32;
+    }
+    if (t & (1 << (32 - q)))
+      x |= (1 << (31 - i));
+  }
+
+  p += 32;
+
+  for (i = 0; i < 32; i++) {
+    int q = p[i];
+    uint32 t;
+    if (!q)
+      continue;
+    else if (q <= 32)
+      t = a;
+    else {
+      t = b;
+      q -= 32;
+    }
+    if (t & (1 << (32 - q)))
+      y |= (1 << (31 - i));
+  }
+
+  d[0] = x;
+  d[1] = y;
+}
+
+/* --- @des_init@ --- *
+ *
+ * Arguments:	@des_ctx *k@ = pointer to key block
+ *		@const void *buf@ = pointer to key buffer
+ *		@size_t sz@ = size of key material
+ *
+ * Returns:	---
+ *
+ * Use:		Initializes a DES key buffer.  The key buffer may be either 7
+ *		or 8 bytes long.  If it's 8 bytes, the key is assumed to be
+ *		padded with parity bits in the low order bit of each octet.
+ *		These are stripped out without checking prior to the actual
+ *		key scheduling.
+ */
+
+void des_init(des_ctx *k, const void *buf, size_t sz)
+{
+  uint32 x, y;
+  uint32 *kp = k->k;
+  int i;
+
+  /* --- @pc1@ --- *
+   *
+   * This cryptographically useless permutation is used to mangle the key
+   * before it's subjected to the key schedule proper.  I've not actually
+   * messed it about much except for inserting padding at the beginning of
+   * the two halves of the key.
+   */
+
+  static const char pc1[] = {
+     0,  0,  0,  0,
+    57, 49, 41, 33, 25, 17,  9,
+     1, 58, 50, 42, 34, 26, 18,
+    10,  2, 59, 51, 43, 35, 27,
+    19, 11,  3, 60, 52, 44, 36,
+     0,  0,  0,  0,
+    63, 55, 47, 39, 31, 23, 15,
+     7, 62, 54, 46, 38, 30, 22,
+    14,  6, 61, 53, 45, 37, 29,
+    21, 13,  5, 28, 20, 12,  4
+  };
+
+  /* --- @pc2@ --- *
+   *
+   * This irritating but necessary permutation mangles the key between the
+   * simple rotation-based schedule and the actual XOR with which it modifies
+   * the behaviour of the cipher.
+   *
+   * This version of the table doesn't look much like the original.  This is
+   * because some parts of the world have been permuted in order to make
+   * things simpler for the round function.  In particular, everything is
+   * rotated left one place to avoid problems with the wraparound of the
+   * expansion permutation, and the key is split between odd and even S-boxes
+   * rather than high and low ones.  That's without the complication of the
+   * padding bits in the representation of the 56-bit proto-key.
+   */
+
+  static const char pc2[] = {
+     0,  0,  3 + 4, 28 + 4, 15 + 4,  6 + 4, 21 + 4, 10 + 4, /* S-box 2 */
+     0,  0, 16 + 4,  7 + 4, 27 + 4, 20 + 4, 13 + 4,  2 + 4, /* S-box 4 */
+     0,  0, 30 + 8, 40 + 8, 51 + 8, 45 + 8, 33 + 8, 48 + 8, /* S-box 6 */
+     0,  0, 46 + 8, 42 + 8, 50 + 8, 36 + 8, 29 + 8, 32 + 8, /* S-box 8 */
+     0,  0, 14 + 4, 17 + 4, 11 + 4, 24 + 4,  1 + 4,  5 + 4, /* S-box 1 */
+     0,  0, 23 + 4, 19 + 4, 12 + 4,  4 + 4, 26 + 4,  8 + 4, /* S-box 3 */
+     0,  0, 41 + 8, 52 + 8, 31 + 8, 37 + 8, 47 + 8, 55 + 8, /* S-box 5 */
+     0,  0, 44 + 8, 49 + 8, 39 + 8, 56 + 8, 34 + 8, 53 + 8  /* S-box 7 */
+  };
+
+  /* --- @v@ --- *
+   *
+   * Contains the rotation amounts for the key halves.
+   */
+
+  static const char v[] = {
+    1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
+  };
+
+  /* --- Extract the key into my registers --- *
+   *
+   * The 7 byte case is rather horrible.  It expands the key to the 8 byte
+   * case before going any further.  It could probably do with its own @pc1@
+   * table.
+   */
+
+  assert(((void)"DES key must be 56 or 64 bits", sz == 7 || sz == 8));
+
+  if (sz == 8) {
+    const octet *p = buf;
+    x = LOAD32(p); y = LOAD32(p + 4);
+  } else {
+    const octet *p = buf;
+    x = LOAD32(p);
+    x = (x & 0xfe000000) | ((x & 0x01fffff0) >> 1);
+    x = (x & 0xfffe0000) | ((x & 0x0001fff8) >> 1);
+    x = (x & 0xfffffe00) | ((x & 0x000001fc) >> 1);
+    y = LOAD32(p + 3) << 1; /* Note: misaligned */
+    y = (y & 0x000000fe) | ((y & 0x1fffff00) << 1);
+    y = (y & 0x0000fefe) | ((y & 0x3fff0000) << 1);
+    y = (y & 0x00fefefe) | ((y & 0x7f000000) << 1);
+  }
+
+  /* --- Permute using the pointless PC1 --- */
+
+  {
+    uint32 ka[2];
+    permute(pc1, x, y, ka);
+    x = ka[0]; y = ka[1];
+  }
+
+  /* --- Now for the key schedule proper --- */
+
+  for (i = 0; i < 16; i++) {
+    if (v[i] == 1) {
+      x = ((x << 1) | (x >> 27)) & 0x0fffffff;
+      y = ((y << 1) | (y >> 27)) & 0x0fffffff;
+    } else {
+      x = ((x << 2) | (x >> 26)) & 0x0fffffff;
+      y = ((y << 2) | (y >> 26)) & 0x0fffffff;
+    }
+    permute(pc2, x, y, kp);
+    kp += 2;
+  }
+}
+
+/* --- @des_eblk@, @des_dblk@ --- *
+ *
+ * Arguments:	@const des_ctx *k@ = pointer to key block
+ *		@const uint32 s[2]@ = pointer to source block
+ *		@uint32 d[2]@ = pointer to destination block
+ *
+ * Returns:	---
+ *
+ * Use:		Low-level block encryption and decryption.
+ */
+
+void des_eblk(const des_ctx *k, const uint32 *s, uint32 *d)
+{
+  uint32 x = s[0], y = s[1];
+  DES_IP(x, y);
+  DES_EBLK(k->k, x, y, x, y);
+  DES_IPINV(x, y);
+  d[0] = x, d[1] = y;
+}
+
+void des_dblk(const des_ctx *k, const uint32 *s, uint32 *d)
+{
+  uint32 x = s[0], y = s[1];
+  DES_IP(x, y);
+  DES_DBLK(k->k, x, y, x, y);
+  DES_IPINV(x, y);
+  d[0] = x, d[1] = y;
+}
+
+BLKC_TEST(DES, des)
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/des.h b/des.h
new file mode 100644
index 0000000..857cec3
--- /dev/null
+++ b/des.h
@@ -0,0 +1,116 @@
+/* -*-c-*-
+ *
+ * $Id: des.h,v 1.1 1999/09/03 08:41:11 mdw Exp $
+ *
+ * The Data Encryption Standard
+ *
+ * (c) 1999 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: des.h,v $
+ * Revision 1.1  1999/09/03 08:41:11  mdw
+ * Initial import.
+ *
+ */
+
+/*----- Notes on the Data Encryption Standard -----------------------------*
+ *
+ * Almost twenty years after it was first accepted, DES is still the standard
+ * block cipher.  It's showing its age in its small key size and poor
+ * optimiziation for software implementations, but it's not really been badly
+ * dented by the intensive analysis thrown at it.
+ *
+ * This interface is here for compatibility with existing protocols, and
+ * because it's a trivial veneer over the base DES code which is used by the
+ * @des3@ interface which implements proper strong triple-DES.
+ */
+
+#ifndef DES_H
+#define DES_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <stddef.h>
+
+#include <mLib/bits.h>
+
+/*----- Magical numbers ---------------------------------------------------*/
+
+#define DES_BLKSZ 8
+#define DES_KEYSZ 7
+#define DES_CLASS (N, B, 64)
+
+/*----- Data structures ---------------------------------------------------*/
+
+typedef struct des_ctx {
+  uint32 k[32];
+} des_ctx;
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @des_init@ --- *
+ *
+ * Arguments:	@des_ctx *k@ = pointer to key block
+ *		@const void *buf@ = pointer to key buffer
+ *		@size_t sz@ = size of key material
+ *
+ * Returns:	---
+ *
+ * Use:		Initializes a DES key buffer.  The key buffer may be either 7
+ *		or 8 bytes long.  If it's 8 bytes, the key is assumed to be
+ *		padded with parity bits in the low order bit of each octet.
+ *		These are stripped out without checking prior to the actual
+ *		key scheduling.
+ */
+
+extern void des_init(des_ctx */*k*/, const void */*buf*/, size_t /*sz*/);
+
+/* --- @des_eblk@, @des_dblk@ --- *
+ *
+ * Arguments:	@const des_ctx *k@ = pointer to key block
+ *		@const uint32 s[2]@ = pointer to source block
+ *		@uint32 d[2]@ = pointer to destination block
+ *
+ * Returns:	---
+ *
+ * Use:		Low-level block encryption and decryption.
+ */
+
+extern void des_eblk(const des_ctx */*k*/,
+		     const uint32 */*s*/, uint32 */*d*/);
+extern void des_dblk(const des_ctx */*k*/,
+		     const uint32 */*s*/, uint32 */*d*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/des3.c b/des3.c
new file mode 100644
index 0000000..281d8ea
--- /dev/null
+++ b/des3.c
@@ -0,0 +1,123 @@
+/* -*-c-*-
+ *
+ * $Id: des3.c,v 1.1 1999/09/03 08:41:11 mdw Exp $
+ *
+ * Implementation of double- and triple-DES
+ *
+ * (c) 1999 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: des3.c,v $
+ * Revision 1.1  1999/09/03 08:41:11  mdw
+ * Initial import.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <mLib/bits.h>
+
+#include "blkc.h"
+#include "des-base.h"
+#include "des.h"
+#include "des3.h"
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @des3_init@ --- *
+ *
+ * Arguments:	@des3_ctx *k@ = pointer to key block
+ *		@const void *buf@ = pointer to key buffer
+ *		@size_t sz@ = size of key material
+ *
+ * Returns:	---
+ *
+ * Use:		Initializes a DES key buffer.  The key buffer may have length
+ *              7, 8, 14, 16, 21, or 24.  These correspond to one, two or
+ *              three DES keys, either packed or unpacked (i.e., still
+ *              containing parity bits).
+ */
+
+void des3_init(des3_ctx *k, const void *buf, size_t sz)
+{
+  size_t step;
+  const octet *p = buf;
+
+  assert(((void)("des3 key length must be one of 7, 8, 14, 16, 21 or 24"),
+		(sz % 7 == 0 || sz % 8 == 0) && sz < 25));
+
+  if (sz % 7 == 0)
+    step = 7;
+  else
+    step = 8;
+
+  des_init(&k->a, p, step);
+  if (sz > 8) p += step;
+  des_init(&k->b, p, step);
+  if (sz > 16) p += step; else p = buf;
+  des_init(&k->c, p, step);
+}
+
+/* --- @des3_eblk@, @des3_dblk@ --- *
+ *
+ * Arguments:	@const des3_ctx *k@ = pointer to key block
+ *		@const uint32 s[2]@ = pointer to source block
+ *		@uint32 d[2]@ = pointer to destination block
+ *
+ * Returns:	---
+ *
+ * Use:		Low-level block encryption and decryption.
+ */
+
+void des3_eblk(const des3_ctx *k, const uint32 *s, uint32 *d)
+{
+  uint32 x = s[0], y = s[1];
+  DES_IP(x, y);
+  DES_EBLK(k->a.k, x, y, x, y);
+  DES_DBLK(k->b.k, x, y, x, y);
+  DES_EBLK(k->c.k, x, y, x, y);
+  DES_IPINV(x, y);
+  d[0] = x, d[1] = y;
+}
+
+void des3_dblk(const des3_ctx *k, const uint32 *s, uint32 *d)
+{
+  uint32 x = s[0], y = s[1];
+  DES_IP(x, y);
+  DES_DBLK(k->c.k, x, y, x, y);
+  DES_EBLK(k->b.k, x, y, x, y);
+  DES_DBLK(k->a.k, x, y, x, y);
+  DES_IPINV(x, y);
+  d[0] = x, d[1] = y;
+}
+
+BLKC_TEST(DES3, des3)
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/des3.h b/des3.h
new file mode 100644
index 0000000..ce57ac9
--- /dev/null
+++ b/des3.h
@@ -0,0 +1,116 @@
+/* -*-c-*-
+ *
+ * $Id: des3.h,v 1.1 1999/09/03 08:41:11 mdw Exp $
+ *
+ * Implementation of double- and triple-DES
+ *
+ * (c) 1999 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: des3.h,v $
+ * Revision 1.1  1999/09/03 08:41:11  mdw
+ * Initial import.
+ *
+ */
+
+#ifndef DES3_H
+#define DES3_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Notes on double- and triple-DES -----------------------------------*
+ *
+ * The normal recommendation for using DES now is `triple DES'.
+ * Conventionally, this involves an encrypt-decrypt-encrypt sequence,
+ * although whether the first and last operations use the same key is
+ * unfortunately not agreed upon.  This interface handles both cases (and the
+ * single-key one too, although the simple @des@ calls are quicker for that).
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <stddef.h>
+
+#include <mLib/bits.h>
+
+#ifndef DES_H
+#  include "des.h"
+#endif
+
+/*----- Magical numbers ---------------------------------------------------*/
+
+#define DES3_BLKSZ 8
+#define DES3_KEYSZ 21
+#define DES3_CLASS (N, B, 64)
+
+/*----- Data structures ---------------------------------------------------*/
+
+typedef struct des3_ctx {
+  des_ctx a, b, c;
+} des3_ctx;
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @des3_init@ --- *
+ *
+ * Arguments:	@des3_ctx *k@ = pointer to key block
+ *		@const void *buf@ = pointer to key buffer
+ *		@size_t sz@ = size of key material
+ *
+ * Returns:	---
+ *
+ * Use:		Initializes a DES key buffer.  The key buffer may have length
+ *		7, 8, 14, 16, 21, or 24.  These correspond to one, two or
+ *		three DES keys, either packed or unpacked (i.e., still
+ *		containing parity bits).
+ */
+
+extern void des3_init(des3_ctx */*k*/, const void */*buf*/, size_t /*sz*/);
+
+/* --- @des3_eblk@, @des_dblk@ --- *
+ *
+ * Arguments:	@const des3_ctx *k@ = pointer to key block
+ *		@const uint32 s[2]@ = pointer to source block
+ *		@uint32 d[2]@ = pointer to destination block
+ *
+ * Returns:	---
+ *
+ * Use:		Low-level block encryption and decryption.
+ */
+
+extern void des3_eblk(const des3_ctx */*k*/,
+		      const uint32 */*s*/, uint32 */*d*/);
+extern void des3_dblk(const des3_ctx */*k*/,
+		      const uint32 */*s*/, uint32 */*d*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/ecb.h b/ecb.h
new file mode 100644
index 0000000..1027591
--- /dev/null
+++ b/ecb.h
@@ -0,0 +1,440 @@
+/* -*-c-*-
+ *
+ * $Id: ecb.h,v 1.1 1999/09/03 08:41:12 mdw Exp $
+ *
+ * Ciphertext block chaining for block ciphers
+ *
+ * (c) 1999 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: ecb.h,v $
+ * Revision 1.1  1999/09/03 08:41:12  mdw
+ * Initial import.
+ *
+ */
+
+#ifndef ECB_H
+#define ECB_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <assert.h>
+#include <string.h>
+
+#include <mLib/bits.h>
+
+#ifndef BLKC_H
+#  include "blkc.h"
+#endif
+
+/*----- Macros ------------------------------------------------------------*/
+
+/* --- @ECB_DECL@ --- *
+ *
+ * Arguments:	@PRE@, @pre@ = prefixes for the underlying block cipher
+ *
+ * Use:		Creates declarations for ECB stealing mode.
+ */
+
+#define ECB_DECL(PRE, pre)						\
+									\
+typedef struct pre ## _ecbctx {						\
+  pre ## _ctx ctx;			/* Underlying cipher context */	\
+} pre ## _ecbctx;							\
+									\
+extern void pre ## _ecbsetkey(pre ## _ecbctx */*ctx*/,			\
+			      const pre ## _ctx */*k*/);		\
+									\
+extern void pre ## _ecbinit(pre ## _ecbctx */*ctx*/,			\
+			    const void */*key*/, size_t /*sz*/,		\
+			    const void */*iv*/);			\
+									\
+extern void pre ## _ecbencrypt(pre ## _ecbctx */*ctx*/,			\
+			       const void */*src*/, void */*dest*/,	\
+			       size_t /*sz*/);				\
+									\
+extern void pre ## _ecbdecrypt(pre ## _ecbctx */*ctx*/,			\
+			       const void */*src*/, void */*dest*/,	\
+			       size_t /*sz*/);				\
+
+/* --- @ECB_DEF@ --- *
+ *
+ * Arguments:	@PRE@, @pre@ = prefixes for the underlying block cipher
+ *
+ * Use:		Creates an implementation for ECB stealing mode.
+ */
+
+#define ECB_DEF(PRE, pre)						\
+									\
+/* --- @pre_ecbsetkey@ --- *						\
+ *									\
+ * Arguments:	@pre_ecbctx *ctx@ = pointer to ECB context block	\
+ *		@const pre_ctx *k@ = pointer to cipher context		\
+ *									\
+ * Returns:	---							\
+ *									\
+ * Use:		Sets the ECB context to use a different cipher key.	\
+ */									\
+									\
+void pre ## _ecbsetkey(pre ## _ecbctx *ctx, const pre ## _ctx *k)	\
+{									\
+  ctx->ctx = *k;							\
+}									\
+									\
+/* --- @pre_ecbinit@ --- *						\
+ *									\
+ * Arguments:	@pre_ecbctx *ctx@ = pointer to cipher context		\
+ *		@const void *key@ = pointer to the key buffer		\
+ *		@size_t sz@ = size of the key				\
+ *		@const void *iv@ = pointer to initialization vector	\
+ *									\
+ * Returns:	---							\
+ *									\
+ * Use:		Initializes an ECB context ready for use.  This is	\
+ *		equivalent to calls to @pre_init@ and @pre_setkey@.	\
+ */									\
+									\
+void pre ## _ecbinit(pre ## _ecbctx *ctx,				\
+		     const void *key, size_t sz,			\
+		     const void *iv)					\
+{									\
+  pre ## _init(&ctx->ctx, key, sz);					\
+}									\
+									\
+/* --- @pre_ecbencrypt@ --- *						\
+ *									\
+ * Arguments:	@pre_ecbctx *ctx@ = pointer to ECB context block	\
+ *		@const void *src@ = pointer to source data		\
+ *		@void *dest@ = pointer to destination data		\
+ *		@size_t sz@ = size of block to be encrypted		\
+ *									\
+ * Returns:	---							\
+ *									\
+ * Use:		Encrypts a block with a block cipher in ECB mode, with	\
+ *		ciphertext stealing and other clever tricks.		\
+ *		Essentially, data can be encrypted in arbitrary sized	\
+ *		chunks, although decryption must use the same chunks.	\
+ */									\
+									\
+void pre ## _ecbencrypt(pre ## _ecbctx *ctx,				\
+			const void *src, void *dest,			\
+			size_t sz)					\
+{									\
+  const octet *s = src;							\
+  octet *d = dest;							\
+									\
+  /* --- Empty blocks are trivial --- */				\
+									\
+  if (!sz)								\
+    return;								\
+									\
+  /* --- Short blocks aren't allowed in ECB --- *			\
+   *									\
+   * There's absolutely nothing secure I can do with them.		\
+   */									\
+									\
+  assert(((void)"ECB must have at least one whole block to work with",	\
+	  sz >= PRE ## _BLKSZ));					\
+									\
+  /* --- Do the main chunk of encryption --- *				\
+   *									\
+   * This will do the whole lot if it's a whole number of blocks.  Just	\
+   * give each block to the cipher in turn.  This is trivial.		\
+   * Hopefully...							\
+   */									\
+									\
+  while (sz >= 2 * PRE ## _BLKSZ || sz == PRE ## _BLKSZ) {		\
+    uint32 x[PRE ## _BLKSZ / 4];					\
+    BLKC_LOAD(PRE, x, s);						\
+    pre ## _eblk(&ctx->ctx, x, x);					\
+    BLKC_STORE(PRE, d, x);						\
+    s += PRE ## _BLKSZ;							\
+    d += PRE ## _BLKSZ;							\
+    sz -= PRE ## _BLKSZ;						\
+  }									\
+									\
+  /* --- Do the tail-end block and bit-left-over --- *			\
+   *									\
+   * This isn't very efficient.  That shouldn't matter much.		\
+   */									\
+									\
+  if (sz) {								\
+    uint32 x[PRE ## _BLKSZ / 4];					\
+    octet b[PRE ## _BLKSZ];						\
+    unsigned i;								\
+									\
+    /* --- Let @sz@ be the size of the partial block --- */		\
+									\
+    sz -= PRE ## _BLKSZ;						\
+									\
+    /* --- First stage --- *						\
+     *									\
+     * Read in the current block, and encrypt it.  The first part of 	\
+     * the result is the partial ciphertext block.  Don't write that	\
+     * out yet, because I've not read the partial plaintext block.	\
+     */									\
+									\
+    BLKC_LOAD(PRE, x, s);						\
+    pre ## _eblk(&ctx->ctx, x, x);					\
+    BLKC_STORE(PRE, b, x);						\
+									\
+    /* --- Second stage --- *						\
+     *									\
+     * Now move in the partial plaintext block, writing out the		\
+     * ciphertext as I go.  Then encrypt, and write the complete	\
+     * ciphertext block.						\
+     */									\
+									\
+    s += PRE ## _BLKSZ;							\
+    d += PRE ## _BLKSZ;							\
+    for (i = 0; i < sz; i++) {						\
+      register octet y = b[i];						\
+      b[i] = s[i];							\
+      d[i] = y;								\
+    }									\
+    BLKC_LOAD(PRE, x, b);						\
+    pre ## _eblk(&ctx->ctx, x, x);					\
+    BLKC_STORE(PRE, d - PRE ## _BLKSZ, x);				\
+  }									\
+									\
+  /* --- Done --- */							\
+									\
+  return;								\
+}									\
+									\
+/* --- @pre_ecbdecrypt@ --- *						\
+ *									\
+ * Arguments:	@pre_ecbctx *ctx@ = pointer to ECB context block	\
+ *		@const void *src@ = pointer to source data		\
+ *		@void *dest@ = pointer to destination data		\
+ *		@size_t sz@ = size of block to be encrypted		\
+ *									\
+ * Returns:	---							\
+ *									\
+ * Use:		Encrypts a block with a block cipher in ECB mode, with	\
+ *		ciphertext stealing and other clever tricks.		\
+ *		Essentially, data can be encrypted in arbitrary sized	\
+ *		chunks, although decryption must use the same chunks.	\
+ */									\
+									\
+void pre ## _ecbdecrypt(pre ## _ecbctx *ctx,				\
+			const void *src, void *dest,			\
+			size_t sz)					\
+{									\
+  const octet *s = src;							\
+  octet *d = dest;							\
+									\
+  /* --- Empty blocks are trivial --- */				\
+									\
+  if (!sz)								\
+    return;								\
+									\
+  /* --- Short blocks aren't allowed in ECB --- *			\
+   *									\
+   * There's absolutely nothing secure I can do with them.		\
+   */									\
+									\
+  assert(((void)"ECB must have at least one whole block to work with",	\
+	  sz >= PRE ## _BLKSZ));					\
+									\
+  /* --- Do the main chunk of decryption --- *				\
+   *									\
+   * This will do the whole lot if it's a whole number of blocks.	\
+   * Each block is just handed to the block cipher in turn.		\
+   */									\
+									\
+  while (sz >= 2 * PRE ## _BLKSZ || sz == PRE ## _BLKSZ) {		\
+    uint32 x[PRE ## _BLKSZ / 4];					\
+    BLKC_LOAD(PRE, x, s);						\
+    pre ## _dblk(&ctx->ctx, x, x);					\
+    BLKC_STORE(PRE, d, x);						\
+    s += PRE ## _BLKSZ;							\
+    d += PRE ## _BLKSZ;							\
+    sz -= PRE ## _BLKSZ;						\
+  }									\
+									\
+  /* --- Do the tail-end block and bit-left-over --- *			\
+   *									\
+   * This isn't very efficient.  That shouldn't matter much.		\
+   */									\
+									\
+  if (sz) {								\
+    uint32 x[PRE ## _BLKSZ / 4];					\
+    octet b[PRE ## _BLKSZ];						\
+    unsigned i;								\
+									\
+    /* --- Let @sz@ be the size of the partial block --- */		\
+									\
+    sz -= PRE ## _BLKSZ;						\
+									\
+    /* --- First stage --- *						\
+     *									\
+     * Take the complete ciphertext block, and decrypt it.  This block	\
+     * is carried over for the next encryption operation.		\
+     */									\
+									\
+    BLKC_LOAD(PRE, x, s);						\
+    pre ## _dblk(&ctx->ctx, x, x);					\
+    BLKC_STORE(PRE, b, x);						\
+									\
+    /* --- Second stage --- *						\
+     *									\
+     * The first few bytes are the partial plaintext block.  Write that	\
+     * and replace with the partial ciphertext block.  Then decrypt	\
+     * what's left as the complete plaintext.				\
+     */									\
+									\
+    s += PRE ## _BLKSZ;							\
+    d += PRE ## _BLKSZ;							\
+    for (i = 0; i < sz; i++) {						\
+      register octet y = s[i];						\
+      d[i] = b[i];							\
+      b[i] = y;								\
+    }									\
+    BLKC_LOAD(PRE, x, b);						\
+    pre ## _dblk(&ctx->ctx, x, x);					\
+    BLKC_STORE(PRE, d - PRE ## _BLKSZ, x);				\
+  }									\
+									\
+  /* --- Done --- */							\
+									\
+  return;								\
+}									\
+									\
+ECB_TEST(PRE, pre)
+
+/*----- Test rig ----------------------------------------------------------*/
+
+#ifdef TEST_RIG
+
+#include <stdio.h>
+
+#include "daftstory.h"
+
+/* --- @ECB_TEST@ --- *
+ *
+ * Arguments:	@PRE@, @pre@ = prefixes for block cipher definitions
+ *
+ * Use:		Standard test rig for ECB functions.
+ */
+
+#define ECB_TEST(PRE, pre)						\
+									\
+/* --- Initial plaintext for the test --- */				\
+									\
+static const octet text[] = TEXT;					\
+									\
+/* --- Key and IV to use --- */						\
+									\
+static const octet key[] = KEY;						\
+static const octet iv[] = IV;						\
+									\
+/* --- Buffers for encryption and decryption output --- */		\
+									\
+static octet ct[sizeof(text)];						\
+static octet pt[sizeof(text)];						\
+									\
+static void hexdump(const octet *p, size_t sz)				\
+{									\
+  const octet *q = p + sz;						\
+  for (sz = 0; p < q; p++, sz++) {					\
+    printf("%02x", *p);							\
+    if ((sz + 1) % PRE ## _BLKSZ == 0)					\
+      putchar(':');							\
+  }									\
+}									\
+									\
+int main(void)								\
+{									\
+  size_t sz = 0, rest;							\
+  pre ## _ecbctx ctx;							\
+  int status = 0;							\
+  int done = 0;								\
+									\
+  size_t keysz = PRE ## _KEYSZ ?					\
+    PRE ## _KEYSZ : strlen((const char *)key);				\
+									\
+  fputs(#pre "-ecb: ", stdout);						\
+									\
+  pre ## _ecbinit(&ctx, key, keysz, iv);				\
+									\
+  while (sz <= sizeof(text)) {						\
+    rest = sizeof(text) - sz;						\
+    if ((sz != 0 && sz < PRE ## _BLKSZ) ||				\
+	(rest != 0 && rest < PRE ## _BLKSZ))				\
+      goto next;							\
+    memcpy(ct, text, sizeof(text));					\
+    pre ## _ecbencrypt(&ctx, ct, ct, sz);				\
+    pre ## _ecbencrypt(&ctx, ct + sz, ct + sz, rest);			\
+    memcpy(pt, ct, sizeof(text));					\
+    pre ## _ecbdecrypt(&ctx, pt, pt, sz);				\
+    pre ## _ecbdecrypt(&ctx, pt + sz, pt + sz, rest);			\
+    if (memcmp(pt, text, sizeof(text)) == 0) {				\
+      done++;								\
+      if (sizeof(text) < 40 || done % 8 == 0)				\
+        fputc('.', stdout);						\
+      if (done % 480 == 0)						\
+        fputs("\n\t", stdout);						\
+      fflush(stdout);							\
+    } else {								\
+      printf("\nError (sz = %lu)\n", (unsigned long)sz);		\
+      status = 1;							\
+      printf("\tplaintext      = "); hexdump(text, sz);			\
+        printf(", "); hexdump(text + sz, rest);				\
+        fputc('\n', stdout);						\
+      printf("\tciphertext     = "); hexdump(ct, sz);			\
+        printf(", "); hexdump(ct + sz, rest);				\
+        fputc('\n', stdout);						\
+      printf("\trecovered text = "); hexdump(pt, sz);			\
+        printf(", "); hexdump(pt + sz, rest);				\
+        fputc('\n', stdout);						\
+      fputc('\n', stdout);						\
+    }									\
+  next:									\
+    if (sz < 63)							\
+      sz++;								\
+    else								\
+      sz += 9;								\
+  }									\
+									\
+  fputs(status ? " failed\n" : " ok\n", stdout);			\
+  return (status);							\
+}
+
+#else
+#  define ECB_TEST(PRE, pre)
+#endif
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/genmodes b/genmodes
new file mode 100755
index 0000000..48175ce
--- /dev/null
+++ b/genmodes
@@ -0,0 +1,50 @@
+#! /bin/sh
+
+baselist=$1
+extlist=$2
+
+for i in $baselist; do
+  I=`echo "$i" | tr -d '\n' | tr a-z A-Z | tr -c a-zA-Z0-9 _`
+  for j in $extlist; do
+    J=`echo "$j" | tr -d '\n' | tr a-z A-Z | tr -c a-zA-Z0-9 _`
+    guard="${I}_${J}_H"
+    cat >$i-$j.h <<EOF
+/* -*-c-*-
+ *
+ * $i-$j.h [generated]
+ */
+
+#ifndef $guard
+#define $guard
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+#ifndef ${J}_H
+#  include "$j.h"
+#endif
+
+#ifndef ${I}_H
+#  include "$i.h"
+#endif
+
+${J}_DECL($I, $i)
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
+EOF
+    cat >$i-$j.c <<EOF
+/* -*-c-*-
+ *
+ * $i-$j.h [generated]
+ */
+
+#include "$i-$j.h"
+${J}_DEF($I, $i)
+EOF
+  done
+done
diff --git a/hash.h b/hash.h
new file mode 100644
index 0000000..5d43bfb
--- /dev/null
+++ b/hash.h
@@ -0,0 +1,234 @@
+/* -*-c-*-
+ *
+ * $Id: hash.h,v 1.1 1999/09/03 08:41:12 mdw Exp $
+ *
+ * Generic handling for message digest functions
+ *
+ * (c) 1998 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: hash.h,v $
+ * Revision 1.1  1999/09/03 08:41:12  mdw
+ * Initial import.
+ *
+ */
+
+#ifndef HASH_H
+#define HASH_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <string.h>
+
+#include <mLib/bits.h>
+
+/*----- Macros ------------------------------------------------------------*/
+
+/* --- @HASH_BUFFER@ --- *
+ *
+ * Arguments:	@PRE@, @pre@ = prefixes for hash-specific definitions
+ *		@ictx@ = pointer to context block for the hash
+ *		@ibuf@ = pointer to input data to hash
+ *		@isz@ = size of buffer
+ *
+ * Use:		Handles buffering of input data to a hash function.  The
+ *		hash's compression function is called when the buffer is
+ *		full.  Note that the compression function can be called on
+ *		data which is at odd alignments; it is expected to cope
+ *		gracefully with this (possibly by copying the data into its
+ *		internal buffer before starting).
+ */
+
+#define HASH_BUFFER(PRE, pre, ictx, ibuf, isz) do {			\
+  pre##_ctx *_bctx = (ictx);						\
+  size_t _bsz = (isz);							\
+  const octet *_bbuf = (octet *)(ibuf);					\
+									\
+  /* --- Add on the size done so far --- */				\
+									\
+  _bctx->count += _bsz;							\
+									\
+  /* --- Handle very small contributions --- */				\
+									\
+  if (_bctx->off + _bsz < PRE##_BUFSZ) {				\
+    memcpy(_bctx->buf + _bctx->off, _bbuf, _bsz);			\
+    _bctx->off += _bsz;							\
+  } else {								\
+									\
+    /* --- Handle an initial partial buffer --- */			\
+									\
+    if (_bctx->off) {							\
+      size_t s = PRE##_BUFSZ - _bctx->off;				\
+      memcpy(_bctx->buf + _bctx->off, _bbuf, s);			\
+      pre##_compress(_bctx, _bctx->buf);				\
+      _bsz -= s; _bbuf += s;						\
+    }									\
+									\
+    /* --- Do whole buffers while we can --- */				\
+									\
+    while (_bsz >= PRE##_BUFSZ) {					\
+      pre##_compress(_bctx, _bbuf);					\
+      _bsz -= PRE##_BUFSZ; _bbuf += PRE##_BUFSZ;			\
+    }									\
+									\
+    /* --- And wrap up at the end --- */				\
+									\
+    if (_bsz)								\
+      memcpy(_bctx->buf, _bbuf, _bsz);					\
+    _bctx->off = _bsz;							\
+  }									\
+} while (0)
+
+/* --- @HASH_PAD@ --- *
+ *
+ * Arguments:	@PRE@, @pre@ = prefixes for hash-specific definitions
+ *		@ictx@ = pointer to context block for the hash
+ *		@term@ = terminator character to write following the data
+ *		@pad@ = pad character to fill with
+ *		@diff@ = size of space to leave at the end of the last block
+ *
+ * Use:		Does padding for message digest functions.
+ */
+
+#define HASH_PAD(PRE, pre, ictx, term, pad, diff) do {			\
+  pre##_ctx *_pctx = (ictx);						\
+									\
+  _pctx->buf[_pctx->off] = term;					\
+  _pctx->off++;								\
+  if (_pctx->off > PRE##_BUFSZ - diff) {				\
+    if (_pctx->off < PRE##_BUFSZ)					\
+      memset(_pctx->buf + _pctx->off, pad, PRE##_BUFSZ - _pctx->off);	\
+    pre##_compress(_pctx, _pctx->buf);					\
+    memset(_pctx->buf, pad, PRE##_BUFSZ - diff);			\
+  } else								\
+    memset(_pctx->buf + _pctx->off, pad,				\
+	   PRE##_BUFSZ - _pctx->off - diff);				\
+} while (0)
+
+/* --- @HASH_MD5STRENGTH@ --- *
+ *
+ * Arguments:	@PRE@, @pre@ = prefixes for hash-specific definitions
+ *		@ictx@ = pointer to context block for the hash
+ *
+ * Use:		Does MD5-style MD strengthening.  The data is terminated
+ *		by a single set bit, padded with zero bits, and then a 64-
+ *		bit length is written, little-end first.
+ */
+
+#define HASH_MD5STRENGTH(PRE, pre, ictx) do {				\
+  pre##_ctx *_mctx = (ictx);						\
+  HASH_PAD(PRE, pre, _mctx, 0x80u, 0, 8);				\
+  STORE32_L(_mctx->buf + PRE##_BUFSZ - 8, _mctx->count << 3);		\
+  STORE32_L(_mctx->buf + PRE##_BUFSZ - 4, _mctx->count >> 29);		\
+  pre##_compress(_mctx, _mctx->buf);					\
+} while (0)
+
+/* --- @HASH_TEST@ --- *
+ *
+ * Arguments:	@PRE@, @pre@ = prefixes for hash-specfic definitions
+ *
+ * Use:		Standard test rig for hash functions.
+ */
+
+#ifdef TEST_RIG
+
+#include <mLib/quis.h>
+#include <mLib/testrig.h>
+
+#define HASH_TEST(PRE, pre)						\
+									\
+static int verify(dstr *v)						\
+{									\
+  pre##_ctx ctx;							\
+  int ok = 1;								\
+  int i;								\
+  octet *p;								\
+  int szs[] = { 1, 7, 192, -1, 0 }, *ip;				\
+  size_t sz;								\
+  dstr d;								\
+									\
+  dstr_create(&d);							\
+  dstr_ensure(&d, PRE##_HASHSZ);					\
+  d.len = PRE##_HASHSZ;							\
+									\
+  for (ip = szs; *ip; ip++) {						\
+    i = *ip;								\
+    sz = v[0].len;							\
+    if (i == -1)							\
+      i = sz;								\
+    if (i > sz)								\
+      continue;								\
+    p = (octet *)v[0].buf;						\
+    pre##_init(&ctx);							\
+    while (sz) {							\
+      if (i > sz)							\
+	i = sz;								\
+      pre##_hash(&ctx, p, i);						\
+      p += i;								\
+      sz -= i;								\
+    }									\
+    pre##_done(&ctx, d.buf);						\
+    if (memcmp(d.buf, v[1].buf, PRE##_HASHSZ) != 0) {			\
+      printf("\nfail:\n\tstep = %i\n\tinput = `%s'\n\texpected = ",	\
+	     *ip, v[0].buf);						\
+      type_hex.dump(&v[1], stdout);					\
+      fputs("\n\tcomputed = ", stdout);					\
+      type_hex.dump(&d, stdout);					\
+      putchar('\n');							\
+      ok = 0;								\
+    }									\
+  }									\
+									\
+  dstr_destroy(&d);							\
+  return (ok);								\
+}									\
+									\
+static test_chunk defs[] = {						\
+  { #pre, verify, { &type_string, &type_hex, 0 } },			\
+  { 0, 0, { 0 } }							\
+};									\
+									\
+int main(int argc, char *argv[])					\
+{									\
+  ego(argv[0]);								\
+  test_run(argc, argv, defs, SRCDIR"/tests/" #pre);			\
+  return (0);								\
+}
+
+#else
+#  define HASH_TEST(PRE, pre)
+#endif
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/hmac.h b/hmac.h
new file mode 100644
index 0000000..8849258
--- /dev/null
+++ b/hmac.h
@@ -0,0 +1,321 @@
+/* -*-c-*-
+ *
+ * $Id: hmac.h,v 1.1 1999/09/03 08:41:12 mdw Exp $
+ *
+ * Generic code for HMAC and NMAC
+ *
+ * (c) 1998 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: hmac.h,v $
+ * Revision 1.1  1999/09/03 08:41:12  mdw
+ * Initial import.
+ *
+ */
+
+/*----- Notes on the HMAC and NMAC constructions --------------------------*
+ *
+ * Designed by Mihir Bellare, Ran Canetti and Hugo Krawczyk, NMAC is a
+ * method for constructing keyed message authentication algorithms from
+ * unkeyed hash functions.  HMAC is an alternative formulation which doesn't
+ * require low-level access to the hash function's implementation.  NMAC was
+ * designed to allow MD5 has a suitable underlying hash function, even though
+ * doubts were already being raised about its collision resistance.
+ */
+
+#ifndef HMAC_H
+#define HMAC_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <mLib/bits.h>
+
+#ifndef PARANOIA_H
+#  include "paranoia.h"
+#endif
+
+/*----- Macros ------------------------------------------------------------*/
+
+/* --- @HMAC_DECL@ --- *
+ *
+ * Arguments:	@PRE@, @pre@ = prefixes for the underlying hash function
+ *
+ * Use:		Creates declarations for the HMAC and NMAC functions.
+ */
+
+#define HMAC_DECL(PRE, pre)						\
+									\
+typedef struct pre##_mackey {						\
+  octet ochain[PRE##_HASHSZ];		/* Chaining for outer hash */	\
+  unsigned long ocount;			/* Byte count for outer hash */	\
+  octet ichain[PRE##_HASHSZ];		/* Chaining for inner hash */	\
+  unsigned long icount;			/* Byte count for inner hash */	\
+} pre##_mackey;								\
+									\
+typedef struct pre##_macctx {						\
+  pre##_ctx ctx;			/* Context for main hashing */	\
+  octet chain[PRE##_HASHSZ];		/* Chaining for outer hash */	\
+  unsigned long count;			/* Byte count for outer hash */	\
+} pre##_macctx;								\
+									\
+extern void pre##_nmac(pre##_mackey */*key*/,				\
+		       const void */*ok*/, const void */*ik*/);		\
+									\
+extern void pre##_hmac(pre##_mackey */*key*/, 				\
+		       const void */*k*/, size_t /*sz*/);		\
+									\
+extern void pre##_macinit(pre##_macctx */*ctx*/,			\
+			  const pre##_mackey */*key*/);			\
+									\
+extern void pre##_mac(pre##_macctx */*ctx*/,				\
+		      const void */*buf*/, size_t /*sz*/);		\
+									\
+extern void pre##_macdone(pre##_macctx */*ctx*/, void */*mac*/);
+
+/* --- @HMAC_DEF@ --- *
+ *
+ * Arguments:	@PRE@, @pre@ = prefixes for the underlying hash function
+ *
+ * Use:		Creates implementations for the HMAC and NMAC functions.
+ */
+
+#define HMAC_DEF(PRE, pre)						\
+									\
+/* --- @pre_nmac@ --- *							\
+ *									\
+ * Arguments:	@pre_macctx *key@ = pointer to a MAC key object		\
+ *		@const void *ok@ = pointer to outer hash init vector	\
+ *		@const void *ik@ = pointer to inner hash init vector	\
+ *									\
+ * Returns:	---							\
+ *									\
+ * Use:		Initializes a MAC key for doing NMAC hashing.		\
+ */									\
+									\
+void pre##_nmac(pre##_mackey *key, const void *ok, const void *ik)	\
+{									\
+  memcpy(key->ochain, ok, PRE##_HASHSZ);				\
+  memcpy(key->ichain, ik, PRE##_HASHSZ);				\
+  key->ocount = key->icount = 0;					\
+}									\
+									\
+/* --- @pre_hmac@ --- *							\
+ *									\
+ * Arguments:	@pre_mackey *key@ = pointer to MAC key object		\
+ *		@const void *k@ = pointer to key to use			\
+ *		@size_t sz@ = size of key data				\
+ *									\
+ * Returns:	---							\
+ *									\
+ * Use:		Initializes a MAC key for doing HMAC hashing.  Keys	\
+ *		longer than the hash function's output size aren't very	\
+ *		useful, but are accepted.  Keys longer than the hash's	\
+ *		block size are also accepted; they are hashed before	\
+ *		use, as specified in RFC2104.				\
+ */									\
+									\
+void pre##_hmac(pre##_mackey *key, const void *k, size_t sz)		\
+{									\
+  int i;								\
+  const octet *kbuf = k;						\
+  pre##_ctx ctx;							\
+  octet buf[PRE##_HASHSZ];						\
+									\
+  if (sz > PRE##_BUFSZ)	{						\
+    pre##_init(&ctx);							\
+    pre##_hash(&ctx, k, sz);						\
+    pre##_done(&ctx, buf);						\
+    kbuf = buf;								\
+    sz = PRE##_HASHSZ;							\
+  }									\
+									\
+  pre##_init(&ctx);							\
+  memset(ctx.buf, 0x5c, PRE##_BUFSZ);					\
+  for (i = 0; i < sz; i++)						\
+    ctx.buf[i] ^= kbuf[i];						\
+  pre##_compress(&ctx, ctx.buf);					\
+  pre##_state(&ctx, key->ochain);					\
+									\
+  pre##_init(&ctx);							\
+  memset(ctx.buf, 0x36, PRE##_BUFSZ);					\
+  for (i = 0; i < sz; i++)						\
+    ctx.buf[i] ^= kbuf[i];						\
+  pre##_compress(&ctx, ctx.buf);					\
+  pre##_state(&ctx, key->ichain);					\
+									\
+  key->ocount = key->icount = PRE##_BUFSZ;				\
+  BURN(ctx);								\
+}									\
+									\
+/* --- @pre_macinit@ --- *						\
+ *									\
+ * Arguments:	@pre_macctx *ctx@ = pointer to MAC context block	\
+ *		@const pre_mackey *key@ = pointer to MAC key block	\
+ *									\
+ * Returns:	---							\
+ *									\
+ * Use:		Instantiates a MAC context from a key block.		\
+ */									\
+									\
+void pre##_macinit(pre##_macctx *ctx, const pre##_mackey *key)		\
+{									\
+  memcpy(ctx->chain, key->ochain, PRE##_HASHSZ);			\
+  ctx->count = key->ocount;						\
+  pre##_set(&ctx->ctx, key->ichain, key->icount);			\
+}									\
+									\
+/* --- @pre_mac@ --- *							\
+ *									\
+ * Arguments:	@pre_macctx *ctx@ = pointer to MAC context block	\
+ *		@const void *buf@ = pointer to buffer			\
+ *		@size_t sz@ = size of the buffer			\
+ *									\
+ * Returns:	---							\
+ *									\
+ * Use:		Hashes a buffer.					\
+ */									\
+									\
+void pre##_mac(pre##_macctx *ctx, const void *buf, size_t sz)		\
+{									\
+  pre##_hash(&ctx->ctx, buf, sz);					\
+}									\
+									\
+/* --- @pre_macdone@ --- *						\
+ *									\
+ * Arguments:	@pre_macctx *ctx@ = pointer to MAC context block	\
+ *		@void *mac@ = pointer to buffer to receive MAC		\
+ *									\
+ * Returns:	---							\
+ *									\
+ * Use:		Returns the result of a MAC computation.		\
+ */									\
+									\
+void pre##_macdone(pre##_macctx *ctx, void *mac)			\
+{									\
+  pre##_done(&ctx->ctx, mac);						\
+  pre##_set(&ctx->ctx, ctx->chain, ctx->count);				\
+  pre##_hash(&ctx->ctx, mac, PRE##_HASHSZ);				\
+  pre##_done(&ctx->ctx, mac);						\
+}									\
+									\
+HMAC_TEST(PRE, pre)							\
+
+/* --- @HMAC_TEST@ --- *
+ *
+ * Arguments:	@PRE@, @pre@ = prefixes for hash-specfic definitions
+ *
+ * Use:		Standard test rig for MAC functions.
+ */
+
+#ifdef TEST_RIG
+
+#include <mLib/dstr.h>
+#include <mLib/quis.h>
+#include <mLib/testrig.h>
+
+#define HMAC_TEST(PRE, pre)						\
+									\
+static int macverify(dstr *v)						\
+{									\
+  pre##_macctx cctx;							\
+  pre##_mackey ckey;							\
+  int ok = 1;								\
+  int i;								\
+  octet *p;								\
+  int szs[] = { 1, 7, 192, -1, 0 }, *ip;				\
+  size_t csz;								\
+  dstr d;								\
+									\
+  dstr_create(&d);							\
+  dstr_ensure(&d, PRE##_HASHSZ);					\
+  d.len = PRE##_HASHSZ;							\
+									\
+  pre##_hmac(&ckey, v[1].buf, v[1].len);				\
+									\
+  for (ip = szs; *ip; ip++) {						\
+    i = *ip;								\
+    csz = v[0].len;							\
+    if (i == -1)							\
+      i = csz;								\
+    if (i > csz)							\
+      continue;								\
+    p = (octet *)v[0].buf;						\
+    pre##_macinit(&cctx, &ckey);					\
+    while (csz) {							\
+      if (i > csz)							\
+	i = csz;							\
+      pre##_mac(&cctx, p, i);						\
+      p += i;								\
+      csz -= i;								\
+    }									\
+    pre##_macdone(&cctx, d.buf);					\
+    if (memcmp(d.buf, v[2].buf, PRE##_HASHSZ) != 0) {			\
+      printf("\nfail:\n\tstep = %i\n\tinput = `%s'\n\tkey = ",		\
+	     *ip, v[0].buf);						\
+      type_hex.dump(&v[1], stdout);					\
+      fputs("\n\texpected = ", stdout);					\
+      type_hex.dump(&v[2], stdout);					\
+      fputs("\n\tcomputed = ", stdout);					\
+      type_hex.dump(&d, stdout);					\
+      putchar('\n');							\
+      ok = 0;								\
+    }									\
+  }									\
+									\
+  dstr_destroy(&d);							\
+  return (ok);								\
+}									\
+									\
+static test_chunk macdefs[] = {						\
+  { #pre "-hmac", macverify,						\
+    { &type_string, &type_hex, &type_hex, 0 } },			\
+  { 0, 0, { 0 } }							\
+};									\
+									\
+int main(int argc, char *argv[])					\
+{									\
+  ego(argv[0]);								\
+  test_run(argc, argv, macdefs, SRCDIR"/tests/" #pre);			\
+  return (0);								\
+}
+
+#else
+#  define HMAC_TEST(PRE, pre)
+#endif
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/idea.c b/idea.c
new file mode 100644
index 0000000..f554bf4
--- /dev/null
+++ b/idea.c
@@ -0,0 +1,276 @@
+/* -*-c-*-
+ *
+ * $Id: idea.c,v 1.1 1999/09/03 08:41:12 mdw Exp $
+ *
+ * Implementation of the IDEA cipher
+ *
+ * (c) 1999 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: idea.c,v $
+ * Revision 1.1  1999/09/03 08:41:12  mdw
+ * Initial import.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <mLib/bits.h>
+
+#include "blkc.h"
+#include "idea.h"
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @inv@ --- *
+ *
+ * Arguments:	@uint16 n@ = number to invert
+ *
+ * Returns:	Multiplicative inverse of @n@ %$\pmod{2^{16} + 1}$%.
+ *
+ * Use:		Computes multiplicative inverses.  This is handy for the
+ *		decryption key scheduling.
+ */
+
+static uint16 inv(uint16 n)
+{
+  uint32 m = 0x10001;
+  uint32 a = 1, b = 0;
+
+  for (;;) {
+    uint32 q, r, t;
+    if (!(r = m % n))
+      break;
+    q = m / n;
+    m = n; n = r;
+    t = a; a = b - q * a; b = t;
+  }
+  if (a > MASK16)
+    a += 1;
+  return (U16(a));
+}
+
+/* --- @MUL@ --- *
+ *
+ * Arguments @x@ and @y@ are two 32-bit values to multiply.  On exit, @x@ is
+ * the product of the two arguments.  The result is not normalized back to 16
+ * bits; the arguments are not expected to be normalized.
+ */
+
+#define MUL(x, y) do {							\
+  uint32 _mx, _my = (y);						\
+  if ((_mx = U16(x)) == 0)						\
+    (x) = 1 - _my;							\
+  else if (_my == 0)							\
+    (x) = 1 - _mx;							\
+  else {								\
+    _my *= _mx;								\
+    _mx = U16(_my); _my >>= 16;						\
+    if (_mx < _my)							\
+      (x) = _mx - _my + 1;						\
+    else								\
+      (x) = _mx - _my;							\
+  }									\
+} while (0)
+
+/* --- @idea_init@ --- *
+ *
+ * Arguments:	@idea_ctx *k@ = pointer to key block
+ *		@const void *buf@ = pointer to key buffer
+ *		@size_t sz@ = size of key material
+ *
+ * Returns:	---
+ *
+ * Use:		Initializes an IDEA key buffer.  The buffer must be exactly
+ *		16 bytes in size, because IDEA is only defined with a key
+ *		size of 128 bits.
+ */
+
+void idea_init(idea_ctx *k, const void *buf, size_t sz)
+{
+  assert(((void)"IDEA key must be 128 bits", sz == IDEA_KEYSZ));
+
+  /* --- Unpack the encryption key --- */
+
+  {
+    const octet *p = buf;
+    uint16 *q = k->e;
+    uint32 a = LOAD32(p +  0);
+    uint32 b = LOAD32(p +  4);
+    uint32 c = LOAD32(p +  8);
+    uint32 d = LOAD32(p + 12);
+    int i;
+
+    /* --- Main unpacking loop --- */
+
+    for (i = 0; i < 6; i++) {
+
+      /* --- Spit out the next 8 subkeys --- */
+
+      q[0] = U16(a >> 16);
+      q[1] = U16(a >>  0);
+      q[2] = U16(b >> 16);
+      q[3] = U16(b >>  0);
+      q[4] = U16(c >> 16);
+      q[5] = U16(c >>  0);
+      q[6] = U16(d >> 16);
+      q[7] = U16(d >>  0);
+      q += 8;
+
+      /* --- Rotate and permute the subkeys --- */
+
+      {
+	uint32 t = a;
+	a = U32((a << 25) | (b >> 7));
+	b = U32((b << 25) | (c >> 7));
+	c = U32((c << 25) | (d >> 7));
+	d = U32((d << 25) | (t >> 7));
+      }
+    }
+
+    /* --- Write out the tail-enders --- */
+
+    q[0] = U16(a >> 16);
+    q[1] = U16(a >>  0);
+    q[2] = U16(b >> 16);
+    q[3] = U16(b >>  0);
+  }
+
+  /* --- Convert this into the decryption key --- */
+
+  {
+    uint16 *p = k->e + 52;
+    uint16 *q = k->d;
+    int i;
+
+    /* --- Translate the main round keys --- */
+
+    for (i = 0; i < 8; i++) {
+      p -= 6;
+      q[4] = p[0];
+      q[5] = p[1];
+      q[0] = inv(p[2]);
+      q[3] = inv(p[5]);
+      if (i) {
+	q[1] = 0x10000 - p[4];
+	q[2] = 0x10000 - p[3];
+      } else {
+	q[1] = 0x10000 - p[3];
+	q[2] = 0x10000 - p[4];
+      }
+      q += 6;
+    }
+
+    /* --- Translate the tail-enders --- */
+
+    p -= 4;
+    q[0] = inv(p[0]);
+    q[1] = 0x10000 - p[1];
+    q[2] = 0x10000 - p[2];
+    q[3] = inv(p[3]);
+  }
+}
+
+/* --- @ROUND@ --- */
+
+#define MIX(k, a, b, c, d) do {						\
+  MUL(a, (k)[0]);							\
+  (b) += (k)[1];							\
+  (c) += (k)[2];							\
+  MUL(d, (k)[3]);							\
+} while (0)
+
+#define MA(k, a, b, c, d) do {						\
+  unsigned _u = (a) ^ (c);						\
+  unsigned _v = (b) ^ (d);						\
+  MUL(_u, (k)[4]);							\
+  _v += _u;								\
+  MUL(_v, (k)[5]);							\
+  _u += _v;								\
+  (a) ^= _v;								\
+  (b) ^= _u;								\
+  (c) ^= _v;								\
+  (d) ^= _u;								\
+} while (0);
+
+#define ROUND(k, a, b, c, d) do {					\
+  MIX((k), (a), (b), (c), (d));						\
+  MA((k), (a), (b), (c), (d));						\
+  (k) += 6;								\
+} while (0)
+
+/* --- Encryption --- */
+
+#define EBLK(k, a, b, c, d) do {					\
+  unsigned _a = U16(a >> 16);						\
+  unsigned _b = U16(a >>  0);						\
+  unsigned _c = U16(b >> 16);						\
+  unsigned _d = U16(b >>  0);						\
+  const uint16 *_k = (k);						\
+									\
+  ROUND(_k, _a, _b, _c, _d);						\
+  ROUND(_k, _a, _c, _b, _d);						\
+  ROUND(_k, _a, _b, _c, _d);						\
+  ROUND(_k, _a, _c, _b, _d);						\
+  ROUND(_k, _a, _b, _c, _d);						\
+  ROUND(_k, _a, _c, _b, _d);						\
+  ROUND(_k, _a, _b, _c, _d);						\
+  ROUND(_k, _a, _c, _b, _d);						\
+  MIX  (_k, _a, _c, _b, _d);						\
+  (c) = (U16(_a) << 16) | U16(_c);					\
+  (d) = (U16(_b) << 16) | U16(_d);					\
+} while (0)
+
+#define DBLK(k, a, b) EBLK((k), (a), (b))
+
+/* --- @idea_eblk@, @idea_dblk@ --- *
+ *
+ * Arguments:	@const idea_ctx *k@ = pointer to a key block
+ *		@const uint32 s[2]@ = pointer to source block
+ *		@uint32 d[2]@ = pointer to destination block
+ *
+ * Returns:	---
+ *
+ * Use:		Low-level block encryption and decryption.
+ */
+
+void idea_eblk(const idea_ctx *k, const uint32 *s, uint32 *d)
+{
+  EBLK(k->e, s[0], s[1], d[0], d[1]);
+}
+
+void idea_dblk(const idea_ctx *k, const uint32 *s, uint32 *d)
+{
+  EBLK(k->d, s[0], s[1], d[0], d[1]);
+}
+
+BLKC_TEST(IDEA, idea)
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/idea.h b/idea.h
new file mode 100644
index 0000000..5d26a57
--- /dev/null
+++ b/idea.h
@@ -0,0 +1,119 @@
+/* -*-c-*-
+ *
+ * $Id: idea.h,v 1.1 1999/09/03 08:41:12 mdw Exp $
+ *
+ * Implementation of the IDEA cipher
+ *
+ * (c) 1999 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: idea.h,v $
+ * Revision 1.1  1999/09/03 08:41:12  mdw
+ * Initial import.
+ *
+ */
+
+/*----- Notes on the IDEA block cipher ------------------------------------*
+ *
+ * IDEA was invented by James Massey and Xuejia Lai.  The fundamental idea
+ * underlying the cipher is combining incompatible group operations.  The
+ * algorithm's main claim to fame is that it is the symmetric cipher in PGP
+ * version 2.
+ *
+ * The IDEA algorithm is allegedly patented by Ascom Tech A.G., even in the
+ * UK and Europe.  Ascom are willing to grant licences for use in software
+ * which forbids commercial use, but this is not compatible with Free
+ * Software Foundation ideology.  The author recommends against the use of
+ * the IDEA cipher entirely.  Blowfish is conceptually simpler, appears more
+ * concervative, offers a larger keyspace, runs faster, and is in the public
+ * domain.
+ */
+
+#ifndef IDEA_H
+#define IDEA_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <stddef.h>
+
+#include <mLib/bits.h>
+
+/*----- Magical numbers ---------------------------------------------------*/
+
+#define IDEA_BLKSZ 8
+#define IDEA_KEYSZ 16
+#define IDEA_CLASS (N, B, 64)
+
+/*----- Data structures ---------------------------------------------------*/
+
+typedef struct idea_ctx {
+  uint16 e[52];
+  uint16 d[52];
+} idea_ctx;
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @idea_init@ --- *
+ *
+ * Arguments:	@idea_ctx *k@ = pointer to key block
+ *		@const void *buf@ = pointer to key buffer
+ *		@size_t sz@ = size of key material
+ *
+ * Returns:	---
+ *
+ * Use:		Initializes an IDEA key buffer.  The buffer must be exactly
+ *		16 bytes in size, because IDEA is only defined with a key
+ *		size of 128 bits.
+ */
+
+extern void idea_init(idea_ctx */*k*/, const void */*buf*/, size_t /*sz*/);
+
+/* --- @idea_eblk@, @idea_dblk@ --- *
+ *
+ * Arguments:	@const idea_ctx *k@ = pointer to a key block
+ *		@const uint32 s[2]@ = pointer to source block
+ *		@uint32 d[2]@ = pointer to destination block
+ *
+ * Returns:	---
+ *
+ * Use:		Low-level block encryption and decryption.
+ */
+
+extern void idea_eblk(const idea_ctx */*k*/,
+		      const uint32 */*s*/, uint32 */*d*/);
+extern void idea_dblk(const idea_ctx */*k*/,
+		      const uint32 */*s*/, uint32 */*d*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/key.1 b/key.1
new file mode 100644
index 0000000..cbac9bc
--- /dev/null
+++ b/key.1
@@ -0,0 +1,245 @@
+.\" -*-nroff-*-
+.TH key 1 "5 June 1999" Catacomb
+.SH NAME
+key \- simple key management system
+.SH SYNOPSIS
+.B key
+.RB [ \-k
+.IR keyring ]
+.I command
+.PP
+where
+.I command
+is one of:
+.PP
+.B add
+.RB [ \-b
+.IR bits ]
+.RB [ \-e
+.IR expire ]
+.RB [ \-c
+.IR comment ]
+.I type
+.IR attr ...
+.br
+.B expire
+.IR keyid ...
+.br
+.B delete
+.IR keyid ...
+.br
+.B setattr
+.I keyid
+.IR attr ...
+.br
+.B list
+.RB [ \-qv ]
+.br
+.B tidy
+.br
+.B extract
+.I file
+.IR keyid ...
+.br
+.B merge
+.I file
+.SH DESCRIPTION
+The
+.B key
+command performs useful operations on Catacomb keyring files.  It
+provides a number of subcommands, by which the various operations may be
+carried out.
+.SS "Global options"
+Before the command name,
+.I "global options"
+may be given.  The following global options are supported:
+.TP
+.B "\-h, \-\-help"
+Writes a brief summary of
+.BR key 's
+various options to standard output, and
+returns a successful exit status.
+.TP
+.B "\-v, \-\-version"
+Writes the program's version number to standard output, and returns a
+successful exit status.
+.TP
+.B "\-u, \-\-usage"
+Writes a very terse command line summary to standard output, and returns
+a successful exit status.
+.TP
+.BI "\-k, \-\-keyring=" file
+Names the keyring file which
+.B key
+is to process.  The default keyring, used if this option doesn't specify
+one, is the file named
+.B keyring
+in the current directory.  The keyring must be stored in a regular file:
+pipes, sockets, devices etc. are not allowed.
+The
+.B key
+program attempts to lock the keyring before accessing it, using
+.BR fcntl (2)
+locking.  It will however time out after a short while (10 seconds) and
+report a failure.
+.SS Concepts
+In addition to the actual key data itself, a Catacomb key has a number
+of other pieces of information attached to it:
+.TP
+.B keyid
+Every key has a 32-bit identifying number, written in hexadecimal.  The
+keyid is derived from the actual key contents (although knowledge of a
+key's keyid doesn't help one to guess the key itself).  Applications use
+keyids to refer to specific keys.  A
+.I deleted
+key cannot be looked up by keyid.
+.TP
+.B type
+A key's type string describes what the key may be used for.  The type
+string is arbitrary, except that it may not contain whitespace
+characters.  Applications use key types to obtain an arbitrary but
+suitable key for some purpose.  An
+.I expired
+key cannot be looked up by type, but may be looked up by keyid.
+.TP
+.B "expiry time"
+Most keys expire after a certain amount of time.  Once a key has
+expired, it will no longer be chosen as a result of a lookup by key
+type.  However, it is not deleted until its deletion time is also
+reached.
+.TP
+.B "deletion time"
+A key's deletion time is the latest expiry time of any of the objects
+which require that key.  For example, a key used for authenticating
+cryptographic cookies should have its deletion time set to the longest
+expiry time of any of the cookies it can authenticate.  A key is never
+deleted until it has also expired.  Once a key has expired
+.I and
+its deletion time is passed, it can no longer be referred to by
+applications, and will be removed from the keyring next time it's
+written to disk.
+.TP
+.B comment
+A key may be given a comment when it's created.  The comment is for the
+benefit of users, and isn't interpreted by applications at all.
+(Hopefully.)
+.TP
+.B attributes
+A key as zero or more name/value pairs.  The names and values are
+arbitrary strings, except they may not contain null bytes.  Some
+attributes may have meaning for particular applications or key types;
+others may be assigned global meanings in future.
+.SH "COMMAND REFERENCE"
+.SS add
+The
+.B add
+command creates a new key and adds it to the keyring.  The command
+accepts the following options:
+.TP
+.BI "\-b, \-\-bits=" bits
+The length of the key to generate, in bits.  The default, if this option
+is not supplied, is 128 bits.  The bit length must be nonzero, and must
+be a multiple of 8.
+.TP
+.BI "\-e, \-\-expire=" expire
+The expiry date for the generated key.  This may be the string
+.RB ` forever '
+if the key should never expire automatically, or any date acceptable to
+the
+.BR getdate (3)
+library function.  Briefly,
+.B getdate
+understands absolute dates such as
+.RB ` 1999-08-02 '
+or
+.RB ` "August 2nd, 1999" ',
+and (perhaps more usefully) relative dates such as
+.RB ` "+2 weeks" '.
+The default is to allow a 2 week expiry, which isn't useful.
+.TP
+.BI "\-c, \-\-comment=" comment
+Sets a comment for the key.  The default is not to attach a comment.
+.PP
+The key's type is given by the required
+.I type
+argument.  Following the type are zero or more attributes, which are
+attached to the key in the same way as for the
+.B setattr
+command.
+.PP
+The
+.B key
+program only generates random bitstrings, which are suitable for most
+symmetric algorithms but not for public key cryptography.  Generating
+keys for more exotic algorithms is a feature which will be added later.
+The keys are generated using the Catacomb random number generator, using
+the
+.B rand_goodbits
+function.  The Catacomb generator is believed to be strong.
+.SS expire
+Forces keys to immediately expire.  An expired key is not chosen when a
+program requests a key by its type.  The keys to expire are listed by
+their
+.IR keyid s.
+.SS delete
+Deletes keys immediately.  The keys to delete are listed by their
+.IR keyid s.
+Be careful when deleting keys.  It might be a better idea
+to expire keys rather than deleting them.
+.SS setattr
+Attaches attributes to a key.  The key to which the attributes should be
+attached is given by its
+.IR keyid .
+Each attribute has the form
+.IB name = value\fR.
+An attribute can be deleted by assigning it an empty value.  Although
+the keyring file format is capable of representing an attribute with an
+empty value as distinct from a nonexistant attribute, this interface
+does not allow empty attributes to be set.
+.SS list
+Lists the keys in the keyring.  A couple of options are supported:
+.TP
+.B "\-v, \-\-verbose"
+Increases the amount of information displayed for each key.  Repeat for
+a greater effect.
+.TP
+.B "\-q, \-\-quiet"
+Decreases the amount of information displayed for each key.  Each use
+cancels a
+.RB ` \-v '
+option.
+.PP
+By default, a single line of output is generated for each, showing
+keyids, types, expiry and deletion dates, and comments.  Additional
+.RB ` \-v '
+options show more information, such as the exact time of day for expiry
+and deletion, key attributes, and a hex dump of the actual key data.
+.SS tidy
+Simply reads the keyring from file and writes it back again.  This has
+the effect of removing any deleted keys from the file.
+.SS extract
+Writes a selection of keys to the named
+.IR file ,
+which may be
+.RB ` \- '
+to designate standard output.  The keys to extract are listed by their
+.IR keyid s.
+The output is a valid keyring file.
+.SS merge
+Merges the keys from the named
+.IR file ,
+which may be
+.RB ` \- '
+to designate standard input, with the keyring.  Keys already in the
+keyring are not overwritten: you must explicitly remove them first if
+you want them to be replaced during the merge.
+.SH BUGS
+The ability to generate keys for specific algorithms ought to be added,
+for DES (setting the parity bits correctly), RSA, ElGamal and DSA, at
+the very least.  (None of these systems are actually implemented in
+Catacomb at the moment, however.)
+.SH "SEE ALSO"
+.BR keyring (5).
+.SH AUTHOR
+Mark Wooding, <mdw@nsict.org>
+
diff --git a/key.c b/key.c
new file mode 100644
index 0000000..9ffa98b
--- /dev/null
+++ b/key.c
@@ -0,0 +1,1077 @@
+/* -*-c-*-
+ *
+ * $Id: key.c,v 1.1 1999/09/03 08:41:12 mdw Exp $
+ *
+ * Simple key management
+ *
+ * (c) 1999 Mark Wooding
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: key.c,v $
+ * Revision 1.1  1999/09/03 08:41:12  mdw
+ * Initial import.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <mLib/alloc.h>
+#include <mLib/base64.h>
+#include <mLib/bits.h>
+#include <mLib/crc32.h>
+#include <mLib/dstr.h>
+#include <mLib/hash.h>
+#include <mLib/lock.h>
+#include <mLib/report.h>
+#include <mLib/str.h>
+#include <mLib/sub.h>
+#include <mLib/sym.h>
+#include <mLib/url.h>
+
+#include "key.h"
+
+/*----- Useful macros -----------------------------------------------------*/
+
+#define KEY_PARANOID
+#define NOTHING
+
+#ifdef KEY_PARANOID
+#  define KEY_WRITE(f, func, val) do {					\
+     if (!(f)->f & KF_WRITE) {						\
+       moan(#func " [caller error]: keyfile is readonly");		\
+       errno = EROFS;							\
+       return val;							\
+     }									\
+   } while (0)
+#else
+#  define KEY_WRITE(f, func) do { ; } while (0)
+#endif
+
+#define KEY_MODIFY(f) do { (f)->f |= KF_MODIFIED; } while (0)
+
+#define KEY_LOAD(n) ((n) * 2)
+
+/*----- Sanity checking of values -----------------------------------------*/
+
+/* --- @key_chktype@ --- *
+ *
+ * Arguments:	@const char *type@ = pointer to a type string
+ *
+ * Returns:	Zero if OK, -1 on error.
+ *
+ * Use:		Checks whether a type string is OK.
+ */
+
+int key_chktype(const char *type)
+{
+  if (!type || !*type)
+    goto fail;
+  while (*type) {
+    if (isspace((unsigned char)*type))
+      goto fail;
+    type++;
+  }
+  return (0);
+
+fail:
+  errno = EINVAL;
+  return (-1);
+}
+
+/* --- @key_chkcomment@ --- *
+ *
+ * Arguments:	@const char *comment@ = pointer to a comment string
+ *
+ * Returns:	Zero if OK, -1 on error.
+ *
+ * Use:		Checks whether a comment string is OK.
+ */
+
+int key_chkcomment(const char *c)
+{
+  if (!c)
+    return (0);
+  if (!*c)
+    goto fail;
+  while (*c) {
+    if (*c == '\n')
+      goto fail;
+    c++;
+  }
+  return (0);
+
+fail:
+  errno = EINVAL;
+  return (-1);
+}
+
+/*----- Low-level fiddling ------------------------------------------------*/
+
+/* --- @insert@ --- *
+ *
+ * Arguments:	@key_file *f@ = pointer to file structure
+ *		@const char *type@ = type of key to insert
+ *		@const void *k@ = pointer to key data
+ *		@size_t ksz@ = size of key data
+ *		@time_t exp@ = expiry time for key
+ *		@time_t del@ = deletion time for key
+ *
+ * Returns:	Pointer to key block to fill in the rest of, or zero.
+ *
+ * Use:		Inserts a key into a key file.
+ */
+
+static key *insert(key_file *f,
+		   const char *type,
+		   const void *k, size_t ksz,
+		   time_t exp, time_t del)
+{
+  uint32 id;
+  key *kk;
+  key_type *t;
+
+  /* --- Sanity preservatives --- */
+
+  if (key_chktype(type))
+    return (0);
+
+  /* --- Insert into the id table --- */
+
+  {
+    hash_base **bin, **b;
+
+    CRC32(id, 0, k, ksz);
+    bin = HASH_BIN(&f->byid, id);
+    for (b = bin; *b; b = &(*b)->next) {
+      if ((*b)->hash == id) {
+	errno = EEXIST;
+	return (0);
+      }
+    }
+
+    kk = CREATE(key);
+    kk->_b.next = 0;
+    *b = &kk->_b;
+    kk->_b.hash = id;
+  }
+
+  /* --- Extend the table --- */
+
+  if (f->idload > 0)
+    f->idload--;
+  else if (hash_extend(&f->byid))
+    f->idload = KEY_LOAD(f->byid.mask / 2);
+
+  /* --- Initialize the key block --- */
+
+  kk->id = id;
+  kk->k = sub_alloc(ksz);
+  memcpy(kk->k, k, ksz);
+  kk->ksz = ksz;
+  kk->type = xstrdup(type);
+  kk->exp = exp;
+  kk->del = del;
+  sym_create(&kk->a);
+  kk->c = 0;
+
+  /* --- Insert into the type table --- */
+
+  {
+    unsigned found;
+    t = sym_find(&f->bytype, type, -1, sizeof(*t), &found);
+    if (!found) {
+      t->k = kk;
+      kk->next = 0;
+    } else {
+      key **p = &t->k;
+      if (exp != KEXP_FOREVER) {
+	while (*p && (*p)->exp != KEXP_EXPIRE && (*p)->exp > exp)
+	  p = &(*p)->next;
+      }
+      kk->next = *p;
+      *p = kk;
+    }
+  }
+
+  return (kk);
+}
+
+/*----- Iteration and iterators -------------------------------------------*/
+
+/* --- @key_mkiter@ --- *
+ *
+ * Arguments:	@key_iter *i@ = pointer to iterator object
+ *		@key_file *f@ = pointer to file structure
+ *
+ * Returns:	---
+ *
+ * Use:		Initializes a key iterator.  The keys are returned by
+ *		@key_next@.
+ */
+
+void key_mkiter(key_iter *i, key_file *f)
+{
+  HASH_MKITER(&i->i, &f->byid);
+  i->t = time(0);
+}
+
+/* --- @key_next@ --- *
+ *
+ * Arguments:	@key_iter *i@ = pointer to iterator object
+ *
+ * Returns:	Pointer to next key, or null.
+ *
+ * Use:		Returns the next key in some arbitrary sequence.
+ */
+
+key *key_next(key_iter *i)
+{
+  hash_base *b;
+  key *k;
+  do {
+    HASH_NEXT(&i->i, b);
+    k = (key *)b;
+  } while (k && KEY_EXPIRED(i->t, k->exp) && KEY_DELETED(i->t, k->del));
+  return (k);
+}
+
+/* --- @key_mkattriter@ --- *
+ *
+ * Arguments:	@key_attriter *i@ = pointer to attribute iterator
+ *		@key_file *f@ = pointer to key file
+ *		@key *k@ = pointer to key
+ *
+ * Returns:	---
+ *
+ * Use:		Initializes an attribute iterator.  The attributes are
+ *		returned by @key_nextattr@.
+ */
+
+void key_mkattriter(key_attriter *i, key_file *f, key *k)
+{
+  sym_mkiter(&i->i, &k->a);
+}
+
+/* --- @key_nextattr@ --- *
+ *
+ * Arguments:	@key_attriter *i@ = pointer to attribute iterator
+ *		@const char **n, **v@ = pointers to name and value
+ *
+ * Returns:	Zero if no attribute available, or nonzero if returned OK.
+ *
+ * Use:		Returns the next attribute.
+ */
+
+int key_nextattr(key_attriter *i, const char **n, const char **v)
+{
+  key_attr *a = sym_next(&i->i);
+  if (!a)
+    return (0);
+  *n = SYM_NAME(a);
+  *v = a->p;
+  return (1);
+}
+
+/*----- Lookup ------------------------------------------------------------*/
+
+/* --- @key_bytype@ --- *
+ *
+ * Arguments:	@key_file *f@ = key file we want a key from
+ *		@const char *type@ = type string for desired key
+ *
+ * Returns:	Pointer to the best key to use, or null.
+ *
+ * Use:		Looks up a key by its type.  Returns the key with the latest
+ *		expiry time.  This function will not return an expired key.
+ */
+
+key *key_bytype(key_file *f, const char *type)
+{
+  time_t now = time(0);
+  key *k;
+  key_type *t;
+
+  if ((t = sym_find(&f->bytype, type, -1, 0, 0)) == 0)
+    return (0);
+  for (k = t->k; k && KEY_EXPIRED(now, k->exp); k = k->next)
+    ;
+  return (k);
+}
+
+/* --- @key_byid@ --- *
+ *
+ * Arguments:	@key_file *f@ = key file to find a key from
+ *		@uint32 id@ = id to look for
+ *
+ * Returns:	Key with matching id.
+ *
+ * Use:		Returns a key given its id.  This function will return an
+ *		expired key, but not a deleted one.
+ */
+
+key *key_byid(key_file *f, uint32 id)
+{
+  time_t t = time(0);
+  hash_base **bin, *b;
+
+  bin = HASH_BIN(&f->byid, id);
+  for (b = *bin; b; b = b->next) {
+    if (b->hash == id) {
+      key *k = (key *)b;
+      if (KEY_EXPIRED(t, k->exp) && KEY_DELETED(t, k->del))
+	return (0);
+      return (k);
+    }
+  }
+  return (0);
+}
+
+/*----- Attributes --------------------------------------------------------*/
+
+/* --- @key_getattr@ --- *
+ *
+ * Arguments:	@key_file *f@ = pointer to file
+ *		@key *k@ = pointer to key
+ *		@const char *n@ = pointer to attribute name
+ *
+ * Returns:	Pointer to attribute value, or null if not found.
+ *
+ * Use:		Returns the value of a key attribute.
+ */
+
+const char *key_getattr(key_file *f, key *k, const char *n)
+{
+  key_attr *a;
+  if ((a = sym_find(&k->a, n, -1, 0, 0)) == 0)
+    return (0);
+  return (a->p);
+}
+
+/* --- @key_putattr@ --- *
+ *
+ * Arguments:	@key_file *f@ = pointer to file
+ *		@key *k@ = pointer to key
+ *		@const char *n@ = pointer to attribute name
+ *		@const char *v@ = pointer to attribute value or null
+ *
+ * Returns:	---
+ *
+ * Use:		Inserts an attribute on a key.  If an attribute with the same
+ *		name already exists, it is deleted.  Setting a null value
+ *		removes the attribute.
+ */
+
+void key_putattr(key_file *f, key *k, const char *n, const char *v)
+{
+  key_attr *a;
+  unsigned found;
+
+  KEY_WRITE(f, key_putattr, NOTHING);
+
+  if (v) {
+    a = sym_find(&k->a, n, -1, sizeof(*a), &found);
+    if (found)
+      free(a->p);
+    a->p = xstrdup(v);
+  } else if ((a = sym_find(&k->a, n, -1, 0, 0)) != 0) {
+    free(a->p);
+    sym_remove(&k->a, a);
+  }
+
+  KEY_MODIFY(f);
+}
+
+/* --- @key_setcomment@ --- *
+ *
+ * Arguments:	@key_file *f@ = pointer to key file block
+ *		@key *k@ = pointer to key block
+ *		@const char *c@ = pointer to comment to set, or zero
+ *
+ * Returns:	---
+ *
+ * Use:		Replaces the key's current comment with a new one.
+ */
+
+void key_setcomment(key_file *f, key *k, const char *c)
+{
+  KEY_WRITE(f, key_setcomment, NOTHING);
+  if (key_chkcomment(c))
+    return;
+  if (k->c)
+    free(k->c);
+  if (c)
+    k->c = xstrdup(c);
+  else
+    k->c = 0;
+  KEY_MODIFY(f);
+}
+
+/*----- Low-level file I/O ------------------------------------------------*/
+
+/* --- @key_merge@ --- *
+ *
+ * Arguments:	@key_file *f@ = pointer to file structure
+ *		@const char *file@ = name of file (for error messages)
+ *		@FILE *fp@ = file handle to read from
+ *
+ * Returns:	---
+ *
+ * Use:		Reads keys from a file, and inserts them into the file.
+ */
+
+void key_merge(key_file *f, const char *file, FILE *fp)
+{
+  int line = 0;
+  dstr l = DSTR_INIT;
+  dstr d = DSTR_INIT;
+  dstr n = DSTR_INIT, v = DSTR_INIT;
+
+  KEY_WRITE(f, key_merge, NOTHING);
+
+  for (; dstr_putline(&l, fp) != EOF; DRESET(&l)) {
+    char *vf[6];
+    char *p = l.buf;
+    key *k;
+
+    /* --- Skip blank lines and comments --- *
+     *
+     * Quite what they're doing in what ought to be an automatically-
+     * maintained file I don't know.
+     */
+
+    line++;
+    while (isspace((unsigned char)*p))
+      p++;
+    if (!*p || *p == '#')
+      continue;
+
+    /* --- Break the line into fields --- *
+     *
+     * There are currently six fields of interest:
+     *
+     *   * The key's type tag.
+     *   * The actual key data itself.
+     *   * The key expiry time.
+     *   * The key deletion time.
+     *   * The attributes field.
+     *   * Any further comments.
+     *
+     * All but the last field can contain no spaces.
+     */
+
+    {
+      int n = str_split(p, vf, 5, &vf[5]);
+      if (n < 4) {
+	moan("key file `%s', line %i: too few fields", file, line);
+	continue;
+      }
+    }
+
+    /* --- Decode various bits and insert the key --- */
+
+    {
+      base64_ctx b;
+      time_t exp, del;
+
+      base64_init(&b);
+      base64_decode(&b, vf[1], strlen(vf[1]), &d);
+      base64_decode(&b, 0, 0, &d);
+
+      exp = (time_t)atol(vf[2]);
+      del = (time_t)atol(vf[3]);
+
+      if ((k = insert(f, vf[0], d.buf, d.len, exp, del)) == 0)
+	continue;
+      DRESET(&d);
+    }
+
+    /* --- Parse up the attributes, if specified --- */
+
+    if (vf[4]) {
+      url_dctx uc;
+      for (url_initdec(&uc, vf[4]); url_dec(&uc, &n, &v); ) {
+	key_putattr(f, k, n.buf, v.buf);
+	DRESET(&n); DRESET(&v);
+      }
+    }
+
+    /* --- Insert the comment --- */
+
+    if (vf[5])
+      k->c = xstrdup(vf[5]);
+  }
+
+  /* --- Extensive tidying up now required --- */
+
+  dstr_destroy(&l);
+  dstr_destroy(&d);
+  dstr_destroy(&n);
+  dstr_destroy(&v);
+  KEY_MODIFY(f);
+}
+
+/* --- @key_extract@ --- *
+ *
+ * Arguments:	@key_file *f@ = pointer to file structure
+ *		@key *k@ = key to extract
+ *		@FILE *fp@ = file to write on
+ *
+ * Returns:	Zero if OK, EOF on error.
+ *
+ * Use:		Extracts a key to an ouptut file.
+ */
+
+int key_extract(key_file *f, key *k, FILE *fp)
+{
+  dstr d = DSTR_INIT;
+
+  /* --- Encode the key and write the easy stuff --- */
+
+  {
+    base64_ctx b;
+    base64_init(&b);
+    b.indent = "";
+    base64_encode(&b, k->k, k->ksz, &d);
+    base64_encode(&b, 0, 0, &d);
+    DPUTZ(&d);
+    fprintf(fp, "%s %s %li %li ",
+	    k->type, d.buf, (long)k->exp, (long)k->del);
+    DRESET(&d);
+  }
+
+  /* --- Output the attributes --- */
+
+  {
+    int none = 1;
+    sym_iter i;
+    key_attr *a;
+    url_ectx uc;
+
+    url_initenc(&uc);
+    for (sym_mkiter(&i, &k->a); (a = sym_next(&i)) != 0; ) {
+      none = 0;
+      url_enc(&uc, &d, SYM_NAME(a), a->p);
+    }
+    if (none)
+      DPUTS(&d, "-");
+    DWRITE(&d, fp);
+  }
+
+  dstr_destroy(&d);
+  if (k->c) {
+    putc(' ', fp);
+    fputs(k->c, fp);
+  }
+  putc('\n', fp);
+  return (ferror(fp) ? EOF : 0);
+}
+
+/* --- @fdcopy@ --- *
+ *
+ * Arguments:	@int source@ = source file descriptor
+ *		@int dest@ = destination file descriptor
+ *
+ * Returns:	Zero if OK, nonzero otherwise.
+ *
+ * Use:		Copies data from one file descriptor to another.
+ */
+
+static int fdcopy(int source, int dest)
+{
+  char buf[4096];
+
+  if (lseek(source, 0, SEEK_SET) < 0||
+      lseek(dest, 0, SEEK_SET) < 0 ||
+      ftruncate(dest, 0) < 0)
+    return (-1);
+  for (;;) {
+    int n = read(source, buf, sizeof(buf));
+    if (n < 0)
+      return (-1);
+    else if (n == 0)
+      break;
+    else if (write(dest, buf, n) < 0)
+      return (-1);
+  }
+  return (0);
+}
+
+/* --- @key_write@ --- *
+ *
+ * Arguments:	@key_file *f@ = pointer to key file block
+ *
+ * Returns:	A @KWRITE_@ code indicating how well it worked.
+ *
+ * Use:		Writes a key file's data back to the actual file.  This code
+ *		is extremely careful about error handling.  It should usually
+ *		be able to back out somewhere sensible, but it can tell when
+ *		it's got itself into a real pickle and starts leaving well
+ *		alone.
+ *
+ *		Callers, please make sure that you ring alarm bells when this
+ *		function returns @KWRITE_BROKEN@.
+ */
+
+int key_write(key_file *f)
+{
+  dstr n_older = DSTR_INIT, n_old = DSTR_INIT, n_new = DSTR_INIT;
+  int rc = KWRITE_FAIL;
+
+  if (!(f->f & KF_MODIFIED))
+    return (KWRITE_OK);
+
+  /* --- Write a new key file out --- *
+   *
+   * Check for an error after each key line.  This ought to be enough.
+   * Checking after each individual byte write and @fprintf@ isn't much fun.
+   */
+
+  dstr_putf(&n_new, "%s.new", f->name);
+
+  {
+    key *k;
+    key_iter i;
+    FILE *fp;
+
+    if ((fp = fopen(n_new.buf, "w")) == 0)
+      goto fail_open;
+
+    for (key_mkiter(&i, f); (k = key_next(&i)) != 0; ) {
+      if (key_extract(f, k, fp)) {
+	fclose(fp);
+	goto fail_write;
+      }
+    }
+
+    if (fclose(fp))
+      goto fail_write;
+  }
+
+  /* --- Set up the other filenames --- */
+
+  dstr_putf(&n_older, "%s.older", f->name);
+  dstr_putf(&n_old, "%s.old", f->name);
+
+  /* --- Move the current backup on one --- *
+   *
+   * If the `older' file exists, then we're in need of attention.
+   */
+
+  {
+    struct stat st;
+    if (stat(n_older.buf, &st) == 0 || errno != ENOENT) {
+      errno = EEXIST;
+      rc = KWRITE_BROKEN;
+      goto fail_shift;
+    }
+    if (rename(n_old.buf, n_older.buf) && errno != ENOENT)
+      goto fail_shift;
+  }
+
+  /* --- Copy the current file to the backup --- */
+
+  {
+    int fd;
+    if ((fd = open(n_old.buf, O_WRONLY | O_CREAT | O_EXCL, 0600)) < 0)
+      goto fail_backup;
+    if (fdcopy(f->fd, fd)) {
+      close(fd);
+      goto fail_backup;
+    }
+    if (close(fd))
+      goto fail_backup;
+  }
+
+  /* --- Copy the newly created file to the current one --- *
+   *
+   * This is the dangerous bit.
+   */
+
+  {
+    int fd;
+    if ((fd = open(n_new.buf, O_RDONLY)) < 0)
+      goto fail_update;
+    if (fdcopy(fd, f->fd)) {
+      close(fd);
+      goto fail_update;
+    }
+    close(fd);
+    if (fsync(f->fd))
+      goto fail_update;
+  }
+
+  /* --- Clean up --- *
+   *
+   * Remove the `new' file and the `older' backup.  Then we're done.
+   */
+
+  unlink(n_new.buf);
+  unlink(n_older.buf);
+  return (KWRITE_OK);
+
+  /* --- Failure while writing the new key file --- *
+   *
+   * I need to copy the backup back.  If that fails then I'm really stuffed.
+   * If not, then I might as well try to get the backups sorted back out
+   * again.
+   */
+
+fail_update:
+  {
+    int fd;
+    int e = errno;
+
+    if ((fd = open(n_old.buf, O_RDONLY)) < 0)
+      rc = KWRITE_BROKEN;
+    else if (fdcopy(fd, f->fd)) {
+      close(fd);
+      rc = KWRITE_BROKEN;
+    } else {
+      close(fd);
+      if (fsync(f->fd))
+	rc = KWRITE_BROKEN;
+    }
+
+    errno = e;
+    if (rc == KWRITE_BROKEN)
+      goto fail_shift;
+  }
+  /* Now drop through */
+
+  /* --- Failure while writing the new backup --- *
+   *
+   * The new backup isn't any use.  Try to recover the old one.
+   */
+
+fail_backup:
+  {
+    int e = errno;
+    unlink(n_old.buf);
+    if (rename(n_older.buf, n_old.buf) && errno != ENOENT)
+      rc = KWRITE_BROKEN;
+    errno = e;
+  }
+  /* Now drop through */
+
+  /* --- Failure while demoting the current backup --- *
+   *
+   * Leave the completed output file there for the operator in case he wants
+   * to clean up.
+   */
+
+fail_shift:
+  dstr_destroy(&n_new);
+  dstr_destroy(&n_old);
+  dstr_destroy(&n_older);
+  return (rc);
+  
+/* --- Failure during write of new data --- *
+ *
+ * Clean up the new file and return.  These errors can never cause
+ * breakage.
+ */
+
+fail_write:
+  unlink(n_new.buf);
+  fail_open:
+  dstr_destroy(&n_new);
+  return (rc);
+}
+
+/*----- Opening and closing files -----------------------------------------*/
+
+/* --- @key_open@ --- *
+ *
+ * Arguments:	@key_file *f@ = pointer to file structure to initialize
+ *		@const char *file@ = pointer to the file name
+ *		@int how@ = opening options (@KOPEN_*@).
+ *
+ * Returns:	Zero if it worked, nonzero otherwise.
+ *
+ * Use:		Opens a key file, reads its contents, and stores them in a
+ *		structure.  The file is locked appropriately until closed
+ *		using @key_close@.  On an error, everything is cleared away
+ *		tidily.  If the file is opened with @KOPEN_WRITE@, it's
+ *		created if necessary, with read and write permissions for its
+ *		owner only.
+ */
+
+int key_open(key_file *f, const char *file, int how)
+{
+  FILE *fp;
+
+  /* --- Trivial bits of initialization --- */
+
+  f->f = 0;
+  f->name = xstrdup(file);
+
+  /* --- Open the file and get the lock --- */
+
+  {
+    int of, lf;
+    const char *ff;
+    int fd;
+
+    /* --- Lots of things depend on whether we're writing --- */
+
+    switch (how) {
+      case KOPEN_READ:
+	of = O_RDONLY;
+	lf = LOCK_NONEXCL;
+	ff = "r";
+	break;
+      case KOPEN_WRITE:
+	of = O_RDWR | O_CREAT;
+	lf = LOCK_EXCL;
+	ff = "r+";
+	break;
+      default:
+	errno = EINVAL;
+	return (-1);
+    }
+
+    if ((fd = open(file, of, 0600)) < 0)
+      return (-1);
+    if (fcntl(fd, F_SETFD, 1) < 0 ||
+	lock_file(fd, lf) < 0 || (fp = fdopen(fd, ff)) == 0) {
+      close(fd);
+      return (-1);
+    }
+    f->fd = fd;
+  }
+
+  /* --- Read the file of keys into the table --- */
+
+  hash_create(&f->byid, 64);
+  f->idload = KEY_LOAD(64);
+  sym_create(&f->bytype);
+  f->f |= KF_WRITE;
+  key_merge(f, file, fp);
+  if (how == KOPEN_READ)
+    f->f &= ~(KF_WRITE | KF_MODIFIED);
+  else
+    f->f &= ~KF_MODIFIED;
+
+  /* --- Close the file if only needed for reading --- */
+
+  if (how == KOPEN_READ) {
+    f->fp = 0;
+    fclose(fp);
+  } else
+    f->fp = fp;
+
+  return (0);
+}
+
+/* --- @key_close@ --- *
+ *
+ * Arguments:	@key_file *f@ = pointer to key file block
+ *
+ * Returns:	A @KWRITE_@ code indicating how it went.
+ *
+ * Use:		Frees all the key data, writes any changes.  Make sure that
+ *		all hell breaks loose if this returns @KWRITE_BROKEN@.
+ */
+
+int key_close(key_file *f)
+{
+  int e;
+  hash_base *b;
+  hash_iter i;
+
+  if ((e = key_write(f)) != KWRITE_OK)
+    return (e);
+
+  /* --- Free all the individual keys --- */
+
+  for (hash_mkiter(&i, &f->byid); (b = hash_next(&i)) != 0; ) {
+    sym_iter j;
+    key_attr *a;
+    key *k = (key *)b;
+
+    sub_free(k->k, k->ksz);
+    free(k->type);
+    if (k->c)
+      free(k->c);
+    for (sym_mkiter(&j, &k->a); (a = sym_next(&j)) != 0; )
+      free(a->p);
+    sym_destroy(&k->a);
+    DESTROY(k);
+  }
+  hash_destroy(&f->byid);
+  sym_destroy(&f->bytype);
+
+  if (f->fp)
+    fclose(f->fp);
+  free(f->name);
+  return (KWRITE_OK);
+}
+
+/*----- Miscellaneous functions -------------------------------------------*/
+
+/* --- @key_new@ ---
+ *
+ * Arguments:	@key_file *f@ = pointer to key file
+ *		@const char *type@ = the type of this key
+ *		@const void *k@ = pointer to key data
+ *		@size_t ksz@ = size of key data
+ *		@time_t exp@ = when the key expires
+ *		@const char *c@ = textual comment to attach
+ *
+ * Returns:	Key block containing new data, or null if it couldn't be
+ *		done.
+ *
+ * Use:		Attaches a new key to a key file.  You must have a writable
+ *		key file for this to work.
+ *
+ *		The type is a key type string.  This interface doesn't care
+ *		about how type strings are formatted: it just treats them as
+ *		opaque gobs of text.  Clients are advised to choose some
+ *		standard for representing key types, though.
+ *
+ *		The key can be any old binary mess.
+ *
+ *		The expiry time should either be a time in the future, or the
+ *		magic value @KEXP_FOREVER@ which means `never expire this
+ *		key'.  Be careful with `forever' keys.  If I were you, I'd
+ *		use a more sophisticated key management system than this for
+ *		them.
+ *
+ *		The comment can be any old text not containing newlines or
+ *		nulls.  This interface doesn't impose any length restrictions
+ *		on comment lengths.
+ */
+
+key *key_new(key_file *f, const char *type,
+	     const void *k, size_t ksz,
+	     time_t exp, const char *c)
+{
+  key *kk;
+  time_t t = time(0);
+
+  KEY_WRITE(f, key_new, 0);
+
+  if (KEY_EXPIRED(t, exp) ||
+      key_chktype(type) || key_chkcomment(c) ||
+      (kk = insert(f, type, k, ksz, exp, KEXP_UNUSED)) == 0)
+    return (0);
+  if (c)
+    kk->c = xstrdup(c);
+  KEY_MODIFY(f);
+  return (kk);
+}
+
+/* --- @key_delete@ --- *
+ *
+ * Arguments:	@key_file *f@ = pointer to file block
+ *		@key *k@ = key to delete
+ *
+ * Returns:	---
+ *
+ * Use:		Removes the given key from the list.  The key file must be
+ *		writable.  (Due to the horridness of the data structures,
+ *		deleted keys aren't actually removed, just marked so that
+ *		they can't be looked up or iterated over.  One upshot of
+ *		this is that they don't get written back to the file when
+ *		it's closed.)
+ */
+
+void key_delete(key_file *f, key *k)
+{
+  KEY_WRITE(f, key_delete, NOTHING);
+  k->exp = KEXP_EXPIRE;
+  k->del = KEXP_UNUSED;
+  KEY_MODIFY(f);
+}
+
+/* --- @key_expire@ --- *
+ *
+ * Arguments:	@key_file *f@ = pointer to file block
+ *		@key *k@ = pointer to key block
+ *
+ * Returns:	---
+ *
+ * Use:		Immediately marks the key as expired.  It may be removed
+ *		immediately, if it is no longer required, and will be removed
+ *		by a tidy operation when it is no longer required.  The key
+ *		file must be writable.
+ */
+
+void key_expire(key_file *f, key *k)
+{
+  KEY_WRITE(f, key_expire, NOTHING);
+  k->exp = KEXP_EXPIRE;
+  if (k->del == KEXP_FOREVER)
+    k->del = KEXP_UNUSED;
+  KEY_MODIFY(f);
+}
+
+/* --- @key_used@ --- *
+ *
+ * Arguments:	@key_file *f@ = pointer to key file
+ *		@key *k@ = pointer to key block
+ *		@time_t t@ = when key can be removed
+ *
+ * Returns:	Zero if OK, nonzero on failure.
+ *
+ * Use:		Marks a key as being required until a given time.  Even
+ *		though the key may expire before then (and won't be returned
+ *		by type after that time), it will still be available when
+ *		requested explicitly by id.  The key file must be writable.
+ *
+ *		The only (current) reason for failure is attempting to use
+ *		a key which can expire for something which can't.
+ */
+
+int key_used(key_file *f, key *k, time_t t)
+{
+  KEY_WRITE(f, key_used, -1);
+  if (t == KEXP_FOREVER) {
+    if (k->exp != KEXP_FOREVER) {
+      errno = EINVAL;
+      return (-1);
+    }
+  } else if (k->del >= t)
+    return (0);
+
+  k->del = t;
+  KEY_MODIFY(f);
+  return (0);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/key.h b/key.h
new file mode 100644
index 0000000..b7eb12b
--- /dev/null
+++ b/key.h
@@ -0,0 +1,462 @@
+/* -*-c-*-
+ *
+ * $Id: key.h,v 1.1 1999/09/03 08:41:12 mdw Exp $
+ *
+ * Simple key management
+ *
+ * (c) 1999 Mark Wooding
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: key.h,v $
+ * Revision 1.1  1999/09/03 08:41:12  mdw
+ * Initial import.
+ *
+ */
+
+#ifndef KEY_H
+#define KEY_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <stdio.h>
+#include <time.h>
+
+#include <mLib/bits.h>
+#include <mLib/hash.h>
+#include <mLib/sym.h>
+
+/*----- Data structures ---------------------------------------------------*/
+
+/* --- Key attributes --- *
+ *
+ * Each attribute is stored as a symbol in a symbol table.  The value is
+ * the plain (not url-encoded) text to be written to the the file.  If the
+ * value is binary data, then by this point it's base-64 encoded.
+ */
+
+typedef struct key_attr {
+  sym_base _b;				/* Symbol table data */
+  char *p;				/* Pointer to attribute value */
+} key_attr;
+
+/* --- Main key structure --- *
+ *
+ * Each key is stored in two symbol tables, one indexed by keyid, and the
+ * other indexed by type.  Because many keys can have the same type, the type
+ * table contains a list of keys, sorted in descending order of expiry.
+ */
+
+typedef struct key {
+  hash_base _b;				/* Symbol table data */
+  struct key *next;			/* Next key of the same type */
+  uint32 id;				/* Key id used to name it */
+  char *type;				/* Textual key type */
+  void *k;				/* Actual key data */
+  size_t ksz;				/* Size of the key data */
+  time_t exp, del;			/* Expiry times for keys */
+  sym_table a;				/* Hashtable of key attributes */
+  char *c;				/* Any additional comments */
+} key;
+
+/* --- The keys-by-type entries --- */
+
+typedef struct key_type {
+  sym_base _b;				/* Symbol table data */
+  key *k;				/* Pointer to first key in list */
+} key_type;
+
+/* --- A key file --- */
+
+typedef struct key_file {
+  FILE *fp;				/* File pointer open on file */
+  int fd;				/* File descriptor open on file */
+  char *name;				/* Filename used to create it */
+  unsigned f;				/* Various useful flags */
+  hash_table byid;			/* Table of keys by keyid */
+  sym_table bytype;			/* Table of keys by type */
+  size_t idload;			/* Loading on id table */
+} key_file;
+
+/* --- Key file flags --- */
+
+enum {
+  KF_WRITE = 1,				/* File opened for writing */
+  KF_MODIFIED = 2			/* File has been modified */
+};
+
+/* --- Iterating over keys --- *
+ *
+ * Both of these are simple symbol table iterators, but they're made distinct
+ * types for the dubious benefits that type safety brings.
+ */
+
+typedef struct { hash_iter i; time_t t; } key_iter;
+typedef struct { sym_iter i; } key_attriter;
+
+/* --- File opening options --- */
+
+enum {
+  KOPEN_READ,
+  KOPEN_WRITE
+};
+
+/* --- Various other magic numbers --- */
+
+#define KEXP_UNUSED ((time_t)0)		/* Key has never been used */
+#define KEXP_FOREVER ((time_t)-1)	/* Never expire this key */
+#define KEXP_EXPIRE ((time_t)-2)	/* Expire this key when unused */
+
+/* --- Write attempt codes --- */
+
+enum {
+  KWRITE_OK,				/* Everything went fine */
+  KWRITE_FAIL = -1,			/* Close attempt failed */
+  KWRITE_BROKEN	= -2			/* Key ring needs manual fixing */
+};
+
+/* --- Macros for testing expiry --- */
+
+#define KEY_EXPIRED(now, exp)						\
+  ((exp) == KEXP_EXPIRE || ((exp) != KEXP_FOREVER && (exp) < (now)))
+
+#define KEY_DELETED(now, del) ((del) == KEXP_FOREVER || (del) < (now))
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @key_chktype@ --- *
+ *
+ * Arguments:	@const char *type@ = pointer to a type string
+ *
+ * Returns:	Zero if OK, -1 on error.
+ *
+ * Use:		Checks whether a type string is OK.
+ */
+
+extern int key_chktype(const char */*type*/);
+
+/* --- @key_chkcomment@ --- *
+ *
+ * Arguments:	@const char *comment@ = pointer to a comment string
+ *
+ * Returns:	Zero if OK, -1 on error.
+ *
+ * Use:		Checks whether a comment string is OK.
+ */
+
+extern int key_chkcomment(const char */*c*/);
+
+/* --- @key_mkiter@ --- *
+ *
+ * Arguments:	@key_iter *i@ = pointer to iterator object
+ *		@key_file *f@ = pointer to file structure
+ *
+ * Returns:	---
+ *
+ * Use:		Initializes a key iterator.  The keys are returned by
+ *		@key_next@.
+ */
+
+extern void key_mkiter(key_iter */*i*/, key_file */*f*/);
+
+/* --- @key_next@ --- *
+ *
+ * Arguments:	@key_iter *i@ = pointer to iterator object
+ *
+ * Returns:	Pointer to next key, or null.
+ *
+ * Use:		Returns the next key in some arbitrary sequence.
+ */
+
+extern key *key_next(key_iter */*i*/);
+
+/* --- @key_mkattriter@ --- *
+ *
+ * Arguments:	@key_attriter *i@ = pointer to attribute iterator
+ *		@key_file *f@ = pointer to key file
+ *		@key *k@ = pointer to key
+ *
+ * Returns:	---
+ *
+ * Use:		Initializes an attribute iterator.  The attributes are
+ *		returned by @key_nextattr@.
+ */
+
+extern void key_mkattriter(key_attriter */*i*/, key_file */*f*/, key */*k*/);
+
+/* --- @key_nextattr@ --- *
+ *
+ * Arguments:	@key_attriter *i@ = pointer to attribute iterator
+ *		@const char **n, **v@ = pointers to name and value
+ *
+ * Returns:	Zero if no attribute available, or nonzero if returned OK.
+ *
+ * Use:		Returns the next attribute.
+ */
+
+extern int key_nextattr(key_attriter */*i*/,
+			const char **/*n*/, const char **/*v*/);
+
+/* --- @key_bytype@ --- *
+ *
+ * Arguments:	@key_file *f@ = key file we want a key from
+ *		@const char *type@ = type string for desired key
+ *
+ * Returns:	Pointer to the best key to use, or null.
+ *
+ * Use:		Looks up a key by its type.  Returns the key with the latest
+ *		expiry time.  This function will not return an expired key.
+ */
+
+extern key *key_bytype(key_file */*f*/, const char */*type*/);
+
+/* --- @key_byid@ --- *
+ *
+ * Arguments:	@key_file *f@ = key file to find a key from
+ *		@uint32 id@ = id to look for
+ *
+ * Returns:	Key with matching id.
+ *
+ * Use:		Returns a key given its id.  This function will return an
+ *		expired key, but not a deleted one.
+ */
+
+extern key *key_byid(key_file */*f*/, uint32 /*id*/);
+
+/* --- @key_getattr@ --- *
+ *
+ * Arguments:	@key_file *f@ = pointer to file
+ *		@key *k@ = pointer to key
+ *		@const char *n@ = pointer to attribute name
+ *
+ * Returns:	Pointer to attribute value, or null if not found.
+ *
+ * Use:		Returns the value of a key attribute.
+ */
+
+extern const char *key_getattr(key_file */*f*/, key */*k*/,
+			       const char */*n*/);
+
+/* --- @key_putattr@ --- *
+ *
+ * Arguments:	@key_file *f@ = pointer to file
+ *		@key *k@ = pointer to key
+ *		@const char *n@ = pointer to attribute name
+ *		@const char *v@ = pointer to attribute value
+ *
+ * Returns:	---
+ *
+ * Use:		Inserts an attribute on a key.  If an attribute with the same
+ *		name already exists, it is deleted.
+ */
+
+extern void key_putattr(key_file */*f*/, key */*k*/,
+			const char */*n*/, const char */*v*/);
+
+/* --- @key_setcomment@ --- *
+ *
+ * Arguments:	@key_file *f@ = pointer to key file block
+ *		@key *k@ = pointer to key block
+ *		@const char *c@ = pointer to comment to set, or zero
+ *
+ * Returns:	---
+ *
+ * Use:		Replaces the key's current comment with a new one.
+ */
+
+extern void key_setcomment(key_file */*f*/, key */*k*/, const char */*c*/);
+
+/* --- @key_merge@ --- *
+ *
+ * Arguments:	@key_file *f@ = pointer to file structure
+ *		@const char *file@ = name of file (for error messages)
+ *		@FILE *fp@ = file handle to read from
+ *
+ * Returns:	---
+ *
+ * Use:		Reads keys from a file, and inserts them into the file.
+ */
+
+extern void key_merge(key_file */*f*/, const char */*file*/, FILE */*fp*/);
+
+/* --- @key_extract@ --- *
+ *
+ * Arguments:	@key_file *f@ = pointer to file structure
+ *		@key *k@ = key to extract
+ *		@FILE *fp@ = file to write on
+ *
+ * Returns:	Zero if OK, EOF on error.
+ *
+ * Use:		Extracts a key to an ouptut file.
+ */
+
+extern int key_extract(key_file */*f*/, key */*k*/, FILE */*fp*/);
+
+/* --- @key_write@ --- *
+ *
+ * Arguments:	@key_file *f@ = pointer to key file block
+ *
+ * Returns:	A @KWRITE_@ code indicating how well it worked.
+ *
+ * Use:		Writes a key file's data back to the actual file.  This code
+ *		is extremely careful about error handling.  It should usually
+ *		be able to back out somewhere sensible, but it can tell when
+ *		it's got itself into a real pickle and starts leaving well
+ *		alone.
+ *
+ *		Callers, please make sure that you ring alarm bells when this
+ *		function returns @KWRITE_BROKEN@.
+ */
+
+extern int key_write(key_file */*f*/);
+
+/* --- @key_open@ --- *
+ *
+ * Arguments:	@key_file *f@ = pointer to file structure to initialize
+ *		@const char *file@ = pointer to the file name
+ *		@int how@ = opening options (@KOPEN_*@).
+ *
+ * Returns:	Zero if it worked, nonzero otherwise.
+ *
+ * Use:		Opens a key file, reads its contents, and stores them in a
+ *		structure.  The file is locked appropriately until closed
+ *		using @key_close@.  On an error, everything is cleared away
+ *		tidily.  If the file is opened with @KOPEN_WRITE@, it's
+ *		created if necessary, with read and write permissions for its
+ *		owner only.
+ */
+
+extern int key_open(key_file */*f*/, const char */*file*/, int /*how*/);
+
+/* --- @key_close@ --- *
+ *
+ * Arguments:	@key_file *f@ = pointer to key file block
+ *
+ * Returns:	A @KWRITE_@ code indicating how it went.
+ *
+ * Use:		Frees all the key data, writes any changes.  Make sure that
+ *		all hell breaks loose if this returns @KWRITE_BROKEN@.
+ */
+
+extern int key_close(key_file */*f*/);
+
+/* --- @key_new@ ---
+ *
+ * Arguments:	@key_file *f@ = pointer to key file
+ *		@const char *type@ = the type of this key
+ *		@const void *k@ = pointer to key data
+ *		@size_t ksz@ = size of key data
+ *		@time_t exp@ = when the key expires
+ *		@const char *c@ = textual comment to attach
+ *
+ * Returns:	Key block containing new data, or null if it couldn't be
+ *		done.
+ *
+ * Use:		Attaches a new key to a key file.  You must have a writable
+ *		key file for this to work.
+ *
+ *		The type is a key type string.  This interface doesn't care
+ *		about how type strings are formatted: it just treats them as
+ *		opaque gobs of text.  Clients are advised to choose some
+ *		standard for representing key types, though.
+ *
+ *		The key can be any old binary mess.
+ *
+ *		The expiry time should either be a time in the future, or the
+ *		magic value @KEXP_FOREVER@ which means `never expire this
+ *		key'.  Be careful with `forever' keys.  If I were you, I'd
+ *		use a more sophisticated key management system than this for
+ *		them.
+ *
+ *		The comment can be any old text not containing newlines or
+ *		nulls.  This interface doesn't impose any length restrictions
+ *		on comment lengths.
+ */
+
+extern key *key_new(key_file */*f*/, const char */*type*/,
+		    const void */*k*/, size_t /*ksz*/,
+		    time_t /*exp*/, const char */*c*/);
+
+/* --- @key_delete@ --- *
+ *
+ * Arguments:	@key_file *f@ = pointer to file block
+ *		@key *k@ = key to delete
+ *
+ * Returns:	---
+ *
+ * Use:		Removes the given key from the list.  The key file must be
+ *		writable.  (Due to the horridness of the data structures,
+ *		deleted keys aren't actually removed, just marked so that
+ *		they can't be looked up or iterated over.  One upshot of
+ *		this is that they don't get written back to the file when
+ *		it's closed.)
+ */
+
+extern void key_delete(key_file */*f*/, key */*k*/);
+
+/* --- @key_expire@ --- *
+ *
+ * Arguments:	@key_file *f@ = pointer to file block
+ *		@key *k@ = pointer to key block
+ *
+ * Returns:	---
+ *
+ * Use:		Immediately marks the key as expired.  It may be removed
+ *		immediately, if it is no longer required, and will be removed
+ *		by a tidy operation when it is no longer required.  The key
+ *		file must be writable.
+ */
+
+extern void key_expire(key_file */*f*/, key */*k*/);
+
+/* --- @key_used@ --- *
+ *
+ * Arguments:	@key_file *f@ = pointer to key file
+ *		@key *k@ = pointer to key block
+ *		@time_t t@ = when key can be removed
+ *
+ * Returns:	Zero if OK, nonzero on failure.
+ *
+ * Use:		Marks a key as being required until a given time.  Even
+ *		though the key may expire before then (and won't be returned
+ *		by type after that time), it will still be available when
+ *		requested explicitly by id.  The key file must be writable.
+ *
+ *		The only (current) reason for failure is attempting to use
+ *		a key which can expire for something which can't.
+ */
+
+extern int key_used(key_file */*f*/, key */*k*/, time_t /*t*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/keyring.5 b/keyring.5
new file mode 100644
index 0000000..a2eb7ae
--- /dev/null
+++ b/keyring.5
@@ -0,0 +1,54 @@
+.\" -*-nroff-*-
+.TH keyring 5 "5 June 1999" Catacomb
+.SH NAME
+keyring \- description of Catacomb keyring files
+.SH DESCRIPTION
+Keyring files are line-oriented text files.  It is recommended that
+programs use only the provided interface for reading and modifying
+keyring files, for consistency of locking and representation: this
+description is provided for the benefit of administrators attempting to
+understand or repair keyring files.
+.PP
+Lines containing only whitespace and lines whose first non-whitespace
+character is
+.RB ` # '
+are ignored, but are not written back to the file.  Thus, the comment
+facility is not particularly useful.
+.PP
+Each other line describes a key.  Key descriptions consist of between 4
+and six whitespace-separated fields.  The final comment field may
+contain whitespace characters.  The fields are, in order:
+.TP
+.B type
+The key's type string, set when the key was created.
+.TP
+.B "key data"
+The actual key, Base64 encoded, as described in RFC2045.
+.TP
+.B "expiry time"
+The time at which this key expires, represented as an integer number of
+seconds since 1970-01-01 00:00:00 UTC, not counting leap seconds.  The
+special value \-1 signifies that this key never expires.
+.TP
+.B "deletion time"
+The time at which this key should be deleted, using the same
+representation as the expiry time.  The special value 0 signifies that
+the key should be deleted on expiry.
+.TP
+.B attributes
+The key's attributes, encoded using the `form-urlencoded' encoding
+defined in RFC1866.  This field is optional: if it is omitted, the key
+has no attributes.  Alternatively, if there are no attributes, this
+field may be given as a single dash
+.RB ` \- '.
+.TP
+.B comment
+The comment field.  This field is optional.  It may contain whitespace.
+It is deliberately not included as an attribute, since the urlencoded
+nature of attributes makes them hard to read when perusing a keyring
+file.
+.PP
+It is not envisaged that the file format will change in the future.  Any
+extensions will be made by defining new attributes.
+.SH AUTHOR
+Mark Wooding, <mdw@nsict.org>
diff --git a/keyutil.c b/keyutil.c
new file mode 100644
index 0000000..adf8c50
--- /dev/null
+++ b/keyutil.c
@@ -0,0 +1,722 @@
+/* -*-c-*-
+ *
+ * $Id: keyutil.c,v 1.1 1999/09/03 08:41:12 mdw Exp $
+ *
+ * Simple key manager program
+ *
+ * (c) 1999 Mark Wooding
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: keyutil.c,v $
+ * Revision 1.1  1999/09/03 08:41:12  mdw
+ * Initial import.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <mLib/mdwopt.h>
+#include <mLib/quis.h>
+#include <mLib/report.h>
+#include <mLib/sub.h>
+
+#include <noise.h>
+#include <rand.h>
+
+#include "getdate.h"
+#include "key.h"
+
+/*----- Handy global state ------------------------------------------------*/
+
+static const char *keyfile = "keyring";
+
+/*----- Useful shared functions -------------------------------------------*/
+
+/* --- @doopen@ --- *
+ *
+ * Arguments:	@key_file *f@ = pointer to key file block
+ *		@unsigned how@ = method to open file with
+ *
+ * Returns:	---
+ *
+ * Use:		Opens a key file and handles errors by panicking
+ *		appropriately.
+ */
+
+static void doopen(key_file *f, unsigned how)
+{
+  if (key_open(f, keyfile, how))
+    die(1, "couldn't open file `%s': %s", keyfile, strerror(errno));
+}
+
+/* --- @doclose@ --- *
+ *
+ * Arguments:	@key_file *f@ = pointer to key file block
+ *
+ * Returns:	---
+ *
+ * Use:		Closes a key file and handles errors by panicking
+ *		appropriately.
+ */
+
+static void doclose(key_file *f)
+{
+  switch (key_close(f)) {
+    case KWRITE_FAIL:
+      die(EXIT_FAILURE, "couldn't write file `%s': %s",
+	  keyfile, strerror(errno));
+    case KWRITE_BROKEN:
+      die(EXIT_FAILURE, "keyring file `%s' broken: %s (repair manually)",
+	  keyfile, strerror(errno));
+  }
+}
+
+/* --- @setattr@ --- *
+ *
+ * Arguments:	@key_file *f@ = pointer to key file block
+ *		@key *k@ = pointer to key block
+ *		@char *v[]@ = array of assignments (overwritten!)
+ *
+ * Returns:	---
+ *
+ * Use:		Applies the attribute assignments to the key.
+ */
+
+static void setattr(key_file *f, key *k, char *v[])
+{
+  while (*v) {
+    char *p = *v;
+    size_t eq = strcspn(p, "=");
+    if (p[eq] == 0)
+      moan("invalid assignment: `%s'", p);
+    p[eq] = 0;
+    p += eq + 1;
+    key_putattr(f, k, *v, *p ? p : 0);
+    v++;
+  }
+}
+
+/*----- Command implementation --------------------------------------------*/
+
+/* --- @cmd_add@ --- */
+
+static int cmd_add(int argc, char *argv[])
+{
+  key_file f;
+  key *k;
+  int bits = 128;
+  time_t exp = KEXP_EXPIRE;
+  unsigned fl = 0;
+  unsigned char *p;
+  size_t sz;
+  const char *c = 0;
+
+  /* --- Various useful flag bits --- */
+
+  enum {
+    f_bogus = 1
+  };
+
+  /* --- Parse options for the subcommand --- */
+
+  for (;;) {
+    static struct option opt[] = {
+      { "bits",		OPTF_ARGREQ,	0,	'b' },
+      { "expire",	OPTF_ARGREQ,	0,	'e' },
+      { "comment",	OPTF_ARGREQ,	0,	'c' },
+      { 0,		0,		0,	0 }
+    };
+    int i = mdwopt(argc, argv, "+b:e:c:", opt, 0, 0, 0);
+    if (i < 0)
+      break;
+
+    /* --- Handle the various options --- */
+
+    switch (i) {
+
+      /* --- Bits must be nonzero and a multiple of 8 --- */
+
+      case 'b':
+	if (!(bits = atoi(optarg)) || bits % 8)
+	  die(EXIT_FAILURE, "bad number of bits: `%s'", optarg);
+	break;
+
+      /* --- Expiry dates get passed to @get_date@ for parsing --- */
+
+      case 'e':
+	if (strcmp(optarg, "forever") == 0)
+	  exp = KEXP_FOREVER;
+	else {
+	  exp = get_date(optarg, 0);
+	  if (exp == -1)
+	    die(EXIT_FAILURE, "bad expiry date: `%s'", optarg);
+	}
+	break;
+
+      /* --- Store comments without interpretation --- */
+
+      case 'c':
+	if (key_chkcomment(c))
+	  die(EXIT_FAILURE, "bad comment string: `%s'", optarg);
+	c = optarg;
+	break;
+
+      /* --- Other things are bogus --- */
+
+      default:
+	fl |= f_bogus;
+	break;
+    }
+  }
+
+  /* --- Various sorts of bogusity --- */
+
+  if (fl & f_bogus || optind + 1 > argc) {
+    die(EXIT_FAILURE,
+	"Usage: add [-b BITS] [-e EXPIRE] [-c COMMENT] TYPE [ATTR...]");
+  }
+  if (key_chktype(argv[optind]))
+    die(EXIT_FAILURE, "bad key type: `%s'", argv[optind]);
+  if (exp == KEXP_EXPIRE)
+    exp = time(0) + 14 * 24 * 60 * 60;
+
+  /* --- Initialize the Catacomb random number generator --- */
+
+  rand_init(RAND_GLOBAL);
+  rand_noisesrc(RAND_GLOBAL, &noise_source);
+
+  /* --- Extract the key data from the generator --- */
+
+  sz = bits / 8;
+  p = xmalloc(sz);
+  rand_getgood(RAND_GLOBAL, p, sz);
+
+  /* --- Open the file, add the key, set attributes, close, return --- */
+
+  doopen(&f, KOPEN_WRITE);
+  if (!(k = key_new(&f, argv[optind], p, sz, exp, c)))
+    moan("key not added: expiry date in past?");
+  setattr(&f, k, argv + optind + 1);
+  doclose(&f);
+  return (0);
+}
+
+/* --- @cmd_expire@ --- */
+
+static int cmd_expire(int argc, char *argv[])
+{
+  key_file f;
+  key *k;
+  uint32 id;
+  int i;
+  int rc = 0;
+
+  if (argc < 2)
+    die(EXIT_FAILURE, "Usage: expire KEYID...");
+  doopen(&f, KOPEN_WRITE);
+  for (i = 1; i < argc; i++) {
+    id = (uint32)strtoul(argv[i], 0, 16);
+    if ((k = key_byid(&f, id)) != 0)
+      key_expire(&f, k);
+    else {
+      moan("keyid %lx not found", (unsigned long)id);
+      rc = 1;
+    }
+  }
+  doclose(&f);
+  return (rc);
+}
+
+/* --- @cmd_delete@ --- */
+
+static int cmd_delete(int argc, char *argv[])
+{
+  key_file f;
+  key *k;
+  uint32 id;
+  int i;
+  int rc = 0;
+
+  if (argc < 2)
+    die(EXIT_FAILURE, "Usage: delete KEYID...");
+  doopen(&f, KOPEN_WRITE);
+  for (i = 1; i < argc; i++) {
+    id = (uint32)strtoul(argv[i], 0, 16);
+    if ((k = key_byid(&f, id)) != 0)
+      key_delete(&f, k);
+    else {
+      moan("keyid %lx not found", (unsigned long)id);
+      rc = 1;
+    }
+  }
+  doclose(&f);
+  return (rc);
+}
+
+/* --- @cmd_setattr@ --- */
+
+static int cmd_setattr(int argc, char *argv[])
+{
+  key_file f;
+  key *k;
+  uint32 id;
+
+  if (argc < 3)
+    die(EXIT_FAILURE, "Usage: setattr KEYID ATTR...");
+  doopen(&f, KOPEN_WRITE);
+  id = (uint32)strtoul(argv[1], 0, 16);
+  if ((k = key_byid(&f, id)) == 0)
+    die(EXIT_FAILURE, "keyid %lx not found", (unsigned long)id);
+  setattr(&f, k, argv + 2);
+  doclose(&f);
+  return (0);
+}
+
+/* --- @cmd_comment@ --- */
+
+static int cmd_comment(int argc, char *argv[])
+{
+  uint32 id;
+  key_file f;
+  key *k;
+
+  if (argc < 2 || argc > 3)
+    die(EXIT_FAILURE, "Usage: comment KEYID [COMMENT]");
+  doopen(&f, KOPEN_WRITE);
+  id = (uint32)strtoul(argv[1], 0, 16);
+  if ((k = key_byid(&f, id)) == 0)
+    die(EXIT_FAILURE, "keyid %lx not found", (unsigned long)id);
+  if (key_chkcomment(argv[2]))
+    die(EXIT_FAILURE, "bad comment: `%s'", argv[2]);
+  key_setcomment(&f, k, argv[2]);
+  doclose(&f);
+  return (0);
+}
+
+/* --- @cmd_list@ --- */
+
+static int cmd_list(int argc, char *argv[])
+{
+  key_file f;
+  key *k;
+  key_iter i;
+  unsigned fl = 0;
+  const char *tfmt;
+  int v = 0;
+  time_t t;
+
+  enum {
+    f_bogus = 1,
+    f_newline = 2,
+    f_attr = 4
+  };
+
+  /* --- Parse subcommand options --- */
+
+  for (;;) {
+    static struct option opt[] = {
+      { "quiet",	0,	0,	'q' },
+      { "verbose",	0,	0,	'v' },
+      { 0,		0,	0,	0 }
+    };
+    int i = mdwopt(argc, argv, "qv", opt, 0, 0, 0);
+    if (i < 0)
+      break;
+
+    switch (i) {
+      case 'q':
+	if (v)
+	  v--;
+	break;
+      case 'v':
+	v++;
+	break;
+      default:
+	fl |= f_bogus;
+	break;
+    }
+  }
+
+  if (fl & f_bogus || optind != argc)
+    die(EXIT_FAILURE, "Usage: list [-qv]");
+
+  /* --- Open the key file --- */
+
+  doopen(&f, KOPEN_READ);
+  t = time(0);
+
+  /* --- Write the header --- */
+
+  tfmt = v ? "%Y-%m-%d %H:%M:%S" : "%Y-%m-%d";
+
+  /* --- Now iterate through the keys --- */
+
+  for (key_mkiter(&i, &f); (k = key_next(&i)) != 0; ) {
+    char ebuf[24], dbuf[24];
+    struct tm *tm;
+
+    /* --- Sort out the expiry times --- */
+
+    if (KEY_EXPIRED(t, k->exp)) {
+      strcpy(ebuf, "expired");
+      if (KEY_DELETED(t, k->del)) {
+	strcpy(dbuf, "deleted");
+	goto donetime;
+      } else
+	goto deltime;
+    }
+
+    if (k->exp == KEXP_FOREVER)
+      strcpy(ebuf, "forever");
+    else {
+      tm = localtime(&k->exp);
+      strftime(ebuf, sizeof(ebuf), tfmt, tm);
+    }
+
+    /* --- Sort out the delete times --- */
+
+  deltime:
+    if (k->del == KEXP_UNUSED)
+      strcpy(dbuf, "on expiry");
+    else if (k->del == KEXP_FOREVER)
+      strcpy(dbuf, "forever");
+    else {
+      tm = localtime(&k->del);
+      strftime(dbuf, sizeof(dbuf), tfmt, tm);
+    }
+
+  donetime:;
+
+    /* --- Display the data obtained so far --- */
+
+    if (!v) {
+      if (!(fl & f_newline)) {
+	printf("%8s  %-20s  %-10s  %-10s  %-23s\n",
+	       "Id", "Type", "Expire", "Delete", "Comment");
+      }
+      printf("%08lx  %-20s  %-10s  %-10s  %-23s\n",
+	     (unsigned long)k->id, k->type, ebuf, dbuf,
+	     k->c ? k->c : "<none>");
+      fl |= f_newline;
+    } else {
+
+      /* --- Display the standard header --- */
+
+      if (fl & f_newline)
+	fputc('\n', stdout);
+      printf("keyid: %08lx\n", (unsigned long)k->id);
+      printf("type: %s\n", k->type);
+      printf("expiry: %s\n", ebuf);
+      printf("delete: %s\n", dbuf);
+      printf("comment: %s\n", k->c ? k->c : "<none>");
+
+      /* --- Display the attributes --- */
+
+      if (v > 1) {
+	key_attriter i;
+	const char *av, *an;
+
+	fl &= ~f_attr;
+	printf("attributes:");
+	for (key_mkattriter(&i, &f, k); key_nextattr(&i, &an, &av); ) {
+	  printf("\n\t%s = %s", an, av);
+	  fl |= f_attr;
+	}
+	if (fl & f_attr)
+	  fputc('\n', stdout);
+	else
+	  puts(" <none>");
+      }
+
+      /* --- If dumping requested, dump the raw key data in hex --- */
+
+      if (v > 2) {
+	unsigned char *p = k->k;
+	unsigned char *l = p + k->ksz;
+	size_t sz = 0;
+
+	fputs("key:", stdout);
+	while (p < l) {
+	  if (sz % 16 == 0)
+	    fputs("\n\t", stdout);
+	  else if (sz % 8 == 0)
+	    fputs("  ", stdout);
+	  else
+	    fputc(' ', stdout);
+	  printf("%02x", *p++);
+	  sz++;
+	}
+	fputc('\n', stdout);
+      }
+    }
+  }
+
+  doclose(&f);
+  return (0);
+}
+
+/* --- @cmd_extract@ --- */
+
+static int cmd_extract(int argc, char *argv[])
+{
+  key_file f;
+  key *k;
+  uint32 id;
+  int i;
+  int rc = 0;
+  FILE *fp;
+
+  if (argc < 3)
+    die(EXIT_FAILURE, "Usage: extract FILE KEYID...");
+  if (strcmp(argv[1], "-") == 0)
+    fp = stdout;
+  else if (!(fp = fopen(argv[1], "w"))) {
+    die(EXIT_FAILURE, "couldn't open `%s' for writing: %s",
+	argv[1], strerror(errno));
+  }
+
+  doopen(&f, KOPEN_WRITE);
+  for (i = 2; i < argc; i++) {
+    id = (uint32)strtoul(argv[i], 0, 16);
+    if ((k = key_byid(&f, id)) != 0)
+      key_extract(&f, k, fp);
+    else {
+      moan("keyid %lx not found", (unsigned long)id);
+      rc = 1;
+    }
+  }
+  doclose(&f);
+  return (rc);
+}
+
+/* --- @cmd_tidy@ --- */
+
+static int cmd_tidy(int argc, char *argv[])
+{
+  key_file f;
+  if (argc != 1)
+    die(EXIT_FAILURE, "usage: tidy");
+  doopen(&f, KOPEN_WRITE);
+  f.f |= KF_MODIFIED; /* Nasty hack */
+  doclose(&f);
+  return (0);
+}
+
+/* --- @cmd_merge@ --- */
+
+static int cmd_merge(int argc, char *argv[])
+{
+  key_file f;
+  FILE *fp;
+
+  if (argc != 2)
+    die(EXIT_FAILURE, "Usage: merge FILE");
+  if (strcmp(argv[1], "-") == 0)
+    fp = stdin;
+  else if (!(fp = fopen(argv[1], "r"))) {
+    die(EXIT_FAILURE, "couldn't open `%s' for writing: %s",
+	argv[1], strerror(errno));
+  }
+
+  doopen(&f, KOPEN_WRITE);
+  key_merge(&f, argv[1], fp);
+  doclose(&f);
+  return (0);
+}
+
+/*----- Main command table ------------------------------------------------*/
+
+static struct cmd {
+  const char *name;
+  int (*cmd)(int /*argc*/, char */*argv*/[]);
+  const char *help;
+} cmds[] = {
+  { "add", cmd_add,
+    "add [-b BITS] [-e EXPIRE] [-c COMMENT] TYPE [ATTR...]" },
+  { "expire", cmd_expire, "expire KEYID..." },
+  { "delete", cmd_delete, "delete KEYID..." },
+  { "setattr", cmd_setattr, "setattr KEYID ATTR..." },
+  { "comment", cmd_comment, "comment KEYID [COMMENT]" },
+  { "list", cmd_list, "list [-qv]" },
+  { "tidy", cmd_tidy, "tidy" },
+  { "extract", cmd_extract, "extract FILE KEYID..." },
+  { "merge", cmd_merge, "merge FILE" },
+  { 0, 0, 0 }
+};
+
+typedef struct cmd cmd;
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- Helpful GNUy functions --- */
+
+void usage(FILE *fp)
+{
+  fprintf(fp, "Usage: %s [-k file] command [args]\n", QUIS);
+}
+
+void version(FILE *fp)
+{
+  fprintf(fp, "%s, Catacomb version " VERSION "\n", QUIS);
+}
+
+void help(FILE *fp)
+{
+  cmd *c;
+  version(fp);
+  fputc('\n', fp);
+  usage(fp);
+  fputs("\n\
+Performs various simple key management operations.  Command line options\n\
+recognized are:\n\
+\n\
+-h, --help		Display this help text.\n\
+-v, --version		Display version number.\n\
+-u, --usage		Display short usage summary.\n\
+\n\
+-k, --keyring=FILE	Read and write keys in FILE.\n\
+\n\
+The following commands are understood:\n\n",
+	fp);
+  for (c = cmds; c->name; c++)
+    fprintf(fp, "%s\n", c->help);
+}
+
+/* --- @main@ --- *
+ *
+ * Arguments:	@int argc@ = number of command line arguments
+ *		@char *argv[]@ = array of command line arguments
+ *
+ * Returns:	Nonzero on failure.
+ *
+ * Use:		Main program.  Performs simple key management functions.
+ */
+
+int main(int argc, char *argv[])
+{
+  unsigned f = 0;
+
+  enum {
+    f_bogus = 1
+  };
+
+  /* --- Initialization --- */
+
+  ego(argv[0]);
+  sub_init();
+
+  /* --- Parse command line options --- */
+
+  for (;;) {
+    static struct option opt[] = {
+
+      /* --- Standard GNUy help options --- */
+
+      { "help",		0,		0,	'h' },
+      { "version",	0,		0,	'v' },
+      { "usage",	0,		0,	'u' },
+
+      /* --- Real live useful options --- */
+
+      { "keyring",	OPTF_ARGREQ,	0,	'k' },
+
+      /* --- Magic terminator --- */
+
+      { 0,		0,		0,	0 }
+    };
+    int i = mdwopt(argc, argv, "+hvu k:", opt, 0, 0, 0);
+
+    if (i < 0)
+      break;
+    switch (i) {
+
+      /* --- GNU help options --- */
+      case 'h':
+	help(stdout);
+	exit(0);
+      case 'v':
+	version(stdout);
+	exit(0);
+      case 'u':
+	usage(stdout);
+	exit(0);
+
+      /* --- Real useful options --- */
+
+      case 'k':
+	keyfile = optarg;
+	break;
+
+      /* --- Bogosity --- */
+
+      default:
+	f |= f_bogus;
+	break;
+    }
+  }
+
+  /* --- Complain about excessive bogons --- */
+
+  if (f & f_bogus || optind == argc) {
+    usage(stderr);
+    exit(1);
+  }
+
+  /* --- Dispatch to appropriate command handler --- */
+
+  argc -= optind;
+  argv += optind;
+  optind = 0;
+
+  {
+    cmd *c, *chosen = 0;
+    size_t sz = strlen(argv[0]);
+
+    for (c = cmds; c->name; c++) {
+      if (strncmp(argv[0], c->name, sz) == 0) {
+	if (c->name[sz] == 0) {
+	  chosen = c;
+	  break;
+	} else if (chosen)
+	  die(EXIT_FAILURE, "ambiguous command name `%s'", argv[0]);
+	else
+	  chosen = c;
+      }
+    }
+    if (!chosen)
+      die(EXIT_FAILURE, "unknown command name `%s'", argv[0]);
+    return (chosen->cmd(argc, argv));
+  }
+}
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/md4.c b/md4.c
new file mode 100644
index 0000000..406c3a0
--- /dev/null
+++ b/md4.c
@@ -0,0 +1,260 @@
+/* -*-c-*-
+ *
+ * $Id: md4.c,v 1.1 1999/09/03 08:41:12 mdw Exp $
+ *
+ * The MD4 message digest function
+ *
+ * (c) 1999 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: md4.c,v $
+ * Revision 1.1  1999/09/03 08:41:12  mdw
+ * Initial import.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <mLib/bits.h>
+
+#include "hash.h"
+#include "md4.h"
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @md4_compress@ --- *
+ *
+ * Arguments:	@md4_ctx *ctx@ = pointer to context block
+ *		@const void *sbuf@ = pointer to buffer of appropriate size
+ *
+ * Returns:	---
+ *
+ * Use:		RIPEMD-160 compression function.
+ */
+
+void md4_compress(md4_ctx *ctx, const void *sbuf)
+{
+  uint32 a, b, c, d;
+  uint32 buf[16];
+
+  /* --- Fetch the chaining variables --- */
+
+  a = ctx->a;
+  b = ctx->b;
+  c = ctx->c;
+  d = ctx->d;
+
+  /* --- Fetch the buffer contents --- */
+
+  {
+    int i;
+    const octet *p;
+
+    for (i = 0, p = sbuf; i < 16; i++, p += 4)
+      buf[i] = LOAD32_L(p);
+  }
+
+  /* --- Definitions for round functions --- */
+
+#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+
+#define T(w, x, y, z, i, r, k, f) do {					\
+  uint32 _t = w + f(x, y, z) + buf[i] + k;				\
+  w = ROL32(_t, r);							\
+} while (0)
+
+#define FF(w, x, y, z, i, r) T(w, x, y, z, i, r, 0x00000000, F)
+#define GG(w, x, y, z, i, r) T(w, x, y, z, i, r, 0x5a827999, G)
+#define HH(w, x, y, z, i, r) T(w, x, y, z, i, r, 0x6ed9eba1, H)
+
+  /* --- The main compression function --- */
+
+  FF(a, b, c, d,  0,  3);
+  FF(d, a, b, c,  1,  7);
+  FF(c, d, a, b,  2, 11);
+  FF(b, c, d, a,  3, 19);
+  FF(a, b, c, d,  4,  3);
+  FF(d, a, b, c,  5,  7);
+  FF(c, d, a, b,  6, 11);
+  FF(b, c, d, a,  7, 19);
+  FF(a, b, c, d,  8,  3);
+  FF(d, a, b, c,  9,  7);
+  FF(c, d, a, b, 10, 11);
+  FF(b, c, d, a, 11, 19);
+  FF(a, b, c, d, 12,  3);
+  FF(d, a, b, c, 13,  7);
+  FF(c, d, a, b, 14, 11);
+  FF(b, c, d, a, 15, 19);
+
+  GG(a, b, c, d,  0,  3);
+  GG(d, a, b, c,  4,  5);
+  GG(c, d, a, b,  8,  9);
+  GG(b, c, d, a, 12, 13);
+  GG(a, b, c, d,  1,  3);
+  GG(d, a, b, c,  5,  5);
+  GG(c, d, a, b,  9,  9);
+  GG(b, c, d, a, 13, 13);
+  GG(a, b, c, d,  2,  3);
+  GG(d, a, b, c,  6,  5);
+  GG(c, d, a, b, 10,  9);
+  GG(b, c, d, a, 14, 13);
+  GG(a, b, c, d,  3,  3);
+  GG(d, a, b, c,  7,  5);
+  GG(c, d, a, b, 11,  9);
+  GG(b, c, d, a, 15, 13);
+
+  HH(a, b, c, d,  0,  3);
+  HH(d, a, b, c,  8,  9);
+  HH(c, d, a, b,  4, 11);
+  HH(b, c, d, a, 12, 15);
+  HH(a, b, c, d,  2,  3);
+  HH(d, a, b, c, 10,  9);
+  HH(c, d, a, b,  6, 11);
+  HH(b, c, d, a, 14, 15);
+  HH(a, b, c, d,  1,  3);
+  HH(d, a, b, c,  9,  9);
+  HH(c, d, a, b,  5, 11);
+  HH(b, c, d, a, 13, 15);
+  HH(a, b, c, d,  3,  3);
+  HH(d, a, b, c, 11,  9);
+  HH(c, d, a, b,  7, 11);
+  HH(b, c, d, a, 15, 15);
+
+  /* --- Update the chaining variables --- */
+
+  ctx->a += a;
+  ctx->b += b;
+  ctx->c += c;
+  ctx->d += d;
+}
+
+/* --- @md4_init@ --- *
+ *
+ * Arguments:	@md4_ctx *ctx@ = pointer to context block to initialize
+ *
+ * Returns:	---
+ *
+ * Use:		Initializes a context block ready for hashing.
+ */
+
+void md4_init(md4_ctx *ctx)
+{
+  ctx->a = 0x67452301;
+  ctx->b = 0xefcdab89;
+  ctx->c = 0x98badcfe;
+  ctx->d = 0x10325476;
+  ctx->off = 0;
+  ctx->count = 0;
+}
+
+/* --- @md4_set@ --- *
+ *
+ * Arguments:	@md4_ctx *ctx@ = pointer to context block
+ *		@const void *buf@ = pointer to state buffer
+ *		@unsigned long count@ = current count of bytes processed
+ *
+ * Returns:	---
+ *
+ * Use:		Initializes a context block from a given state.  This is
+ *		useful in cases where the initial hash state is meant to be
+ *		secret, e.g., for NMAC and HMAC support.
+ */
+
+void md4_set(md4_ctx *ctx, const void *buf, unsigned long count)
+{
+  const octet *p = buf;
+  ctx->a = LOAD32_L(p +  0);
+  ctx->b = LOAD32_L(p +  4);
+  ctx->c = LOAD32_L(p +  8);
+  ctx->d = LOAD32_L(p + 12);
+  ctx->off = 0;
+  ctx->count = count;
+}
+
+/* --- @md4_hash@ --- *
+ *
+ * Arguments:	@md4_ctx *ctx@ = pointer to context block
+ *		@const void *buf@ = buffer of data to hash
+ *		@size_t sz@ = size of buffer to hash
+ *
+ * Returns:	---
+ *
+ * Use:		Hashes a buffer of data.  The buffer may be of any size and
+ *		alignment.
+ */
+
+void md4_hash(md4_ctx *ctx, const void *buf, size_t sz)
+{
+  HASH_BUFFER(MD4, md4, ctx, buf, sz);
+}
+
+/* --- @md4_done@ --- *
+ *
+ * Arguments:	@md4_ctx *ctx@ = pointer to context block
+ *		@void *hash@ = pointer to output buffer
+ *
+ * Returns:	---
+ *
+ * Use:		Returns the hash of the data read so far.
+ */
+
+void md4_done(md4_ctx *ctx, void *hash)
+{
+  octet *p = hash;
+  HASH_MD5STRENGTH(MD4, md4, ctx);
+  STORE32_L(p +  0, ctx->a);
+  STORE32_L(p +  4, ctx->b);
+  STORE32_L(p +  8, ctx->c);
+  STORE32_L(p + 12, ctx->d);
+}
+
+/* --- @md4_state@ --- *
+ *
+ * Arguments:	@md4_ctx *ctx@ = pointer to context
+ *		@void *state@ = pointer to buffer for current state
+ *
+ * Returns:	Number of bytes written to the hash function so far.
+ *
+ * Use:		Returns the current state of the hash function such that
+ *		it can be passed to @md4_set@.
+ */
+
+unsigned long md4_state(md4_ctx *ctx, void *state)
+{
+  octet *p = state;
+  STORE32_L(p +  0, ctx->a);
+  STORE32_L(p +  4, ctx->b);
+  STORE32_L(p +  8, ctx->c);
+  STORE32_L(p + 12, ctx->d);
+  return (ctx->count);
+}
+
+/* --- Test rig --- */
+
+HASH_TEST(MD4, md4)
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/md4.h b/md4.h
new file mode 100644
index 0000000..1697814
--- /dev/null
+++ b/md4.h
@@ -0,0 +1,158 @@
+/* -*-c-*-
+ *
+ * $Id: md4.h,v 1.1 1999/09/03 08:41:12 mdw Exp $
+ *
+ * The MD4 message digest function
+ *
+ * (c) 1998 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: md4.h,v $
+ * Revision 1.1  1999/09/03 08:41:12  mdw
+ * Initial import.
+ *
+ */
+
+/*----- Notes on the MD4 hash function ------------------------------------*
+ *
+ * MD4 was designed by Ron Rivest.  It's now well and truly broken: not only
+ * have collisions been discovered, a slightly cut-down version has been
+ * shown to be non-preimage-resistant.  On the other hand, MD4 is fast and
+ * makes a good heavy-duty checksum.  Just don't rely on it being
+ * cryptographically strong, 'cos it ain't.
+ */
+
+#ifndef MD4_H
+#define MD4_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <mLib/bits.h>
+
+/*----- Magic numbers -----------------------------------------------------*/
+
+#define MD4_BUFSZ 64
+#define MD4_HASHSZ 16
+
+/*----- Data structures ---------------------------------------------------*/
+
+typedef struct md4_ctx {
+  uint32 a, b, c, d;			/* Chaining variables */
+  unsigned long count;			/* Byte count so far */
+  int off;				/* Offset into buffer */
+  octet buf[MD4_BUFSZ];			/* Accumulation buffer */
+} md4_ctx;
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @md4_compress@ --- *
+ *
+ * Arguments:	@md4_ctx *ctx@ = pointer to context block
+ *		@const void *sbuf@ = pointer to buffer of appropriate size
+ *
+ * Returns:	---
+ *
+ * Use:		MD4 compression function.
+ */
+
+extern void md4_compress(md4_ctx */*ctx*/, const void */*sbuf*/);
+
+/* --- @md4_init@ --- *
+ *
+ * Arguments:	@md4_ctx *ctx@ = pointer to context block to initialize
+ *
+ * Returns:	---
+ *
+ * Use:		Initializes a context block ready for hashing.
+ */
+
+extern void md4_init(md4_ctx */*ctx*/);
+
+/* --- @md4_set@ --- *
+ *
+ * Arguments:	@md4_ctx *ctx@ = pointer to context block
+ *		@const void *buf@ = pointer to state buffer
+ *		@unsigned long count@ = current count of bytes processed
+ *
+ * Returns:	---
+ *
+ * Use:		Initializes a context block from a given state.  This is
+ *		useful in cases where the initial hash state is meant to be
+ *		secret, e.g., for NMAC and HMAC support.
+ */
+
+extern void md4_set(md4_ctx */*ctx*/, const void */*buf*/,
+		    unsigned long /*count*/);
+
+/* --- @md4_hash@ --- *
+ *
+ * Arguments:	@md4_ctx *ctx@ = pointer to context block
+ *		@const void *buf@ = buffer of data to hash
+ *		@size_t sz@ = size of buffer to hash
+ *
+ * Returns:	---
+ *
+ * Use:		Hashes a buffer of data.  The buffer may be of any size and
+ *		alignment.
+ */
+
+extern void md4_hash(md4_ctx */*ctx*/, const void */*buf*/, size_t /*sz*/);
+
+/* --- @md4_done@ --- *
+ *
+ * Arguments:	@md4_ctx *ctx@ = pointer to context block
+ *		@void *hash@ = pointer to output buffer
+ *
+ * Returns:	---
+ *
+ * Use:		Returns the hash of the data read so far.
+ */
+
+extern void md4_done(md4_ctx */*ctx*/, void */*hash*/);
+
+/* --- @md4_state@ --- *
+ *
+ * Arguments:	@md4_ctx *ctx@ = pointer to context
+ *		@void *state@ = pointer to buffer for current state
+ *
+ * Returns:	Number of bytes written to the hash function so far.
+ *
+ * Use:		Returns the current state of the hash function such that
+ *		it can be passed to @md4_set@.
+ */
+
+extern unsigned long md4_state(md4_ctx */*ctx*/, void */*state*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/md5.c b/md5.c
new file mode 100644
index 0000000..c6e2a2a
--- /dev/null
+++ b/md5.c
@@ -0,0 +1,279 @@
+/* -*-c-*-
+ *
+ * $Id: md5.c,v 1.1 1999/09/03 08:41:12 mdw Exp $
+ *
+ * The MD5 message digest function
+ *
+ * (c) 1998 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: md5.c,v $
+ * Revision 1.1  1999/09/03 08:41:12  mdw
+ * Initial import.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <mLib/bits.h>
+
+#include "hash.h"
+#include "md5.h"
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @md5_compress@ --- *
+ *
+ * Arguments:	@md5_ctx *ctx@ = pointer to context block
+ *		@const void *sbuf@ = pointer to buffer of appropriate size
+ *
+ * Returns:	---
+ *
+ * Use:		MD5 compression function.
+ */
+
+void md5_compress(md5_ctx *ctx, const void *sbuf)
+{
+  uint32 a, b, c, d;
+  uint32 buf[16];
+
+  /* --- Fetch the chaining variables --- */
+
+  a = ctx->a;
+  b = ctx->b;
+  c = ctx->c;
+  d = ctx->d;
+
+  /* --- Fetch the buffer contents --- */
+
+  {
+    int i;
+    const octet *p;
+
+    for (i = 0, p = sbuf; i < 16; i++, p += 4)
+      buf[i] = LOAD32_L(p);
+  }
+
+  /* --- Definitions for round functions --- */
+
+#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+
+#define T(w, x, y, z, i, r, k, f) do {					\
+  uint32 _t = w + f(x, y, z) + buf[i] + k;				\
+  w = ROL32(_t, r) + x;							\
+} while (0)
+
+#define FF(w, x, y, z, i, r, k) T(w, x, y, z, i, r, k, F)
+#define GG(w, x, y, z, i, r, k) T(w, x, y, z, i, r, k, G)
+#define HH(w, x, y, z, i, r, k) T(w, x, y, z, i, r, k, H)
+#define II(w, x, y, z, i, r, k) T(w, x, y, z, i, r, k, I)
+
+  /* --- The main compression function --- */
+
+  FF(a, b, c, d,  0,  7, 0xd76aa478);
+  FF(d, a, b, c,  1, 12, 0xe8c7b756);
+  FF(c, d, a, b,  2, 17, 0x242070db);
+  FF(b, c, d, a,  3, 22, 0xc1bdceee);
+  FF(a, b, c, d,  4,  7, 0xf57c0faf);
+  FF(d, a, b, c,  5, 12, 0x4787c62a);
+  FF(c, d, a, b,  6, 17, 0xa8304613);
+  FF(b, c, d, a,  7, 22, 0xfd469501);
+  FF(a, b, c, d,  8,  7, 0x698098d8);
+  FF(d, a, b, c,  9, 12, 0x8b44f7af);
+  FF(c, d, a, b, 10, 17, 0xffff5bb1);
+  FF(b, c, d, a, 11, 22, 0x895cd7be);
+  FF(a, b, c, d, 12,  7, 0x6b901122);
+  FF(d, a, b, c, 13, 12, 0xfd987193);
+  FF(c, d, a, b, 14, 17, 0xa679438e);
+  FF(b, c, d, a, 15, 22, 0x49b40821);
+
+  GG(a, b, c, d,  1,  5, 0xf61e2562);
+  GG(d, a, b, c,  6,  9, 0xc040b340);
+  GG(c, d, a, b, 11, 14, 0x265e5a51);
+  GG(b, c, d, a,  0, 20, 0xe9b6c7aa);
+  GG(a, b, c, d,  5,  5, 0xd62f105d);
+  GG(d, a, b, c, 10,  9, 0x02441453);
+  GG(c, d, a, b, 15, 14, 0xd8a1e681);
+  GG(b, c, d, a,  4, 20, 0xe7d3fbc8);
+  GG(a, b, c, d,  9,  5, 0x21e1cde6);
+  GG(d, a, b, c, 14,  9, 0xc33707d6);
+  GG(c, d, a, b,  3, 14, 0xf4d50d87);
+  GG(b, c, d, a,  8, 20, 0x455a14ed);
+  GG(a, b, c, d, 13,  5, 0xa9e3e905);
+  GG(d, a, b, c,  2,  9, 0xfcefa3f8);
+  GG(c, d, a, b,  7, 14, 0x676f02d9);
+  GG(b, c, d, a, 12, 20, 0x8d2a4c8a);
+
+  HH(a, b, c, d,  5,  4, 0xfffa3942);
+  HH(d, a, b, c,  8, 11, 0x8771f681);
+  HH(c, d, a, b, 11, 16, 0x6d9d6122);
+  HH(b, c, d, a, 14, 23, 0xfde5380c);
+  HH(a, b, c, d,  1,  4, 0xa4beea44);
+  HH(d, a, b, c,  4, 11, 0x4bdecfa9);
+  HH(c, d, a, b,  7, 16, 0xf6bb4b60);
+  HH(b, c, d, a, 10, 23, 0xbebfbc70);
+  HH(a, b, c, d, 13,  4, 0x289b7ec6);
+  HH(d, a, b, c,  0, 11, 0xeaa127fa);
+  HH(c, d, a, b,  3, 16, 0xd4ef3085);
+  HH(b, c, d, a,  6, 23, 0x04881d05);
+  HH(a, b, c, d,  9,  4, 0xd9d4d039);
+  HH(d, a, b, c, 12, 11, 0xe6db99e5);
+  HH(c, d, a, b, 15, 16, 0x1fa27cf8);
+  HH(b, c, d, a,  2, 23, 0xc4ac5665);
+
+  II(a, b, c, d,  0,  6, 0xf4292244);
+  II(d, a, b, c,  7, 10, 0x432aff97);
+  II(c, d, a, b, 14, 15, 0xab9423a7);
+  II(b, c, d, a,  5, 21, 0xfc93a039);
+  II(a, b, c, d, 12,  6, 0x655b59c3);
+  II(d, a, b, c,  3, 10, 0x8f0ccc92);
+  II(c, d, a, b, 10, 15, 0xffeff47d);
+  II(b, c, d, a,  1, 21, 0x85845dd1);
+  II(a, b, c, d,  8,  6, 0x6fa87e4f);
+  II(d, a, b, c, 15, 10, 0xfe2ce6e0);
+  II(c, d, a, b,  6, 15, 0xa3014314);
+  II(b, c, d, a, 13, 21, 0x4e0811a1);
+  II(a, b, c, d,  4,  6, 0xf7537e82);
+  II(d, a, b, c, 11, 10, 0xbd3af235);
+  II(c, d, a, b,  2, 15, 0x2ad7d2bb);
+  II(b, c, d, a,  9, 21, 0xeb86d391);
+
+  /* --- Update the chaining variables --- */
+
+  ctx->a += a;
+  ctx->b += b;
+  ctx->c += c;
+  ctx->d += d;
+}
+
+/* --- @md5_init@ --- *
+ *
+ * Arguments:	@md5_ctx *ctx@ = pointer to context block to initialize
+ *
+ * Returns:	---
+ *
+ * Use:		Initializes a context block ready for hashing.
+ */
+
+void md5_init(md5_ctx *ctx)
+{
+  ctx->a = 0x67452301;
+  ctx->b = 0xefcdab89;
+  ctx->c = 0x98badcfe;
+  ctx->d = 0x10325476;
+  ctx->off = 0;
+  ctx->count = 0;
+}
+
+/* --- @md5_set@ --- *
+ *
+ * Arguments:	@md5_ctx *ctx@ = pointer to context block
+ *		@const void *buf@ = pointer to state buffer
+ *		@unsigned long count@ = current count of bytes processed
+ *
+ * Returns:	---
+ *
+ * Use:		Initializes a context block from a given state.  This is
+ *		useful in cases where the initial hash state is meant to be
+ *		secret, e.g., for NMAC and HMAC support.
+ */
+
+void md5_set(md5_ctx *ctx, const void *buf, unsigned long count)
+{
+  const octet *p = buf;
+  ctx->a = LOAD32_L(p +  0);
+  ctx->b = LOAD32_L(p +  4);
+  ctx->c = LOAD32_L(p +  8);
+  ctx->d = LOAD32_L(p + 12);
+  ctx->off = 0;
+  ctx->count = count;
+}
+
+/* --- @md5_hash@ --- *
+ *
+ * Arguments:	@md5_ctx *ctx@ = pointer to context block
+ *		@const void *buf@ = buffer of data to hash
+ *		@size_t sz@ = size of buffer to hash
+ *
+ * Returns:	---
+ *
+ * Use:		Hashes a buffer of data.  The buffer may be of any size and
+ *		alignment.
+ */
+
+void md5_hash(md5_ctx *ctx, const void *buf, size_t sz)
+{
+  HASH_BUFFER(MD5, md5, ctx, buf, sz);
+}
+
+/* --- @md5_done@ --- *
+ *
+ * Arguments:	@md5_ctx *ctx@ = pointer to context block
+ *		@void *hash@ = pointer to output buffer
+ *
+ * Returns:	---
+ *
+ * Use:		Returns the hash of the data read so far.
+ */
+
+void md5_done(md5_ctx *ctx, void *hash)
+{
+  octet *p = hash;
+  HASH_MD5STRENGTH(MD5, md5, ctx);
+  STORE32_L(p +  0, ctx->a);
+  STORE32_L(p +  4, ctx->b);
+  STORE32_L(p +  8, ctx->c);
+  STORE32_L(p + 12, ctx->d);
+}
+
+/* --- @md5_state@ --- *
+ *
+ * Arguments:	@md5_ctx *ctx@ = pointer to context
+ *		@void *state@ = pointer to buffer for current state
+ *
+ * Returns:	Number of bytes written to the hash function so far.
+ *
+ * Use:		Returns the current state of the hash function such that
+ *		it can be passed to @md5_set@.
+ */
+
+unsigned long md5_state(md5_ctx *ctx, void *state)
+{
+  octet *p = state;
+  STORE32_L(p +  0, ctx->a);
+  STORE32_L(p +  4, ctx->b);
+  STORE32_L(p +  8, ctx->c);
+  STORE32_L(p + 12, ctx->d);
+  return (ctx->count);
+}
+
+/* --- Test code --- */
+
+HASH_TEST(MD5, md5)
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/md5.h b/md5.h
new file mode 100644
index 0000000..8c54e6d
--- /dev/null
+++ b/md5.h
@@ -0,0 +1,159 @@
+/* -*-c-*-
+ *
+ * $Id: md5.h,v 1.1 1999/09/03 08:41:12 mdw Exp $
+ *
+ * The MD5 message digest function
+ *
+ * (c) 1998 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: md5.h,v $
+ * Revision 1.1  1999/09/03 08:41:12  mdw
+ * Initial import.
+ *
+ */
+
+/*----- Notes on the MD5 hash function ------------------------------------*
+ *
+ * MD5 was designed by Ron Rivest.  It was intended to be a more conservative
+ * design than the slightly earlier MD4, and indeed while MD4 has been pretty
+ * much demolished by subsequent cryptanalysis, MD5 is still standing
+ * shakily.  It's provided here not because the author recommends its use but
+ * because it's a common standard, and is often handy in non-cryptographic
+ * applications.  It's also still useful in constructions such as HMAC.
+ */
+
+#ifndef MD5_H
+#define MD5_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <mLib/bits.h>
+
+/*----- Magic numbers -----------------------------------------------------*/
+
+#define MD5_BUFSZ 64
+#define MD5_HASHSZ 16
+
+/*----- Data structures ---------------------------------------------------*/
+
+typedef struct md5_ctx {
+  uint32 a, b, c, d;			/* Chaining variables */
+  unsigned long count;			/* Byte count so far */
+  int off;				/* Offset into buffer */
+  octet buf[MD5_BUFSZ];			/* Accumulation buffer */
+} md5_ctx;
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @md5_compress@ --- *
+ *
+ * Arguments:	@md5_ctx *ctx@ = pointer to context block
+ *		@const void *sbuf@ = pointer to buffer of appropriate size
+ *
+ * Returns:	---
+ *
+ * Use:		MD5 compression function.
+ */
+
+extern void md5_compress(md5_ctx */*ctx*/, const void */*sbuf*/);
+
+/* --- @md5_init@ --- *
+ *
+ * Arguments:	@md5_ctx *ctx@ = pointer to context block to initialize
+ *
+ * Returns:	---
+ *
+ * Use:		Initializes a context block ready for hashing.
+ */
+
+extern void md5_init(md5_ctx */*ctx*/);
+
+/* --- @md5_set@ --- *
+ *
+ * Arguments:	@md5_ctx *ctx@ = pointer to context block
+ *		@const void *buf@ = pointer to state buffer
+ *		@unsigned long count@ = current count of bytes processed
+ *
+ * Returns:	---
+ *
+ * Use:		Initializes a context block from a given state.  This is
+ *		useful in cases where the initial hash state is meant to be
+ *		secret, e.g., for NMAC and HMAC support.
+ */
+
+extern void md5_set(md5_ctx */*ctx*/, const void */*buf*/,
+		    unsigned long /*count*/);
+
+/* --- @md5_hash@ --- *
+ *
+ * Arguments:	@md5_ctx *ctx@ = pointer to context block
+ *		@const void *buf@ = buffer of data to hash
+ *		@size_t sz@ = size of buffer to hash
+ *
+ * Returns:	---
+ *
+ * Use:		Hashes a buffer of data.  The buffer may be of any size and
+ *		alignment.
+ */
+
+extern void md5_hash(md5_ctx */*ctx*/, const void */*buf*/, size_t /*sz*/);
+
+/* --- @md5_done@ --- *
+ *
+ * Arguments:	@md5_ctx *ctx@ = pointer to context block
+ *		@void *hash@ = pointer to output buffer
+ *
+ * Returns:	---
+ *
+ * Use:		Returns the hash of the data read so far.
+ */
+
+extern void md5_done(md5_ctx */*ctx*/, void */*hash*/);
+
+/* --- @md5_state@ --- *
+ *
+ * Arguments:	@md5_ctx *ctx@ = pointer to context
+ *		@void *state@ = pointer to buffer for current state
+ *
+ * Returns:	Number of bytes written to the hash function so far.
+ *
+ * Use:		Returns the current state of the hash function such that
+ *		it can be passed to @md5_set@.
+ */
+
+extern unsigned long md5_state(md5_ctx */*ctx*/, void */*state*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/mp.c b/mp.c
new file mode 100644
index 0000000..fb5d627
--- /dev/null
+++ b/mp.c
@@ -0,0 +1,1252 @@
+/* -*-c-*-
+ *
+ * $Id: mp.c,v 1.1 1999/09/03 08:41:12 mdw Exp $
+ *
+ * Multiprecision arithmetic
+ *
+ * (c) 1998 Mark Wooding
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: mp.c,v $
+ * Revision 1.1  1999/09/03 08:41:12  mdw
+ * Initial import.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <limits.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "alloc.c"
+#include "bits.h"
+#include "exc.h"
+
+/*----- Important types ---------------------------------------------------*/
+
+/* For now... */
+
+#ifndef TEST_RIG
+
+typedef unsigned short mpw;
+#define MPW_BITS 16
+#define MPW_MAX 0xffffu
+typedef unsigned int mpd;
+#define MPD_BITS 32
+#define MPD_MAX 0xffffffffu
+
+#else
+
+typedef unsigned long mpw;
+static unsigned mpw_bits;
+static mpw mpw_max;
+typedef unsigned long mpd;
+
+#define MPW_BITS mpw_bits
+#define MPW_MAX mpw_max
+
+#endif
+
+/*----- Data structures ---------------------------------------------------*/
+
+/*----- Some basic values which are important -----------------------------*/
+
+static mpw mp__v[] = { 1, 2, 3, 4, 5, 10 };
+const mp mp_std[] = {
+  { 0,		0,	0,	0 },	/* 0 */
+  { mp__v + 0,	0,	0,	1 },	/* 1 */
+  { mp__v + 1,	0,	0,	1 },	/* 2 */
+  { mp__v + 2,	0,	0,	1 },	/* 3 */
+  { mp__v + 3,	0,	0,	1 },	/* 4 */
+  { mp__v + 4,	0,	0,	1 },	/* 5 */
+  { mp__v + 5,	0,	0,	1 },	/* 10 */
+  { mp__v + 0,	MPF_SIGN, 0,	1 }	/* -1 */
+};
+
+/*----- Memory management -------------------------------------------------*/
+
+/* --- @mp_create@ --- *
+ *
+ * Arguments	@mp *x@ = pointer to MP head
+ *
+ * Returns:	---
+ *
+ * Use:		Initializes an MP ready for use.  The initial value is zero.
+ */
+
+void mp_create(mp *x)
+{
+  x->v = 0;
+  x->f = 0;
+  x->sz = 0;
+  x->len = 0;
+}
+
+/* --- @mp_destroy@ --- *
+ *
+ * Arguments:	@mp *x@ = pointer to MP head
+ *
+ * Returns:	---
+ *
+ * Use:		Releases the memory used by an MP.
+ */
+
+void mp_destroy(mp *x) { MP_DESTROY(x); }
+
+/* --- @mp_resize@ --- *
+ *
+ * Arguments:	@mp *x@ = pointer to MP head
+ *		@size_t sz@ = size required (in words)
+ *
+ * Returns:	---
+ *
+ * Use:		Resizes the MP so that its word vector has space for
+ *		exactly @sz@ words.
+ */
+
+void mp_resize(mp *x, size_t sz)
+{
+  if (sz == 0)
+    mp_destroy(x);
+  else {
+    size_t min = sz > x->sz ? x->sz : sz;
+    mpw *v = xmalloc(sz * sizeof(mpw));
+    if (min)
+      memcpy(v, x->v, min * sizeof(mpw));
+    MP_BURN(x);
+    if (x->v)
+      free(x->v);
+    if (sz > min)
+      memset(v + min, 0, (sz - min) * sizeof(mpw));
+    x->v = v;
+    x->sz = sz;
+  }
+}
+
+/* --- @mp_norm@ --- *
+ *
+ * Arguments:	@mp *x@ = pointer to MP head
+ *
+ * Returns:	---
+ *
+ * Use:		`Normalizes' an MP.  Fixes the @len@ field so that it's
+ *		correct.  Assumes that @len@ is either already correct or
+ *		too large.
+ */
+
+void mp_norm(mp *x) { MP_NORM(x); }
+
+/* --- @mp_dump@ --- *
+ *
+ * Arguments:	@mp *x@ = pointer to MP head
+ *		@FILE *fp@ = pointer to stream to write on
+ *
+ * Returns:	---
+ *
+ * Use:		Dumps an MP to a stream.
+ */
+
+void mp_dump(mp *x, FILE *fp)
+{
+  if (x->v) {
+    mpw *v = x->v, *vl = v + x->len;
+    while (v < vl) {
+      fprintf(fp, "%lx", (unsigned long)*v++);
+      if (v < vl) fputc('-', fp);
+    }
+  } else
+    fputs("<0>", fp);
+}
+
+/* --- @mp_copy@ --- *
+ *
+ * Arguments:	@mp *d@ = pointer to MP head for destination
+ *		@const mp *s@ = pointer to MP head for source
+ *
+ * Returns:	---
+ *
+ * Use:		Copies an MP.
+ */
+
+void mp_copy(mp *d, const mp *s)
+{
+  if (d == s) {
+    MP_NORM(d);
+    return;
+  }
+  MP_BURN(d);
+  MP_ENSURE(d, s->len);
+  memcpy(d->v, s->v, s->len * sizeof(mpw));
+  if (d->f & MPF_BURN && d->sz > s->len)
+    memset(d->v + s->len, 0, (d->sz - s->len) * sizeof(mpw));
+  d->len = s->len;
+  d->f = s->f;
+  MP_NORM(d);
+}
+
+/* --- @mp_bits@ --- *
+ *
+ * Arguments:	@mp *x@ = pointer to MP head
+ *
+ * Returns:	Length of the number in bits.
+ *
+ * Use:		Calculates the number of bits required to represent a number.
+ *		The number must be normalized.
+ */
+
+unsigned long mp_bits(mp *x)
+{
+  if (!x->v)
+    return (0);
+  else {
+    unsigned long bits = MPW_BITS * (x->len - 1);
+    mpw w = x->v[x->len - 1];
+    while (w) {
+      bits++;
+      w >>= 1;
+    }
+    return (bits);
+  }
+}
+
+/* --- @mp_octets@ --- *
+ *
+ * Arguments:	@mp *x@ = pointer to MP head
+ *
+ * Returns:	Length of number in octets.
+ *
+ * Use:		Calculates the number of octets required to represent a
+ *		number.  The number must be normalized.
+ */
+
+size_t mp_octets(mp *x)
+{
+  return ((mp_bits(x) + 7) & 7);
+}  
+
+/*----- Loading and storing as binary data --------------------------------*/
+
+/* --- @mp_storel@ --- *
+ *
+ * Arguments:	@mp *x@ = pointer to MP head
+ *		@octet *p@ = pointer to octet array
+ *		@size_t sz@ = size of octet array
+ *
+ * Returns:	---
+ *
+ * Use:		Stores an MP in an octet array, least significant octet
+ *		first.  High-end octets are silently discarded if there
+ *		isn't enough space for them.
+ */
+
+void mp_storel(mp *x, octet *p, size_t sz)
+{
+  mpw *v = x->v, *vl = x->v + x->len;
+  mpw n, w = 0;
+  octet *q = p + sz;
+  unsigned bits = 0;
+
+  while (p < q) {
+    if (bits < 8) {
+      n = (v >= vl) ? 0 : *v++;
+      *p++ = (w | n << bits) & MASK8;
+      w = n >> (8 - bits);
+      bits += MPW_BITS - 8;
+    } else {
+      *p++ = w & MASK8;
+      w >>= 8;
+      bits -= 8;
+    }
+  }
+}
+
+/* --- @mp_loadl@ --- *
+ *
+ * Arguments:	@mp *x@ = pointer to MP head
+ *		@const octet *p@ = pointer to octet array
+ *		@size_t sz@ = size of octet array
+ *
+ * Returns:	---
+ *
+ * Use:		Loads an MP in an octet array, least significant octet
+ *		first.
+ */
+
+void mp_loadl(mp *x, const octet *p, size_t sz)
+{
+  mpw *v;
+  mpw w = 0;
+  unsigned n;
+  const octet *q = p + sz;
+  unsigned bits = 0;
+
+  MP_BURN(x);
+  MP_ENSURE(x, ((sz * 8 + MPW_BITS - 1) * MPW_BITS) / MPW_BITS);
+  v = x->v;
+
+  while (p < q) {
+    n = *p++ & MASK8;
+    w |= n << bits;
+    bits += 8;
+    if (bits >= MPW_BITS) {
+      *v++ = w & MPW_MAX;
+      w = n >> (MPW_BITS - bits + 8);
+      bits -= MPW_BITS;
+    }
+  }
+  *v++ = w;
+  x->len = v - x->v;
+  x->f = 0;
+  MP_NORM(x);
+}
+
+/* --- @mp_storeb@ --- *
+ *
+ * Arguments:	@mp *x@ = pointer to MP head
+ *		@octet *p@ = pointer to octet array
+ *		@size_t sz@ = size of octet array
+ *
+ * Returns:	---
+ *
+ * Use:		Stores an MP in an octet array, most significant octet
+ *		first.  High-end octets are silently discarded if there
+ *		isn't enough space for them.
+ */
+
+void mp_storeb(mp *x, octet *p, size_t sz)
+{
+  mpw *v = x->v, *vl = x->v + x->len;
+  mpw n, w = 0;
+  octet *q = p + sz;
+  unsigned bits = 0;
+
+  while (q > p) {
+    if (bits < 8) {
+      n = (v >= vl) ? 0 : *v++;
+      *--q = (w | n << bits) & MASK8;
+      w = n >> (8 - bits);
+      bits += MPW_BITS - 8;
+    } else {
+      *--q= w & MASK8;
+      w >>= 8;
+      bits -= 8;
+    }
+  }
+}
+
+/* --- @mp_loadb@ --- *
+ *
+ * Arguments:	@mp *x@ = pointer to MP head
+ *		@const octet *p@ = pointer to octet array
+ *		@size_t sz@ = size of octet array
+ *
+ * Returns:	---
+ *
+ * Use:		Loads an MP in an octet array, most significant octet
+ *		first.
+ */
+
+void mp_loadb(mp *x, const octet *p, size_t sz)
+{
+  mpw *v;
+  mpw w = 0;
+  unsigned n;
+  const octet *q = p + sz;
+  unsigned bits = 0;
+
+  MP_BURN(x);
+  MP_ENSURE(x, ((sz * 8 + MPW_BITS - 1) * MPW_BITS) / MPW_BITS); 
+  v = x->v;
+
+  while (q > p) {
+    n = *--q & MASK8;
+    w |= n << bits;
+    bits += 8;
+    if (bits >= MPW_BITS) {
+      *v++ = w & MPW_MAX;
+      w = n >> (MPW_BITS - bits + 8);
+      bits -= MPW_BITS;
+    }
+  }
+  *v++ = w;
+  x->len = v - x->v;
+  x->f = 0;
+  MP_NORM(x);
+}
+
+/*----- Iterating through bits --------------------------------------------*/
+
+/* --- @mp_mkbitscan@ --- *
+ *
+ * Arguments:	@mp_bitscan *sc@ = pointer to bitscan object
+ *		@const mp *x@ = pointer to MP head
+ *
+ * Returns:	---
+ *
+ * Use:		Initializes a bitscan object.
+ */
+
+void mp_mkbitscan(mp_bitscan *sc, const mp *x)
+{
+  sc->x = x;
+  sc->bits = 0;
+  sc->i = 0;
+}
+
+/* --- @mp_bstep@ --- *
+ *
+ * Arguments:	@mp_bitscan *sc@ = pointer to bitscanner object
+ *
+ * Returns:	Nonzero if there is another bit to read.
+ *
+ * Use:		Steps on to the next bit, and tells the caller whether one
+ *		exists.
+ */
+
+int mp_bstep(mp_bitscan *sc)
+{
+  if (sc->bits) {
+    sc->w >>= 1;
+    sc->bits--;
+    return (1);
+  }
+  if (sc->i >= sc->x->len)
+    return (0);
+  sc->w = sc->x->v[sc->i++];
+  sc->bits = MPW_BITS - 1;
+  return (1);
+}
+
+/* --- @mp_bit@ --- *
+ *
+ * Arguments:	@const mp_bitscan *sc@ = pointer to bitscanner
+ *
+ * Returns:	Current bit value.
+ *
+ * Use:		Returns the value of the current bit.
+ */
+
+int mp_bit(const mp_bitscan *sc) { return (MP_BIT(sc)); }
+
+/*----- Shifting ----------------------------------------------------------*/
+
+/* --- @mp_lsl@ --- *
+ *
+ * Arguments:	@mp *d@ = pointer to MP head of destination
+ *		@const mp *x@ = pointer to MP head of source
+ *		@size_t n@ = number of bits to shift
+ *
+ * Returns:	---
+ *
+ * Use:		Shifts a number left by a given number of bit positions.
+ */
+
+void mp_lsl(mp *d, const mp *x, size_t n)
+{
+  size_t nw = n / MPW_BITS;
+  unsigned nb = n % MPW_BITS;
+  unsigned nr = MPW_BITS - nb;
+  size_t req;
+
+  /* --- Trivial special case --- */
+
+  if (n == 0 || mp_ucmp(x, MP_ZERO) == 0) {
+    mp_copy(d, x);
+    goto copied;
+  }
+
+  /* --- Decide on the about of memory needed in the destination --- */
+
+  req = x->len + nw;
+  if (nb && (x->v[x->len - 1] >> nr) != 0)
+    req++;
+  if (d != x)
+    MP_BURN(d);
+  MP_ENSURE(d, req);
+
+  /* --- Handle single bit shifting --- */
+
+  if (n == 1) {
+    mpw *v = d->v;
+    const mpw *vx = x->v;
+    const mpw *vl;
+    mpw w = 0;
+
+    vl = vx + x->len;
+    while (vx < vl) {
+      mpw t = *vx++;
+      *v++ = ((t << 1) | w) & MPW_MAX;
+      w = t >> (MPW_BITS - 1);
+    }
+    if (v < d->v + req)
+      *v++ = w & MPW_MAX;
+    goto done;
+  }
+
+  /* --- Handle shifts by a multiple of the word size --- *
+   *
+   * This would be easy, except that C is irritating.  Shifting an integer
+   * by an amount equal to the type's width yields undefined behaviour;
+   * in particular, under Intel it's a no-op.
+   */
+
+  if (!nb) {
+    mpw *v = d->v;
+    const mpw *vx = x->v;
+    memmove(v + nw, vx, (req - nw) * sizeof(mpw));
+    memset(v, 0, nw * sizeof(mpw));
+    goto done;
+  }
+
+  /* --- Now do the difficult version --- */
+
+  {
+    mpw *v = d->v + req;
+    const mpw *vx = x->v + x->len;
+    const mpw *vl;
+    mpw w = *--vx;
+
+    /* --- First shift the data over --- */
+
+    if (req > x->len + nw)
+      *--v = w >> nr;
+    vl = x->v;
+    while (vx > vl) {
+      mpw t = *--vx;
+      *--v = ((w << nb) | (t >> nr)) & MPW_MAX;
+      w = t;
+    }
+
+    /* --- Deal with tail-end data --- */
+
+    vl = d->v;
+    if (v > vl) {
+      *--v = w << nb;
+      while (v > vl)
+	*--v = 0;
+    }
+  }
+
+  /* --- Common end code --- */
+
+done:
+  d->len = req;
+  d->f = x->f;
+copied:
+  MP_NORM(d);
+}
+
+/* --- @mp_lsr@ --- *
+ *
+ * Arguments:	@mp *d@ = pointer to MP head of destination
+ *		@const mp *x@ = pointer to MP head of source
+ *		@size_t n@ = number of bits to shift
+ *
+ * Returns:	---
+ *
+ * Use:		Shifts a number right by a given number of bit positions.
+ */
+
+void mp_lsr(mp *d, const mp *x, size_t n)
+{
+  size_t nw = n / MPW_BITS;
+  unsigned nb = n % MPW_BITS;
+  unsigned nr = MPW_BITS - nb;
+  size_t req;
+
+  /* --- Trivial special case --- */
+
+  if (n == 0 || mp_ucmp(x, MP_ZERO) == 0) {
+    mp_copy(d, x);
+    goto copied;
+  }
+
+  /* --- Decide on the about of memory needed in the destination --- */
+
+  req = x->len - nw;
+  if ((x->v[x->len - 1] >> nb) == 0)
+    req--;
+  if (d != x)
+    MP_BURN(d);
+  MP_ENSURE(d, req);
+
+  /* --- Handle single bit shifting --- */
+
+  if (n == 1) {
+    mpw *v = d->v + req;
+    const mpw *vx = x->v + x->len;
+    const mpw *vl;
+    mpw w = *--vx;
+
+    if (req == x->len)
+      *--v = (w >> 1) & MPW_MAX;
+    vl = x->v;
+    while (vx > vl) {
+      mpw t = *--vx;
+      *--v = (w << (MPW_BITS - 1) | (t >> 1)) & MPW_MAX;
+      w = t;
+    }
+    goto done;
+  }
+
+  /* --- Handle shifts by a multiple of the word size --- *
+   *
+   * This would be easy, except that C is irritating.  Shifting an integer
+   * by an amount equal to the type's width yields undefined behaviour;
+   * in particular, under Intel it's a no-op.
+   */
+
+  if (!nb) {
+    mpw *v = d->v;
+    const mpw *vx = x->v;
+    memmove(v, vx + nw, (x->len - nw) * sizeof(mpw));
+    goto done;
+  }
+
+  /* --- Now do the difficult version --- */
+
+  {
+    mpw *v = d->v;
+    const mpw *vx = x->v + nw;
+    const mpw *vl;
+    mpw w = *vx++;
+
+    /* --- First shift the data over --- */
+
+    vl = x->v + x->len;
+    while (vx < vl) {
+      mpw t = *vx++;
+      *v++ = ((w >> nb) | (t << nr)) & MPW_MAX;
+      w = t;
+    }
+    if (req == x->len - nw) {
+      *v++ = (w >> nb) & MPW_MAX;
+    }
+  }
+
+  /* --- Common end code --- */
+
+done:
+  d->len = req;
+  d->f = x->f;
+copied:
+  MP_NORM(d);
+}
+
+/*----- Adding and subtracting --------------------------------------------*/
+
+/* --- @mp_uadd@ --- *
+ *
+ * Arguments:	@const mp *d@ = pointers to MP head of destination
+ *		@const mp *x, *y@ = pointers to MP heads of operands
+ *
+ * Returns:	---
+ *
+ * Use:		Performs unsigned MP addition.
+ */
+
+void mp_uadd(mp *d, const mp *x, const mp *y)
+{
+  mpd c;
+  mpw *v;
+  const mpw *vx, *vy;
+  const mpw *vxl, *vyl;
+
+  /* --- Some trivial initialization --- */
+
+  if (d != x && d != y)
+    MP_BURN(d);
+  MP_ENSURE(d, (x->len > y->len ? x->len : y->len) + 1);
+  vx = x->v; vxl = vx + x->len;
+  vy = y->v; vyl = vy + y->len;
+  v = d->v;
+  c = 0;
+
+  /* --- Start on the work --- */
+
+  while (vx < vxl || vy < vyl) {
+    if (vx < vxl) c += *vx++;
+    if (vy < vyl) c += *vy++;
+    *v++ = c & MPW_MAX;
+    c >>= MPW_BITS;
+  }
+  if (c)
+    *v++ = c & MPW_MAX;
+
+  /* --- Tidy up --- */
+
+  d->len = v - d->v;
+  d->f = 0;
+  if (x->f & MPF_BURN || y->f & MPF_BURN)
+    MP_PARANOID(d);
+  MP_NORM(d);
+} 
+
+/* --- @mp_usub@ --- *
+ *
+ * Arguments:	@const mp *d@ = pointers to MP head of destination
+ *		@const mp *x, *y@ = pointers to MP heads of operands
+ *
+ * Returns:	---
+ *
+ * Use:		Performs unsigned MP subtraction.
+ */
+
+void mp_usub(mp *d, const mp *x, const mp *y)
+{
+  mpd c;
+  mpw *v;
+  const mpw *vx, *vy;
+  const mpw *vxl, *vyl;
+
+  /* --- Some trivial initialization --- */
+
+  if (d != x && d != y)
+    MP_BURN(d);
+  MP_ENSURE(d, x->len);
+  vx = x->v; vxl = vx + x->len;
+  vy = y->v; vyl = vy + y->len;
+  v = d->v;
+  c = 0;
+
+  /* --- Start on the work --- */
+
+  while (vx < vxl) {
+    c += *vx++;
+    if (vy < vyl) c -= *vy++;
+    *v++ = c & MPW_MAX;
+    if (c & ~MPW_MAX)
+      c = ~0ul;
+    else
+      c = 0;
+  }
+
+  /* --- Tidy up --- */
+
+  d->len = v - d->v;
+  d->f = 0;
+  if (x->f & MPF_BURN || y->f & MPF_BURN)
+    MP_PARANOID(d);
+  MP_NORM(d);
+}
+
+/* --- @mp_ucmp@ --- *
+ *
+ * Arguments:	@const mp *x, *y@ = pointers to MP heads of operands
+ *
+ * Returns:	Less than, equal to, or greater than zero.
+ *
+ * Use:		Performs unsigned MP comparison.
+ */
+
+int mp_ucmp(const mp *x, const mp *y)
+{
+  int i;
+  mpw a, b;
+
+  /* --- Decide which to examine --- */
+
+  if (x->len > y->len)
+    i = x->len;
+  else
+    i = y->len;
+
+  /* --- Loop through the data --- */
+
+  while (i > 0) {
+    i--;
+    a = (i < x->len ? x->v[i] : 0);
+    b = (i < y->len ? y->v[i] : 0);
+    if (a < b)
+      return (-1);
+    else if (a > b)
+      return (1);
+  }
+
+  /* --- Finished --- */
+
+  return (0);    
+}
+
+/*----- Multiplying and dividing ------------------------------------------*/
+
+/* --- @mp_umul@ --- *
+ *
+ * Arguments:	@mp *d@ = pointer to MP head of destination
+ *		@const mp *x, *y@ = pointes to MP heads of operands
+ *
+ * Returns:	---
+ *
+ * Use:		Performs unsigned MP multiplication.
+ */
+
+void mp_umul(mp *d, const mp *x, const mp *y)
+{
+  mpd c, f;
+  mpw *v = 0, *vbase;
+  const mpw *vx, *vy;
+  const mpw *vxl, *vyl;
+
+  /* --- Check for special cases --- */
+
+  if (mp_ucmp(x, MP_ZERO) == 0 || mp_ucmp(y, MP_ZERO) == 0) {
+    mp_copy(d, MP_ZERO);
+    return;
+  }
+
+  /* --- Some trivial initialization --- */
+
+  MP_BURN(d);
+  MP_ENSURE(d, x->len + y->len);
+  vxl = x->v + x->len;
+  vyl = y->v + y->len;
+  vbase = d->v;
+  memset(v, 0, (x->len + y->len) * sizeof(mpw));
+
+  /* --- Main loop --- */
+
+  for (vx = x->v; vx < vxl; vx++) {
+    c = 0;
+    f = *vx;
+    v = vbase++;
+    for (vy = y->v; vy < vyl; vy++) {
+      c += *v + f * *vy;
+      *v++ = c & MPW_MAX;
+      c >>= MPW_BITS;
+    }
+    *v++ = c & MPW_MAX;
+  }
+
+  /* --- Tidying up --- */
+
+  d->len = v - d->v;
+  d->f = 0;
+  if (x->f & MPF_BURN || y->f & MPF_BURN)
+    MP_PARANOID(d);
+  MP_NORM(d);
+}
+
+/* --- @mp_udiv@ --- *
+ *
+ * Arguments:	@mp *q, *r@ = pointers to MP heads for quotient, remainder
+ *		@const mp *x, *y@ = pointers to MP heads for operands
+ *
+ * Returns:	---
+ *
+ * Use:		Performs unsigned MP division.
+ */
+
+void mp_udiv(mp *q, mp *r, const mp *x, const mp *y)
+{
+  size_t n = x->len, t = y->len;
+  const mpw *vy, *ovy;
+  mpw *vx, *vq, *vxl, *va, *vb;
+  mpw yt, ytt;
+  mpd yd;
+  mp nx = MP_INIT, ny = MP_INIT, nq = MP_INIT;
+  mp tt = MP_INIT;
+  unsigned int shift;
+
+  /* --- Fiddle with some pointers --- */
+
+  if (!r)
+    r = &nx;
+  if (!q)
+    q = &nq;
+
+  /* --- Find the top word in @y@ --- */
+
+  vy = y->v;
+  for (;;) {
+    if (!t)
+      THROW(EXC_FAIL, "mp_udiv detected divide-by-zero");
+    if ((yt = vy[t - 1]) != 0)
+      break;
+    t--;
+  }
+
+  /* --- Check for a zero dividend --- */
+
+  if (mp_ucmp(x, MP_ZERO)) {
+    if (q) mp_copy(q, MP_ZERO);
+    if (r) mp_copy(r, MP_ZERO);
+    return;
+  }
+
+  /* --- Test for some other trivial cases --- */
+
+  {
+    int i = mp_ucmp(x, y);
+    if (i < 0) {
+      if (r) mp_copy(r, x);
+      if (q) mp_copy(q, MP_ZERO);
+      return;
+    } else if (i == 0) {
+      if (r) mp_copy(r, MP_ZERO);
+      if (q) mp_copy(q, MP_ONE);
+      return;
+    }
+  }
+
+  /* --- Normalize the divisor --- *
+   *
+   * Cheat.  The original algorithm wants two-word values at least, so I just
+   * shift everything up by a word if necessary.
+   */
+
+  shift = 0;
+  while (yt < MPW_MAX / 2) {
+    yt <<= 1;
+    shift++;
+  }
+  if (t <= 1)
+    shift += MPW_BITS;
+  mp_lsl(r, x, shift);
+  mp_lsl(&ny, y, shift);
+
+  n = r->len;
+  t = ny.len;
+  ytt = ny.v[t - 2];
+  yd = ((mpd)yt << MPW_BITS) | (mpd)ytt;
+
+  /* --- Initialize the quotient --- */
+
+  MP_ENSURE(q, n - t + 1);
+  vq = q->v + n - t;
+  memset(vq, 0, (n - t) * sizeof(mpw));
+
+  /* --- Shift the divisor up to match the dividend --- */
+
+  mp_lsl(&ny, (n - t) * MPW_BITS);
+  ovy = ny.v;
+
+  /* --- Get the most significant quotient digit --- *
+   *
+   * Because of the normalization, this should only happen once.
+   */
+
+  while (mp_ucmp(x, y) >= 0) {
+    mp_usub(x, x, y);
+    (*vq)++;
+  }
+
+  /* --- Now do the main loop --- */
+
+  vx = x->v + n;
+  vxl = x->v + t;
+
+  while (vx > vxl) {
+    mpw xi, xii, xiii;
+    mpd qi;
+    mpd xd, yhi, ylo;
+
+    /* --- Fetch the top words from @x@ --- */
+
+    vx--;
+    xi = vx[0];
+    xii = vx[-1];
+    xiii = vx[-2];
+    xd = ((mpd)xi << MPW_BITS) | (mpd)xii;
+
+    /* --- Get an approximation for @qi@ --- */
+
+    if (xi == yt)
+      qi = MPW_MAX;
+    else
+      qi = xd / yt;
+
+    /* --- Work out how close the approximation is --- *
+     *
+     * This is more than a little ugly.
+     */
+
+    yhi = (mpd)yt * qi;
+    ylo = (mpd)ytt * qi;
+    yhi += ylo >> MPW_BITS;
+    ylo &= MPW_MAX;
+
+    /* --- Now fix the approximation --- *
+     *
+     * Again, the normalization helps; this is never done more than twice.
+     */
+
+    while (!(yhi <= xd && ylo <= xiii)) {
+      qi--;
+      yhi -= yt;
+      if (ylo < ytt)
+	yhi--;
+      ylo = (ylo - ytt) & MPW_MAX;
+    }
+
+    /* --- Subtract off a goodly big chunk --- */
+
+    
+}
+
+/*----- Test rig ----------------------------------------------------------*/
+
+#ifdef TEST_RIG
+
+#include <stdio.h>
+#include "dstr.h"
+#include "testrig.h"
+
+/* --- Loading and storing numbers --- *
+ *
+ * The most reliable way I can think of for doing this is to convert both
+ * numbers (the MP and the original hexgorp) into binary text, and compare.
+ * This ought to be reliable, and it's sufficiently different from what the
+ * actual load and store routines are doing not to have any common bugs.
+ */
+
+static void mpbindump(const mp *x, char *p)
+{
+  mp_bitscan sc;
+  for (mp_mkbitscan(&sc, x); mp_bstep(&sc); )
+    *p++ = '0' + MP_BIT(&sc);
+  *p++ = 0;
+}
+
+static void bindumpl(const octet *q, size_t sz, char *p)
+{
+  unsigned w;
+  int bits = 0;
+
+  while (sz) {
+    w = *q++;
+    for (bits = 8; bits > 0; bits--) {
+      *p++ = '0' + (w & 1);
+      w >>= 1;
+    }
+    sz--;
+  }
+  *p++ = 0;
+}
+
+static void bindumpb(const octet *q, size_t sz, char *p)
+{
+  unsigned w;
+  int bits = 0;
+
+  q += sz;
+  while (sz) {
+    w = *--q;
+    for (bits = 8; bits > 0; bits--) {
+      *p++ = '0' + (w & 1);
+      w >>= 1;
+    }
+    sz--;
+  }
+  *p++ = 0;
+}
+
+static int bincmp(const char *p, const char *q)
+{
+  for (;;) {
+    if (!*p && !*q)
+      return (0);
+    else if (!*p && *q == '0')
+      q++;
+    else if (*p == '0' && !*q)
+      p++;
+    else if (*p == *q)
+      p++, q++;
+    else
+      return (-1);
+  }
+}
+
+static int lscheck(const char *reason, int w, dstr *d, mp *x,
+		   const char *bufa, const char *bufb)
+{
+  if (bincmp(bufa, bufb)) {
+    printf("\nmismatch in %s (width = %i):\n"
+	     "\tvalue = ", reason, w);
+    type_hex.dump(d, stdout);
+    printf("\n\tmp = ");
+    mp_dump(x, stdout);
+    printf("\n\texpected   = %s\n"
+	     "\tcalculated = %s\n",
+	   bufb, bufa);
+    return (1);
+  }
+  return (0);
+} 
+
+static int loadstore(dstr *d)
+{
+  octet *p = (octet *)d[0].buf;
+  size_t sz = d[0].len;
+  char bufa[1024], bufb[1024];
+  octet bufc[64];
+  mp x;
+  int i;
+  int ok = 1;
+
+  mp_wmax = 0x7ful;
+  for (i = 8; ok && i <= 32; i++) {
+    mpw_bits = i;
+    mpw_max = (mp_wmax << 1) + 1;
+
+    mp_create(&x); mp_loadl(&x, p, sz);
+    mpbindump(&x, bufa); bindumpl(p, sz, bufb);
+    if (lscheck("mp_loadl", i, d, &x, bufa, bufb)) ok = 0;
+    mp_storeb(&x, bufc, sizeof(bufc));
+    bindumpb(bufc, sizeof(bufc), bufa);
+    if (lscheck("mp_storeb", i, d, &x, bufa, bufb)) ok = 0;
+    mp_destroy(&x);
+
+    mp_create(&x); mp_loadb(&x, p, sz);
+    mpbindump(&x, bufa); bindumpb(p, sz, bufb);
+    if (lscheck("mp_loadb", i, d, &x, bufa, bufb)) ok = 0;
+    mp_storel(&x, bufc, sizeof(bufc));
+    bindumpl(bufc, sizeof(bufc), bufa);
+    if (lscheck("mp_storel", i, d, &x, bufa, bufb)) ok = 0;
+    mp_destroy(&x);
+  }
+
+  mpw_max = 0xffff;
+  mpw_bits = 16;
+  return (ok);
+}
+
+/* --- Comparison test --- */
+
+static int compare(dstr *d)
+{
+  mp x, y;
+  int r, s;
+  int ok = 1;
+
+  mp_create(&x); mp_create(&y);
+  mp_loadb(&x, (octet *)d[0].buf, d[0].len);
+  mp_loadb(&y, (octet *)d[1].buf, d[1].len);
+  r = *(int *)d[2].buf;
+  s = mp_ucmp(&x, &y);
+
+  if (r != s) {
+    ok = 0;
+    printf("\nfailed compare:"
+	   "\n\tx = ");
+    type_hex.dump(&d[0], stdout);
+    printf("\n\ty = ");
+    type_hex.dump(&d[1], stdout);
+    printf("\n\texpected %i; found %i\n", r, s);
+  }
+
+  return (ok);
+}
+
+/* --- Addition and subtraction test --- */
+
+static int addsub(dstr *d)
+{
+  mp t, x, y, z;
+  int ok = 1;
+
+  mp_create(&t); mp_create(&x); mp_create(&y); mp_create(&z);
+  mp_loadb(&x, (octet *)d[0].buf, d[0].len);
+  mp_loadb(&y, (octet *)d[1].buf, d[1].len);
+  mp_loadb(&z, (octet *)d[2].buf, d[2].len);
+
+  mp_uadd(&t, &x, &y);
+  if (mp_ucmp(&t, &z)) {
+    ok = 0;
+    printf("\nfailed add:");
+    printf("\n\tx = "); type_hex.dump(&d[0], stdout);
+    printf("\n\ty = "); type_hex.dump(&d[1], stdout);
+    printf("\n\tz = "); type_hex.dump(&d[2], stdout);
+    fputc('\n', stdout);
+  }
+
+  mp_usub(&t, &z, &x);
+  if (mp_ucmp(&t, &y)) {
+    ok = 0;
+    printf("\nfailed subtract:");
+    printf("\n\tz = "); type_hex.dump(&d[2], stdout);
+    printf("\n\tx = "); type_hex.dump(&d[0], stdout);
+    printf("\n\ty = "); type_hex.dump(&d[1], stdout);
+    fputc('\n', stdout);
+  }
+
+  return (ok);
+}
+
+/* --- Shifting --- */
+
+static int shift(dstr *d)
+{
+  char bufa[1024], bufb[1024];
+  mp x, y;
+  int n = *(int *)d[1].buf;
+  char *p;
+  int i;
+  int ok = 1;
+
+  mp_create(&x);
+  mp_create(&y);
+
+  mp_loadb(&x, (octet *)d[0].buf, d[0].len);
+  p = bufa;
+  for (i = 0; i < n; i++)
+    *p++ = '0';
+  mpbindump(&x, p);
+
+  mp_lsl(&y, &x, n);
+  mpbindump(&y, bufb);
+  if (lscheck("lsl", n, d, &x, bufb, bufa))
+    ok = 0;
+
+  mp_lsr(&y, &x, n);
+  mpbindump(&y, bufb);
+  if (lscheck("lsr", n, d, &x, bufb, bufa + 2 * n))
+    ok = 0;
+
+  return (ok);
+}
+
+/* --- Test driver stub --- */
+
+static test_chunk mp__defs[] = {
+  { "mp-loadstore", loadstore, { &type_hex, 0 } },
+  { "mp-cmp", compare, { &type_hex, &type_hex, &type_int, 0 } },
+  { "mp-addsub", addsub, { &type_hex, &type_hex, &type_hex, 0 } },
+  { "mp-shift", shift, { &type_hex, &type_int, 0 } },
+  { 0, 0, { 0 } }
+};
+
+int main(int argc, char *argv[])
+{
+  test_run(argc, argv, mp__defs, SRCDIR"/tests/mp");
+  return (0);
+}
+
+#endif
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#endif
diff --git a/mp.h b/mp.h
new file mode 100644
index 0000000..fcccee2
--- /dev/null
+++ b/mp.h
@@ -0,0 +1,449 @@
+/* -*-c-*-
+ *
+ * $Id: mp.h,v 1.1 1999/09/03 08:41:12 mdw Exp $
+ *
+ * Multiprecision arithmetic
+ *
+ * (c) 1999 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: mp.h,v $
+ * Revision 1.1  1999/09/03 08:41:12  mdw
+ * Initial import.
+ *
+ */
+
+#ifndef MP_H
+#define MP_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef MPTYPES_H
+#  include "mptypes.h"
+#endif
+
+#ifndef MPX_H
+#  include "mpx.h"
+#endif
+
+#ifndef MPSCAN_H
+#  include "mpscan.h"
+#endif
+
+/*----- Data structures ---------------------------------------------------*/
+
+typedef struct mp {
+  mpw *v;				/* Vector of words */
+  unsigned f;				/* Various flags */
+  size_t sz;				/* Size allocated to word vector */
+  size_t len;				/* Length of current word vector */
+} mp;
+
+enum {
+  MPF_SIGN = 1,				/* Sign bit */
+  MPF_BURN = 2				/* Burn word vector after use */
+};
+
+typedef struct mp_bitscan {
+  const mp *x;				/* Pointer to target MP */
+  size_t i;				/* Index into MP vector */
+  mpw w;				/* Current value being examined */
+  unsigned bits;			/* Number of bits left in @w@ */
+} mp_bitscan;
+
+/*----- External variables ------------------------------------------------*/
+
+extern mp mp_std;
+
+#define MP_ZERO (mp_std + 0)
+#define MP_ONE (mp_std + 1)
+#define MP_TWO (mp_std + 2)
+#define MP_THREE (mp_std + 3)
+#define MP_FOUR (mp_std + 4)
+#define MP_FIVE (mp_std + 5)
+#define MP_TEN (mp_std + 6)
+#define MP_MONE (mp_std + 7)
+
+/*----- Memory allocation and low-level fiddling --------------------------*/
+
+/* --- @mp_create@ --- *
+ *
+ * Arguments	@mp *x@ = pointer to MP head
+ *
+ * Returns:	---
+ *
+ * Use:		Initializes an MP ready for use.  The initial value is zero.
+ */
+
+#define MP_INIT { 0, 0, 0, 0 }
+
+extern void mp_create(mp */*x*/);
+
+/* --- @MP_BURN@ --- *
+ *
+ * Arguments:	@x@ = pointer to MP head
+ *
+ * Use:		Burns the contents of the MP, if it contains sensitive data.
+ */
+
+#define MP_BURN(x) do {							\
+  mp *_y = (x)								\
+  if (_y->v && _y->f & mpf_burn) {					\
+    memset(_y->v, 0, _y->sz * sizeof(mpw));				\
+    _y->f &= ~MPF_BURN;							\
+  }									\
+} while (0)
+
+/* --- @mp_free@ --- *
+ *
+ * Arguments:	@mp *x@ = pointer to MP head
+ *
+ * Returns:	---
+ *
+ * Use:		Releases the memory used by an MP.
+ */
+
+#define MP_DESTROY(x) do {						\
+  mp *_x = (x);								\
+  MP_BURN(_x);								\
+  if (_x->v)								\
+    free(_x->v);							\
+  _x->v = 0;								\
+  _x->f = 0;								\
+  _x->sz = 0;								\
+  _x->len = 0;								\
+} while (0)  
+		   
+extern void mp_free(mp */*x*/);
+
+/* --- @MP_ENSURE@ --- *
+ *
+ * Arguments:	@x@ = pointer to MP head
+ *		@len@ = length required (in words)
+ *
+ * Use:		Ensures that the MP has enough memory to store a @len@-word
+ *		value.
+ */
+
+#define MP_ENSURE(x, len) do {						\
+  mp *_x = (x);								\
+  size_t _len = (len);							\
+  if (_x->sz < _len)							\
+    mp_resize(_x, _len);						\
+} while (0)
+
+/* --- @mp_resize@ --- *
+ *
+ * Arguments:	@mp *x@ = pointer to MP head
+ *		@size_t sz@ = size required (in words)
+ *
+ * Returns:	---
+ *
+ * Use:		Resizes the MP so that its word vector has space for
+ *		exactly @sz@ words.
+ */
+
+extern void mp_resize(mp */*x*/, size_t /*sz*/);
+
+/* --- @mp_norm@ --- *
+ *
+ * Arguments:	@mp *x@ = pointer to MP head
+ *
+ * Returns:	---
+ *
+ * Use:		`Normalizes' an MP.  Fixes the @len@ field so that it's
+ *		correct.  Assumes that @len@ is either already correct or
+ *		too large.
+ */
+
+#define MP_NORM(x) do {							\
+  mp *_y = (x);								\
+  MPX_LEN(_y->len, _y->v, _y->len);					\
+} while (0)
+
+extern void mp_norm(mp */*x*/);
+
+/* --- @mp_dump@ --- *
+ *
+ * Arguments:	@mp *x@ = pointer to MP head
+ *		@FILE *fp@ = pointer to stream to write on
+ *
+ * Returns:	---
+ *
+ * Use:		Dumps an MP to a stream.
+ */
+
+extern void mp_dump(mp */*x*/, FILE */*fp*/);
+
+/* --- @MP_PARANOID@ --- *
+ *
+ * Arguments:	@x@ = pointer to MP head
+ *
+ * Use:		Marks the MP as containing sensitive data which should be
+ *		burnt when no longer required.
+ */
+
+#define MP_PARANOID(x) ((x)->f |= MPF_BURN)
+
+/* --- @mp_copy@ --- *
+ *
+ * Arguments:	@mp *d@ = pointer to MP head for destination
+ *		@const mp *s@ = pointer to MP head for source
+ *
+ * Returns:	---
+ *
+ * Use:		Copies an MP.
+ */
+
+extern void mp_copy(mp */*d*/, const mp */*s*/);
+
+/* --- @mp_bits@ --- *
+ *
+ * Arguments:	@mp *x@ = pointer to MP head
+ *
+ * Returns:	Length of the number in bits.
+ *
+ * Use:		Calculates the number of bits required to represent a number.
+ *		The number must be normalized.
+ */
+
+unsigned long mp_bits(mp */*x*/);
+
+/* --- @mp_octets@ --- *
+ *
+ * Arguments:	@mp *x@ = pointer to MP head
+ *
+ * Returns:	Length of number in octets.
+ *
+ * Use:		Calculates the number of octets required to represent a
+ *		number.  The number must be normalized.
+ */
+
+extern size_t mp_octets(mp */*x*/);
+
+/*----- Loading and storing as binary data --------------------------------*/
+
+/* --- @mp_storel@ --- *
+ *
+ * Arguments:	@mp *x@ = pointer to MP head
+ *		@octet *p@ = pointer to octet array
+ *		@size_t sz@ = size of octet array
+ *
+ * Returns:	---
+ *
+ * Use:		Stores an MP in an octet array, least significant octet
+ *		first.  High-end octets are silently discarded if there
+ *		isn't enough space for them.
+ */
+
+extern void mp_storel(mp */*x*/, octet */*p*/, size_t /*sz*/);
+
+/* --- @mp_loadl@ --- *
+ *
+ * Arguments:	@mp *x@ = pointer to MP head
+ *		@const octet *p@ = pointer to octet array
+ *		@size_t sz@ = size of octet array
+ *
+ * Returns:	---
+ *
+ * Use:		Loads an MP in an octet array, least significant octet
+ *		first.
+ */
+
+extern void mp_loadl(mp */*x*/, const octet */*p*/, size_t /*sz*/);
+
+/* --- @mp_storeb@ --- *
+ *
+ * Arguments:	@mp *x@ = pointer to MP head
+ *		@octet *p@ = pointer to octet array
+ *		@size_t sz@ = size of octet array
+ *
+ * Returns:	---
+ *
+ * Use:		Stores an MP in an octet array, most significant octet
+ *		first.  High-end octets are silently discarded if there
+ *		isn't enough space for them.
+ */
+
+extern void mp_storeb(mp */*x*/, octet */*p*/, size_t /*sz*/);
+
+/* --- @mp_loadb@ --- *
+ *
+ * Arguments:	@mp *x@ = pointer to MP head
+ *		@const octet *p@ = pointer to octet array
+ *		@size_t sz@ = size of octet array
+ *
+ * Returns:	---
+ *
+ * Use:		Loads an MP in an octet array, most significant octet
+ *		first.
+ */
+
+extern void mp_loadb(mp */*x*/, const octet */*p*/, size_t /*sz*/);
+
+/*----- Iterating through bits --------------------------------------------*/
+
+/* --- @mp_mkbitscan@ --- *
+ *
+ * Arguments:	@mp_bitscan *sc@ = pointer to bitscan object
+ *		@const mp *x@ = pointer to MP head
+ *
+ * Returns:	---
+ *
+ * Use:		Initializes a bitscan object.
+ */
+
+extern void mp_mkbitscan(mp_bitscan */*sc*/, const mp */*x*/);
+
+/* --- @mp_bstep@ --- *
+ *
+ * Arguments:	@mp_bitscan *sc@ = pointer to bitscanner object
+ *
+ * Returns:	Nonzero if there is another bit to read.
+ *
+ * Use:		Steps on to the next bit, and tells the caller whether one
+ *		exists.
+ */
+
+extern int mp_bstep(mp_bitscan */*sc*/);
+
+/* --- @mp_bit@ --- *
+ *
+ * Arguments:	@const mp_bitscan *sc@ = pointer to bitscanner
+ *
+ * Returns:	Current bit value.
+ *
+ * Use:		Returns the value of the current bit.
+ */
+
+#define MP_BIT(sc) ((sc)->w & 1)
+
+extern int mp_bit(const mp_bitscan */*sc*/);
+
+/*----- Shifting ----------------------------------------------------------*/
+
+/* --- @mp_lsl@ --- *
+ *
+ * Arguments:	@mp *d@ = pointer to MP head of destination
+ *		@const mp *x@ = pointer to MP head of source
+ *		@size_t n@ = number of bits to shift
+ *
+ * Returns:	---
+ *
+ * Use:		Shifts a number left by a given number of bit positions.
+ */
+
+extern void mp_lsl(mp */*d*/, const mp */*x*/, size_t /*n*/);
+
+/* --- @mp_lsr@ --- *
+ *
+ * Arguments:	@mp *d@ = pointer to MP head of destination
+ *		@const mp *x@ = pointer to MP head of source
+ *		@size_t n@ = number of bits to shift
+ *
+ * Returns:	---
+ *
+ * Use:		Shifts a number right by a given number of bit positions.
+ */
+
+extern void mp_lsr(mp */*d*/, const mp */*x*/, size_t /*n*/);
+
+/*----- Unsigned arithmetic -----------------------------------------------*/
+
+/* --- @mp_uadd@ --- *
+ *
+ * Arguments:	@const mp *d@ = pointers to MP head of destination
+ *		@const mp *x, *y@ = pointers to MP heads of operands
+ *
+ * Returns:	---
+ *
+ * Use:		Performs unsigned MP addition.
+ */
+
+extern void mp_uadd(mp */*d*/, const mp */*x*/, const mp */*y*/);
+
+/* --- @mp_usub@ --- *
+ *
+ * Arguments:	@const mp *d@ = pointers to MP head of destination
+ *		@const mp *x, *y@ = pointers to MP heads of operands
+ *
+ * Returns:	---
+ *
+ * Use:		Performs unsigned MP subtraction.
+ */
+
+extern void mp_usub(mp */*d*/, const mp */*x*/, const mp */*y*/);
+
+/* --- @mp_ucmp@ --- *
+ *
+ * Arguments:	@const mp *x, *y@ = pointers to MP heads of operands
+ *
+ * Returns:	Less than, equal to, or greater than zero.
+ *
+ * Use:		Performs unsigned MP comparison.
+ */
+
+#define MP_UCMP(x, op, y) (mp_ucmp((x), (y)) op 0)
+
+extern int mp_ucmp(const mp */*x*/, const mp */*y*/);
+
+/* --- @mp_umul@ --- *
+ *
+ * Arguments:	@mp *d@ = pointer to MP head of destination
+ *		@const mp *x, *y@ = pointes to MP heads of operands
+ *
+ * Returns:	---
+ *
+ * Use:		Performs unsigned MP multiplication.
+ */
+
+extern void mp_umul(mp */*d*/, const mp */*x*/, const mp */*y*/);
+
+/* --- @mp_udiv@ --- *
+ *
+ * Arguments:	@mp *q, *r@ = pointers to MP heads for quotient, remainder
+ *		@const mp *x, *y@ = pointers to MP heads for operands
+ *
+ * Returns:	---
+ *
+ * Use:		Performs unsigned MP division.
+ */
+
+extern void mp_udiv(mp */*q*/, mp */*r*/, const mp */*x*/, const mp */*y*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/mpscan.c b/mpscan.c
new file mode 100644
index 0000000..a49eddf
--- /dev/null
+++ b/mpscan.c
@@ -0,0 +1,89 @@
+/* -*-c-*-
+ *
+ * $Id: mpscan.c,v 1.1 1999/09/03 08:41:12 mdw Exp $
+ *
+ * Sequential bit scan of multiprecision integers
+ *
+ * (c) 1999 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: mpscan.c,v $
+ * Revision 1.1  1999/09/03 08:41:12  mdw
+ * Initial import.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+/*----- Data structures ---------------------------------------------------*/
+
+/*----- Static variables --------------------------------------------------*/
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @mpscan_initx@ --- *
+ *
+ * Arguments:	@mpscan *m@ = pointer to bitscanner structure
+ *		@const mpw *v@ = vector of words to scan
+ *		@size_t len@ = length of vector in words
+ *
+ * Returns:	---
+ *
+ * Use:		Initializes a bitscanner from a low-level vector-and-length
+ *		representation of an integer.  Initially no bit is ready; you
+ *		must call @mpscan_step@ before anything useful will come
+ *		out.
+ */
+
+void mpscan_initx(mpscan *m, const mpw *v, size_t len)
+{
+  MPSCAN_INITX(m, v, len);
+}
+
+/* --- @mpscan_step@ --- *
+ *
+ * Arguments:	@mpscan *m@ = pointer to bitscanner
+ *
+ * Returns:	Nonzero if there is another bit to read.
+ *
+ * Use:		Steps on to the next bit in the integer.  The macro version
+ *		evaluates its argument multiple times.
+ */
+
+int mpscan_step(mpscan *m) { return (MPSCAN_STEP(m)); }
+
+/* --- @mpscan_bit@ --- *
+ *
+ * Arguments:	@const mpscan *m@ = pointer to bitscanner
+ *
+ * Returns:	The value of the current bit.
+ *
+ * Use:		Reads the value of the current bit looked at by a
+ *		bitscanner.
+ */
+
+int mpscan_bit(const mpscan *m) { return (MPSCAN_BIT(m)); }
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/mpscan.h b/mpscan.h
new file mode 100644
index 0000000..6ec09cc
--- /dev/null
+++ b/mpscan.h
@@ -0,0 +1,125 @@
+/* -*-c-*-
+ *
+ * $Id: mpscan.h,v 1.1 1999/09/03 08:41:12 mdw Exp $
+ *
+ * Sequential bit scan of multiprecision integers
+ *
+ * (c) 1999 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: mpscan.h,v $
+ * Revision 1.1  1999/09/03 08:41:12  mdw
+ * Initial import.
+ *
+ */
+
+#ifndef MPSCAN_H
+#define MPSCAN_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#ifndef MPTYPES_H
+#  include "mptypes.h"
+#endif
+
+/*----- Data structures ---------------------------------------------------*/
+
+typedef struct mpscan {
+  const mpw *v;				/* Vector of words to scan */
+  mpw w;				/* Current word to scan */
+  int bits;				/* Number of bits left in @w@ */
+  size_t len;				/* Length of the vector in words */
+} mpscan;
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @mpscan_initx@ --- *
+ *
+ * Arguments:	@mpscan *m@ = pointer to bitscanner structure
+ *		@const mpw *v@ = vector of words to scan
+ *		@size_t len@ = length of vector in words
+ *
+ * Returns:	---
+ *
+ * Use:		Initializes a bitscanner from a low-level vector-and-length
+ *		representation of an integer.  Initially no bit is ready; you
+ *		must call @mpscan_step@ before anything useful will come
+ *		out.
+ */
+
+#define MPSCAN_INITX(m_, v_, len_) do {					\
+  mpscan *_m = (m_);							\
+  _m->v = (v_);								\
+  _m->len = (len_);							\
+  _m->bits = 0;								\
+} while (0)
+
+extern void mpscan_initx(mpscan */*m*/, const mpw */*v*/, size_t /*len*/);
+
+/* --- @mpscan_step@ --- *
+ *
+ * Arguments:	@mpscan *m@ = pointer to bitscanner
+ *
+ * Returns:	Nonzero if there is another bit to read.
+ *
+ * Use:		Steps on to the next bit in the integer.  The macro version
+ *		evaluates its argument multiple times.
+ */
+
+#define MPSCAN_STEP(m)							\
+  ((m)->bits ? ((m)->w >>= 1,						\
+		(m)->bits--, 1) :					\
+   (m)->len ? ((m)->len--,						\
+	       (m)->w = *(m)->v++,					\
+	       (m)->bits = MP_WBITS - 1, 1) :				\
+   0)
+
+extern int mpscan_step(mpscan */*m*/);
+
+/* --- @mpscan_bit@ --- *
+ *
+ * Arguments:	@const mpscan *m@ = pointer to bitscanner
+ *
+ * Returns:	The value of the current bit.
+ *
+ * Use:		Reads the value of the current bit looked at by a
+ *		bitscanner.
+ */
+
+#define MPSCAN_BIT(m) ((m)->w & 1)
+
+extern int mpscan_bit(const mpscan */*m*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/mptypes.h b/mptypes.h
new file mode 100644
index 0000000..8513b6e
--- /dev/null
+++ b/mptypes.h
@@ -0,0 +1,206 @@
+/* -*-c-*-
+ *
+ * $Id: mptypes.h,v 1.1 1999/09/03 08:41:12 mdw Exp $
+ *
+ * Types for multiprecision arithmetic
+ *
+ * (c) 1999 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: mptypes.h,v $
+ * Revision 1.1  1999/09/03 08:41:12  mdw
+ * Initial import.
+ *
+ */
+
+#ifndef MPTYPES_H
+#define MPTYPES_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <limits.h>
+#include <stddef.h>
+
+#include <mLib/bits.h>
+
+/*----- Important types ---------------------------------------------------*/
+
+/* --- Choose word and doubleword types --- *
+ *
+ * The types I need, and their properties, are as follows:
+ *
+ *   * @mpw@ is the radix in which all the arithmetic is actually performed.
+ *     It must be an unsigned type that is efficient on the host's hardware.
+ *     @MPW_MAX@ is the largest value of type @mpw@ I'll use.  (Although
+ *     there may be bigger values available, I'll not use them.)  @MPW_BITS@
+ *     is the number of bits required to represent @MPW_MAX@.
+ *
+ *   * @mpd@ must be twice as large as @mpw@: I must be able to multiply any
+ *     two @mpw@ values and end up with an @mpd@ as the result.  @MPD_MAX@ is
+ *     the largest value of type @mpd@ I'll use, and @MPD_BITS@ is the number
+ *     of bits required to represent it.
+ *
+ * The algorithm used to choose these types is:
+ *
+ *   * Try to use @unsigned int@ as @mpw@ and @unsigned long@ as @mpd@.  If
+ *     @unsigned long@ looks to small to do this, try to use @unsigned long
+ *     long@, if it exists.
+ *
+ *   * If that's not good enough, then fall back to @unsigned short@ as @mpw@
+ *     and choose one of @unsigned int@, @unsigned long@ or @unsigned long
+ *     long@ (in decreasing order of preference) as @mpd@.
+ *
+ *   * If that doesn't work either, choose @unsigned int@ as both, and use
+ *     half its width only as @mpw@.
+ */
+
+#ifndef MP_TEST
+
+/* --- Hack for GCC until it catches up with C9X --- *
+ *
+ * GCC uses bogus names for the @long long@ minimum and maximum values in its
+ * <limits.h> file.  This little hack makes the proper portable C9X names
+ * work as well.
+ */
+
+#if defined(ULONG_LONG_MAX) && !defined(ULLONG_MAX)
+#  define LLONG_MIN LONG_LONG_MIN
+#  define LLONG_MAX LONG_LONG_MAX
+#  define ULLONG_MAX ULONG_LONG_MAX
+#endif
+
+/* --- Decide on the actual types to use --- *
+ *
+ * The division trick here makes sure that one type is twice another without
+ * the risk of overflowing the arithmetic provided by the preprocessor.
+ */
+
+#if ULONG_MAX / UINT_MAX >= UINT_MAX
+   typedef unsigned int mpw;
+   typedef unsigned long mpd;
+#  define MPW_MAX UINT_MAX
+#elif ULLONG_MAX / UINT_MAX >= UINT_MAX
+   typedef unsigned int mpw;
+   typedef unsigned long long mpd;
+#  define MPW_MAX UINT_MAX
+#elif UINT_MAX / USHRT_MAX >= USHRT_MAX
+   typedef unsigned short mpw;
+   typedef unsigned int mpd;
+#  define MPW_MAX USHRT_MAX
+#elif ULONG_MAX / USHRT_MAX >= USHRT_MAX
+   typedef unsigned short mpw;
+   typedef unsigned long mpd;
+#  define MPW_MAX USHRT_MAX
+#elif ULLONG_MAX / USHRT_MAX >= USHRT_MAX
+   typedef unsigned short mpw;
+   typedef unsigned long long mpd;
+#  define MPW_MAX USHRT_MAX
+#else
+   typedef unsigned int mpw;
+   typedef unsigned int mpd;
+#  define MPD_MAX UINT_MAX
+#endif
+
+/* --- Fix a value for @MPD_MAX@ --- *
+ *
+ * This will then be the target for finding the bit widths and things.
+ */
+
+#ifdef MPW_MAX
+#  define MPD_MAX ((MPW_MAX + 1) * MPW_MAX + MW_WMAX)
+#endif
+
+/* --- Find the width of @MPD_MAX@ in bits --- *
+ *
+ * It must be at least 16 bits wide, because the smallest type I bother to
+ * try is @unsigned short@.  I only bother testing for up to 64 bits, and in
+ * power-of-two chunks, because I don't care about efficiency on more bizarre
+ * systems.
+ */
+
+#if MPD_MAX <= 0xfffffffff
+#  if MPD_MAX == 0xffffffff
+#    define MPD_BITS 32
+#  elif MPD_MAX >= 0x0fffffff
+#    define MPD_BITS 28
+#  elif MPD_MAX >= 0x00ffffff
+#    define MPD_BITS 24
+#  elif MPD_MAX >= 0x000fffff
+#    define MPD_BITS 20
+#  elif MPD_MAX >= 0x0000ffff
+#    define MPD_BITS 16
+#  else
+#    error "Abject failure deciding on type `mpw'"
+#else
+#  if MPD_MAX / 0xffffffff < 0xffffffff
+#    define MPD_BITS 32
+#  else
+#    define MPD_BITS 64			/* Slightly dodgy */
+#endif
+
+/* --- Now sort out the other magical values --- */
+
+#undef MPD_MAX
+#undef MPW_MAX
+
+#define MPW_BITS (MPD_BITS / 2)
+#define MPD_MAX (((1 << (MPD_BITS - 1)) - 1) * 2 + 1)
+#define MPW_MAX ((1 << MPW_BITS) - 1)
+
+#endif
+
+/*----- Macros for coercion and masking -----------------------------------*/
+
+/* --- @MPW@ --- *
+ *
+ * Arguments:	@x@ = an unsigned value
+ *
+ * Use:		Expands to the value of @x@ masked and typecast to a
+ *		multiprecision integer word.
+ */
+
+#define MPW(x) ((mpw)((x) & MPW_MAX))
+
+/* --- @MPWS@ --- *
+ *
+ * Arguments:	@n@ = number of words
+ *
+ * Use:		Expands to the number of bytes occupied by a given number of
+ *		words.
+ */
+
+#define MPWS(n) ((n) * sizeof(mpw))
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/mpx.c b/mpx.c
new file mode 100644
index 0000000..7ac5a8b
--- /dev/null
+++ b/mpx.c
@@ -0,0 +1,644 @@
+/* -*-c-*-
+ *
+ * $Id: mpx.c,v 1.1 1999/09/03 08:41:12 mdw Exp $
+ *
+ * Low-level multiprecision arithmetic
+ *
+ * (c) 1999 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: mpx.c,v $
+ * Revision 1.1  1999/09/03 08:41:12  mdw
+ * Initial import.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <mLib/bits.h>
+
+#include "mptypes.h"
+#include "mpx.h"
+
+/*----- Loading and storing -----------------------------------------------*/
+
+/* --- @mpx_storel@ --- *
+ *
+ * Arguments:	@const mpw *v, *vl@ = base and limit of source vector
+ *		@octet *p@ = pointer to octet array
+ *		@size_t sz@ = size of octet array
+ *
+ * Returns:	---
+ *
+ * Use:		Stores an MP in an octet array, least significant octet
+ *		first.  High-end octets are silently discarded if there
+ *		isn't enough space for them.
+ */
+
+void mpx_storel(const mpw *v, const mpw *vl, octet *p, size_t sz)
+{
+  mpw n, w = 0;
+  octet *q = p + sz;
+  unsigned bits = 0;
+
+  while (p < q) {
+    if (bits < 8) {
+      if (v >= vl) {
+	*p++ = U8(w);
+	break;
+      }
+      n = *v++;
+      *p++ = U8(w | n << bits);
+      w = n >> (8 - bits);
+      bits += MPW_BITS - 8;
+    } else {
+      *p++ = U8(w);
+      w >>= 8;
+      bits -= 8;
+    }
+  }
+  memset(p, 0, q - p);
+}
+
+/* --- @mpx_loadl@ --- *
+ *
+ * Arguments:	@mpw *v, *vl@ = base and limit of destination vector
+ *		@const octet *p@ = pointer to octet array
+ *		@size_t sz@ = size of octet array
+ *
+ * Returns:	---
+ *
+ * Use:		Loads an MP in an octet array, least significant octet
+ *		first.  High-end octets are ignored if there isn't enough
+ *		space for them.
+ */
+
+void mpx_loadl(mpw *v, const mpw *vl, const octet *p, size_t sz)
+{
+  unsigned n;
+  const octet *q = p + sz;
+  unsigned bits = 0;
+
+  if (v >= vl)
+    return;
+  while (p < q) {
+    n = U8(*p++);
+    w |= n << bits;
+    bits += 8;
+    if (bits >= MPW_BITS) {
+      *v++ = MPW(w);
+      w = n >> (MPW_BITS - bits + 8);
+      bits -= MPW_BITS;
+      if (v >= vl)
+	return;
+    }
+  }
+  *v++ = w;
+  MPX_ZERO(v, vl);
+}
+
+/* --- @mpx_storeb@ --- *
+ *
+ * Arguments:	@const mpw *v, *vl@ = base and limit of source vector
+ *		@octet *p@ = pointer to octet array
+ *		@size_t sz@ = size of octet array
+ *
+ * Returns:	---
+ *
+ * Use:		Stores an MP in an octet array, most significant octet
+ *		first.  High-end octets are silently discarded if there
+ *		isn't enough space for them.
+ */
+
+void mpx_storeb(const mpw *v, const mpw *vl, octet *p, size_t sz);
+{
+  mpw n, w = 0;
+  octet *q = p + sz;
+  unsigned bits = 0;
+
+  while (q > p) {
+    if (bits < 8) {
+      if (v >= vl) {
+	*--q = U8(w);
+	break;
+      }
+      n = *v++;
+      *--q = U8(w | n << bits);
+      w = n >> (8 - bits);
+      bits += MPW_BITS - 8;
+    } else {
+      *--q = U8(w);
+      w >>= 8;
+      bits -= 8;
+    }
+  }
+  memset(p, 0, q - p);
+}
+
+/* --- @mpx_loadb@ --- *
+ *
+ * Arguments:	@mpw *v, *vl@ = base and limit of destination vector
+ *		@const octet *p@ = pointer to octet array
+ *		@size_t sz@ = size of octet array
+ *
+ * Returns:	---
+ *
+ * Use:		Loads an MP in an octet array, most significant octet
+ *		first.  High-end octets are ignored if there isn't enough
+ *		space for them.
+ */
+
+void mpx_loadb(mpw *v, const mpw *vl, const octet *p, size_t sz)
+{
+  unsigned n;
+  const octet *q = p + sz;
+  unsigned bits = 0;
+
+  if (v >= vl)
+    return;
+  while (q > p) {
+    n = U8(*--q);
+    w |= n << bits;
+    bits += 8;
+    if (bits >= MPW_BITS) {
+      *v++ = MPW(w);
+      w = n >> (MPW_BITS - bits + 8);
+      bits -= MPW_BITS;
+      if (v >= vl)
+	return;
+    }
+  }
+  *v++ = w;
+  MPX_ZERO(v, vl);
+}
+
+/*----- Logical shifting --------------------------------------------------*/
+
+/* --- @mpx_lsl@ --- *
+ *
+ * Arguments:	@mpw *dv, *dvl@ = destination vector base and limit
+ *		@const mpw *av, *avl@ = source vector base and limit
+ *		@size_t n@ = number of bit positions to shift by
+ *
+ * Returns:	---
+ *
+ * Use:		Performs a logical shift left operation on an integer.
+ */
+
+void mpx_lsl(mpw *dv, mpw *dvl, const mpw *av, const mpw *avl, size_t n)
+{
+  size_t nw;
+  unsigned nb;
+
+  /* --- Trivial special case --- */
+
+  if (n == 0)
+    MPX_COPY(dv, dvl, av, avl);
+
+  /* --- Single bit shifting --- */
+
+  else if (n == 1) {
+    mpw w = 0;
+    while (av < avl) {
+      mpw t;
+      if (dv >= dvl)
+	goto done;
+      t = *av++;
+      *dv++ = MPW((t << 1) | w);
+      w = t >> (MPW_BITS - 1);
+    }
+    if (dv >= dvl)
+      goto done;
+    *dv++ = MPW(w);
+    MPX_ZERO(dv, dvl);
+  }
+
+  /* --- Break out word and bit shifts for more sophisticated work --- */
+	
+  nw = n / MPW_BITS;
+  nb = n % MPW_BITS;
+
+  /* --- Handle a shift by a multiple of the word size --- */
+
+  if (nb == 0) {
+    MPX_COPY(dv + nw, dvl, av, avl);
+    memset(dv, 0, MPWS(nw));
+  }
+
+  /* --- And finally the difficult case --- */
+
+  else {
+    mpw w;
+    size_t nr = MPW_BITS - nb;
+
+    if (dv + nw >= dvl) {
+      MPX_ZERO(dv, dvl);
+      goto done;
+    }
+    memset(dv, 0, MPWS(nw));
+    dv += nw;
+    w = *av++;
+
+    while (av < avl) {
+      mpw t;
+      if (dv >= dvl)
+	goto done;
+      t = *av++;
+      *dv++ = MPW((w >> nr) | (t << nb));
+      w = t;
+    }
+
+    if (dv < dvl) {
+      *dv++ = MPW(w >> nr);
+      MPX_ZERO(dv, dvl);
+    }
+  }
+
+done:;
+}
+
+/* --- @mpx_lsr@ --- *
+ *
+ * Arguments:	@mpw *dv, *dvl@ = destination vector base and limit
+ *		@const mpw *av, *avl@ = source vector base and limit
+ *		@size_t n@ = number of bit positions to shift by
+ *
+ * Returns:	---
+ *
+ * Use:		Performs a logical shift right operation on an integer.
+ */
+
+void mpx_lsr(mpw *dv, mpw *dvl, const mpw *av, const mpw *avl, size_t n)
+{
+  size_t nw;
+  unsigned nb;
+
+  /* --- Trivial special case --- */
+
+  if (n == 0)
+    MPX_COPY(dv, dvl, av, avl);
+
+  /* --- Single bit shifting --- */
+
+  else if (n == 1) {
+    mpw w = *av++ >> 1;
+    while (av < avl) {
+      mpw t;
+      if (dv >= dvl)
+	goto done;
+      t = *av++;
+      *dv++ = MPW((t << (MPW_BITS - 1)) | w);
+      w = t >> 1;
+    }
+    if (dv >= dvl)
+      goto done;
+    *dv++ = MPW(w);
+    MPX_ZERO(dv, dvl);
+  }
+
+  /* --- Break out word and bit shifts for more sophisticated work --- */
+
+  nw = n / MPW_BITS;
+  nb = n % MPW_BITS;
+
+  /* --- Handle a shift by a multiple of the word size --- */
+
+  if (nb == 0)
+    MPX_COPY(dv, dvl, av + nw, avl);
+
+  /* --- And finally the difficult case --- */
+
+  else {
+    mpw w;
+    size_t nr = MPW_BITS - nb;
+
+    av += nw;
+    w = *av++;
+    while (av < avl) {
+      mpw t;
+      if (dv >= dvl)
+	goto done;
+      t = *av++;
+      *dv++ = MPW((w >> nb) | (t << nr));
+      w = t;
+    }
+    if (dv < dvl) {
+      *dv++ = MPW(w >> nb);
+      MPX_ZERO(dv, dvl);
+    }
+  }
+
+done:;
+}
+
+/*----- Unsigned arithmetic -----------------------------------------------*/
+
+/* --- @mpx_ucmp@ --- *
+ *
+ * Arguments:	@const mpw *av, *avl@ = first argument vector base and limit
+ *		@const mpw *bv, *bvl@ = second argument vector base and limit
+ *
+ * Returns:	Less than, equal to, or greater than zero depending on
+ *		whether @a@ is less than, equal to or greater than @b@,
+ *		respectively.
+ *
+ * Use:		Performs an unsigned integer comparison.
+ */
+
+int mpx_ucmp(const mpw *av, const mpw *avl, const mpw *bv, const mpw *bvl)
+{
+  MPX_SHRINK(av, avl);
+  MPX_SHRINK(bv, bvl);
+
+  if (avl - av > bvl - bv)
+    return (+1);
+  else if (avl - av < bvl - bv)
+    return (-1);
+  else while (avl > av) {
+    mpw a = *--avl, b = *--bvl;
+    if (a > b)
+      return (+1);
+    else if (a < b)
+      return (-1);
+  }
+  return (0);
+}
+  
+/* --- @mpx_uadd@ --- *
+ *
+ * Arguments:	@mpw *dv, *dvl@ = destination vector base and limit
+ *		@const mpw *av, *avl@ = first addend vector base and limit
+ *		@const mpw *bv, *bvl@ = second addend vector base and limit
+ *
+ * Returns:	---
+ *
+ * Use:		Performs unsigned integer addition.  If the result overflows
+ *		the destination vector, high-order bits are discarded.  This
+ *		means that two's complement addition happens more or less for
+ *		free, although that's more a side-effect than anything else.
+ *		The result vector may be equal to either or both source
+ *		vectors, but may not otherwise overlap them.
+ */
+
+void mpx_uadd(mpw *dv, mpw *dvl, const mpw *av, const mpw *avl,
+	      const mpw *bv, const mpw *bvl)
+{
+  mpw c = 0;
+
+  while (av < avl || bv < bvl) {
+    mpw a, b;
+    mpd x;
+    if (dv >= dvl)
+      return;
+    a = (av < avl) ? *av++ : 0;
+    b = (bv < bvl) ? *bv++ : 0;
+    x = (mpd)a + (mpd)b + c;
+    *dv++ = MPW(x);
+    c = x >> MPW_BITS;
+  }
+  if (dv < dvl) {
+    *dv++ = c;
+    MPX_ZERO(dv, dvl);
+  }
+}
+
+/* --- @mpx_usub@ --- *
+ *
+ * Arguments:	@mpw *dv, *dvl@ = destination vector base and limit
+ *		@const mpw *av, *avl@ = first argument vector base and limit
+ *		@const mpw *bv, *bvl@ = second argument vector base and limit
+ *
+ * Returns:	---
+ *
+ * Use:		Performs unsigned integer subtraction.  If the result
+ *		overflows the destination vector, high-order bits are
+ *		discarded.  This means that two's complement subtraction
+ *		happens more or less for free, althuogh that's more a side-
+ *		effect than anything else.  The result vector may be equal to
+ *		either or both source vectors, but may not otherwise overlap
+ *		them.
+ */
+
+void mpx_usub(mpw *dv, mpw *dvl, const mpw *av, const mpw *avl,
+	      const mpw *bv, const mpw *bvl)
+{
+  mpw c = 0;
+
+  while (av < avl || bv < bvl) {
+    mpw a, b;
+    mpd x;
+    if (dv >= dvl)
+      return;
+    a = (av < avl) ? *av++ : 0;
+    b = (bv < bvl) ? *bv++ : 0;
+    x = (mpd)a - (mpd)b + c;
+    *dv++ = MPW(x);
+    if (c >> MPW_BITS)
+      c = MPW(~0u);
+  }
+  c = c ? ~0u : 0;
+  while (dv < dvl)
+    *dv++ = c
+}
+
+/* --- @mpx_umul@ --- *
+ *
+ * Arguments:	@mpw *dv, *dvl@ = destination vector base and limit
+ *		@const mpw *av, *avl@ = multiplicand vector base and limit
+ *		@const mpw *bv, *bvl@ = multiplier vector base and limit
+ *
+ * Returns:	---
+ *
+ * Use:		Performs unsigned integer multiplication.  If the result
+ *		overflows the desination vector, high-order bits are
+ *		discarded.  The result vector may not overlap the argument
+ *		vectors in any way.
+ */
+
+void mpx_umul(mpw *dv, mpw *dvl, const mpw *av, const mpw *avl,
+	      const mpw *bv, const mpw *bvl)
+{
+  /* --- This is probably worthwhile on a multiply --- */
+
+  MPX_SHRINK(av, avl);
+  MPX_SHRINK(bv, bvl);
+
+  /* --- Deal with a multiply by zero --- */
+  
+  if (bv == bvl) {
+    MPX_COPY(dv, dvl, bv, bvl);
+    return;
+  }
+
+  /* --- Do the initial multiply and initialize the accumulator --- */
+
+  MPX_UMULN(dv, dvl, av, avl, *bv++);
+
+  /* --- Do the remaining multiply/accumulates --- */
+
+  while (bv < bvl) {
+    mpw m = *bv++;
+    mpw c = ;
+    const mpw *avv = av;
+    mpw *dvv = ++dv;
+
+    while (avv < avl) {
+      mpd x;
+      if (dvv >= dvl)
+	goto next;
+      x = *dvv + m * *av++ + c;
+      *dv++ = MPW(x);
+      c = x >> MPW_BITS;
+    }
+    if (dvv < dvl)
+      *dvv++ = MPW(c);
+  next:;
+  }
+}
+
+/* --- @mpx_udiv@ --- *
+ *
+ * Arguments:	@mpw *qv, *qvl@ = quotient vector base and limit
+ *		@mpw *rv, *rvl@ = dividend/remainder vector base and limit
+ *		@const mpw *dv, *dvl@ = divisor vector base and limit
+ *
+ * Returns:	---
+ *
+ * Use:		Performs unsigned integer division.  If the result overflows
+ *		the quotient vector, high-order bits are discarded.  (Clearly
+ *		the remainder vector can't overflow.)  The various vectors
+ *		may not overlap in any way.  Yes, I know it's a bit odd
+ *		requiring the dividend to be in the result position but it
+ *		does make some sense really.  The remainder must have
+ *		headroom for at least two extra words.
+ */
+
+void mpx_udiv(mpw *qv, mpw *qvl, mpw *rv, mpw *rvl,
+	      const mpw *dv, const mpw *dvl)
+{
+  mpw spare[2];
+  unsigned norm = 0;
+  size_t scale;
+  mpw d, dd;
+
+  /* --- Initialize the quotient --- */
+
+  MPX_ZERO(qv, qvl);
+
+  /* --- Normalize the divisor --- *
+   *
+   * The algorithm requires that the divisor be at least two digits long.
+   * This is easy to fix.
+   */
+
+  MPX_SHRINK(dv, dvl);
+
+  assert(((void)"division by zero in mpx_udiv", dv < dvl));
+
+  d = dvl[-1];
+  if (dv + 1 == dvl) {
+    spare[0] = 0;
+    spare[1] = d;
+    dv = spare;
+    dvl = spare + 2;
+    norm += MPW_BITS;
+  }
+
+  while (d < MPW_MAX / 2) {
+    d <<= 1;
+    norm += 1;
+  }
+  dd = dvl[-2];
+
+  /* --- Normalize the dividend/remainder to match --- */
+
+  mpx_lsl(rv, rvl, rv, rvl, norm);
+  MPX_SHRINK(rv, rvl);
+
+  /* --- Work out the relative scales --- */
+
+  {
+    size_t rvn = rvl - rv;
+    size_t dvn = dvn - dv;
+
+    /* --- If the divisor is clearly larger, notice this --- */
+
+    if (dvn > rvn) {
+      mpx_lsr(rv, rvl, rv, rvl, norm);
+      return;
+    }
+
+    scale = rvn - dvn;
+  }
+
+  /* --- Calculate the most significant quotient digit --- *
+   *
+   * Because the divisor has its top bit set, this can only happen once.  The
+   * pointer arithmetic is a little contorted, to make sure that the
+   * behaviour is defined.
+   */
+
+  if (MPX_UCMP(rv + scale, rvl, >=, dv, dvl)) {
+    mpx_usub(rv + scale, rvl, rv + scale, rvl, dv, dvl);
+    if (qvl - qv > scale)
+      qv[scale] = 1;
+  }
+
+  /* --- Now for the main loop --- */
+
+  {
+    mpw *rvv;
+    mpw r;
+
+    scale--;
+    rvv = rvl - 2;
+    r = rvv[1];
+
+    while (scale) {
+      mpw q, rr;
+
+      /* --- Get an estimate for the next quotient digit --- */
+
+      rr = *rvv--;
+      if (r == d)
+	q = MPW_MAX;
+      else {
+	mpd rx = (r << MPW_BITS) | rr;
+	q = MPW(rx / d);
+      }
+
+      /* --- Refine the estimate --- */
+
+      {
+	mpd yh = (mpd)d * q;
+	mpd yl = (mpd)dd * q;
+	
+}
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/mpx.h b/mpx.h
new file mode 100644
index 0000000..edaea71
--- /dev/null
+++ b/mpx.h
@@ -0,0 +1,440 @@
+/* -*-c-*-
+ *
+ * $Id: mpx.h,v 1.1 1999/09/03 08:41:12 mdw Exp $
+ *
+ * Low level multiprecision arithmetic
+ *
+ * (c) 1999 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: mpx.h,v $
+ * Revision 1.1  1999/09/03 08:41:12  mdw
+ * Initial import.
+ *
+ */
+
+#ifndef MPX_H
+#define MPX_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- The idea ----------------------------------------------------------*
+ *
+ * This file provides functions and macros which work on vectors of words as
+ * unsigned multiprecision integers.  The interface works in terms of base
+ * and limit pointers (i.e., a pointer to the start of a vector, and a
+ * pointer just past its end) rather than base pointer and length, because
+ * that requires more arithmetic and state to work on.
+ *
+ * The interfaces are slightly bizarre in other ways.  Try to use the
+ * higher-level functions where you can: they're rather better designed to
+ * actually be friendly and useful.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <string.h>
+
+#ifndef MPTYPES_H
+#  include "mptypes.h"
+#endif
+
+/*----- General manipulation ----------------------------------------------*/
+
+/* --- @MPX_SHRINK@ --- *
+ *
+ * Arguments:	@const mpw *v@ = pointer to vector of words
+ *		@const mpw *vl@ = (updated) current limit of vector
+ *
+ * Use:		Shrinks down the limit of a multiprecision integer vector.
+ */
+
+#define MPX_SHRINK(v, vl) do {						\
+  const mpw *_v = (v), *_vl = (vl);					\
+  while (_vl > _v && *--_vl)						\
+    ;									\
+  (vl) = _vl;								\
+} while (0)
+
+/* --- @MPX_BITS@ --- *
+ *
+ * Arguments:	@unsigned long b@ = result variable
+ *		@const mpw *v@ = pointer to array of words
+ *		@const mpw *vl@ = limit of vector (from @MPX_SHRINK@)
+ *
+ * Use:		Calculates the number of bits in a multiprecision value.
+ */
+
+#define MPX_BITS(b, v, vl) do {						\
+  const mpw *_v = (v), *_vl = (vl);					\
+  if (_v == _vl)							\
+    (b) = 0;								\
+  else {								\
+    unsigned long _b = MPW_BITS * (_vl - _v - 1) + 1;	       		\
+    mpw _w = _vl[-1];							\
+    unsigned _k = MPW_BITS / 2;						\
+    while (_k) {							\
+      if (_w >> _k) {							\
+	_w >>= _k;							\
+	_b += _k;							\
+      }									\
+      _k >>= 1;								\
+    }									\
+    (b) = _b;								\
+  }									\
+} while (0)
+
+/* --- @MPX_OCTETS@ --- *
+ *
+ * Arguments:	@size_t o@ = result variable
+ *		@const mpw *v@ = pointer to array of words
+ *		@size_t len@ = length of the array (from @MPX_LEN@)
+ *
+ * Use:		Calculates the number of octets in a multiprecision value.
+ */
+
+#define MPX_OCTETS(o, v, len) do {					\
+  const mpw *_v = (v), *_vl = (vl);					\
+  if (_v == _vl)							\
+    (o) = 0;								\
+  else {								\
+    _size_t _o = (MPW_BITS / 8) * (_vl - _v - 1);			\
+    mpw _w = _vl[-1];							\
+    unsigned _k = MPW_BITS / 2;						\
+    while (_k > 3) {							\
+      if (_w >> _k) {							\
+	_w >>= _k;							\
+	_o += _k - 3;							\
+      }									\
+      _k >>= 1;								\
+    }									\
+    if (_w)								\
+      _o++;								\
+    (o) = _o;								\
+  }									\
+} while (0)
+
+/* --- @MPX_COPY@ --- *
+ *
+ * Arguments:	@dv, dvl@ = destination vector base and limit
+ *		@av, avl@ = source vector base and limit
+ *
+ * Use:		Copies a multiprecision integer.
+ */
+
+#define MPX_COPY(dv, dvl, av, dvl) do {					\
+  mpw *_dv = (dv);							\
+  size_t _dn = (dvl) - _dv;						\
+  const mpw *_av = (av);						\
+  size_t _an = (avl) - _av;						\
+  if (_av == _dv) {							\
+    if (_dvl > _avl)							\
+      memset(_avl, 0, MPWS(_dn - _an));					\
+  } else if (_an >= _dn)						\
+    memmove(_dv, _av, MPWS(_dn));					\
+  else {								\
+    memmove(_dv, _av, MPWS(_an));					\
+    memset(_dv + _an, 0, MPWS(_dn - _an));				\
+  }									\
+} while (0)
+
+/* --- @MPX_ZERO@ --- *
+ *
+ * Arguments:	@v, vl@ = base and limit of vector to clear
+ *
+ * Use:		Zeroes the area between the two vector pointers.
+ */
+
+#define MPX_ZERO(v, vl) {						\
+  mpw *_v = (v), *_vl = (vl);						\
+  memset(_v, 0, MPWS(_vl - _v));					\
+} while (0)
+
+/*----- Loading and storing -----------------------------------------------*/
+
+/* --- @mpx_storel@ --- *
+ *
+ * Arguments:	@const mpw *v, *vl@ = base and limit of source vector
+ *		@octet *p@ = pointer to octet array
+ *		@size_t sz@ = size of octet array
+ *
+ * Returns:	---
+ *
+ * Use:		Stores an MP in an octet array, least significant octet
+ *		first.  High-end octets are silently discarded if there
+ *		isn't enough space for them.
+ */
+
+extern void mpx_storel(const mpw */*v*/, const mpw */*vl*/,
+		       octet */*p*/, size_t /*sz*/);
+
+/* --- @mpx_loadl@ --- *
+ *
+ * Arguments:	@mpw *v, *vl@ = base and limit of destination vector
+ *		@const octet *p@ = pointer to octet array
+ *		@size_t sz@ = size of octet array
+ *
+ * Returns:	---
+ *
+ * Use:		Loads an MP in an octet array, least significant octet
+ *		first.  High-end octets are ignored if there isn't enough
+ *		space for them.
+ */
+
+extern void mpx_loadl(mpw */*v*/, mpw */*vl*/,
+		      const octet */*p*/, size_t /*sz*/);
+
+/* --- @mpx_storeb@ --- *
+ *
+ * Arguments:	@const mpw *v, *vl@ = base and limit of source vector
+ *		@octet *p@ = pointer to octet array
+ *		@size_t sz@ = size of octet array
+ *
+ * Returns:	---
+ *
+ * Use:		Stores an MP in an octet array, most significant octet
+ *		first.  High-end octets are silently discarded if there
+ *		isn't enough space for them.
+ */
+
+extern void mpx_storeb(const mpw */*v*/, const mpw */*vl*/,
+		       octet */*p*/, size_t /*sz*/);
+
+/* --- @mpx_loadb@ --- *
+ *
+ * Arguments:	@mpw *v, *vl@ = base and limit of destination vector
+ *		@const octet *p@ = pointer to octet array
+ *		@size_t sz@ = size of octet array
+ *
+ * Returns:	---
+ *
+ * Use:		Loads an MP in an octet array, most significant octet
+ *		first.  High-end octets are ignored if there isn't enough
+ *		space for them.
+ */
+
+extern void mpx_loadb(mpw */*v*/, mpw */*vl*/,
+		      const octet */*p*/, size_t /*sz*/);
+
+/*----- Logical shifting --------------------------------------------------*/
+
+/* --- @mpx_lsl@ --- *
+ *
+ * Arguments:	@mpw *dv, *dvl@ = destination vector base and limit
+ *		@const mpw *av, *avl@ = source vector base and limit
+ *		@size_t n@ = number of bit positions to shift by
+ *
+ * Returns:	---
+ *
+ * Use:		Performs a logical shift left operation on an integer.
+ */
+
+extern void mpx_lsl(mpw */*dv*/, mpw */*dvl*/,
+		    const mpw */*av*/, const mpw */*avl*/,
+		    size_t /*n*/);
+
+/* --- @mpx_lsr@ --- *
+ *
+ * Arguments:	@mpw *dv, *dvl@ = destination vector base and limit
+ *		@const mpw *av, *avl@ = source vector base and limit
+ *		@size_t n@ = number of bit positions to shift by
+ *
+ * Returns:	---
+ *
+ * Use:		Performs a logical shift right operation on an integer.
+ */
+
+extern void mpx_lsr(mpw */*dv*/, mpw */*dvl*/,
+		    const mpw */*av*/, const mpw */*avl*/,
+		    size_t /*n*/);
+
+/*----- Unsigned arithmetic -----------------------------------------------*/
+
+/* --- @mpx_ucmp@ --- *
+ *
+ * Arguments:	@const mpw *av, *avl@ = first argument vector base and limit
+ *		@const mpw *bv, *bvl@ = second argument vector base and limit
+ *
+ * Returns:	Less than, equal to, or greater than zero depending on
+ *		whether @a@ is less than, equal to or greater than @b@,
+ *		respectively.
+ *
+ * Use:		Performs an unsigned integer comparison.
+ */
+
+#define MPX_UCMP(av, avl, op, dv, dvl)					\
+  (mpx_ucmp((av), (avl), (dv), (dvl)) op 0)
+
+extern int mpx_ucmp(const mpw */*av*/, const mpw */*avl*/,
+		    const mpw */*bv*/, const mpw */*bvl*/);
+
+/* --- @mpx_uadd@ --- *
+ *
+ * Arguments:	@mpw *dv, *dvl@ = destination vector base and limit
+ *		@const mpw *av, *avl@ = first addend vector base and limit
+ *		@const mpw *bv, *bvl@ = second addend vector base and limit
+ *
+ * Returns:	---
+ *
+ * Use:		Performs unsigned integer addition.  If the result overflows
+ *		the destination vector, high-order bits are discarded.  This
+ *		means that two's complement addition happens more or less for
+ *		free, although that's more a side-effect than anything else.
+ *		The result vector may be equal to either or both source
+ *		vectors, but may not otherwise overlap them.
+ */
+
+extern void mpx_uadd(mpw */*dv*/, mpw */*dvl*/,
+		     const mpw */*av*/, const mpw */*avl*/,
+		     const mpw */*bv*/, const mpw */*bvl*/);
+
+/* --- @mpx_usub@ --- *
+ *
+ * Arguments:	@mpw *dv, *dvl@ = destination vector base and limit
+ *		@const mpw *av, *avl@ = first argument vector base and limit
+ *		@const mpw *bv, *bvl@ = second argument vector base and limit
+ *
+ * Returns:	---
+ *
+ * Use:		Performs unsigned integer subtraction.  If the result
+ *		overflows the destination vector, high-order bits are
+ *		discarded.  This means that two's complement subtraction
+ *		happens more or less for free, althuogh that's more a side-
+ *		effect than anything else.  The result vector may be equal to
+ *		either or both source vectors, but may not otherwise overlap
+ *		them.
+ */
+
+extern void mpx_usub(mpw */*dv*/, mpw */*dvl*/,
+		     const mpw */*av*/, const mpw */*avl*/,
+		     const mpw */*bv*/, const mpw */*bvl*/);
+
+/* --- @MPX_UMULN@ --- *
+ *
+ * Arguments:	@dv, dvl@ = destination vector base and limit
+ *		@av, avl@ = multiplicand vector base and limit
+ *		@m@ = multiplier
+ *
+ * Use:		Multiplies a multiprecision integer by a single-word value.
+ *		The destination and source may be equal.  The destination
+ *		is completely cleared after use.
+ */
+
+#define MPX_UMULN(dv, dvl, av, avl, m) do {				\
+  mpw *_dv = (dv), *_dvl = (dvl);					\
+  const mpw *_av = (av), *_avl = (avl);					\
+  mpw _c = 0;								\
+  mpd _m = (m);								\
+									\
+  while (_av < _avl) {							\
+    mpd _x;								\
+    if (_dv >= _dvl)							\
+      break;								\
+    _x = _m * *_av++ + c;						\
+    *_dv++ = MPW(_x);							\
+    _c = _x >> MPW_BITS;						\
+  }									\
+  if (_dv < _dvl) {							\
+    *_dv++ = MPW(_c);							\
+    MPX_ZERO(_dv, _dvl);						\
+  }									\
+} while (0)
+
+/* --- @MPX_UMLAN@ --- *
+ *
+ * Arguments:	@dv, dvl@ = destination/accumulator vector base and limit
+ *		@av, avl@ = multiplicand vector base and limit
+ *		@m@ = multiplier
+ *
+ * Use:		Multiplies a multiprecision integer by a single-word value
+ *		and adds the result to an accumulator.
+ */
+
+#define MPX_UMLAN(dv, dvl, av, avl, m) do {				\
+  mpw *_dv = (dv), *_dvl = (dvl);					\
+  const mpw *_av = (av), *_avl = (avl);					\
+  mpw _c = 0;								\
+  mpd _m = (m);								\
+									\
+  while (_av < _avl) {							\
+    mpd _x;								\
+    if (_dv >= _dvl)							\
+      break;								\
+    _x = *_dv + _m * *_av++ + _c;					\
+    *_dv++ = MPW(_x);							\
+    _c = _x >> MPW_BITS;						\
+  }									\
+  if (_dv < _dvl) {							\
+    *_dv++ = MPW(_c);							\
+    MPX_ZERO(_dv, _dvl);						\
+  }									\
+} while (0)
+
+/* --- @mpx_umul@ --- *
+ *
+ * Arguments:	@mpw *dv, *dvl@ = destination vector base and limit
+ *		@const mpw *av, *avl@ = multiplicand vector base and limit
+ *		@const mpw *bv, *bvl@ = multiplier vector base and limit
+ *
+ * Returns:	---
+ *
+ * Use:		Performs unsigned integer multiplication.  If the result
+ *		overflows the desination vector, high-order bits are
+ *		discarded.  The result vector may not overlap the argument
+ *		vectors in any way.
+ */
+
+extern void mpx_umul(mpw */*dv*/, mpw */*dvl*/,
+		     const mpw */*av*/, const mpw */*avl*/,
+		     const mpw */*bv*/, const mpw */*bvl*/);
+
+/* --- @mpx_udiv@ --- *
+ *
+ * Arguments:	@mpw *qv, *qvl@ = quotient vector base and limit
+ *		@mpw *rv, *rvl@ = dividend/remainder vector base and limit
+ *		@const mpw *dv, *dvl@ = divisor vector base and limit
+ *
+ * Returns:	---
+ *
+ * Use:		Performs unsigned integer division.  If the result overflows
+ *		the quotient vector, high-order bits are discarded.  (Clearly
+ *		the remainder vector can't overflow.)  The various vectors
+ *		may not overlap in any way.  Yes, I know it's a bit odd
+ *		requiring the dividend to be in the result position but it
+ *		does make some sense really.
+ */
+
+extern void mpx_udiv(mpw */*qv*/, mpw */*qvl*/, mpw */*rv*/, mpw */*rvl*/,
+		     const mpw */*dv*/, const mpw */*dvl*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/noise.c b/noise.c
new file mode 100644
index 0000000..094447c
--- /dev/null
+++ b/noise.c
@@ -0,0 +1,396 @@
+/* -*-c-*-
+ *
+ * $Id: noise.c,v 1.1 1999/09/03 08:41:12 mdw Exp $
+ *
+ * Acquisition of environmental noise (Unix specific)
+ *
+ * (c) 1998 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: noise.c,v $
+ * Revision 1.1  1999/09/03 08:41:12  mdw
+ * Initial import.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#ifdef HAVE_SETGROUPS
+#  include <grp.h>
+#endif
+
+#include <mLib/bits.h>
+#include <mLib/tv.h>
+
+#include "noise.h"
+#include "paranoia.h"
+#include "rand.h"
+
+/*----- Magical numbers ---------------------------------------------------*/
+
+#define NOISE_KIDLIFE 100000		/* @noise_filter@ child lifetime */
+#define MILLION 1000000			/* One million */
+
+/*----- Noise source definition -------------------------------------------*/
+
+rand_source noise_source = { noise_acquire, noise_timer };
+
+/*----- Static variables --------------------------------------------------*/
+
+/* --- Timer differences --- */
+
+static unsigned long noise_last;	/* Last time recorded */
+static unsigned long noise_diff;	/* Last first order difference */
+
+/* --- Setuid program handling --- */
+
+static uid_t noise_uid;			/* Uid to set to spawn processes */
+static gid_t noise_gid;			/* Gid to set to spawn processes */
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @bitcount@ --- *
+ *
+ * Arguments:	@unsigned long x@ = a word containing bits
+ *
+ * Returns:	The number of bits set in the word.
+ */
+
+static int bitcount(unsigned long x)
+{
+  char ctab[] = { 0, 1, 1, 2, 1, 2, 2, 3,
+		  1, 2, 2, 3, 2, 3, 3, 4 };
+  int count = 0;
+  while (x) {
+    count += ctab[x & 0xfu];
+    x >>= 4;
+  }
+  return (count);
+}
+
+/* --- @timer@ --- *
+ *
+ * Arguments:	@rand_pool *r@ = pointer to randomness pool
+ *		@struct timeval *tv@ = pointer to time block
+ *
+ * Returns:	Nonzer if some randomness was contributed.
+ *
+ * Use:		Low-level timer contributor.
+ */
+
+static int timer(rand_pool *r, struct timeval *tv)
+{
+  unsigned long x, d, dd;
+  int de, dde;
+  int ret;
+  
+  x = tv->tv_usec + MILLION * tv->tv_sec;
+  d = x ^ noise_last;
+  dd = d ^ noise_diff;
+  noise_diff = d;
+  de = bitcount(d);
+  dde = bitcount(dd);
+  rand_add(r, tv, sizeof(*tv), de <= dde ? de : dde);
+  ret = (de || dde);
+  BURN(tv); x = d = dd = de = dde = 0;
+  return (ret);
+}
+
+/* --- @noise_timer@ --- *
+ *
+ * Arguments:	@rand_pool *r@ = pointer to a randomness pool
+ *
+ * Returns:	Nonzero if some randomness was contributed.
+ *
+ * Use:		Contributes the current time to the randomness pool.
+ *		A guess at the number of useful bits contributed is made,
+ *		based on first and second order bit differences.  This isn't
+ *		ever-so	reliable, but it's better than nothing.
+ */
+
+int noise_timer(rand_pool *r)
+{
+  struct timeval tv;
+  gettimeofday(&tv, 0);
+  return (timer(r, &tv));
+}
+
+/* --- @noise_devrandom@ --- *
+ *
+ * Arguments:	@rand_pool *r@ = pointer to a randomness pool
+ *
+ * Returns:	Nonzero if some randomness was contributed.
+ *
+ * Use:		Attempts to obtain some randomness from the system entropy
+ *		pool.  All bits from the device are assumed to be good.
+ */
+
+int noise_devrandom(rand_pool *r)
+{
+  int fd;
+  octet buf[RAND_POOLSZ];
+  ssize_t len;
+  int ret = 0;
+
+  /* --- Be nice to other clients of the random device --- *
+   *
+   * Attempt to open the device nonblockingly.  If that works, take up to
+   * one bufferful and then close again.  If there's no data to be read,
+   * then that's tough and we go away again, on the grounds that the device
+   * needs to get some more entropy from somewhere.
+   */
+
+  if ((fd = open("/dev/random", O_RDONLY | O_NONBLOCK)) >= 0) {
+    if ((len = read(fd, buf, sizeof(buf))) > 0) {
+      rand_add(r, buf, len, len * 8);
+      BURN(buf);
+      ret = 1;
+    }
+    close(fd);
+  }
+  noise_timer(r);
+  return (ret);
+}
+
+/* --- @noise_setid@ --- *
+ *
+ * Arguments:	@uid_t uid@ = uid to set
+ *		@gid_t gid@ = gid to set
+ *
+ * Returns:	---
+ *
+ * Use:		Sets the user and group ids to be used by @noise_filter@
+ *		when running child processes.  This is useful to avoid
+ *		giving shell commands (even carefully written ones) undue
+ *		privileges.
+ */
+
+void noise_setid(uid_t uid, gid_t gid)
+{
+  noise_uid = uid;
+  noise_gid = gid;
+}
+
+/* --- @noise_filter@ --- *
+ *
+ * Arguments:	@rand_pool *r@ = pointer to a randomness pool
+ *		@int good@ = number of good bits per 1024 bits
+ *		@const char *c@ = shell command to run
+ *
+ * Returns:	Nonzero if some randomness was contributed.
+ *
+ * Use:		Attempts to execute a shell command, and dump it into the
+ *		randomness pool.  A very rough estimate of the number of
+ *		good bits is made, based on the size of the command's output.
+ *		This function calls @waitpid@, so be careful.  Before execing
+ *		the command, the process uid and gid are set to the values
+ *		given to @noise_setid@, and an attempt is made to reset the
+ *		list of supplementary groups.  The environment passed to
+ *		the command has been severly lobotimized.  If the command
+ *		fails to complete within a short time period, it is killed.
+ *		Paranoid use of close-on-exec flags for file descriptors is
+ *		recommended.
+ */
+
+int noise_filter(rand_pool *r, int good, const char *c)
+{
+  char buf[4096];
+  pid_t kid;
+  int fd[2];
+  struct timeval dead;
+  int ret = 0;
+  const char *env[] = {
+    "PATH=/bin:/usr/bin:/sbin:/usr/sbin:/etc",
+    0
+  };
+
+  /* --- Remember when this business started --- */
+
+  gettimeofday(&dead, 0);
+  timer(r, &dead);
+
+  /* --- Create a pipe --- */
+
+  if (pipe(fd))
+    return (ret);
+
+  /* --- Fork a child off --- */
+
+  kid = fork();
+  if (kid < 0) {
+    close(fd[0]);
+    close(fd[1]);
+    return (ret);
+  }
+
+  /* --- Handle the child end of the deal --- */
+
+  fflush(0);
+  if (kid == 0) {
+    int f;
+
+    /* --- Set the pipe as standard output, close standard input --- */
+
+    close(0); close(1); close(2);
+
+    if (fd[1] != 1) {
+      if (dup2(fd[1], 1) < 0) _exit(127);
+      close(fd[1]);
+    }
+
+    if ((f = open("/dev/null", O_RDONLY)) != 0 ||
+	(f = open("/dev/null", O_WRONLY)) != 2)
+      _exit(127);
+
+    /* --- Play games with uids --- */
+
+    if (noise_gid != NOISE_NOSETGID) {
+      setgid(noise_gid);
+      setegid(noise_gid);
+#ifdef HAVE_SETGROUPS
+      setgroups(1, &noise_gid);
+#endif
+    }
+
+    if (noise_uid != NOISE_NOSETUID) {
+      setuid(noise_uid);
+      seteuid(noise_uid);
+    }
+
+    /* --- Start the process up --- */
+
+    execle("/bin/sh", "-c", c, (char *)0, env);
+    _exit(127);
+  }
+
+  /* --- Sort out my end of the deal --- */
+
+  close(fd[1]);
+
+  /* --- Decide on the deadline --- */
+
+  TV_ADDL(&dead, &dead, 0, NOISE_KIDLIFE);
+
+  /* --- Now read, and think --- */
+
+  for (;;) {
+    struct timeval now, diff;
+    fd_set rd;
+
+    gettimeofday(&now, 0);
+    timer(r, &now);
+    if (TV_CMP(&now, >, &dead))
+      break;
+    TV_SUB(&diff, &dead, &now);
+
+    FD_ZERO(&rd);
+    FD_SET(fd[0], &rd);
+
+    if (select(fd[0] + 1, &rd, 0, 0, &diff) < 0)
+      break;
+    if (FD_ISSET(fd[0], &rd)) {
+      ssize_t sz;
+      int goodbits;
+
+      if ((sz = read(fd[0], buf, sizeof(buf))) <= 0)
+	break;
+      goodbits = (sz * good) / 128;
+      rand_add(r, buf, sz, goodbits);
+      ret = 1;
+    }
+  }
+
+  /* --- We've finished with it: kill the child process --- *
+   *
+   * This is morally questionable.  On the other hand, I don't want to be
+   * held up in the @waitpid@ call if I can possibly help it.  Maybe a
+   * double-fork is worth doing here.
+   */
+
+  close(fd[0]);
+  BURN(buf);
+  noise_timer(r);
+  kill(kid, SIGKILL);
+  waitpid(kid, 0, 0);
+  return (ret);
+}
+
+/* --- @noise_acquire@ --- *
+ *
+ * Arguments:	@rand_pool *r@ = pointer to a randomness pool
+ *
+ * Returns:	---
+ *
+ * Use:		Acquires some randomness from somewhere.
+ */
+
+void noise_acquire(rand_pool *r)
+{
+  if (!noise_devrandom(r)) {
+
+    /* --- Output of `ps' --- *
+     *
+     * This is a hopefully cheap way of getting a bit of noise.  I'm guessing
+     * the good bit ratio based on about 90 bytes per line of output, and
+     * each line containing maybe 12 bits worth of interesting data.  (Some
+     * quick experiments with gzip seem to bear this idea out.)  So, 12 good
+     * bits per 90 bytes of output gives slightly more than 17 good bits per
+     * 1024 bits of output.  So I'll be a pessimist and say 16 bits.
+     */
+
+    (void)
+      (noise_filter(r, 16, "ps alxww") ||
+       noise_filter(r, 16, "ps -elf"));
+
+    /* --- Output of `netstat' --- *
+     *
+     * Another possibly cheap way of getting noise.  My output has about
+     * 72 bytes per line of output.  My wild guesses are telling me that
+     * there are probably only about four good bits per line (gzip tells
+     * me there's six, but I want to underestimate).  Four bits per 72 bytes
+     * is 7 good bits per 1024 bits of output.  Pessimist that I am, I'll
+     * call it six.
+     */
+
+    noise_filter(r, 6, "netstat -n");
+  }
+}
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/noise.h b/noise.h
new file mode 100644
index 0000000..cadb747
--- /dev/null
+++ b/noise.h
@@ -0,0 +1,145 @@
+/* -*-c-*-
+ *
+ * $Id: noise.h,v 1.1 1999/09/03 08:41:12 mdw Exp $
+ *
+ * Acquisition of environmental noise (Unix specific)
+ *
+ * (c) 1998 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: noise.h,v $
+ * Revision 1.1  1999/09/03 08:41:12  mdw
+ * Initial import.
+ *
+ */
+
+#ifndef NOISE_H
+#define NOISE_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <sys/types.h>
+
+#ifndef RAND_H
+#  include "rand.h"
+#endif
+
+/*----- Noise source definition -------------------------------------------*/
+
+extern rand_source noise_source;
+
+/*----- Magic numbers -----------------------------------------------------*/
+
+#define NOISE_NOSETUID ((uid_t)-1)
+#define NOISE_NOSETGID ((gid_t)-1)
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @noise_timer@ --- *
+ *
+ * Arguments:	@rand_pool *r@ = pointer to a randomness pool
+ *
+ * Returns:	Nonzero if some randomness was contributed.
+ *
+ * Use:		Contributes the current time to the randomness pool.
+ *		A guess at the number of useful bits contributed is made,
+ *		based on first and second order bit differences.  This isn't
+ *		ever-so	reliable, but it's better than nothing.
+ */
+
+extern int noise_timer(rand_pool */*r*/);
+
+/* --- @noise_devrandom@ --- *
+ *
+ * Arguments:	@rand_pool *r@ = pointer to a randomness pool
+ *
+ * Returns:	Nonzero if some randomness was contributed.
+ *
+ * Use:		Attempts to obtain some randomness from the system entropy
+ *		pool.  All bits from the device are assumed to be good.
+ */
+
+extern int noise_devrandom(rand_pool */*r*/);
+
+/* --- @noise_setid@ --- *
+ *
+ * Arguments:	@uid_t uid@ = uid to set
+ *		@gid_t gid@ = gid to set
+ *
+ * Returns:	---
+ *
+ * Use:		Sets the user and group ids to be used by @noise_filter@
+ *		when running child processes.  This is useful to avoid
+ *		giving shell commands (even carefully written ones) undue
+ *		privileges.
+ */
+
+extern void noise_setid(uid_t /*uid*/, gid_t /*gid*/);
+
+/* --- @noise_filter@ --- *
+ *
+ * Arguments:	@rand_pool *r@ = pointer to a randomness pool
+ *		@int good@ = number of good bits per 1024 bits
+ *		@const char *c@ = shell command to run
+ *
+ * Returns:	Nonzero if some randomness was contributed.
+ *
+ * Use:		Attempts to execute a shell command, and dump it into the
+ *		randomness pool.  A very rough estimate of the number of
+ *		good bits is made, based on the size of the command's output.
+ *		This function calls @waitpid@, so be careful.  Before execing
+ *		the command, the process uid and gid are set to the values
+ *		given to @noise_setid@, and an attempt is made to reset the
+ *		list of supplementary groups.  The environment passed to
+ *		the command has been severly lobotimized.  If the command
+ *		fails to complete within a short time period, it is killed.
+ *		Paranoid use of close-on-exec flags for file descriptors is
+ *		recommended.
+ */
+
+extern int noise_filter(rand_pool */*r*/, int /*good*/, const char */*c*/);
+
+/* --- @noise_acquire@ --- *
+ *
+ * Arguments:	@rand_pool *r@ = pointer to a randomness pool
+ *
+ * Returns:	---
+ *
+ * Use:		Acquires some randomness from somewhere.
+ */
+
+extern void noise_acquire(rand_pool */*r*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/ofb.h b/ofb.h
new file mode 100644
index 0000000..85ff08c
--- /dev/null
+++ b/ofb.h
@@ -0,0 +1,406 @@
+/* -*-c-*-
+ *
+ * $Id: ofb.h,v 1.1 1999/09/03 08:41:12 mdw Exp $
+ *
+ * Output feedback for block ciphers
+ *
+ * (c) 1999 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: ofb.h,v $
+ * Revision 1.1  1999/09/03 08:41:12  mdw
+ * Initial import.
+ *
+ */
+
+#ifndef OFB_H
+#define OFB_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <string.h>
+
+#include <mLib/bits.h>
+
+#ifndef BLKC_H
+#  include "blkc.h"
+#endif
+
+#ifndef PARANOIA_H
+#  include "paranoia.h"
+#endif
+
+/*----- Macros ------------------------------------------------------------*/
+
+/* --- @OFB_DECL@ --- *
+ *
+ * Arguments:	@PRE@, @pre@ = prefixes for block cipher definitions
+ *
+ * Use:		Makes declarations for output feedback mode.
+ */
+
+#define OFB_DECL(PRE, pre)						\
+									\
+typedef struct pre ## _ofbctx {						\
+  pre ## _ctx ctx;			/* Underlying cipher context */	\
+  int off;				/* Current offset in buffer */	\
+  octet iv[PRE ## _BLKSZ];		/* Output buffer and IV */	\
+} pre ## _ofbctx;							\
+									\
+extern void pre ## _ofbgetiv(const pre ## _ofbctx */*ctx*/,		\
+			     void */*iv*/);				\
+									\
+extern void pre ## _ofbsetiv(pre ## _ofbctx */*ctx*/,			\
+			     const void */*iv*/);			\
+									\
+extern void pre ## _ofbbdry(pre ## _ofbctx */*ctx*/);			\
+									\
+extern void pre ## _ofbsetkey(pre ## _ofbctx */*ctx*/,			\
+			      const pre ## _ctx */*k*/);		\
+									\
+extern void pre ## _ofbinit(pre ## _ofbctx */*ctx*/,			\
+			    const void */*key*/, size_t /*sz*/,		\
+			    const void */*iv*/);			\
+									\
+extern void pre ## _ofbencrypt(pre ## _ofbctx */*ctx*/,			\
+			       const void */*src*/, void */*dest*/,	\
+			       size_t /*sz*/);
+
+/* --- @OFB_DEF@ --- *
+ *
+ * Arguments:	@PRE@, @pre@ = prefixes for the underlying block cipher
+ *
+ * Use:		Creates definitions for output feedback mode.
+ */
+
+#define OFB_DEF(PRE, pre)						\
+									\
+/* --- @pre_ofbgetiv@ --- *						\
+ *									\
+ * Arguments:	@const pre_ofbctx *ctx@ = pointer to OFB context block	\
+ *		@void *iv#@ = pointer to output data block		\
+ *									\
+ * Returns:	---							\
+ *									\
+ * Use:		Reads the currently set IV.  Reading and setting an IV	\
+ *		is not transparent to the cipher.  It will add a `step'	\
+ *		which must be matched by a similar operation during	\
+ *		decryption.						\
+ */									\
+									\
+void pre ## _ofbgetiv(const pre ## _ofbctx *ctx, void *iv)		\
+{									\
+  octet *p = iv;							\
+  int off = ctx->off;							\
+  int rest = PRE ## _BLKSZ - off;					\
+  memcpy(p, ctx->iv + off, rest);					\
+  memcpy(p + rest, ctx->iv, off);					\
+}									\
+									\
+/* --- @pre_ofbsetiv@ --- *						\
+ *									\
+ * Arguments:	@pre_ofbctx *ctx@ = pointer to OFB context block	\
+ *		@cnost void *iv@ = pointer to IV to set			\
+ *									\
+ * Returns:	---							\
+ *									\
+ * Use:		Sets the IV to use for subsequent encryption.		\
+ */									\
+									\
+void pre ## _ofbsetiv(pre ## _ofbctx *ctx, const void *iv)		\
+{									\
+  uint32 niv[PRE ## _BLKSZ / 4];					\
+  BLKC_LOAD(PRE, niv, iv);						\
+  pre ## _eblk(&ctx->ctx, niv, niv);					\
+  BLKC_STORE(PRE, ctx->iv, niv);					\
+  ctx->off = 0;								\
+}									\
+									\
+/* --- @pre_ofbbdry@ --- *						\
+ *									\
+ * Arguments:	@pre_ofbctx *ctx@ = pointer to OFB context block	\
+ *									\
+ * Returns:	---							\
+ *									\
+ * Use:		Inserts a boundary during encryption.  Successful	\
+ *		decryption must place a similar boundary.		\
+ */									\
+									\
+void pre ## _ofbbdry(pre ## _ofbctx *ctx)				\
+{									\
+  octet iv[PRE ## _BLKSZ];						\
+  pre ## _ofbgetiv(ctx, iv);						\
+  pre ## _ofbsetiv(ctx, iv);						\
+  BURN(iv);								\
+}									\
+									\
+/* --- @pre_ofbsetkey@ --- *						\
+ *									\
+ * Arguments:	@pre_ofbctx *ctx@ = pointer to OFB context block	\
+ *		@const pre_ctx *k@ = pointer to cipher context		\
+ *									\
+ * Returns:	---							\
+ *									\
+ * Use:		Sets the OFB context to use a different cipher key.	\
+ */									\
+									\
+void pre ## _ofbsetkey(pre ## _ofbctx *ctx, const pre ## _ctx *k)	\
+{									\
+  ctx->ctx = *k;							\
+}									\
+									\
+/* --- @pre_ofbinit@ --- *						\
+ *									\
+ * Arguments:	@pre_ofbctx *ctx@ = pointer to cipher context		\
+ *		@const void *key@ = pointer to the key buffer		\
+ *		@size_t sz@ = size of the key				\
+ *		@const void *iv@ = pointer to initialization vector	\
+ *									\
+ * Returns:	---							\
+ *									\
+ * Use:		Initializes a OFB context ready for use.  You should	\
+ *		ensure that the IV chosen is unique: reusing an IV will	\
+ *		compromise the security of the entire plaintext.  This	\
+ *		is equivalent to calls to @pre_init@, @pre_ofbsetkey@	\
+ *		and @pre_ofbsetiv@.					\
+ */									\
+									\
+void pre ## _ofbinit(pre ## _ofbctx *ctx,				\
+		     const void *key, size_t sz,			\
+		     const void *iv)					\
+{									\
+  static octet zero[PRE ## _BLKSZ] = { 0 };				\
+  pre ## _init(&ctx->ctx, key, sz);					\
+  pre ## _ofbsetiv(ctx, iv ? iv : zero);				\
+}									\
+									\
+/* --- @pre_ofbencrypt@ --- *						\
+ *									\
+ * Arguments:	@pre_ofbctx *ctx@ = pointer to OFB context block	\
+ *		@const void *src@ = pointer to source data		\
+ *		@void *dest@ = pointer to destination data		\
+ *		@size_t sz@ = size of block to be encrypted		\
+ *									\
+ * Returns:	---							\
+ *									\
+ * Use:		Encrypts or decrypts a block with a block cipher in OFB	\
+ *		mode: encryption and decryption are the same in OFB.	\
+ *		The destination may be null to just churn the feedback	\
+ *		round for a bit.  The source may be null to use the	\
+ *		cipher as a random data generator.			\
+ */									\
+									\
+void pre ## _ofbencrypt(pre ## _ofbctx *ctx,				\
+			const void *src, void *dest,			\
+			size_t sz)					\
+{									\
+  const octet *s = src;							\
+  octet *d = dest;							\
+  int off = ctx->off;							\
+									\
+  /* --- Empty blocks are trivial --- */				\
+									\
+  if (!sz)								\
+    return;								\
+									\
+  /* --- If I can deal with the block from my buffer, do that --- */	\
+									\
+  if (sz < PRE ## _BLKSZ - off)						\
+    goto small;								\
+									\
+  /* --- Finish off what's left in my buffer --- */			\
+									\
+  if (!d)								\
+    sz -= off;								\
+  else {								\
+    while (off < PRE ## _BLKSZ) {					\
+      register octet x = s ? *s++ : 0;					\
+      *d++ = ctx->iv[off++] ^ x;					\
+      sz--;								\
+    }									\
+  }    									\
+									\
+  /* --- Main encryption loop --- */					\
+									\
+  {									\
+    uint32 iv[PRE ## _BLKSZ / 4];					\
+    BLKC_LOAD(PRE, iv, ctx->iv);					\
+									\
+    for (;;) {								\
+      pre ## _eblk(&ctx->ctx, iv, iv);					\
+      if (sz < PRE ## _BLKSZ)						\
+	break;								\
+      if (d) {								\
+	if (!s)								\
+	  BLKC_STORE(PRE, d, iv);					\
+	else {								\
+	  uint32 x[PRE ## _BLKSZ / 4];					\
+	  BLKC_LOAD(PRE, x, s);						\
+	  BLKC_XSTORE(PRE, d, iv, x);					\
+	  s += PRE ## _BLKSZ;						\
+	}								\
+	d += PRE ## _BLKSZ;						\
+      }									\
+      sz -= PRE ## _BLKSZ;						\
+    }									\
+									\
+    BLKC_STORE(PRE, ctx->iv, iv);					\
+    off = 0;								\
+  }									\
+									\
+  /* --- Tidying up the tail end --- */					\
+									\
+  if (sz) {								\
+  small:								\
+    if (!d)								\
+      off += sz;							\
+    else do {								\
+      register octet x = s ? *s++ : 0;					\
+      *d++ = ctx->iv[off++] ^ x;					\
+      sz--;								\
+    } while (sz);							\
+  }									\
+									\
+  /* --- Done --- */							\
+									\
+  ctx->off = off;							\
+  return;								\
+}									\
+									\
+OFB_TEST(PRE, pre)
+
+/*----- Test rig ----------------------------------------------------------*/
+
+#ifdef TEST_RIG
+
+#include <stdio.h>
+
+#include "daftstory.h"
+
+/* --- @OFB_TEST@ --- *
+ *
+ * Arguments:	@PRE@, @pre@ = prefixes for block cipher definitions
+ *
+ * Use:		Standard test rig for OFB functions.
+ */
+
+#define OFB_TEST(PRE, pre)						\
+									\
+/* --- Initial plaintext for the test --- */				\
+									\
+static const octet text[] = TEXT;					\
+									\
+/* --- Key and IV to use --- */						\
+									\
+static const octet key[] = KEY;						\
+static const octet iv[] = IV;						\
+									\
+/* --- Buffers for encryption and decryption output --- */		\
+									\
+static octet ct[sizeof(text)];						\
+static octet pt[sizeof(text)];						\
+									\
+static void hexdump(const octet *p, size_t sz)				\
+{									\
+  const octet *q = p + sz;						\
+  for (sz = 0; p < q; p++, sz++) {					\
+    printf("%02x", *p);							\
+    if ((sz + 1) % PRE ## _BLKSZ == 0)					\
+      putchar(':');							\
+  }									\
+}									\
+									\
+int main(void)								\
+{									\
+  size_t sz = 0, rest;							\
+  pre ## _ofbctx ctx;							\
+  int status = 0;							\
+  int done = 0;								\
+  pre ## _ctx k;							\
+									\
+  size_t keysz = PRE ## _KEYSZ ?					\
+    PRE ## _KEYSZ : strlen((const char *)key);				\
+									\
+  fputs(#pre "-ofb: ", stdout);						\
+									\
+  pre ## _init(&k, key, keysz);						\
+  pre ## _ofbsetkey(&ctx, &k);						\
+									\
+  while (sz <= sizeof(text)) {						\
+    rest = sizeof(text) - sz;						\
+    memcpy(ct, text, sizeof(text));					\
+    pre ## _ofbsetiv(&ctx, iv);						\
+    pre ## _ofbencrypt(&ctx, ct, ct, sz);				\
+    pre ## _ofbencrypt(&ctx, ct + sz, ct + sz, rest);			\
+    memcpy(pt, ct, sizeof(text));					\
+    pre ## _ofbsetiv(&ctx, iv);						\
+    pre ## _ofbencrypt(&ctx, pt, pt, rest);				\
+    pre ## _ofbencrypt(&ctx, pt + rest, pt + rest, sz);			\
+    if (memcmp(pt, text, sizeof(text)) == 0) {				\
+      done++;								\
+      if (sizeof(text) < 40 || done % 8 == 0)				\
+        fputc('.', stdout);						\
+      if (done % 480 == 0)						\
+        fputs("\n\t", stdout);						\
+      fflush(stdout);							\
+    } else {								\
+      printf("\nError (sz = %lu)\n", (unsigned long)sz);		\
+      status = 1;							\
+      printf("\tplaintext      = "); hexdump(text, sz);			\
+        printf(", "); hexdump(text + sz, rest);				\
+        fputc('\n', stdout);						\
+      printf("\tciphertext     = "); hexdump(ct, sz);			\
+        printf(", "); hexdump(ct + sz, rest);				\
+        fputc('\n', stdout);						\
+      printf("\trecovered text = "); hexdump(pt, sz);			\
+        printf(", "); hexdump(pt + sz, rest);				\
+        fputc('\n', stdout);						\
+      fputc('\n', stdout);						\
+    }									\
+    if (sz < 63)							\
+      sz++;								\
+    else								\
+      sz += 9;								\
+  }									\
+									\
+  fputs(status ? " failed\n" : " ok\n", stdout);			\
+  return (status);							\
+}
+
+#else
+#  define OFB_TEST(PRE, pre)
+#endif
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/papers/.cvsignore b/papers/.cvsignore
new file mode 100644
index 0000000..4c052b9
--- /dev/null
+++ b/papers/.cvsignore
@@ -0,0 +1,4 @@
+rand.aux
+rand.dvi
+rand.log
+rand.toc
diff --git a/papers/crypto.sty b/papers/crypto.sty
new file mode 100644
index 0000000..7c0dcb7
--- /dev/null
+++ b/papers/crypto.sty
@@ -0,0 +1 @@
+\endinput
diff --git a/papers/rand.tex b/papers/rand.tex
new file mode 100644
index 0000000..77a986c
--- /dev/null
+++ b/papers/rand.tex
@@ -0,0 +1,179 @@
+%%% -*-latex-*-
+%%%
+%%% $Id: rand.tex,v 1.1 1999/09/03 08:41:13 mdw Exp $
+%%%
+%%% Description of Catacomb's random number generator
+%%%
+%%% (c) 1999 Straylight/Edgeware
+%%%
+
+%%%----- Licensing notice ---------------------------------------------------
+%%%
+%%% This file is part of Catacomb.
+%%%
+%%% Catacomb is free software; you can redistribute it and/or modify
+%%% it under the terms of the GNU Library General Public License as
+%%% published by the Free Software Foundation; either version 2 of the
+%%% License, or (at your option) any later version.
+%%% 
+%%% Catacomb is distributed in the hope that it will be useful,
+%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
+%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+%%% GNU Library General Public License for more details.
+%%% 
+%%% You should have received a copy of the GNU Library General Public
+%%% License along with Catacomb; if not, write to the Free
+%%% Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+%%% MA 02111-1307, USA.
+
+%%%----- Revision history ---------------------------------------------------
+%%%
+%%% $Log: rand.tex,v $
+%%% Revision 1.1  1999/09/03 08:41:13  mdw
+%%% Initial import.
+%%%
+
+%%%----- Header -------------------------------------------------------------
+
+\documentclass[a4paper, article, 10pt, notitlepage, numbering]{strayman}
+\usepackage[palatino, helvetica, courier, maths=cmr]{mdwfonts}
+\usepackage{mdwtab, mathenv}
+\usepackage[T1]{fontenc}
+\usepackage{cmtt, url}
+\usepackage[tpic, all]{xy}
+\usepackage{mathbbol}
+% \usepackage{crypto}
+
+\def\mdw{{\normalfont[{\bfseries\itshape mdw}]}}
+\urlstyle{tt}
+\def\email{\begingroup\urlstyle{rm}\Url}
+\urldef\myemail\email{mdw@nsict.org}
+\def\Z{\mathbb{Z}}
+\let\assign\leftarrow
+\let\xor\oplus
+\let\bigxor\bigoplus
+
+\title{The Catacomb random number generator}
+\author{Mark Wooding, \myemail}
+
+%%%----- The main document --------------------------------------------------
+
+\begin{document}
+
+\maketitle
+
+\begin{abstract}
+  The author describes the random number generator used in the
+  Straylight/\-Edgeware `Catacomb' library.  While the generator is
+  superficially similar to (for example) the Linux and OpenBSD random number
+  generators, it introduces a number of its own innovations which improve
+  both security and performance.
+  
+  The Catacomb generator uses an optional secret key, which can provide
+  additional security against forward state compromise extension.  It uses a
+  catastrophic reseeding operation to prevent a compromise yielding
+  information about past generator states.  This operation works on
+  arbitrary-sized blocks of data, so the generator's output buffer can be
+  large.  This minimizes the effect of the reseeding overhead.
+\end{abstract}
+
+\tableofcontents
+
+
+\section{The one-way transformation}
+
+The most novel part of the generator\footnote{I believe this construction to
+be novel.  If I'm wrong, let me know.} is the one-way transformation which is
+used to allow pooled input data to affect the output buffer.
+
+Let $H$ be some collision-resistant hash function, and let $E_k$ be a
+symmetric cipher with key $k$.  Then I can define the one-way transformation
+$T$ by
+\[ T(x) = E_{H(x)}(x) \]
+
+I believe, although formal proof seems difficult, that an adversary in
+posession of $T(x)$ and a portion of the original $x$ cannot reconstruct the
+remainder of $x$ without breaking one of the cryptographic primitives (which
+I assume is `difficult') or performing an exhaustive search of one of: the
+space of the unknown portion of $x$, the range of the hash function $H$, or
+the keyspace of the cipher $E$.
+
+A similar feat of cryptanalysis or exhaustive search seems necessary to work
+in a forwards direction: given partial knowledge of both $x$ and $T(x)$, the
+adversary cannot work out the remainder of either without trying every
+possibility for one or the other unknown portions, or working through the
+hash- or keyspace.
+
+A keyed version of $T$ may be defined, given a keyed hash (or MAC) $H_k$:
+\[ T_k(x) = E_{H_k(x)}(x) \]
+If this is done, the adversary cannot work forwards even with \emph{complete}
+knowledge of $B$, or performing one of the obvious exhaustive searches.
+
+
+\section{Description of the generator}
+
+The generator is divided into two parts: an \emph{input pool} which
+accumulates random input data from the environment, and an \emph{output
+buffer} which contains data to be passed to clients of the generator on
+request.
+
+New information is contributed to the generator by mixing it with the input
+pool, using a mixing function derived from the Linux random number generator
+\cite{linux:devrandom}.  The mixing function views the input pool as eight
+parallel shift registers.  Input data is added one octet at a time.  Each bit
+of an input octet is mixed with a different shift register.
+
+Formally, let $I$ be the input pool, with size $n_I$ bytes; let $P(x) = a_0 +
+a_1 x + a_2 x^2 + \cdots + a_{n_I} x^{n_I}$ be a primitive polynomial in
+$\mathrm{GF}(2^{n_I})$ with degree $n_I$; let $i$ be an integer such that $0
+\le i < n_I$, and $r$ be an integer such that $0 \le r < 8$; and let $x$ be
+an input byte.  The result of mixing $x$ with the pool $I$ is calculated as
+follows:
+\begin{eqlines*}
+  \begin{spliteqn*}
+    I'[8j + b] =
+    \begin{cases}
+      x\bigl[(r + b) \bmod 8\bigr] \xor
+        \bigxor_{0 \le k < n_I}
+          a_k I\bigl[8\bigl((j + k) \bmod n_I\bigr) + b\bigr] & if $i = j$ \\
+      I[j + b] & otherwise
+    \end{cases} \\
+    \textrm{for all integers $j$ and $b$ where $0 \le j < n_I$ and
+      $0 \le b < 8$}
+  \end{spliteqn*}
+  \\
+  I \assign I' \qquad
+  i \assign (i + 1) \bmod n_I \qquad
+  r \assign (r + 5) \bmod 8
+\end{eqlines*}
+Initially, $i$ and $r$ are both zero.  The use of 8-bit bytes above is
+arbitrary.
+
+Newly added data doesn't affect the output buffer until a `gating' operation
+is performed.  This uses the one-way transformation described earlier over
+the entire generator state.
+
+Data requested by clients of the generator is read from the output buffer
+$O$.  Initially the buffer contains zeroes.  
+
+\begin{thebibliography}{99}
+  
+\bibitem{cp:rand}
+  J.~Kelsey, B.~Schneier, D.~Wagner, and C.~Hall, ``Cryptographic Attacks on
+  Pseudorandom Number Generators'', \emph{Fast Software Encryption, Fifth
+  International Workshop Proceedings (March 1998)}, Springer-Verlag, 1998,
+  pp. 168--188, \url{http://www.counterpane.com/pseudorandom_number.html}
+
+\bibitem{linux:devrandom}
+  T.~Ts'o, ``A string random number generator'', Linux sources,
+  \path{drivers/char/random.c}.
+
+\bibitem{mdw:devrandom}
+  M.~Wooding, ``Linux \path{/dev/random} generator security'', Usenet article
+  posted to \mtt{sci.crypt}, July 1998.
+
+\end{thebibliography}
+
+%%%----- That's all, folks --------------------------------------------------
+
+\end{document}
\ No newline at end of file
diff --git a/paranoia.h b/paranoia.h
new file mode 100644
index 0000000..b2d19d8
--- /dev/null
+++ b/paranoia.h
@@ -0,0 +1,59 @@
+/* -*-c-*-
+ *
+ * $Id: paranoia.h,v 1.1 1999/09/03 08:41:12 mdw Exp $
+ *
+ * Macros and functions for cryptographic paranoia
+ *
+ * (c) 1998 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: paranoia.h,v $
+ * Revision 1.1  1999/09/03 08:41:12  mdw
+ * Initial import.
+ *
+ */
+
+#ifndef PARANOIA_H
+#define PARANOIA_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <string.h>
+
+/*----- Macros ------------------------------------------------------------*/
+
+#define BURN(x) (memset(&(x), 0, sizeof(x)))
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/rand.c b/rand.c
new file mode 100644
index 0000000..036ba4d
--- /dev/null
+++ b/rand.c
@@ -0,0 +1,392 @@
+/* -*-c-*-
+ *
+ * $Id: rand.c,v 1.1 1999/09/03 08:41:12 mdw Exp $
+ *
+ * Secure random number generator
+ *
+ * (c) 1998 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: rand.c,v $
+ * Revision 1.1  1999/09/03 08:41:12  mdw
+ * Initial import.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <stdio.h>
+#include <string.h>
+
+#include <mLib/bits.h>
+
+#include "blowfish-cbc.h"
+#include "paranoia.h"
+#include "rand.h"
+#include "rmd160.h"
+#include "rmd160-hmac.h"
+
+/*----- Static variables --------------------------------------------------*/
+
+static rand_pool pool;		/* Default random pool */
+
+/*----- Macros ------------------------------------------------------------*/
+
+#define RAND_RESOLVE(r) do {						\
+  if ((r) == RAND_GLOBAL)						\
+    (r) = &pool;							\
+} while (0)
+
+#define TIMER(r) do {							\
+  if ((r)->s && (r)->s->timer)						\
+    (r)->s->timer(r);							\
+} while (0)
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @rand_init@ --- *
+ *
+ * Arguments:	@rand_pool *r@ = pointer to a randomness pool
+ *
+ * Returns:	---
+ *
+ * Use:		Initializes a randomness pool.  The pool doesn't start out
+ *		very random: that's your job to sort out.
+ */
+
+void rand_init(rand_pool *r)
+{
+  RAND_RESOLVE(r);
+  memset(r->pool, 0, sizeof(r->pool));
+  memset(r->buf, 0, sizeof(r->buf));
+  r->i = 0;
+  r->irot = 0;
+  r->ibits = r->obits = 0;
+  r->o = RAND_SECSZ;
+  r->s = 0;
+  rmd160_hmac(&r->k, 0, 0);
+  rand_gate(r);
+}
+
+/* --- @rand_noisesrc@ --- *
+ *
+ * Arguments:	@rand_pool *r@ = pointer to a randomness pool
+ *		@const rand_source *s@ = pointer to source definition
+ *
+ * Returns:	---
+ *
+ * Use:		Sets a noise source for a randomness pool.  When the pool's
+ *		estimate of good random bits falls to zero, the @getnoise@
+ *		function is called, passing the pool handle as an argument.
+ *		It is expected to increase the number of good bits by at
+ *		least one, because it'll be called over and over again until
+ *		there are enough bits to satisfy the caller.  The @timer@
+ *		function is called frequently throughout the generator's
+ *		operation.
+ */
+
+void rand_noisesrc(rand_pool *r, const rand_source *s)
+{
+  RAND_RESOLVE(r);
+  r->s = s;
+}
+
+/* --- @rand_key@ --- *
+ *
+ * Arguments:	@rand_pool *r@ = pointer to a randomness pool
+ *		@const void *k@ = pointer to key data
+ *		@size_t sz@ = size of key data
+ *
+ * Returns:	---
+ *
+ * Use:		Sets the secret key for a randomness pool.  The key is used
+ *		when mixing in new random bits.
+ */
+
+void rand_key(rand_pool *r, const void *k, size_t sz)
+{
+  RAND_RESOLVE(r);
+  rmd160_hmac(&r->k, k, sz);
+}
+
+/* --- @rand_add@ --- *
+ *
+ * Arguments:	@rand_pool *r@ = pointer to a randomness pool
+ *		@const void *p@ = pointer a buffer of data to add
+ *		@size_t sz@ = size of the data buffer
+ *		@unsigned goodbits@ = number of good bits estimated in buffer
+ *
+ * Returns:	---
+ *
+ * Use:		Mixes the data in the buffer with the contents of the
+ *		pool.  The estimate of the number of good bits is added to
+ *		the pool's own count.  The mixing operation is not
+ *		cryptographically strong.  However, data in the input pool
+ *		isn't output directly, only through the one-way gating
+ *		operation, so that shouldn't matter.
+ */
+
+void rand_add(rand_pool *r, const void *p, size_t sz, unsigned goodbits)
+{
+  const octet *c = p;
+  int i, rot, mid;
+
+#if RAND_POOLSZ != 1279
+#  error Polynomial in rand_add is out of date.  Fix it.
+#endif
+
+  RAND_RESOLVE(r);
+
+  i = r->i; rot = r->irot; mid = i + 418;
+  if (mid >= RAND_POOLSZ) mid -= RAND_POOLSZ;
+
+  while (sz) {
+    octet o = *c++;
+    r->pool[i] ^= (ROL8(o, rot) ^ r->pool[mid]);
+    rot = (rot + 5) & 7;
+    i++; if (i >= RAND_POOLSZ) i -= RAND_POOLSZ;
+    mid++; if (mid >= RAND_POOLSZ) mid -= RAND_POOLSZ;
+    sz--;
+  }
+
+  r->i = i;
+  r->irot = rot;
+  r->ibits += goodbits;
+  if (r->ibits > RAND_IBITS)
+    r->ibits = RAND_IBITS;
+}
+
+/* --- @rand_goodbits@ --- *
+ *
+ * Arguments:   @rand_pool *r@ = pointer to a randomness pool
+ *
+ * Returns:     Estimate of the number of good bits remaining in the pool.
+ */
+
+unsigned rand_goodbits(rand_pool *r)
+{
+  RAND_RESOLVE(r);
+  return (r->ibits + r->obits);
+}
+
+/* --- @rand_gate@ --- *
+ *
+ * Arguments:	@rand_pool *r@ = pointer to a randomness pool
+ *
+ * Returns:	---
+ *
+ * Use:		Mixes up the entire state of the generator in a nonreversible
+ *		way.
+ */
+
+void rand_gate(rand_pool *r)
+{
+  octet mac[RMD160_HASHSZ];
+
+  RAND_RESOLVE(r);
+  TIMER(r);
+
+  /* --- Hash up all the data in the pool --- */
+
+  {
+    rmd160_macctx mc;
+
+    rmd160_macinit(&mc, &r->k);
+    rmd160_mac(&mc, r->pool, sizeof(r->pool));
+    rmd160_mac(&mc, r->buf, sizeof(r->buf));
+    rmd160_macdone(&mc, mac);
+    BURN(mc);
+  }
+
+  /* --- Now mangle all of the data based on the hash --- */
+
+  {
+    blowfish_cbcctx bc;
+
+    blowfish_cbcinit(&bc, mac, sizeof(mac), 0);
+    blowfish_cbcencrypt(&bc, r->pool, r->pool, sizeof(r->pool));
+    blowfish_cbcencrypt(&bc, r->buf, r->buf, sizeof(r->buf));
+    BURN(bc);
+  }
+
+  /* --- Reset the various state variables --- */
+
+  r->o = RAND_SECSZ;
+  r->obits += r->ibits;
+  if (r->obits > RAND_OBITS) {
+    r->ibits = r->obits - r->ibits;
+    r->obits = RAND_OBITS;
+  } else
+    r->ibits = 0;
+  TIMER(r);
+}
+
+/* --- @rand_stretch@ --- *
+ *
+ * Arguments:	@rand_pool *r@ = pointer to a randomness pool
+ *
+ * Returns:	---
+ *
+ * Use:		Stretches the contents of the output buffer by transforming
+ *		it in a nonreversible way.  This doesn't add any entropy
+ *		worth speaking about, but it works well enough when the
+ *		caller doesn't care about that sort of thing.
+ */
+
+void rand_stretch(rand_pool *r)
+{
+  octet mac[RMD160_HASHSZ];
+
+  RAND_RESOLVE(r);
+  TIMER(r);
+
+  /* --- Hash up all the data in the buffer --- */
+
+  {
+    rmd160_macctx mc;
+
+    rmd160_macinit(&mc, &r->k);
+    rmd160_mac(&mc, r->pool, sizeof(r->pool));
+    rmd160_mac(&mc, r->buf, sizeof(r->buf));
+    rmd160_macdone(&mc, mac);
+    BURN(mc);
+  }
+
+  /* --- Now mangle the buffer based on that hash --- */
+
+  {
+    blowfish_cbcctx bc;
+
+    blowfish_cbcinit(&bc, mac, sizeof(mac), 0);
+    blowfish_cbcencrypt(&bc, r->buf, r->buf, sizeof(r->buf));
+    BURN(bc);
+  }
+
+  /* --- Reset the various state variables --- */
+
+  r->o = RAND_SECSZ;
+  TIMER(r);
+}
+
+/* --- @rand_get@ --- *
+ *
+ * Arguments:	@rand_pool *r@ = pointer to a randomness pool
+ *		@void *p@ = pointer to output buffer
+ *		@size_t sz@ = size of output buffer
+ *
+ * Returns:	---
+ *
+ * Use:		Gets random data from the pool.  The pool's contents can't be
+ *		determined from the output of this function; nor can the
+ *		output data be determined from a knowledge of the data input
+ *		to the pool wihtout also having knowledge of the secret key.
+ *		The good bits counter is decremented, although no special
+ *		action is taken if it reaches zero.
+ */
+
+void rand_get(rand_pool *r, void *p, size_t sz)
+{
+  octet *o = p;
+
+  RAND_RESOLVE(r);
+  TIMER(r);
+
+  if (!sz)
+    return;
+  for (;;) {
+    if (r->o + sz <= RAND_BUFSZ) {
+      memcpy(o, r->buf + r->o, sz);
+      r->o += sz;
+      break;
+    } else {
+      size_t chunk = RAND_BUFSZ - r->o;
+      if (chunk) {
+	memcpy(o, r->buf + r->o, chunk);
+	sz -= chunk;
+	o += chunk;
+      }
+      rand_stretch(r);
+    }
+  }
+
+  if (r->obits > sz * 8)
+    r->obits -= sz * 8;
+  else
+    r->obits = 0;
+}
+
+/* --- @rand_getgood@ --- *
+ *
+ * Arguments:	@rand_pool *r@ = pointer to a randomness pool
+ *		@void *p@ = pointer to output buffer
+ *		@size_t sz@ = size of output buffer
+ *
+ * Returns:	---
+ *
+ * Use:		Gets random data from the pool.  The pool's contents can't be
+ *		determined from the output of this function; nor can the
+ *		output data be determined from a knowledge of the data input
+ *		to the pool wihtout also having knowledge of the secret key.
+ *		If a noise source is attached to the pool in question, it is
+ *		called to replenish the supply of good bits in the pool;
+ *		otherwise this call is equivalent to @rand_get@.
+ */
+
+void rand_getgood(rand_pool *r, void *p, size_t sz)
+{
+  octet *o = p;
+
+  RAND_RESOLVE(r);
+
+  if (!sz)
+    return;
+  if (!r->s || !r->s->getnoise) {
+    rand_get(r, p, sz);
+    return;
+  }
+  TIMER(r);
+
+  while (sz) {
+    size_t chunk = sz;
+
+    if (chunk * 8 > r->obits) {
+      if (chunk * 8 > r->ibits + r->obits)
+	do r->s->getnoise(r); while (r->ibits + r->obits < 128);
+      rand_gate(r);
+      if (chunk * 8 > r->obits)
+	chunk = r->obits / 8;
+    }
+
+    if (chunk + r->o > RAND_BUFSZ)
+      chunk = RAND_BUFSZ - r->o;
+
+    memcpy(o, r->buf + r->o, chunk);
+    r->obits -= chunk * 8;
+    o += chunk;
+    sz -= chunk;
+  }
+}
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/rand.h b/rand.h
new file mode 100644
index 0000000..8ccd38c
--- /dev/null
+++ b/rand.h
@@ -0,0 +1,270 @@
+/* -*-c-*-
+ *
+ * $Id: rand.h,v 1.1 1999/09/03 08:41:12 mdw Exp $
+ *
+ * Secure random number generator
+ *
+ * (c) 1999 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: rand.h,v $
+ * Revision 1.1  1999/09/03 08:41:12  mdw
+ * Initial import.
+ *
+ */
+
+/*----- Notes on the random number generator ------------------------------*
+ *
+ * The algorithm is one of the author's own devising.  It may therefore be
+ * worth a certain amount of skepticism.  However, I've thought about this
+ * method for over a year before actually considering it worth implementing.
+ * With a little bit of luck, it should have received some peer review by the
+ * time this code is actually properly released, and it'll be worth a bit
+ * more confidence.  My earlier generator was very similar in structure to
+ * the Linux /dev/random device.  This generator is intended to address
+ * concerns I expressed about the Linux generator in a Usenet article to
+ * sci.crypt.
+ *
+ * The generator is divided into two parts: an input pool and an outpu
+ * buffer.  New random data is placed into the pool in the way described
+ * below, which is shamelessly stolen from the Linux /dev/random generator.
+ * The only interaction that the pool has on the output buffer is through the
+ * keyed `gating' operation, which mixes up and redistributes all of the
+ * generator's state in an irreversible manner.  Random bytes, when
+ * requested, are extracted from the output buffer in a linear fashion.
+ *
+ * The input pool is best seen as being eight shift registers in parallel.
+ * Data is added to the pool one octet at a time.  Each bit of a new octet is
+ * added to a different shift register, by adding it (mod 2) with other bits
+ * according to the coefficients of a primitive polynomial.  Each new byte is
+ * rotated before being added into the pool, in a half-hearted attempt to
+ * protect against biases in the input data (e.g., top bits being clear on
+ * ASCII text).
+ *
+ * The gating operation takes a keyed hash of the entire generator state,
+ * uses it as the key for a symmetric cipher, and encrypts the state.  The
+ * key is then discarded.  The result is that every ouptut bit of the
+ * operation depends in a complex way on every input bit, but the operation
+ * cannot be reversed.
+ *
+ * As an added wrinkle, 160 bits of the output buffer are never actually
+ * output.  They are used in the gating operation only, as an extra item that
+ * an adversary has to guess before predicting generator output.
+ */
+
+#ifndef RAND_H
+#define RAND_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <stddef.h>
+
+#include "rmd160-hmac.h"
+
+/*----- Magic numbers -----------------------------------------------------*/
+
+#define RAND_POOLSZ 1279		/* Input pool size in bytes */
+#define RAND_BUFSZ 1024			/* Output buffer size in bytes */
+#define RAND_SECSZ 20			/* Secret octets in output buffer */
+
+#define RAND_IBITS (RAND_POOLSZ * 8)
+#define RAND_OBITS (RAND_BUFSZ * 8)
+
+/*----- Data structures ---------------------------------------------------*/
+
+/* --- A random number generator pool --- */
+
+typedef struct rand_pool {
+  octet pool[RAND_POOLSZ];		/* Actual contents of the pool */
+  unsigned i;				/* Current index into pool */
+  unsigned irot;			/* Current rotation applied */
+  unsigned ibits;			/* Number of good bits in pool */
+  octet buf[RAND_BUFSZ];		/* Random octet output buffer */
+  unsigned o;				/* Current index into buffer */
+  unsigned obits;			/* Number of good bits in buffer */
+  rmd160_mackey k;			/* Secret key for this pool */
+  const struct rand_source *s;		/* System-specific noise source */
+} rand_pool;
+
+#define RAND_GLOBAL ((rand_pool *)0)	/* The global randomness pool */
+
+/* --- A noise source --- */
+
+typedef struct rand_source {
+  void (*getnoise)(rand_pool */*r*/);	/* Acquire more noise */
+  int (*timer)(rand_pool */*r*/);	/* Get noise from current time */
+} rand_source;
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @rand_init@ --- *
+ *
+ * Arguments:	@rand_pool *r@ = pointer to a randomness pool
+ *
+ * Returns:	---
+ *
+ * Use:		Initializes a randomness pool.  The pool doesn't start out
+ *		very random: that's your job to sort out.
+ */
+
+extern void rand_init(rand_pool */*r*/);
+
+/* --- @rand_noisesrc@ --- *
+ *
+ * Arguments:	@rand_pool *r@ = pointer to a randomness pool
+ *		@const rand_source *s@ = pointer to source definition
+ *
+ * Returns:	---
+ *
+ * Use:		Sets a noise source for a randomness pool.  When the pool's
+ *		estimate of good random bits falls to zero, the @getnoise@
+ *		function is called, passing the pool handle as an argument.
+ *		It is expected to increase the number of good bits by at
+ *		least one, because it'll be called over and over again until
+ *		there are enough bits to satisfy the caller.  The @timer@
+ *		function is called frequently throughout the generator's
+ *		operation.
+ */
+
+extern void rand_noisesrc(rand_pool */*r*/, const rand_source */*s*/);
+
+/* --- @rand_key@ --- *
+ *
+ * Arguments:	@rand_pool *r@ = pointer to a randomness pool
+ *		@const void *k@ = pointer to key data
+ *		@size_t sz@ = size of key data
+ *
+ * Returns:	---
+ *
+ * Use:		Sets the secret key for a randomness pool.  The key is used
+ *		when mixing in new random bits.
+ */
+
+extern void rand_key(rand_pool */*r*/, const void */*k*/, size_t /*sz*/);
+
+/* --- @rand_add@ --- *
+ *
+ * Arguments:	@rand_pool *r@ = pointer to a randomness pool
+ *		@const void *p@ = pointer a buffer of data to add
+ *		@size_t sz@ = size of the data buffer
+ *		@unsigned goodbits@ = number of good bits estimated in buffer
+ *
+ * Returns:	---
+ *
+ * Use:		Mixes the data in the buffer with the contents of the
+ *		pool.  The estimate of the number of good bits is added to
+ *		the pool's own count.  The mixing operation is not
+ *		cryptographically strong.  However, data in the input pool
+ *		isn't output directly, only through the one-way gating
+ *		operation, so that shouldn't matter.
+ */
+
+extern void rand_add(rand_pool */*r*/,
+		     const void */*p*/, size_t /*sz*/,
+		     unsigned /*goodbits*/);
+
+/* --- @rand_goodbits@ --- *
+ *
+ * Arguments:   @rand_pool *r@ = pointer to a randomness pool
+ *
+ * Returns:     Estimate of the number of good bits remaining in the pool.
+ */
+
+extern unsigned rand_goodbits(rand_pool */*r*/);
+
+/* --- @rand_gate@ --- *
+ *
+ * Arguments:	@rand_pool *r@ = pointer to a randomness pool
+ *
+ * Returns:	---
+ *
+ * Use:		Mixes up the entire state of the generator in a nonreversible
+ *		way.
+ */
+
+extern void rand_gate(rand_pool */*r*/);
+
+/* --- @rand_stretch@ --- *
+ *
+ * Arguments:	@rand_pool *r@ = pointer to a randomness pool
+ *
+ * Returns:	---
+ *
+ * Use:		Stretches the contents of the output buffer by transforming
+ *		it in a nonreversible way.  This doesn't add any entropy
+ *		worth speaking about, but it works well enough when the
+ *		caller doesn't care about that sort of thing.
+ */
+
+extern void rand_stretch(rand_pool */*r*/);
+
+/* --- @rand_get@ --- *
+ *
+ * Arguments:	@rand_pool *r@ = pointer to a randomness pool
+ *		@void *p@ = pointer to output buffer
+ *		@size_t sz@ = size of output buffer
+ *
+ * Returns:	---
+ *
+ * Use:		Gets random data from the pool.  The pool's contents can't be
+ *		determined from the output of this function; nor can the
+ *		output data be determined from a knowledge of the data input
+ *		to the pool wihtout also having knowledge of the secret key.
+ *		The good bits counter is decremented, although no special
+ *		action is taken if it reaches zero.
+ */
+
+extern void rand_get(rand_pool */*r*/, void */*p*/, size_t /*sz*/);
+
+/* --- @rand_getgood@ --- *
+ *
+ * Arguments:	@rand_pool *r@ = pointer to a randomness pool
+ *		@void *p@ = pointer to output buffer
+ *		@size_t sz@ = size of output buffer
+ *
+ * Returns:	---
+ *
+ * Use:		Gets random data from the pool.  The pool's contents can't be
+ *		determined from the output of this function; nor can the
+ *		output data be determined from a knowledge of the data input
+ *		to the pool wihtout also having knowledge of the secret key.
+ *		If a noise source is attached to the pool in question, it is
+ *		called to replenish the supply of good bits in the pool;
+ *		otherwise this call is equivalent to @rand_get@.
+ */
+
+extern void rand_getgood(rand_pool */*r*/, void */*p*/, size_t /*sz*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/rc4.c b/rc4.c
new file mode 100644
index 0000000..6417635
--- /dev/null
+++ b/rc4.c
@@ -0,0 +1,197 @@
+/* -*-c-*-
+ *
+ * $Id: rc4.c,v 1.1 1999/09/03 08:41:12 mdw Exp $
+ *
+ * The alleged RC4 stream cipher
+ *
+ * (c) 1999 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: rc4.c,v $
+ * Revision 1.1  1999/09/03 08:41:12  mdw
+ * Initial import.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <assert.h>
+#include <stdio.h>
+
+#include <mLib/bits.h>
+
+#include "rc4.h"
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @rc4_init@ --- *
+ *
+ * Arguments:	@rc4_ctx *ctx@ = pointer to context to initialize
+ *		@const void *k@ = pointer to key data to use
+ *		@size_t sz@ = size of the key data
+ *
+ * Returns:	---
+ *
+ * Use:		Initializes an RC4 context ready for use.
+ */
+
+void rc4_init(rc4_ctx *ctx, const void *k, size_t sz)
+{
+  unsigned i, j;
+  const octet *p = k, *q = p + sz;
+
+  assert(((void)"RC4 does not support zero length keys", sz != 0));
+
+  for (i = 0; i < 256; i++)
+    ctx->s[i] = i;
+  ctx->f = 0;
+  ctx->i = ctx->j = 0;
+  
+  for (i = j = 0; i < 256; i++) {
+    unsigned si = ctx->s[i];
+    j = (j + si + *p++) & 0xff;
+    ctx->s[i] = ctx->s[j];
+    ctx->s[j] = si;
+    if (p == q)
+      p = k;
+  }
+
+#ifdef notdef
+  for (i = 0; i < 256; i += 16) {
+    printf("%3d :", i);
+    for (j = i; j < i + 16; j++)
+      printf(" %02x", ctx->s[j]);
+    putchar('\n');
+  }
+#endif
+}
+
+/* --- @rc4_encrypt@ --- *
+ *
+ * Arguments:	@rc4_ctx *ctx@ = pointer to context to use
+ *		@const void *src@ = pointer to the source block
+ *		@void *dest@ = pointer to the destination block
+ *		@size_t sz@ = size of the block
+ *
+ * Returns:	---
+ *
+ * Use:		Encrypts or decrypts a block of data.  The destination may
+ *		be null to just grind the generator around for a while.  It's
+ *		recommended that you say `@rc4_encrypt(&ctx, 0, 0, 1024)@'
+ *		after initializing a new context, to prevent keystream
+ *		guessing attacks.  The source may be null to just extract a
+ *		big lump of data from the generator.
+ */
+
+void rc4_encrypt(rc4_ctx *ctx, const void *src, void *dest, size_t sz)
+{
+  const octet *s = src;
+  octet *d = dest;
+
+  if (!d)
+    RC4_OPEN(ctx, while (sz) { unsigned x; RC4_BYTE(x); sz--; });
+  else if (!s)
+    RC4_OPEN(ctx, while (sz) { RC4_BYTE(*d++); sz--; });
+  else
+    RC4_OPEN(ctx,
+	     while (sz) { unsigned x; RC4_BYTE(x); *d++ = *s++ ^ x; sz--; });
+}
+
+/*----- Test rig ----------------------------------------------------------*/
+
+#ifdef TEST_RIG
+
+#include <stdio.h>
+#include <string.h>
+
+#include <mLib/quis.h>
+#include <mLib/testrig.h>
+
+static int v_encrypt(dstr *v)
+{
+  rc4_ctx ctx;
+  dstr d = DSTR_INIT;
+  int ok = 1;
+
+  rc4_init(&ctx, v[0].buf, v[0].len);
+  dstr_ensure(&d, v[1].len);
+  d.len = v[1].len;
+  rc4_encrypt(&ctx, v[1].buf, d.buf, d.len);
+
+  if (memcmp(v[2].buf, d.buf, d.len) != 0) {
+    ok = 0;
+    printf("\nfail encryption:"
+	   "\n\tkey        = ");
+    type_hex.dump(&v[0], stdout);
+    printf("\n\tplaintext  = "); type_hex.dump(&v[1], stdout);
+    printf("\n\texpected   = "); type_hex.dump(&v[2], stdout);
+    printf("\n\tcalculated = "); type_hex.dump(&d, stdout);
+    putchar('\n');
+  }
+
+  return (ok);
+}
+
+static int v_generate(dstr *v)
+{
+  rc4_ctx ctx;
+  dstr d = DSTR_INIT;
+  int ok = 1;
+
+  rc4_init(&ctx, v[0].buf, v[0].len);
+  rc4_encrypt(&ctx, 0, 0, *(int *)v[1].buf);
+  dstr_ensure(&d, v[2].len);
+  d.len = v[2].len;
+  rc4_encrypt(&ctx, 0, d.buf, d.len);
+
+  if (memcmp(v[2].buf, d.buf, d.len) != 0) {
+    ok = 0;
+    printf("\nfail generation:"
+	   "\n\tkey        = ");
+    type_hex.dump(&v[0], stdout);
+    printf("\n\tskip len   = %i", *(int *)v[1].buf);
+    printf("\n\texpected   = "); type_hex.dump(&v[2], stdout);
+    printf("\n\tcalculated = "); type_hex.dump(&d, stdout);
+    putchar('\n');
+  }
+
+  return (ok);
+}
+
+static test_chunk defs[] = {
+  { "rc4-encrypt", v_encrypt, { &type_hex, &type_hex, &type_hex, 0 } },
+  { "rc4-generate", v_generate, { &type_hex, &type_int, &type_hex, 0 } },
+  { 0, 0, { 0 } }
+};
+
+int main(int argc, char *argv[])
+{
+  test_run(argc, argv, defs, SRCDIR"/tests/rc4");
+  return (0);
+}
+
+#endif
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/rc4.h b/rc4.h
new file mode 100644
index 0000000..adaf731
--- /dev/null
+++ b/rc4.h
@@ -0,0 +1,160 @@
+/* -*-c-*-
+ *
+ * $Id: rc4.h,v 1.1 1999/09/03 08:41:12 mdw Exp $
+ *
+ * The alleged RC4 stream cipher
+ *
+ * (c) 1999 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: rc4.h,v $
+ * Revision 1.1  1999/09/03 08:41:12  mdw
+ * Initial import.
+ *
+ */
+
+/*----- Notes on RC4 ------------------------------------------------------*
+ *
+ * RC4 is a stream cipher desgigned by Ron Rivest.  For a while RC4 was a
+ * trade secret of RSA Data Security, Inc., but somehow source code for a
+ * cipher which interworks with RC4 was posted to the Cypherpunks mailing
+ * list.
+ */
+
+#ifndef RC4_H
+#define RC4_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <assert.h>
+
+#include <mLib/bits.h>
+
+/*----- Data structures ---------------------------------------------------*/
+
+typedef struct rc4_ctx {
+  unsigned i, j;			/* Indices into the @S@ table */
+  unsigned f;				/* Flags word */
+  octet s[256];				/* The ever-changing @S@ table */
+} rc4_ctx;
+
+#define RC4F_OPEN 1u
+
+/*----- Macros ------------------------------------------------------------*/
+
+/* --- @RC4_OPEN@ --- *
+ *
+ * Arguments:	@ctx@ = pointer to an RC4 context
+ *		@guts@ = code to perform within the RC4 context
+ *
+ * Use:		Performs some code within an RC4 context.  Some of the
+ *		parameters are extracted from the context and held in local
+ *		variables for speed.  Multiple calls to @RC4_BYTE@ may be
+ *		made within the open context.  A context must only be
+ *		opened once at a time.
+ */
+
+#define RC4_OPEN(ctx, guts) do {					\
+  unsigned _rc4_i = (ctx)->i;						\
+  unsigned _rc4_j = (ctx)->j;						\
+  octet *_rc4_s = (ctx)->s;						\
+									\
+  assert(((void)"RC4 context may only be opened once at a time",	\
+	  ((ctx)->f & RC4F_OPEN) == 0));				\
+  (ctx)->f |= RC4F_OPEN;						\
+									\
+  guts									\
+									\
+  (ctx)->f &= ~RC4F_OPEN;						\
+  (ctx)->i = _rc4_i;							\
+  (ctx)->j = _rc4_j;							\
+} while (0)
+
+/* --- @RC4_BYTE@ --- *
+ *
+ * Arguments:	@x@ = output variable to set
+ *
+ * Use:		Extracts an octet from the lexically innermost open RC4
+ *		context and places it in the variable @x@.
+ */
+
+#define RC4_BYTE(x) do {						\
+  unsigned _si, _sj;							\
+  _rc4_i = (_rc4_i + 1) & 0xff;						\
+  _si = _rc4_s[_rc4_i];							\
+  _rc4_j = (_rc4_j + _si) & 0xff;					\
+  _sj = _rc4_s[_rc4_j];							\
+  _rc4_s[_rc4_i] = _sj;							\
+  _rc4_s[_rc4_j] = _si;							\
+  (x) = _rc4_s[(_si + _sj) & 0xff];					\
+} while (0)
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @rc4_init@ --- *
+ *
+ * Arguments:	@rc4_ctx *ctx@ = pointer to context to initialize
+ *		@const void *k@ = pointer to key data to use
+ *		@size_t sz@ = size of the key data
+ *
+ * Returns:	---
+ *
+ * Use:		Initializes an RC4 context ready for use.
+ */
+
+extern void rc4_init(rc4_ctx */*ctx*/, const void */*k*/, size_t /*sz*/);
+
+/* --- @rc4_encrypt@ --- *
+ *
+ * Arguments:	@rc4_ctx *ctx@ = pointer to context to use
+ *		@const void *src@ = pointer to the source block
+ *		@void *dest@ = pointer to the destination block
+ *		@size_t sz@ = size of the block
+ *
+ * Returns:	---
+ *
+ * Use:		Encrypts or decrypts a block of data.  The destination may
+ *		be null to just grind the generator around for a while.  It's
+ *		recommended that you say `@rc4_encrypt(&ctx, 0, 0, 1024)@'
+ *		after initializing a new context, to prevent keystream
+ *		guessing attacks.  The source may be null to just extract a
+ *		big lump of data from the generator.
+ */
+
+extern void rc4_encrypt(rc4_ctx */*ctx*/,
+			const void */*src*/, void */*dest*/,
+			size_t /*sz*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/rc5.c b/rc5.c
new file mode 100644
index 0000000..5d03be7
--- /dev/null
+++ b/rc5.c
@@ -0,0 +1,235 @@
+/* -*-c-*-
+ *
+ * $Id: rc5.c,v 1.1 1999/09/03 08:41:12 mdw Exp $
+ *
+ * The RC5-32/12 block cipher
+ *
+ * (c) 1999 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: rc5.c,v $
+ * Revision 1.1  1999/09/03 08:41:12  mdw
+ * Initial import.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <mLib/alloc.h>
+#include <mLib/bits.h>
+
+#include "blkc.h"
+#include "rc5.h"
+
+/*----- Internal magical constants ----------------------------------------*/
+
+#define RC5_ROUNDS 12
+
+#define T ((RC5_ROUNDS + 1) * 2)
+#define P 0xb7e15163
+#define Q 0x9e3779b9
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @rc5_init@ --- *
+ *
+ * Arguments:	@rc5_ctx *k@ = pointer to a key block
+ *		@const void *sbuf@ = pointer to key material
+ *		@size_t sz@ = size of the key material
+ *
+ * Returns:	---
+ *
+ * Use:		Initializes an RC5 key block.
+ */
+
+void rc5_init(rc5_ctx *k, const void *sbuf, size_t sz)
+{
+  uint32 *l;
+  size_t w;
+
+  /* --- Set up the @L@ table --- *
+   *
+   * This is slightly unfortunately defined.
+   */
+
+  assert(((void)"RC5 does not support zero-length keys", sz != 0));
+
+  {
+    int i;
+    const octet *p = sbuf;
+
+    /* --- Create the buffer --- */
+
+    w = (sz + 3) / 4;
+    l = xmalloc(w * sizeof(uint32));
+
+    /* --- Extract the key material --- */
+
+    for (i = 0; sz > 3; i++) {
+      l[i] = LOAD32_L(p);
+      p += 4;
+      sz -= 4;
+    }
+
+    /* --- Fix up the tail end --- */
+
+    if (sz) {
+      uint32 x = U8(*p++);
+      if (sz > 1) x |= (U8(*p++) <<  8);
+      if (sz > 2) x |= (U8(*p++) << 16);
+      l[i] = x;
+    }
+  }
+
+  /* --- Initialize the @S@ table --- */
+
+  {
+    int i;
+
+    k->s[0] = P;
+    for (i = 1; i < T; i++)
+      k->s[i] = k->s[i - 1] + Q;
+  }
+
+  /* --- Mix in the key --- */
+
+  {
+    int m = 3 * (w > T ? w : T);
+    int i, j, c;
+    uint32 a, b;
+
+    for (c = i = j = a = b = 0; c < m; c++) {
+      uint32 x;
+
+      x = k->s[i] + a + b;
+      k->s[i] = a = ROL32(x, 3);
+      i++; if (i >= T) i = 0;
+
+      x = l[j] + a + b;
+      l[j] = b = ROL32(x, a + b);
+      j++; if (j >= w) j = 0;
+    }
+  }
+
+  free(l);
+}
+
+/* --- @EROUND@, @DROUND@ --- */
+
+#define EROUND(x, y, k) do {						\
+  uint32 _x;								\
+  _x = x ^ y; x = ROL32(_x, y) + k[0];					\
+  _x = y ^ x; y = ROL32(_x, x) + k[1];					\
+  k += 2;								\
+} while (0)
+
+#define DROUND(x, y, k) do {						\
+  uint32 _x;								\
+  k -= 2;								\
+  _x = y - k[1]; y = ROR32(_x, x) ^ x;					\
+  _x = x - k[0]; x = ROR32(_x, y) ^ y;					\
+} while (0)
+
+/* --- @EBLK@, @DBLK@ --- */
+
+#define EBLK(a, b, c, d, k) do {					\
+  uint32 _l, _r;							\
+  const uint32 *_k = (k)->s;						\
+									\
+  _l = (a) + _k[0];							\
+  _r = (b) + _k[1];							\
+  _k += 2;								\
+									\
+  EROUND(_l, _r, _k);							\
+  EROUND(_l, _r, _k);							\
+  EROUND(_l, _r, _k);							\
+  EROUND(_l, _r, _k);							\
+  EROUND(_l, _r, _k);							\
+  EROUND(_l, _r, _k);							\
+  EROUND(_l, _r, _k);							\
+  EROUND(_l, _r, _k);							\
+  EROUND(_l, _r, _k);							\
+  EROUND(_l, _r, _k);							\
+  EROUND(_l, _r, _k);							\
+  EROUND(_l, _r, _k);							\
+  (c) = _l;								\
+  (d) = _r;								\
+} while (0)
+
+#define DBLK(a, b, c, d, k) do {					\
+  uint32 _l, _r;							\
+  const uint32 *_k = (k)->s + T;					\
+									\
+  _l = (a);								\
+  _r = (b);								\
+									\
+  DROUND(_l, _r, _k);							\
+  DROUND(_l, _r, _k);							\
+  DROUND(_l, _r, _k);							\
+  DROUND(_l, _r, _k);							\
+  DROUND(_l, _r, _k);							\
+  DROUND(_l, _r, _k);							\
+  DROUND(_l, _r, _k);							\
+  DROUND(_l, _r, _k);							\
+  DROUND(_l, _r, _k);							\
+  DROUND(_l, _r, _k);							\
+  DROUND(_l, _r, _k);							\
+  DROUND(_l, _r, _k);							\
+									\
+  _k -= 2;								\
+  (d) = _r - _k[1];							\
+  (c) = _l - _k[0];							\
+} while (0)
+
+/* --- @rc5_eblk@, @rc5_dblk@ --- *
+ *
+ * Arguments:	@const rc5_ctx *k@ = pointer to RC5 context block
+ *		@const uint32 s[2]@ = pointer to source block
+ *		@uint32 *d[2]@ = pointer to destination block
+ *
+ * Returns:	---
+ *
+ * Use:		Low level block encryption and decryption.
+ */
+
+void rc5_eblk(const rc5_ctx *k, const uint32 *s, uint32 *d)
+{
+  EBLK(s[0], s[1], d[0], d[1], k);
+}
+
+void rc5_dblk(const rc5_ctx *k, const uint32 *s, uint32 *d)
+{
+  DBLK(s[0], s[1], d[0], d[1], k);
+}
+
+/* --- Test rig --- */
+
+BLKC_TEST(RC5, rc5)
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/rc5.h b/rc5.h
new file mode 100644
index 0000000..23e6080
--- /dev/null
+++ b/rc5.h
@@ -0,0 +1,99 @@
+/* -*-c-*-
+ *
+ * $Id: rc5.h,v 1.1 1999/09/03 08:41:12 mdw Exp $
+ *
+ * The RC5-32/12 block cipher
+ *
+ * (c) 1999 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: rc5.h,v $
+ * Revision 1.1  1999/09/03 08:41:12  mdw
+ * Initial import.
+ *
+ */
+
+#ifndef RC5_H
+#define RC5_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <mLib/bits.h>
+
+/*----- Magic numbers -----------------------------------------------------*/
+
+#define RC5_ROUNDS 12
+#define RC5_KEYSZ 0
+#define RC5_BLKSZ 8
+#define RC5_CLASS (N, L, 64)
+
+/*----- Data structures ---------------------------------------------------*/
+
+typedef struct rc5_ctx {
+  uint32 s[2 * (RC5_ROUNDS + 1)];
+} rc5_ctx;
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @rc5_init@ --- *
+ *
+ * Arguments:	@rc5_ctx *k@ = pointer to a key block
+ *		@const void *sbuf@ = pointer to key material
+ *		@size_t sz@ = size of the key material
+ *
+ * Returns:	---
+ *
+ * Use:		Initializes an RC5 key block.
+ */
+
+extern void rc5_init(rc5_ctx */*k*/, const void */*sbuf*/, size_t /*sz*/);
+
+/* --- @rc5_eblk@, @rc5_dblk@ --- *
+ *
+ * Arguments:	@const rc5_ctx *k@ = pointer to RC5 context block
+ *		@const uint32 s[2]@ = pointer to source block
+ *		@uint32 *d[2]@ = pointer to destination block
+ *
+ * Returns:	---
+ *
+ * Use:		Low level block encryption and decryption.
+ */
+
+extern void rc5_eblk(const rc5_ctx */*k*/,
+		     const uint32 */*s*/, uint32 */*d*/);
+extern void rc5_dblk(const rc5_ctx */*k*/,
+		     const uint32 */*s*/, uint32 */*d*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/rmd160.c b/rmd160.c
new file mode 100644
index 0000000..a42c21c
--- /dev/null
+++ b/rmd160.c
@@ -0,0 +1,402 @@
+/* -*-c-*-
+ *
+ * $Id: rmd160.c,v 1.1 1999/09/03 08:41:12 mdw Exp $
+ *
+ * The RIPEMD-160 message digest function
+ *
+ * (c) 1998 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: rmd160.c,v $
+ * Revision 1.1  1999/09/03 08:41:12  mdw
+ * Initial import.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <mLib/bits.h>
+
+#include "hash.h"
+#include "rmd160.h"
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @rmd160_compress@ --- *
+ *
+ * Arguments:	@rmd160_ctx *ctx@ = pointer to context block
+ *		@const void *sbuf@ = pointer to buffer of appropriate size
+ *
+ * Returns:	---
+ *
+ * Use:		RIPEMD-160 compression function.
+ */
+
+void rmd160_compress(rmd160_ctx *ctx, const void *sbuf)
+{
+  uint32 a, b, c, d, e;
+  uint32 A, B, C, D, E;
+  uint32 buf[16];
+
+  /* --- Fetch the chaining variables --- */
+
+  a = A = ctx->a;
+  b = B = ctx->b;
+  c = C = ctx->c;
+  d = D = ctx->d;
+  e = E = ctx->e;
+
+  /* --- Fetch the buffer contents --- */
+
+  {
+    int i;
+    const octet *p;
+
+    for (i = 0, p = sbuf; i < 16; i++, p += 4)
+      buf[i] = LOAD32_L(p);
+  }
+
+  /* --- Definitions for round functions --- */
+
+#define F(x, y, z) ((x) ^ (y) ^ (z))
+#define G(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define H(x, y, z) (((x) | ~(y)) ^ (z))
+#define I(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#define J(x, y, z) ((x) ^ ((y) | ~(z)))
+
+#define T(v, w, x, y, z, i, r, f, k) do {				\
+  uint32 _t = v + f(w, x, y) + buf[i] + k;				\
+  v = ROL32(_t, r) + z; x = ROL32(x, 10);				\
+} while (0)
+
+#define F1(v, w, x, y, z, i, r) T(v, w, x, y, z, i, r, F, 0x00000000)
+#define G1(v, w, x, y, z, i, r) T(v, w, x, y, z, i, r, G, 0x5a827999)
+#define H1(v, w, x, y, z, i, r) T(v, w, x, y, z, i, r, H, 0x6ed9eba1)
+#define I1(v, w, x, y, z, i, r) T(v, w, x, y, z, i, r, I, 0x8f1bbcdc)
+#define J1(v, w, x, y, z, i, r) T(v, w, x, y, z, i, r, J, 0xa953fd4e)
+
+#define F2(v, w, x, y, z, i, r) T(v, w, x, y, z, i, r, J, 0x50a28be6)
+#define G2(v, w, x, y, z, i, r) T(v, w, x, y, z, i, r, I, 0x5c4dd124)
+#define H2(v, w, x, y, z, i, r) T(v, w, x, y, z, i, r, H, 0x6d703ef3)
+#define I2(v, w, x, y, z, i, r) T(v, w, x, y, z, i, r, G, 0x7a6d76e9)
+#define J2(v, w, x, y, z, i, r) T(v, w, x, y, z, i, r, F, 0x00000000)
+
+  /* --- First the left hand side --- */
+
+  F1(a, b, c, d, e,  0, 11);
+  F1(e, a, b, c, d,  1, 14);
+  F1(d, e, a, b, c,  2, 15);
+  F1(c, d, e, a, b,  3, 12);
+  F1(b, c, d, e, a,  4,  5);
+  F1(a, b, c, d, e,  5,  8);
+  F1(e, a, b, c, d,  6,  7);
+  F1(d, e, a, b, c,  7,  9);
+  F1(c, d, e, a, b,  8, 11);
+  F1(b, c, d, e, a,  9, 13);
+  F1(a, b, c, d, e, 10, 14);
+  F1(e, a, b, c, d, 11, 15);
+  F1(d, e, a, b, c, 12,  6);
+  F1(c, d, e, a, b, 13,  7);
+  F1(b, c, d, e, a, 14,  9);
+  F1(a, b, c, d, e, 15,  8);
+                  
+  G1(e, a, b, c, d,  7,	 7);
+  G1(d, e, a, b, c,  4,	 6);
+  G1(c, d, e, a, b, 13,	 8);
+  G1(b, c, d, e, a,  1,	13);
+  G1(a, b, c, d, e, 10,	11);
+  G1(e, a, b, c, d,  6,	 9);
+  G1(d, e, a, b, c, 15,	 7);
+  G1(c, d, e, a, b,  3,	15);
+  G1(b, c, d, e, a, 12,	 7);
+  G1(a, b, c, d, e,  0,	12);
+  G1(e, a, b, c, d,  9,	15);
+  G1(d, e, a, b, c,  5,	 9);
+  G1(c, d, e, a, b,  2,	11);
+  G1(b, c, d, e, a, 14,	 7);
+  G1(a, b, c, d, e, 11,	13);
+  G1(e, a, b, c, d,  8,	12);
+                  
+  H1(d, e, a, b, c,  3,	11);
+  H1(c, d, e, a, b, 10,	13);
+  H1(b, c, d, e, a, 14,	 6);
+  H1(a, b, c, d, e,  4,	 7);
+  H1(e, a, b, c, d,  9,	14);
+  H1(d, e, a, b, c, 15,	 9);
+  H1(c, d, e, a, b,  8,	13);
+  H1(b, c, d, e, a,  1,	15);
+  H1(a, b, c, d, e,  2,	14);
+  H1(e, a, b, c, d,  7,	 8);
+  H1(d, e, a, b, c,  0,	13);
+  H1(c, d, e, a, b,  6,	 6);
+  H1(b, c, d, e, a, 13,	 5);
+  H1(a, b, c, d, e, 11,	12);
+  H1(e, a, b, c, d,  5,	 7);
+  H1(d, e, a, b, c, 12,	 5);
+                  
+  I1(c, d, e, a, b,  1,	11);
+  I1(b, c, d, e, a,  9,	12);
+  I1(a, b, c, d, e, 11,	14);
+  I1(e, a, b, c, d, 10,	15);
+  I1(d, e, a, b, c,  0,	14);
+  I1(c, d, e, a, b,  8,	15);
+  I1(b, c, d, e, a, 12,	 9);
+  I1(a, b, c, d, e,  4,	 8);
+  I1(e, a, b, c, d, 13,	 9);
+  I1(d, e, a, b, c,  3,	14);
+  I1(c, d, e, a, b,  7,	 5);
+  I1(b, c, d, e, a, 15,	 6);
+  I1(a, b, c, d, e, 14,	 8);
+  I1(e, a, b, c, d,  5,	 6);
+  I1(d, e, a, b, c,  6,	 5);
+  I1(c, d, e, a, b,  2,	12);
+                  
+  J1(b, c, d, e, a,  4,	 9);
+  J1(a, b, c, d, e,  0,	15);
+  J1(e, a, b, c, d,  5,	 5);
+  J1(d, e, a, b, c,  9,	11);
+  J1(c, d, e, a, b,  7,	 6);
+  J1(b, c, d, e, a, 12,	 8);
+  J1(a, b, c, d, e,  2,	13);
+  J1(e, a, b, c, d, 10,	12);
+  J1(d, e, a, b, c, 14,	 5);
+  J1(c, d, e, a, b,  1,	12);
+  J1(b, c, d, e, a,  3,	13);
+  J1(a, b, c, d, e,  8,	14);
+  J1(e, a, b, c, d, 11,	11);
+  J1(d, e, a, b, c,  6,	 8);
+  J1(c, d, e, a, b, 15,	 5);
+  J1(b, c, d, e, a, 13,	 6);
+
+  /* --- And then the right hand side --- */
+
+  F2(A, B, C, D, E,  5,	 8);
+  F2(E, A, B, C, D, 14,	 9);
+  F2(D, E, A, B, C,  7,	 9);
+  F2(C, D, E, A, B,  0,	11);
+  F2(B, C, D, E, A,  9,	13);
+  F2(A, B, C, D, E,  2,	15);
+  F2(E, A, B, C, D, 11,	15);
+  F2(D, E, A, B, C,  4,	 5);
+  F2(C, D, E, A, B, 13,	 7);
+  F2(B, C, D, E, A,  6,	 7);
+  F2(A, B, C, D, E, 15,	 8);
+  F2(E, A, B, C, D,  8,	11);
+  F2(D, E, A, B, C,  1,	14);
+  F2(C, D, E, A, B, 10,	14);
+  F2(B, C, D, E, A,  3,	12);
+  F2(A, B, C, D, E, 12,	 6);
+                  
+  G2(E, A, B, C, D,  6,	 9);
+  G2(D, E, A, B, C, 11,	13);
+  G2(C, D, E, A, B,  3,	15);
+  G2(B, C, D, E, A,  7,	 7);
+  G2(A, B, C, D, E,  0,	12);
+  G2(E, A, B, C, D, 13,	 8);
+  G2(D, E, A, B, C,  5,	 9);
+  G2(C, D, E, A, B, 10,	11);
+  G2(B, C, D, E, A, 14,	 7);
+  G2(A, B, C, D, E, 15,	 7);
+  G2(E, A, B, C, D,  8,	12);
+  G2(D, E, A, B, C, 12,	 7);
+  G2(C, D, E, A, B,  4,	 6);
+  G2(B, C, D, E, A,  9,	15);
+  G2(A, B, C, D, E,  1,	13);
+  G2(E, A, B, C, D,  2,	11);
+                  
+  H2(D, E, A, B, C, 15,	 9);
+  H2(C, D, E, A, B,  5,	 7);
+  H2(B, C, D, E, A,  1,	15);
+  H2(A, B, C, D, E,  3,	11);
+  H2(E, A, B, C, D,  7,	 8);
+  H2(D, E, A, B, C, 14,	 6);
+  H2(C, D, E, A, B,  6,	 6);
+  H2(B, C, D, E, A,  9,	14);
+  H2(A, B, C, D, E, 11,	12);
+  H2(E, A, B, C, D,  8,	13);
+  H2(D, E, A, B, C, 12,	 5);
+  H2(C, D, E, A, B,  2,	14);
+  H2(B, C, D, E, A, 10,	13);
+  H2(A, B, C, D, E,  0,	13);
+  H2(E, A, B, C, D,  4,	 7);
+  H2(D, E, A, B, C, 13,	 5);
+                  
+  I2(C, D, E, A, B,  8,	15);
+  I2(B, C, D, E, A,  6,	 5);
+  I2(A, B, C, D, E,  4,	 8);
+  I2(E, A, B, C, D,  1,	11);
+  I2(D, E, A, B, C,  3,	14);
+  I2(C, D, E, A, B, 11,	14);
+  I2(B, C, D, E, A, 15,	 6);
+  I2(A, B, C, D, E,  0,	14);
+  I2(E, A, B, C, D,  5,	 6);
+  I2(D, E, A, B, C, 12,	 9);
+  I2(C, D, E, A, B,  2,	12);
+  I2(B, C, D, E, A, 13,	 9);
+  I2(A, B, C, D, E,  9,	12);
+  I2(E, A, B, C, D,  7,	 5);
+  I2(D, E, A, B, C, 10,	15);
+  I2(C, D, E, A, B, 14,	 8);
+                  
+  J2(B, C, D, E, A, 12,	 8);
+  J2(A, B, C, D, E, 15,	 5);
+  J2(E, A, B, C, D, 10,	12);
+  J2(D, E, A, B, C,  4,	 9);
+  J2(C, D, E, A, B,  1,	12);
+  J2(B, C, D, E, A,  5,	 5);
+  J2(A, B, C, D, E,  8,	14);
+  J2(E, A, B, C, D,  7,	 6);
+  J2(D, E, A, B, C,  6,	 8);
+  J2(C, D, E, A, B,  2,	13);
+  J2(B, C, D, E, A, 13,	 6);
+  J2(A, B, C, D, E, 14,	 5);
+  J2(E, A, B, C, D,  0,	15);
+  J2(D, E, A, B, C,  3,	13);
+  J2(C, D, E, A, B,  9,	11);
+  J2(B, C, D, E, A, 11,	11);
+
+  /* --- Recombine the two halves --- */
+
+  {
+    uint32
+       tmp = ctx->b + c + D;
+    ctx->b = ctx->c + d + E;
+    ctx->c = ctx->d + e + A;
+    ctx->d = ctx->e + a + B;
+    ctx->e = ctx->a + b + C;
+    ctx->a = tmp;
+  }
+}
+
+/* --- @rmd160_init@ --- *
+ *
+ * Arguments:	@rmd160_ctx *ctx@ = pointer to context block to initialize
+ *
+ * Returns:	---
+ *
+ * Use:		Initializes a context block ready for hashing.
+ */
+
+void rmd160_init(rmd160_ctx *ctx)
+{
+  ctx->a = 0x67452301;
+  ctx->b = 0xefcdab89;
+  ctx->c = 0x98badcfe;
+  ctx->d = 0x10325476;
+  ctx->e = 0xc3d2e1f0;
+  ctx->off = 0;
+  ctx->count = 0;
+}
+
+/* --- @rmd160_set@ --- *
+ *
+ * Arguments:	@rmd160_ctx *ctx@ = pointer to context block
+ *		@const void *buf@ = pointer to state buffer
+ *		@unsigned long count@ = current count of bytes processed
+ *
+ * Returns:	---
+ *
+ * Use:		Initializes a context block from a given state.  This is
+ *		useful in cases where the initial hash state is meant to be
+ *		secret, e.g., for NMAC and HMAC support.
+ */
+
+void rmd160_set(rmd160_ctx *ctx, const void *buf, unsigned long count)
+{
+  const octet *p = buf;
+  ctx->a = LOAD32_L(p +  0);
+  ctx->b = LOAD32_L(p +  4);
+  ctx->c = LOAD32_L(p +  8);
+  ctx->d = LOAD32_L(p + 12);
+  ctx->e = LOAD32_L(p + 16);
+  ctx->off = 0;
+  ctx->count = count;
+}
+
+/* --- @rmd160_hash@ --- *
+ *
+ * Arguments:	@rmd160_ctx *ctx@ = pointer to context block
+ *		@const void *buf@ = buffer of data to hash
+ *		@size_t sz@ = size of buffer to hash
+ *
+ * Returns:	---
+ *
+ * Use:		Hashes a buffer of data.  The buffer may be of any size and
+ *		alignment.
+ */
+
+void rmd160_hash(rmd160_ctx *ctx, const void *buf, size_t sz)
+{
+  HASH_BUFFER(RMD160, rmd160, ctx, buf, sz);
+}
+
+/* --- @rmd160_done@ --- *
+ *
+ * Arguments:	@rmd160_ctx *ctx@ = pointer to context block
+ *		@void *hash@ = pointer to output buffer
+ *
+ * Returns:	---
+ *
+ * Use:		Returns the hash of the data read so far.
+ */
+
+void rmd160_done(rmd160_ctx *ctx, void *hash)
+{
+  octet *p = hash;
+  HASH_MD5STRENGTH(RMD160, rmd160, ctx);
+  STORE32_L(p +  0, ctx->a);
+  STORE32_L(p +  4, ctx->b);
+  STORE32_L(p +  8, ctx->c);
+  STORE32_L(p + 12, ctx->d);
+  STORE32_L(p + 16, ctx->e);
+}
+
+/* --- @rmd160_state@ --- *
+ *
+ * Arguments:	@rmd160_ctx *ctx@ = pointer to context
+ *		@void *state@ = pointer to buffer for current state
+ *
+ * Returns:	Number of bytes written to the hash function so far.
+ *
+ * Use:		Returns the current state of the hash function such that
+ *		it can be passed to @rmd160_set@.
+ */
+
+unsigned long rmd160_state(rmd160_ctx *ctx, void *state)
+{
+  octet *p = state;
+  STORE32_L(p +  0, ctx->a);
+  STORE32_L(p +  4, ctx->b);
+  STORE32_L(p +  8, ctx->c);
+  STORE32_L(p + 12, ctx->d);
+  STORE32_L(p + 16, ctx->e);
+  return (ctx->count);
+}
+
+/* --- Test code --- */
+
+HASH_TEST(RMD160, rmd160)
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/rmd160.h b/rmd160.h
new file mode 100644
index 0000000..5aa9579
--- /dev/null
+++ b/rmd160.h
@@ -0,0 +1,160 @@
+/* -*-c-*-
+ *
+ * $Id: rmd160.h,v 1.1 1999/09/03 08:41:12 mdw Exp $
+ *
+ * The RIPEMD-160 message digest function
+ *
+ * (c) 1998 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: rmd160.h,v $
+ * Revision 1.1  1999/09/03 08:41:12  mdw
+ * Initial import.
+ *
+ */
+
+/*----- Notes on the RIPEMD-160 hash function -----------------------------*
+ *
+ * RIPEMD-160 was invented by Hans Dobbertin, Antoon Bosselaers and Bart
+ * Preneel.  It's a strengthened version of the original RIPEMD hash
+ * function, fixing a vulnerability discovered by Hans Dobbertin.  The
+ * RIPEMD-160 design team appears well respected in the cryptographic
+ * community.  The author finds them more plausible than SHA-1, which is the
+ * best alternative hash function.
+ */
+
+#ifndef RMD160_H
+#define RMD160_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <mLib/bits.h>
+
+/*----- Magic numbers -----------------------------------------------------*/
+
+#define RMD160_BUFSZ 64
+#define RMD160_HASHSZ 20
+
+/*----- Data structures ---------------------------------------------------*/
+
+typedef struct rmd160_ctx {
+  uint32 a, b, c, d, e;			/* Chaining variables */
+  unsigned long count;			/* Byte count so far */
+  int off;				/* Offset into buffer */
+  octet buf[RMD160_BUFSZ];		/* Accumulation buffer */
+} rmd160_ctx;
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @rmd160_compress@ --- *
+ *
+ * Arguments:	@rmd160_ctx *ctx@ = pointer to context block
+ *		@const void *sbuf@ = pointer to buffer of appropriate size
+ *
+ * Returns:	---
+ *
+ * Use:		RIPEMD-160 compression function.
+ */
+
+extern void rmd160_compress(rmd160_ctx */*ctx*/, const void */*sbuf*/);
+
+/* --- @rmd160_init@ --- *
+ *
+ * Arguments:	@rmd160_ctx *ctx@ = pointer to context block to initialize
+ *
+ * Returns:	---
+ *
+ * Use:		Initializes a context block ready for hashing.
+ */
+
+extern void rmd160_init(rmd160_ctx */*ctx*/);
+
+/* --- @rmd160_set@ --- *
+ *
+ * Arguments:	@rmd160_ctx *ctx@ = pointer to context block
+ *		@const void *buf@ = pointer to state buffer
+ *		@unsigned long count@ = current count of bytes processed
+ *
+ * Returns:	---
+ *
+ * Use:		Initializes a context block from a given state.  This is
+ *		useful in cases where the initial hash state is meant to be
+ *		secret, e.g., for NMAC and HMAC support.
+ */
+
+extern void rmd160_set(rmd160_ctx */*ctx*/,
+		       const void */*buf*/, unsigned long /*count*/);
+
+/* --- @rmd160_hash@ --- *
+ *
+ * Arguments:	@rmd160_ctx *ctx@ = pointer to context block
+ *		@const void *buf@ = buffer of data to hash
+ *		@size_t sz@ = size of buffer to hash
+ *
+ * Returns:	---
+ *
+ * Use:		Hashes a buffer of data.  The buffer may be of any size and
+ *		alignment.
+ */
+
+extern void rmd160_hash(rmd160_ctx */*ctx*/,
+			const void */*buf*/, size_t /*sz*/);
+
+/* --- @rmd160_done@ --- *
+ *
+ * Arguments:	@rmd160_ctx *ctx@ = pointer to context block
+ *		@void *hash@ = pointer to output buffer
+ *
+ * Returns:	---
+ *
+ * Use:		Returns the hash of the data read so far.
+ */
+
+extern void rmd160_done(rmd160_ctx */*ctx*/, void */*hash*/);
+
+/* --- @rmd160_state@ --- *
+ *
+ * Arguments:	@rmd160_ctx *ctx@ = pointer to context
+ *		@void *state@ = pointer to buffer for current state
+ *
+ * Returns:	Number of bytes written to the hash function so far.
+ *
+ * Use:		Returns the current state of the hash function such that
+ *		it can be passed to @rmd160_set@.
+ */
+
+extern unsigned long rmd160_state(rmd160_ctx */*ctx*/, void */*state*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/setup b/setup
new file mode 100755
index 0000000..3c35243
--- /dev/null
+++ b/setup
@@ -0,0 +1,11 @@
+#! /bin/sh
+
+set -e
+mklinks
+ln -s ../mLib/build mLib
+mkaclocal
+autoheader
+m4 Makefile.m4 >Makefile.am
+autoconf
+automake
+mkdir build
diff --git a/sha.c b/sha.c
new file mode 100644
index 0000000..a5a3bf3
--- /dev/null
+++ b/sha.c
@@ -0,0 +1,307 @@
+/* -*-c-*-
+ *
+ * $Id: sha.c,v 1.1 1999/09/03 08:41:12 mdw Exp $
+ *
+ * Implementation of the SHA-1 hash function
+ *
+ * (c) 1999 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: sha.c,v $
+ * Revision 1.1  1999/09/03 08:41:12  mdw
+ * Initial import.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <mLib/bits.h>
+
+#include "hash.h"
+#include "sha.h"
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @sha_compress@ --- *
+ *
+ * Arguments:	@sha_ctx *ctx@ = pointer to context block
+ *		@const void *sbuf@ = pointer to buffer of appropriate size
+ *
+ * Returns:	---
+ *
+ * Use:		SHA-1 compression function.
+ */
+
+void sha_compress(sha_ctx *ctx, const void *sbuf)
+{
+  uint32 a, b, c, d, e;
+  uint32 buf[80];
+
+  /* --- Fetch the chaining variables --- */
+
+  a = ctx->a;
+  b = ctx->b;
+  c = ctx->c;
+  d = ctx->d;
+  e = ctx->e;
+
+  /* --- Fetch and expand the buffer contents --- */
+
+  {
+    int i;
+    const octet *p;
+
+    for (i = 0, p = sbuf; i < 16; i++, p += 4)
+      buf[i] = LOAD32(p);
+    for (i = 16; i < 80; i++) {
+      uint32 x = buf[i - 3] ^ buf[i - 8] ^ buf[i - 14] ^ buf[i - 16];
+      buf[i] = ROL32(x, 1);
+    }
+  }
+
+  /* --- Definitions for round functions --- */
+
+#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define G(x, y, z) ((x) ^ (y) ^ (z))
+#define H(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z)))
+
+#define T(v, w, x, y, z, i, f, k) do {					\
+  z = ROL32(v, 5) + f(w, x, y) + z + buf[i] + k;			\
+  w = ROR32(w, 2);							\
+} while (0)
+
+#define FF(v, w, x, y, z, i) T(v, w, x, y, z, i, F, 0x5a827999)
+#define GG(v, w, x, y, z, i) T(v, w, x, y, z, i, G, 0x6ed9eba1)
+#define HH(v, w, x, y, z, i) T(v, w, x, y, z, i, H, 0x8f1bbcdc)
+#define II(v, w, x, y, z, i) T(v, w, x, y, z, i, G, 0xca62c1d6)
+
+  /* --- The main compression function --- */
+
+  FF(a, b, c, d, e,  0);
+  FF(e, a, b, c, d,  1);
+  FF(d, e, a, b, c,  2);
+  FF(c, d, e, a, b,  3);
+  FF(b, c, d, e, a,  4);
+  FF(a, b, c, d, e,  5);
+  FF(e, a, b, c, d,  6);
+  FF(d, e, a, b, c,  7);
+  FF(c, d, e, a, b,  8);
+  FF(b, c, d, e, a,  9);
+  FF(a, b, c, d, e, 10);
+  FF(e, a, b, c, d, 11);
+  FF(d, e, a, b, c, 12);
+  FF(c, d, e, a, b, 13);
+  FF(b, c, d, e, a, 14);
+  FF(a, b, c, d, e, 15);
+  FF(e, a, b, c, d, 16);
+  FF(d, e, a, b, c, 17);
+  FF(c, d, e, a, b, 18);
+  FF(b, c, d, e, a, 19);
+
+  GG(a, b, c, d, e, 20);
+  GG(e, a, b, c, d, 21);
+  GG(d, e, a, b, c, 22);
+  GG(c, d, e, a, b, 23);
+  GG(b, c, d, e, a, 24);
+  GG(a, b, c, d, e, 25);
+  GG(e, a, b, c, d, 26);
+  GG(d, e, a, b, c, 27);
+  GG(c, d, e, a, b, 28);
+  GG(b, c, d, e, a, 29);
+  GG(a, b, c, d, e, 30);
+  GG(e, a, b, c, d, 31);
+  GG(d, e, a, b, c, 32);
+  GG(c, d, e, a, b, 33);
+  GG(b, c, d, e, a, 34);
+  GG(a, b, c, d, e, 35);
+  GG(e, a, b, c, d, 36);
+  GG(d, e, a, b, c, 37);
+  GG(c, d, e, a, b, 38);
+  GG(b, c, d, e, a, 39);
+
+  HH(a, b, c, d, e, 40);
+  HH(e, a, b, c, d, 41);
+  HH(d, e, a, b, c, 42);
+  HH(c, d, e, a, b, 43);
+  HH(b, c, d, e, a, 44);
+  HH(a, b, c, d, e, 45);
+  HH(e, a, b, c, d, 46);
+  HH(d, e, a, b, c, 47);
+  HH(c, d, e, a, b, 48);
+  HH(b, c, d, e, a, 49);
+  HH(a, b, c, d, e, 50);
+  HH(e, a, b, c, d, 51);
+  HH(d, e, a, b, c, 52);
+  HH(c, d, e, a, b, 53);
+  HH(b, c, d, e, a, 54);
+  HH(a, b, c, d, e, 55);
+  HH(e, a, b, c, d, 56);
+  HH(d, e, a, b, c, 57);
+  HH(c, d, e, a, b, 58);
+  HH(b, c, d, e, a, 59);
+
+  II(a, b, c, d, e, 60);
+  II(e, a, b, c, d, 61);
+  II(d, e, a, b, c, 62);
+  II(c, d, e, a, b, 63);
+  II(b, c, d, e, a, 64);
+  II(a, b, c, d, e, 65);
+  II(e, a, b, c, d, 66);
+  II(d, e, a, b, c, 67);
+  II(c, d, e, a, b, 68);
+  II(b, c, d, e, a, 69);
+  II(a, b, c, d, e, 70);
+  II(e, a, b, c, d, 71);
+  II(d, e, a, b, c, 72);
+  II(c, d, e, a, b, 73);
+  II(b, c, d, e, a, 74);
+  II(a, b, c, d, e, 75);
+  II(e, a, b, c, d, 76);
+  II(d, e, a, b, c, 77);
+  II(c, d, e, a, b, 78);
+  II(b, c, d, e, a, 79);
+
+  /* --- Update the chaining variables --- */
+
+  ctx->a += a;
+  ctx->b += b;
+  ctx->c += c;
+  ctx->d += d;
+  ctx->e += e;
+}
+
+/* --- @sha_init@ --- *
+ *
+ * Arguments:	@sha_ctx *ctx@ = pointer to context block to initialize
+ *
+ * Returns:	---
+ *
+ * Use:		Initializes a context block ready for hashing.
+ */
+
+void sha_init(sha_ctx *ctx)
+{
+  ctx->a = 0x67452301;
+  ctx->b = 0xefcdab89;
+  ctx->c = 0x98badcfe;
+  ctx->d = 0x10325476;
+  ctx->e = 0xc3d2e1f0;
+  ctx->off = 0;
+  ctx->count = 0;
+}
+
+/* --- @sha_set@ --- *
+ *
+ * Arguments:	@sha_ctx *ctx@ = pointer to context block
+ *		@const void *buf@ = pointer to state buffer
+ *		@unsigned long count@ = current count of bytes processed
+ *
+ * Returns:	---
+ *
+ * Use:		Initializes a context block from a given state.  This is
+ *		useful in cases where the initial hash state is meant to be
+ *		secret, e.g., for NMAC and HMAC support.
+ */
+
+void sha_set(sha_ctx *ctx, const void *buf, unsigned long count)
+{
+  const octet *p = buf;
+  ctx->a = LOAD32(p +  0);
+  ctx->b = LOAD32(p +  4);
+  ctx->c = LOAD32(p +  8);
+  ctx->d = LOAD32(p + 12);
+  ctx->e = LOAD32(p + 16);
+  ctx->off = 0;
+  ctx->count = count;
+}
+
+/* --- @sha_hash@ --- *
+ *
+ * Arguments:	@sha_ctx *ctx@ = pointer to context block
+ *		@const void *buf@ = buffer of data to hash
+ *		@size_t sz@ = size of buffer to hash
+ *
+ * Returns:	---
+ *
+ * Use:		Hashes a buffer of data.  The buffer may be of any size and
+ *		alignment.
+ */
+
+void sha_hash(sha_ctx *ctx, const void *buf, size_t sz)
+{
+  HASH_BUFFER(SHA, sha, ctx, buf, sz);
+}
+
+/* --- @sha_done@ --- *
+ *
+ * Arguments:	@sha_ctx *ctx@ = pointer to context block
+ *		@void *hash@ = pointer to output buffer
+ *
+ * Returns:	---
+ *
+ * Use:		Returns the hash of the data read so far.
+ */
+
+void sha_done(sha_ctx *ctx, void *hash)
+{
+  octet *p = hash;
+  HASH_PAD(SHA, sha, ctx, 0x80, 0, 8);
+  STORE32(ctx->buf + SHA_BUFSZ - 8, ctx->count >> 29);
+  STORE32(ctx->buf + SHA_BUFSZ - 4, ctx->count << 3);
+  sha_compress(ctx, ctx->buf);
+  STORE32(p +  0, ctx->a);
+  STORE32(p +  4, ctx->b);
+  STORE32(p +  8, ctx->c);
+  STORE32(p + 12, ctx->d);
+  STORE32(p + 16, ctx->e);
+}
+
+/* --- @sha_state@ --- *
+ *
+ * Arguments:	@sha_ctx *ctx@ = pointer to context
+ *		@void *state@ = pointer to buffer for current state
+ *
+ * Returns:	Number of bytes written to the hash function so far.
+ *
+ * Use:		Returns the current state of the hash function such that
+ *		it can be passed to @sha_set@.
+ */
+
+unsigned long sha_state(sha_ctx *ctx, void *state)
+{
+  octet *p = state;
+  STORE32(p +  0, ctx->a);
+  STORE32(p +  4, ctx->b);
+  STORE32(p +  8, ctx->c);
+  STORE32(p + 12, ctx->d);
+  STORE32(p + 16, ctx->e);
+  return (ctx->count);
+}
+
+/* --- Test code --- */
+
+HASH_TEST(SHA, sha)
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/sha.h b/sha.h
new file mode 100644
index 0000000..8f72def
--- /dev/null
+++ b/sha.h
@@ -0,0 +1,157 @@
+/* -*-c-*-
+ *
+ * $Id: sha.h,v 1.1 1999/09/03 08:41:12 mdw Exp $
+ *
+ * Implementation of the SHA-1 hash function
+ *
+ * (c) 1999 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: sha.h,v $
+ * Revision 1.1  1999/09/03 08:41:12  mdw
+ * Initial import.
+ *
+ */
+
+/*----- Notes on the SHA-1 hash function ----------------------------------*
+ *
+ * SHA (Secure Hash Algorithm) was designed by the NSA, for use with the
+ * Digital Signature Algorithm.  It has gained wide acceptance since then,
+ * and is probably now most people's collision-resistant function of choice.
+ * The author prefers RIPEMD-160, for no particularly good reasons.
+ */
+
+#ifndef SHA_H
+#define SHA_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <mLib/bits.h>
+
+/*----- Magic numbers -----------------------------------------------------*/
+
+#define SHA_BUFSZ 64
+#define SHA_HASHSZ 20
+
+/*----- Data structures ---------------------------------------------------*/
+
+typedef struct sha_ctx {
+  uint32 a, b, c, d, e;			/* Chaining variables */
+  unsigned long count;			/* Byte count so far */
+  int off;				/* Offset into buffer */
+  octet buf[SHA_BUFSZ];			/* Accumulation buffer */
+} sha_ctx;
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @sha_compress@ --- *
+ *
+ * Arguments:	@sha_ctx *ctx@ = pointer to context block
+ *		@const void *sbuf@ = pointer to buffer of appropriate size
+ *
+ * Returns:	---
+ *
+ * Use:		SHA compression function.
+ */
+
+extern void sha_compress(sha_ctx */*ctx*/, const void */*sbuf*/);
+
+/* --- @sha_init@ --- *
+ *
+ * Arguments:	@sha_ctx *ctx@ = pointer to context block to initialize
+ *
+ * Returns:	---
+ *
+ * Use:		Initializes a context block ready for hashing.
+ */
+
+extern void sha_init(sha_ctx */*ctx*/);
+
+/* --- @sha_set@ --- *
+ *
+ * Arguments:	@sha_ctx *ctx@ = pointer to context block
+ *		@const void *buf@ = pointer to state buffer
+ *		@unsigned long count@ = current count of bytes processed
+ *
+ * Returns:	---
+ *
+ * Use:		Initializes a context block from a given state.  This is
+ *		useful in cases where the initial hash state is meant to be
+ *		secret, e.g., for NMAC and HMAC support.
+ */
+
+extern void sha_set(sha_ctx */*ctx*/, const void */*buf*/,
+		    unsigned long /*count*/);
+
+/* --- @sha_hash@ --- *
+ *
+ * Arguments:	@sha_ctx *ctx@ = pointer to context block
+ *		@const void *buf@ = buffer of data to hash
+ *		@size_t sz@ = size of buffer to hash
+ *
+ * Returns:	---
+ *
+ * Use:		Hashes a buffer of data.  The buffer may be of any size and
+ *		alignment.
+ */
+
+extern void sha_hash(sha_ctx */*ctx*/, const void */*buf*/, size_t /*sz*/);
+
+/* --- @sha_done@ --- *
+ *
+ * Arguments:	@sha_ctx *ctx@ = pointer to context block
+ *		@void *hash@ = pointer to output buffer
+ *
+ * Returns:	---
+ *
+ * Use:		Returns the hash of the data read so far.
+ */
+
+extern void sha_done(sha_ctx */*ctx*/, void */*hash*/);
+
+/* --- @sha_state@ --- *
+ *
+ * Arguments:	@sha_ctx *ctx@ = pointer to context
+ *		@void *state@ = pointer to buffer for current state
+ *
+ * Returns:	Number of bytes written to the hash function so far.
+ *
+ * Use:		Returns the current state of the hash function such that
+ *		it can be passed to @sha_set@.
+ */
+
+extern unsigned long sha_state(sha_ctx */*ctx*/, void */*state*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/tests/blowfish b/tests/blowfish
new file mode 100644
index 0000000..612dc7d
--- /dev/null
+++ b/tests/blowfish
@@ -0,0 +1,124 @@
+# Test vectors for Blowfish
+#
+# $Id: blowfish,v 1.1 1999/09/03 08:41:14 mdw Exp $
+
+# --- Main block cipher ---
+#
+# Taken from Bruce Schneier's web pages, I think...
+
+blowfish {
+  0000000000000000 0000000000000000 4ef997456198dd78;
+  ffffffffffffffff ffffffffffffffff 51866fd5b85ecb8a;
+  3000000000000000 1000000000000001 7d856f9a613063f2;
+  1111111111111111 1111111111111111 2466dd878b963c9d;
+  0123456789abcdef 1111111111111111 61f9c3802281b096;
+  1111111111111111 0123456789abcdef 7d0cc630afda1ec7;
+  0000000000000000 0000000000000000 4ef997456198dd78;
+  fedcba9876543210 0123456789abcdef 0aceab0fc6a0a28d;
+  7ca110454a1a6e57 01a1d6d039776742 59c68245eb05282b;
+  0131d9619dc1376e 5cd54ca83def57da b1b8cc0b250f09a0;
+  07a1133e4a0b2686 0248d43806f67172 1730e5778bea1da4;
+  3849674c2602319e 51454b582ddf440a a25e7856cf2651eb;
+  04b915ba43feb5b6 42fd443059577fa2 353882b109ce8f1a;
+  0113b970fd34f2ce 059b5e0851cf143a 48f4d0884c379918;
+  0170f175468fb5e6 0756d8e0774761d2 432193b78951fc98;
+  43297fad38e373fe 762514b829bf486a 13f04154d69d1ae5;
+  07a7137045da2a16 3bdd119049372802 2eedda93ffd39c79;
+  04689104c2fd3b2f 26955f6835af609a d887e0393c2da6e3;
+  37d06bb516cb7546 164d5e404f275232 5f99d04f5b163969;
+  1f08260d1ac2465e 6b056e18759f5cca 4a057a3b24d3977b;
+  584023641aba6176 004bd6ef09176062 452031c1e4fada8e;
+  025816164629b007 480d39006ee762f2 7555ae39f59b87bd;
+  49793ebc79b3258f 437540c8698f3cfa 53c55f9cb49fc019;
+  4fb05e1515ab73a7 072d43a077075292 7a8e7bfa937e89a3;
+  49e95d6d4ca229bf 02fe55778117f12a cf9c5d7a4986adb5;
+  018310dc409b26d6 1d9d5c5018f728c2 d1abb290658bc778;
+  1c587f1c13924fef 305532286d6f295a 55cb3774d13ef201;
+  0101010101010101 0123456789abcdef fa34ec4847b268b2;
+  1f1f1f1f0e0e0e0e 0123456789abcdef a790795108ea3cae;
+  e0fee0fef1fef1fe 0123456789abcdef c39e072d9fac631d;
+  0000000000000000 ffffffffffffffff 014933e0cdaff6e4;
+  ffffffffffffffff 0000000000000000 f21e9a77b71c49bc;
+  0123456789abcdef 0000000000000000 245946885754369a;
+  fedcba9876543210 ffffffffffffffff 6b5c5a9c5d9e0a5a;
+}
+
+# --- Key schedule test ---
+#
+# From wherever the previous tests came from.
+
+blowfish-sched {
+
+  f0						  
+	fedcba9876543210	f9ad597c49db005e;
+
+  f0e1						  
+	fedcba9876543210	e91d21c1d961a6d6;
+
+  f0e1d2					  
+	fedcba9876543210	e9c2b70a1bc65cf3;
+
+  f0e1d2c3					  
+	fedcba9876543210	be1e639408640f05;
+
+  f0e1d2c3b4					  
+	fedcba9876543210	b39e44481bdb1e6e;
+
+  f0e1d2c3b4a5					  
+	fedcba9876543210	9457aa83b1928c0d;
+
+  f0e1d2c3b4a596				  
+	fedcba9876543210	8bb77032f960629d;
+
+  f0e1d2c3b4a59687				  
+	fedcba9876543210	e87a244e2cc85e82;
+
+  f0e1d2c3b4a5968778				  
+	fedcba9876543210	15750e7a4f4ec577;
+
+  f0e1d2c3b4a596877869				  
+	fedcba9876543210	122ba70b3ab64ae0;
+
+  f0e1d2c3b4a5968778695a			  
+	fedcba9876543210	3a833c9affc537f6;
+
+  f0e1d2c3b4a5968778695a4b			  
+	fedcba9876543210	9409da87a90f6bf2;
+
+  f0e1d2c3b4a5968778695a4b3c			  
+	fedcba9876543210	884f80625060b8b4;
+
+  f0e1d2c3b4a5968778695a4b3c2d			  
+	fedcba9876543210	1f85031c19e11968;
+
+  f0e1d2c3b4a5968778695a4b3c2d1e		  
+	fedcba9876543210	79d9373a714ca34f;
+
+  f0e1d2c3b4a5968778695a4b3c2d1e0f		  
+	fedcba9876543210	93142887ee3be15c;
+
+  f0e1d2c3b4a5968778695a4b3c2d1e0f00		  
+	fedcba9876543210	03429e838ce2d14b;
+
+  f0e1d2c3b4a5968778695a4b3c2d1e0f0011		  
+	fedcba9876543210	a4299e27469ff67b;
+
+  f0e1d2c3b4a5968778695a4b3c2d1e0f001122	  
+	fedcba9876543210	afd5aed1c1bc96a8;
+
+  f0e1d2c3b4a5968778695a4b3c2d1e0f00112233	  
+	fedcba9876543210	10851c0e3858da9f;
+
+  f0e1d2c3b4a5968778695a4b3c2d1e0f0011223344	  
+	fedcba9876543210	e6f51ed79b9db21f;
+
+  f0e1d2c3b4a5968778695a4b3c2d1e0f001122334455	  
+	fedcba9876543210	64a6e14afd36b46f;
+
+  f0e1d2c3b4a5968778695a4b3c2d1e0f00112233445566  
+	fedcba9876543210	80c7d7d45a5479ad;
+
+  f0e1d2c3b4a5968778695a4b3c2d1e0f0011223344556677
+	fedcba9876543210	05044b62fa52d080;
+
+}
diff --git a/tests/des b/tests/des
new file mode 100644
index 0000000..c4fbba1
--- /dev/null
+++ b/tests/des
@@ -0,0 +1,19 @@
+# Test vectors for DES
+#
+# $Id: des,v 1.1 1999/09/03 08:41:14 mdw Exp $
+
+des {
+  # --- 7-byte keys ---
+
+  00451338957377 4e6f772069732074 3fa40e8a984d4815;
+  b6c74cbf60c1fd 328da675ff5abd2c cd3e9f9b670671d1;
+
+  # --- 8-byte keys ---
+
+  0123456789abcdef 4e6f772069732074 3fa40e8a984d4815;
+  0022446688aaccee 4e6f772069732074 3fa40e8a984d4815;
+  0123456789abcdef 68652074696d6520 6a271787ab8883f9;
+  0123456789abcdef 666f7220616c6c20 893d51ec4b563b53;
+  0123456789abcdef 0123456789abcde7 c95744256a5ed31d;
+  b763d297f70606fb 328da675ff5abd2c cd3e9f9b670671d1;
+}
diff --git a/tests/des3 b/tests/des3
new file mode 100644
index 0000000..0f8b3d1
--- /dev/null
+++ b/tests/des3
@@ -0,0 +1,43 @@
+# Test vectors for double and triple DES
+#
+# $Id: des3,v 1.1 1999/09/03 08:41:14 mdw Exp $
+
+des3 {
+  # --- Some simple single-DES things ---
+
+  00451338957377 4e6f772069732074 3fa40e8a984d4815;
+  b6c74cbf60c1fd 328da675ff5abd2c cd3e9f9b670671d1;
+
+  0123456789abcdef 4e6f772069732074 3fa40e8a984d4815;
+  0123456789abcdef 68652074696d6520 6a271787ab8883f9;
+  0123456789abcdef 666f7220616c6c20 893d51ec4b563b53;
+  0123456789abcdef 0123456789abcde7 c95744256a5ed31d;
+
+  0045133895737700451338957377 4e6f772069732074 3fa40e8a984d4815;
+  b6c74cbf60c1fdb6c74cbf60c1fd 328da675ff5abd2c cd3e9f9b670671d1;
+
+  0123456789abcdef0123456789abcdef 4e6f772069732074 3fa40e8a984d4815;
+  0123456789abcdef0123456789abcdef 68652074696d6520 6a271787ab8883f9;
+  0123456789abcdef0123456789abcdef 666f7220616c6c20 893d51ec4b563b53;
+  0123456789abcdef0123456789abcdef 0123456789abcde7 c95744256a5ed31d;
+
+  004513389573770045133895737700451338957377
+	4e6f772069732074 3fa40e8a984d4815;
+  b6c74cbf60c1fdb6c74cbf60c1fdb6c74cbf60c1fd
+	328da675ff5abd2c cd3e9f9b670671d1;
+
+  0123456789abcdef0123456789abcdef0123456789abcdef
+	4e6f772069732074 3fa40e8a984d4815;
+  0123456789abcdef0123456789abcdef0123456789abcdef
+	68652074696d6520 6a271787ab8883f9;
+  0123456789abcdef0123456789abcdef0123456789abcdef
+	666f7220616c6c20 893d51ec4b563b53;
+  0123456789abcdef0123456789abcdef0123456789abcdef
+	0123456789abcde7 c95744256a5ed31d;
+
+  # --- Genuine longer keys ---
+
+  0123456789abcdeffedcba9876543210 0123456789abcde7 7f1d0a77826b8aff;
+  0123456789abcdeffedcba987654321089abcdef01234567
+	0123456789abcde7 de0b7c06ae5e0ed5;
+}
diff --git a/tests/idea b/tests/idea
new file mode 100644
index 0000000..f6a7675
--- /dev/null
+++ b/tests/idea
@@ -0,0 +1,9 @@
+# Test vectors for IDEA
+#
+# $Id: idea,v 1.1 1999/09/03 08:41:14 mdw Exp $
+
+# --- Thin on the ground, these are ---
+
+idea {
+  00010002000300040005000600070008 0000000100020003 11fbed2b01986de5;
+}
diff --git a/tests/md4 b/tests/md4
new file mode 100644
index 0000000..551b6a3
--- /dev/null
+++ b/tests/md4
@@ -0,0 +1,60 @@
+# Test vectors for MD4 hash function
+#
+# $Id: md4,v 1.1 1999/09/03 08:41:14 mdw Exp $
+
+# --- Basic hash function ---
+#
+# Test vectors from RFC1186 and some others I generated using the reference
+# implementation.
+
+md4 {
+
+  ""			31d6cfe0d16ae931b73c59d7e0c089c0;
+  "a"			bde52cb31de33e46245e05fbdbd6fb24;
+  "abc"			a448017aaf21d8525fc10ae87aa6729d;
+  "message digest"	d9130a8164549fe818874806e1c7014b;
+  "abcdefghijklmnopqrstuvwxyz"
+			d79e1c308aa5bbcdeea8ed63df412da9;
+  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+			043f8582f241db351ce627e153e7f0e4;
+}
+
+# --- HMAC mode ---
+#
+# No test vectors available.  The HMAC implementation has not been tested
+# against an external reference.  However, MD4 isn't strong enough to make
+# a realistic MAC anyway, and use of md4-mac is thoroughly deprecated.  The
+# HMAC code is autogenerated anyway, and ought to be reliable and correct.
+#
+# These test vectors are here to spot changes in behaviour rather than ensure
+# interoperability.  
+
+md4-hmac {
+  "Hi There"
+	0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b
+	90a79458f58f437e21f169cdba283da6;
+
+  "what do ya want for nothing?"
+	4a656665
+	be192c588a8e914d8a59b474a828128f;
+
+  "ÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝ"
+	aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+	75e5fb6e71ca6dcdd9fca269a9a3cd9c;
+
+  "ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ"
+	0102030405060708090a0b0c0d0e0f10111213141516171819
+	fb14cddf9efe11ad24033fc70f37bb9e;
+
+  "Test With Truncation"
+	0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c
+	6306262f9ba0e83f9ce3f15aafc23be8;
+
+  "Test Using Larger Than Block-Size Key - Hash Key First"
+	aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+	545b8f2577657042df628fbb98430d5f;
+
+  "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data"
+	aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+	0192f3442ad5d1ea5268306ab0d4962e;
+}
diff --git a/tests/md5 b/tests/md5
new file mode 100644
index 0000000..05f0bf1
--- /dev/null
+++ b/tests/md5
@@ -0,0 +1,84 @@
+# Test vectors for MD5 hash function
+#
+# $Id: md5,v 1.1 1999/09/03 08:41:14 mdw Exp $
+
+# --- Basic hash function ---
+#
+# Test vectors from RFC1321 and some others I generated using the reference
+# implementation.
+
+md5 {
+
+  ""			d41d8cd98f00b204e9800998ecf8427e;
+  "a"			0cc175b9c0f1b6a831c399e269772661;
+  "abc"			900150983cd24fb0d6963f7d28e17f72;
+  "message digest"	f96b697d7cb7938d525a2f31aaf161d0;
+  "abcdefghijklmnopqrstuvwxyz"
+			c3fcd3d76192e4007dfb496cca67e13b;
+  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+			d174ab98d277d9f5a5611c2c9f419d9f;
+  "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
+			57edf4a22be3c955ac49da2e2107b67a;
+
+  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+"			2b65a7ba4acd105ef2cb9adebd9f0bfa;
+
+}
+
+# --- HMAC mode ---
+#
+# Test vectors from RFC2104 and RFC2202.
+
+md5-hmac {
+
+  "Hi There"
+	0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b
+	9294727a3638bb1c13f48ef8158bfc9d;
+
+  "what do ya want for nothing?"
+	4a656665
+	750c783e6ab0b503eaa86e310a5db738;
+
+  "ÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝ"
+	aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+	56be34521d144c88dbb8c733f0e8b3f6;
+
+  "ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ"
+	0102030405060708090a0b0c0d0e0f10111213141516171819
+	697eaf0aca3a3aea3a75164746ffaa79;
+
+  "Test With Truncation"
+	0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c
+	56461ef2342edc00f9bab995690efd4c;
+
+  "Test Using Larger Than Block-Size Key - Hash Key First"
+	aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+	6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd;
+
+  "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data"
+	aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+	6f630fad67cda0ee1fb1f562db3aa53e;
+
+}
diff --git a/tests/rc4 b/tests/rc4
new file mode 100644
index 0000000..374430d
--- /dev/null
+++ b/tests/rc4
@@ -0,0 +1,28 @@
+# Test vectors for RC4
+#
+# $Id: rc4,v 1.1 1999/09/03 08:41:14 mdw Exp $
+
+# --- Note about these test vectors ---
+#
+# These test vectors come from two places: the Usenet article confirming that
+# the alleged RC4 source is interoperable with the BSAFE version, and some
+# tests I performed with the RC4-in-a-signature Perl version.  Some testing
+# with the SSLeay version shows that for an all-zero key I'm compatible, but
+# with any other I'm not.  Since I pass the Usenet test vectors (which I
+# found in the SSLeay sources!), and I'm compatible with at least one other
+# implementation, I'm happy that I'm in the right here.
+
+rc4-encrypt {
+  0123456789abcdef	0123456789abcdef	75b7878099e0c596;
+  0123456789abcdef
+	010101010101010101010101010101010101010101
+	7595c3e6114a09780c4ad452338e1ffd9a1be9498f;
+}
+
+rc4-generate {
+  00			0	de188941a3375d3a8a061e67576e926d;
+  01fe23dc45ba6798	0	643ded26ae29c15cda4f2b96d66baf2c;
+  08192a3b4c5d6e7f	1024	abb3a32a2ba4d409752923aa4032be16;
+  0123456789abcdef	0	7494c2e7104b0879;
+  ef012345		0	d6a141a7ec3c38dfbd61;
+}
diff --git a/tests/rc5 b/tests/rc5
new file mode 100644
index 0000000..340354f
--- /dev/null
+++ b/tests/rc5
@@ -0,0 +1,17 @@
+# Test vectors for RC5-32/12
+#
+# $Id: rc5,v 1.1 1999/09/03 08:41:14 mdw Exp $
+
+# --- Main cipher test vectors ---
+#
+# Taken from `The RC5 Encryption Algorithm' by Rivest.  Important: the
+# test vectors given in the paper are given as 32-bit words, not bytes, so
+# since RC5 is little-endian they need to be swapped here.
+
+rc5 {
+  00000000000000000000000000000000 0000000000000000 21a5dbee154b8f6d;
+  915f4619be41b2516355a50110a9ce91 21a5dbee154b8f6d f7c013ac5b2b8952;
+  783348e75aeb0f2fd7b169bb8dc16787 f7c013ac5b2b8952 2f42b3b70369fc92;
+  dc49db1375a5584f6485b413b5f12baf 2f42b3b70369fc92 65c178b284d197cc;
+  5269f149d41ba0152497574d7f153125 65c178b284d197cc eb44e415da319824;
+}
diff --git a/tests/rmd160 b/tests/rmd160
new file mode 100644
index 0000000..b1b0fc0
--- /dev/null
+++ b/tests/rmd160
@@ -0,0 +1,59 @@
+# Test vectors for RIPEMD-160
+#
+# $Id: rmd160,v 1.1 1999/09/03 08:41:14 mdw Exp $
+
+# --- Basic hash function ---
+#
+# Taken from the authors' web pages.
+
+rmd160 {
+  ""			9c1185a5c5e9fc54612808977ee8f548b2258d31;
+  "a"			0bdc9d2d256b3ee9daae347be6f4dc835a467ffe;
+  "abc"			8eb208f7e05d987a9b044a8e98c6b087f15a0bfc;
+  "message digest"	5d0689ef49d2fae572b881b123a85ffa21595f36;
+  "abcdefghijklmnopqrstuvwxyz"
+			f71c27109c692c1b56bbdceb5b9d2865b3708dbc;
+  "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+			12a053384a9c0c88e405a06c27dcf49ada62eb2b;
+  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+			b0e20b6e3116640286ed3a87a5713079b21f5189;
+  "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
+			9b752e45573d4b39f4dbd3323cab82bf63326bfb;
+
+}
+
+# --- HMAC mode ---
+#
+# Test vectors from RFC2286.
+
+rmd160-hmac {
+
+    "Hi There"
+	0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b
+	24cb4bd67d20fc1a5d2ed7732dcc39377f0a5668;
+
+  "what do ya want for nothing?"
+	4a656665
+	dda6c0213a485a9e24f4742064a7f033b43c4069;
+
+  "ÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝ"
+	aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+	b0b105360de759960ab4f35298e116e295d8e7c1;
+
+  "ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ"
+	0102030405060708090a0b0c0d0e0f10111213141516171819
+	d5ca862f4d21d5e610e18b4cf1beb97a4365ecf4;
+
+  "Test With Truncation"
+	0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c
+	7619693978f91d90539ae786500ff3d8e0518e39;
+
+  "Test Using Larger Than Block-Size Key - Hash Key First"
+	aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+	6466ca07ac5eac29e1bd523e5ada7605b791fd8b;
+
+  "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data"
+	aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+	69ea60798d71616cce5fd0871e23754cd75d5a0a;
+
+}
\ No newline at end of file
diff --git a/tests/sha b/tests/sha
new file mode 100644
index 0000000..15c94e0
--- /dev/null
+++ b/tests/sha
@@ -0,0 +1,83 @@
+# Test vectors for the SHA-1 hash function
+#
+# $Id: sha,v 1.1 1999/09/03 08:41:14 mdw Exp $
+
+# --- Basic hash function ---
+#
+# These were generated using the SSLeay implementation of SHA-1.
+
+sha {
+
+  ""			da39a3ee5e6b4b0d3255bfef95601890afd80709;
+  "a"			86f7e437faa5a7fce15d1ddcb9eaeaea377667b8;
+  "abc"			a9993e364706816aba3e25717850c26c9cd0d89d;
+  "message digest"	c12252ceda8be8994d5fa0290a47231c1d16aae3;
+  "abcdefghijklmnopqrstuvwxyz"
+			32d10c7b8cf96570ca04ce37f2a19d84240d3a89;
+  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+			761c457bf73b14d27e9e9265c46f4b4dda11f940;
+  "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
+			50abf5706a150990a08b2c5ea40fa0e585554732;
+
+  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
+"			f74d36bf17ee23c46ec166a48a24da6ab999eaea;
+
+}
+
+# --- HMAC mode ---
+#
+# Test vectors from RFC2202.
+
+sha-hmac {
+
+  "Hi There"
+	0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b
+	b617318655057264e28bc0b6fb378c8ef146be00;
+
+  "what do ya want for nothing?"
+	4a656665
+	effcdf6ae5eb2fa2d27416d5f184df9c259a7c79;
+
+  "ÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝ"
+	aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+	125d7342b9ac11cd91a39af48aa17b4f63f175d3;
+
+  "ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ"
+	0102030405060708090a0b0c0d0e0f10111213141516171819
+	4c9007f4026250c6bc8414f9bf50c86c2d7235da;
+
+  "Test With Truncation"
+	0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c
+	4c1a03424b55e07fe7f27be1d58bb9324a9a5a04;
+
+  "Test Using Larger Than Block-Size Key - Hash Key First"
+	aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+	aa4ae5e15272d00e95705637ce8a3b55ed402112;
+
+  "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data"
+	aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+	e8e99d0f45237d786d6bbaa7965c7808bbff1a91;
+
+}
-- 
2.11.0

