Initial import.
authormdw <mdw>
Fri, 3 Sep 1999 08:41:14 +0000 (08:41 +0000)
committermdw <mdw>
Fri, 3 Sep 1999 08:41:14 +0000 (08:41 +0000)
70 files changed:
.cvsignore [new file with mode: 0644]
.links [new file with mode: 0644]
.skelrc [new file with mode: 0644]
Makefile.m4 [new file with mode: 0644]
acconfig.h [new file with mode: 0644]
bf_ikey.h [new file with mode: 0644]
blkc.h [new file with mode: 0644]
blowfish.c [new file with mode: 0644]
blowfish.h [new file with mode: 0644]
cbc.h [new file with mode: 0644]
cfb.h [new file with mode: 0644]
configure.in [new file with mode: 0644]
daftstory.h [new file with mode: 0644]
des-base.c [new file with mode: 0644]
des-base.h [new file with mode: 0644]
des-mktab.c [new file with mode: 0644]
des.c [new file with mode: 0644]
des.h [new file with mode: 0644]
des3.c [new file with mode: 0644]
des3.h [new file with mode: 0644]
ecb.h [new file with mode: 0644]
genmodes [new file with mode: 0755]
hash.h [new file with mode: 0644]
hmac.h [new file with mode: 0644]
idea.c [new file with mode: 0644]
idea.h [new file with mode: 0644]
key.1 [new file with mode: 0644]
key.c [new file with mode: 0644]
key.h [new file with mode: 0644]
keyring.5 [new file with mode: 0644]
keyutil.c [new file with mode: 0644]
md4.c [new file with mode: 0644]
md4.h [new file with mode: 0644]
md5.c [new file with mode: 0644]
md5.h [new file with mode: 0644]
mp.c [new file with mode: 0644]
mp.h [new file with mode: 0644]
mpscan.c [new file with mode: 0644]
mpscan.h [new file with mode: 0644]
mptypes.h [new file with mode: 0644]
mpx.c [new file with mode: 0644]
mpx.h [new file with mode: 0644]
noise.c [new file with mode: 0644]
noise.h [new file with mode: 0644]
ofb.h [new file with mode: 0644]
papers/.cvsignore [new file with mode: 0644]
papers/crypto.sty [new file with mode: 0644]
papers/rand.tex [new file with mode: 0644]
paranoia.h [new file with mode: 0644]
rand.c [new file with mode: 0644]
rand.h [new file with mode: 0644]
rc4.c [new file with mode: 0644]
rc4.h [new file with mode: 0644]
rc5.c [new file with mode: 0644]
rc5.h [new file with mode: 0644]
rmd160.c [new file with mode: 0644]
rmd160.h [new file with mode: 0644]
setup [new file with mode: 0755]
sha.c [new file with mode: 0644]
sha.h [new file with mode: 0644]
tests/blowfish [new file with mode: 0644]
tests/des [new file with mode: 0644]
tests/des3 [new file with mode: 0644]
tests/idea [new file with mode: 0644]
tests/md4 [new file with mode: 0644]
tests/md5 [new file with mode: 0644]
tests/rc4 [new file with mode: 0644]
tests/rc5 [new file with mode: 0644]
tests/rmd160 [new file with mode: 0644]
tests/sha [new file with mode: 0644]

diff --git a/.cvsignore b/.cvsignore
new file mode 100644 (file)
index 0000000..ed9b81e
--- /dev/null
@@ -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 (file)
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 (file)
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 (file)
index 0000000..06e1b7d
--- /dev/null
@@ -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 (file)
index 0000000..b8e2eb5
--- /dev/null
@@ -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 (file)
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 (file)
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 (file)
index 0000000..16c862e
--- /dev/null
@@ -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 (file)
index 0000000..9f4fb56
--- /dev/null
@@ -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 (file)
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 (file)
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 (file)
index 0000000..4893b92
--- /dev/null
@@ -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 (file)
index 0000000..8715cdc
--- /dev/null
@@ -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 (file)
index 0000000..73246b6
--- /dev/null
@@ -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 (file)
index 0000000..dd4fdb7
--- /dev/null
@@ -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 (file)
index 0000000..07cff39
--- /dev/null
@@ -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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (executable)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
index 0000000..4c052b9
--- /dev/null
@@ -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 (file)
index 0000000..7c0dcb7
--- /dev/null
@@ -0,0 +1 @@
+\endinput
diff --git a/papers/rand.tex b/papers/rand.tex
new file mode 100644 (file)
index 0000000..77a986c
--- /dev/null
@@ -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 (file)
index 0000000..b2d19d8
--- /dev/null
@@ -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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (executable)
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 (file)
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 (file)
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 (file)
index 0000000..612dc7d
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..0f8b3d1
--- /dev/null
@@ -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 (file)
index 0000000..f6a7675
--- /dev/null
@@ -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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
index 0000000..b1b0fc0
--- /dev/null
@@ -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 (file)
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;
+
+}