From: mdw Date: Fri, 3 Sep 1999 08:41:14 +0000 (+0000) Subject: Initial import. X-Git-Url: https://git.distorted.org.uk/u/mdw/catacomb/commitdiff_plain/d03ab969116fe715d569304c1c474749b2f64529 Initial import. --- d03ab969116fe715d569304c1c474749b2f64529 diff --git a/.cvsignore b/.cvsignore new file mode 100644 index 0000000..ed9b81e --- /dev/null +++ b/.cvsignore @@ -0,0 +1,57 @@ +Makefile.am +Makefile.in +_mpdiv.c +aclocal.m4 +blowfish-cbc.c +blowfish-cbc.h +blowfish-cfb.c +blowfish-cfb.h +blowfish-ecb.c +blowfish-ecb.h +blowfish-ofb.c +blowfish-ofb.h +build +config.h.in +configure +des-cbc.c +des-cbc.h +des-cfb.c +des-cfb.h +des-ecb.c +des-ecb.h +des-ofb.c +des-ofb.h +des3-cbc.c +des3-cbc.h +des3-cfb.c +des3-cfb.h +des3-ecb.c +des3-ecb.h +des3-ofb.c +des3-ofb.h +des_sp.h +idea-cbc.c +idea-cbc.h +idea-cfb.c +idea-cfb.h +idea-ecb.c +idea-ecb.h +idea-ofb.c +idea-ofb.h +md4-hmac.c +md4-hmac.h +md5-hmac.c +md5-hmac.h +rc5-cbc.c +rc5-cbc.h +rc5-cfb.c +rc5-cfb.h +rc5-ecb.c +rc5-ecb.h +rc5-ofb.c +rc5-ofb.h +rmd160-hmac.c +rmd160-hmac.h +sha-hmac.c +sha-hmac.h +stamp-h.in diff --git a/.links b/.links new file mode 100644 index 0000000..a4f5a17 --- /dev/null +++ b/.links @@ -0,0 +1,6 @@ +COPYING.LIB +install-sh +missing +mkinstalldirs +getdate.y +getdate.h diff --git a/.skelrc b/.skelrc new file mode 100644 index 0000000..4847795 --- /dev/null +++ b/.skelrc @@ -0,0 +1,9 @@ +;;; -*-emacs-lisp-*- + +(setq skel-alist + (append + '((author . "Straylight/Edgeware") + (licence-text . "[[lgpl]]") + (full-title . "Catacomb") + (library . "Catacomb")) + skel-alist)) diff --git a/Makefile.m4 b/Makefile.m4 new file mode 100644 index 0000000..06e1b7d --- /dev/null +++ b/Makefile.m4 @@ -0,0 +1,173 @@ +## -*-makefile-*- +## +## $Id: Makefile.m4,v 1.1 1999/09/03 08:41:11 mdw Exp $ +## +## Makefile for Catacomb +## +## (c) 1999 Straylight/Edgeware +## + +##----- Licensing notice ---------------------------------------------------- +## +## This file is part of Catacomb. +## +## Catacomb is free software; you can redistribute it and/or modify +## it under the terms of the GNU Library General Public License as +## published by the Free Software Foundation; either version 2 of the +## License, or (at your option) any later version. +## +## Catacomb is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU Library General Public License for more details. +## +## You should have received a copy of the GNU Library General Public +## License along with Catacomb; if not, write to the Free +## Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, +## MA 02111-1307, USA. + +##----- Revision history ---------------------------------------------------- +## +## $Log: Makefile.m4,v $ +## Revision 1.1 1999/09/03 08:41:11 mdw +## Initial import. +## +## The `CVS' directory can't be hardlinked. Don't worry about this +## overmuch. +## +## + +AUTOMAKE_OPTIONS = foreign + +archincludedir = $(libdir)/catacomb/include + +## --- List handling macros --- +## +## List handling is nice, usually. Unfortunately, I based this design on +## TeX list macros rather than anything sensible... + +define(`_', `define(`_item', $1)define(`_item_2', $2)emit`'') +define(`adorn', `pushdef(`emit', `$1`'_item`'$3')$2`'popdef(`emit')') +define(`join', +`pushdef(`emit', `adorn(``_(''_item`$2', `$3', ``)'')`'')$1`'popdef(`emit')') +define(`addsuffix', `adorn(`', `$1', `$2')') +define(`lit', `adorn(`', `$1', `')') +define(`nl', ` +') + +define(`allwithsuffix', +`addsuffix(`$1', `$3') addsuffix(`$2', `$3') dnl +addsuffix(join(`$1', `-', `$2'), `$3')') + +## --- Autogenerated source files --- + +define(`ciphers', `_(des) _(des3) _(blowfish) _(idea) _(rc5)') +define(`cipher_modes', `_(ecb) _(cbc) _(cfb) _(ofb)') + +define(`hashes', `_(md5) _(md4) _(rmd160) _(sha)') +define(`hash_modes', `_(hmac)') + +MODES = \ + adorn(`$(srcdir)/', join(`ciphers', `-', `cipher_modes'), `.c') \ + adorn(`$(srcdir)/', join(`ciphers', `-', `cipher_modes'), `.h') \ + adorn(`$(srcdir)/', join(`hashes', `-', `hash_modes'), `.c') \ + adorn(`$(srcdir)/', join(`hashes', `-', `hash_modes'), `.h') + +$(MODES): $(srcdir)/genmodes + +archinclude_HEADERS = mptypes.h + +mptypes.h: mptypes + des_sp.h \ + ./mptypes >mptypes.h + +BUILT_SOURCES = \ + getdate.c \ + addsuffix(join(`ciphers', `-', `cipher_modes'), `.c') \ + addsuffix(join(`ciphers', `-', `cipher_modes'), `.h') \ +INCLUDES = -I$(srcdir)/.. + addsuffix(join(`hashes', `-', `hash_modes'), `.c') \ + addsuffix(join(`hashes', `-', `hash_modes'), `.h') +include_HEADERS = \ +libcatacomb_la_LDFLAGS = -version-info 0:4:0 +## Middle number is the patchlevel. Final number is the minor version. The +## difference between the first and last numbers is major version. + +define(`emit', `$1.h ') +pkginclude_HEADERS = \ + paranoia.h \ + blkc.h hash.h \ + mpx.h mpw.h mpscan.h mparena.h mp.h mptext.h mpmont.h \ + ptab.h pgen.h rabin.h \ + dsa.h dh.h \ + allwithsuffix(`ciphers', `cipher_modes', `.h') \ +libcatacomb_a_SOURCES = \ + +define(`emit', `$1.c ') +libcatacomb_la_SOURCES = \ + des-base.c des-base.h des_sp.h bf_ikey.h daftstory.h \ + ptab.c pgen.c rabin.c \ + dsa-sign.c dsa-verify.c dsa-gen.c \ + dh-prime.c \ + addsuffix(join(`ciphers', `-', `cipher_modes'), `.c') \ +noinst_PROGRAMS = des-mktab +LDADD = libcatacomb.a ../mLib/libmLib.a +## --- Utility programs --- +noinst_PROGRAMS = des-mktab mptypes +LDADD = libcatacomb.a +bin_SCRIPTS = catacomb-config +noinst_PROGRAMS = des-mktab genprimes mptypes +LDADD = libcatacomb.la +genprimes_SOURCES = genprimes.c +genprimes_LDADD = + +mptypes_SOURCES = mptypes.c +mptypes_LDADD = + +## --- Documentation --- + +man_MANS = key.1 keyring.5 + +## --- Other handy definitions --- + @ln $(srcdir)/tests/* $(distdir)/tests +EXTRA_DIST = Makefile.m4 genmodes $(man_MANS) + +dist-hook: + @ln getdate.c $(distdir) || ln $(srcdir)/getdate.c $(distdir) || true + @mkdir $(distdir)/tests + @ln $(srcdir)/tests/* $(distdir)/tests || true + @rm -f $(distdir)/tests/*~ +TESTS = \ + rc4.t \ + addsuffix(join(`ciphers', `-', `cipher_modes'), `.t') \ + addsuffix(join(`hashes', `-', `hash_modes'), `.t') \ + addsuffix(`ciphers', `.t') addsuffix(`hashes', `.t') + $(COMPILE) -DTEST_RIG -DSRCDIR=\"$(srcdir)\" \ + $(srcdir)/$1.c libcatacomb.a ../mLib/libmLib.a -o $1.t') + +`$1.t: $1.c libcatacomb.a + $(COMPILE) -DTEST_RIG -DSRCDIR=\"$(srcdir)\" $(srcdir)/$1.c libcatacomb.a $(LIBS) -o $1.t') + $1.t)dnl +$1.t: $1.c libcatacomb.la + +adorn(`nl`'CTESTRIG(', `ciphers', `)') +adorn(`nl`'CTESTRIG(', `hashes', `)') +CLEANFILES = *.t +adorn(`nl`'CTESTRIG(', join(`hashes', `-', `hash_modes'), `)') +CTESTRIG(dsa-verify) +CLEANFILES = *.t mptypes.h +TESTS = testprogs + +CLEANFILES = *.t mptypes.h des_sp.h ptab.c ptab.h + +## --- Makefile building (haha!) --- + +$(srcdir)/Makefile.am: $(srcdir)/Makefile.m4 + m4 $(srcdir)/Makefile.m4 >$(srcdir)/Makefile.am + $(MODES) $(srcdir)/des_sp.h \ + $(srcdir)/getdate.c getdate.c + $(srcdir)/Makefile.am \ + $(srcdir)/getdate.c getdate.c \ + $(MODES) + +##----- That's all, folks --------------------------------------------------- diff --git a/acconfig.h b/acconfig.h new file mode 100644 index 0000000..b8e2eb5 --- /dev/null +++ b/acconfig.h @@ -0,0 +1,68 @@ +/* -*-c-*- + * + * $Id: acconfig.h,v 1.1 1999/09/03 08:41:11 mdw Exp $ + * + * Configuration header for Catacomb + * + * (c) 1999 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: acconfig.h,v $ + * Revision 1.1 1999/09/03 08:41:11 mdw + * Initial import. + * + */ + +#ifndef ACCONFIG_H +#define ACCONFIG_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Autoconfiguration data --------------------------------------------*/ +@TOP@ + +/* Package and version number. */ +#define PACKAGE "catacomb" +#define VERSION "1.0.0" + +/* If it's not provided already, define to be a signed integer type capable + * of representing any object size. (If in doubt, make it an `int'.) */ +#undef ssize_t + +/* If not provided already, define to be whatever your system uses for + * representing time. (Probably `long'.) */ +#undef time_t + +@BOTTOM@ + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/bf_ikey.h b/bf_ikey.h new file mode 100644 index 0000000..9d602fb --- /dev/null +++ b/bf_ikey.h @@ -0,0 +1,324 @@ +/* -*-c-*- + * + * $Id: bf_ikey.h,v 1.1 1999/09/03 08:41:11 mdw Exp $ + * + * Blowfish initial key data + * + * (c) 1998 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: bf_ikey.h,v $ + * Revision 1.1 1999/09/03 08:41:11 mdw + * Initial import. + * + */ + +#ifndef BF_IKEY_H +#define BF_IKEY_H + +/*----- Macros ------------------------------------------------------------*/ + +#define BLOWFISH_IKEY { \ + \ + /* --- P-array of round-specific subkeys --- */ \ + \ + { 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, \ + 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, \ + 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, \ + 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, \ + 0x9216d5d9, 0x8979fb1b }, \ + \ + /* --- First S-box --- */ \ + \ + { 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, \ + 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, \ + 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, \ + 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, \ + 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, \ + 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, \ + 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, \ + 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, \ + 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, \ + 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, \ + 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, \ + 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, \ + 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, \ + 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, \ + 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, \ + 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, \ + 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, \ + 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, \ + 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, \ + 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, \ + 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, \ + 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, \ + 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, \ + 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, \ + 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, \ + 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, \ + 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, \ + 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, \ + 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, \ + 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, \ + 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, \ + 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, \ + 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, \ + 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, \ + 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, \ + 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, \ + 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, \ + 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, \ + 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, \ + 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, \ + 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, \ + 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, \ + 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, \ + 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, \ + 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, \ + 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, \ + 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, \ + 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, \ + 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, \ + 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, \ + 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, \ + 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, \ + 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, \ + 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, \ + 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, \ + 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, \ + 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, \ + 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, \ + 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, \ + 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, \ + 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, \ + 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, \ + 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, \ + 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a }, \ + \ + /* --- Second S-box --- */ \ + \ + { 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, \ + 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, \ + 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, \ + 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, \ + 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, \ + 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, \ + 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, \ + 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, \ + 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, \ + 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, \ + 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, \ + 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, \ + 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, \ + 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, \ + 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, \ + 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, \ + 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, \ + 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, \ + 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, \ + 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, \ + 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, \ + 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, \ + 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, \ + 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, \ + 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, \ + 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, \ + 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, \ + 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, \ + 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, \ + 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, \ + 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, \ + 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, \ + 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, \ + 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, \ + 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, \ + 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, \ + 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, \ + 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, \ + 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, \ + 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, \ + 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, \ + 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, \ + 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, \ + 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, \ + 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, \ + 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, \ + 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, \ + 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, \ + 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, \ + 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, \ + 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, \ + 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, \ + 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, \ + 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, \ + 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, \ + 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, \ + 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, \ + 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, \ + 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, \ + 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, \ + 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, \ + 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, \ + 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, \ + 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7 }, \ + \ + /* --- Third S-box --- */ \ + \ + { 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, \ + 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, \ + 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, \ + 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, \ + 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, \ + 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, \ + 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, \ + 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, \ + 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, \ + 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, \ + 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, \ + 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, \ + 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, \ + 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, \ + 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, \ + 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, \ + 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, \ + 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, \ + 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, \ + 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, \ + 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, \ + 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, \ + 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, \ + 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, \ + 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, \ + 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, \ + 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, \ + 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, \ + 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, \ + 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, \ + 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, \ + 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, \ + 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, \ + 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, \ + 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, \ + 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, \ + 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, \ + 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, \ + 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, \ + 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, \ + 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, \ + 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, \ + 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, \ + 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, \ + 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, \ + 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, \ + 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, \ + 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, \ + 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, \ + 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, \ + 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, \ + 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, \ + 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, \ + 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, \ + 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, \ + 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, \ + 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, \ + 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, \ + 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, \ + 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, \ + 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, \ + 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, \ + 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, \ + 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0 }, \ + \ + /* --- Fourth and final S-box --- */ \ + \ + { 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, \ + 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, \ + 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, \ + 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, \ + 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, \ + 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, \ + 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, \ + 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, \ + 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, \ + 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, \ + 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, \ + 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, \ + 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, \ + 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, \ + 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, \ + 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, \ + 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, \ + 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, \ + 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, \ + 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, \ + 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, \ + 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, \ + 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, \ + 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, \ + 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, \ + 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, \ + 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, \ + 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, \ + 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, \ + 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, \ + 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, \ + 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, \ + 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, \ + 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, \ + 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, \ + 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, \ + 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, \ + 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, \ + 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, \ + 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, \ + 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, \ + 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, \ + 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, \ + 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, \ + 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, \ + 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, \ + 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, \ + 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, \ + 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, \ + 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, \ + 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, \ + 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, \ + 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, \ + 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, \ + 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, \ + 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, \ + 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, \ + 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, \ + 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, \ + 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, \ + 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, \ + 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, \ + 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, \ + 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6 } \ +} + +/*----- That's all, folks -------------------------------------------------*/ + +#endif diff --git a/blkc.h b/blkc.h new file mode 100644 index 0000000..e7f5518 --- /dev/null +++ b/blkc.h @@ -0,0 +1,281 @@ +/* -*-c-*- + * + * $Id: blkc.h,v 1.1 1999/09/03 08:41:11 mdw Exp $ + * + * Common definitions for block ciphers + * + * (c) 1999 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: blkc.h,v $ + * Revision 1.1 1999/09/03 08:41:11 mdw + * Initial import. + * + */ + +#ifndef BLKC_H +#define BLKC_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#include + +/*----- 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 +#include + +#define BLKC_TEST(PRE, pre) \ + \ +static int verify(dstr *v) \ +{ \ + pre ## _ctx k; \ + uint32 p[PRE ## _BLKSZ / 4]; \ + uint32 c[PRE ## _BLKSZ / 4]; \ + uint32 d[PRE ## _BLKSZ / 4]; \ + dstr b = DSTR_INIT; \ + int ok = 1; \ + \ + /* --- Initialize the key buffer --- */ \ + \ + dstr_ensure(&b, PRE ## _BLKSZ); \ + b.len = PRE ## _BLKSZ; \ + pre ## _init(&k, v[0].buf, v[0].len); \ + BLKC_LOAD(PRE, p, v[1].buf); \ + BLKC_LOAD(PRE, c, v[2].buf); \ + \ + /* --- Test encryption --- */ \ + \ + BLKC_MOVE(PRE, d, p); \ + pre ## _eblk(&k, d, d); \ + BLKC_STORE(PRE, b.buf, d); \ + if (memcmp(b.buf, v[2].buf, PRE ## _BLKSZ)) { \ + ok = 0; \ + printf("\nfail encryption:" \ + "\n\tkey = "); \ + type_hex.dump(&v[0], stdout); \ + printf("\n\tplaintext = "); type_hex.dump(&v[1], stdout); \ + printf("\n\texpected = "); type_hex.dump(&v[2], stdout); \ + printf("\n\tcalculated = "); type_hex.dump(&b, stdout); \ + putchar('\n'); \ + } \ + \ + /* --- Test decryption --- */ \ + \ + BLKC_MOVE(PRE, d, c); \ + pre ## _dblk(&k, d, d); \ + BLKC_STORE(PRE, b.buf, d); \ + if (memcmp(b.buf, v[1].buf, PRE ## _BLKSZ)) { \ + ok = 0; \ + printf("\nfail decryption:" \ + "\n\tkey = "); \ + type_hex.dump(&v[0], stdout); \ + printf("\n\tciphertext = "); type_hex.dump(&v[2], stdout); \ + printf("\n\texpected = "); type_hex.dump(&v[1], stdout); \ + printf("\n\tcalculated = "); type_hex.dump(&b, stdout); \ + putchar('\n'); \ + } \ + \ + /* --- Return --- */ \ + \ + return (ok); \ +} \ + \ +static test_chunk defs[] = { \ + { #pre, verify, { &type_hex, &type_hex, &type_hex, 0 } }, \ + { #pre "-sched", verify, { &type_hex, &type_hex, &type_hex, 0 } }, \ + { 0, 0, { 0 } } \ +}; \ + \ +int main(int argc, char *argv[]) \ +{ \ + test_run(argc, argv, defs, SRCDIR"/tests/" #pre); \ + return (0); \ +} + +#else +# define BLKC_TEST(PRE, pre) +#endif + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/blowfish.c b/blowfish.c new file mode 100644 index 0000000..16c862e --- /dev/null +++ b/blowfish.c @@ -0,0 +1,208 @@ +/* -*-c-*- + * + * $Id: blowfish.c,v 1.1 1999/09/03 08:41:11 mdw Exp $ + * + * The Blowfish block cipher + * + * (c) 1998 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: blowfish.c,v $ + * Revision 1.1 1999/09/03 08:41:11 mdw + * Initial import. + * + */ + +/*----- Header files ------------------------------------------------------*/ + +#include + +#include "blowfish.h" +#include "bf_ikey.h" +#include "blkc.h" +#include "paranoia.h" + +/*----- Global variables --------------------------------------------------*/ + +static blowfish_ctx ikey = BLOWFISH_IKEY; + +/*----- Macros ------------------------------------------------------------*/ + +#define ROUND(k, x, y, r) \ + ((x) ^= (k)->p[(r)], \ + (y) ^= (((((k)->s0[((x) >> 24) & MASK8]) + \ + ((k)->s1[((x) >> 16) & MASK8])) ^ \ + ((k)->s2[((x) >> 8) & MASK8])) + \ + ((k)->s3[((x) >> 0) & MASK8]))) + +#define EBLK(k, a, b, c, d) do { \ + uint32 _x = (a); \ + uint32 _y = (b); \ + ROUND((k), _x, _y, 0); \ + ROUND((k), _y, _x, 1); \ + ROUND((k), _x, _y, 2); \ + ROUND((k), _y, _x, 3); \ + ROUND((k), _x, _y, 4); \ + ROUND((k), _y, _x, 5); \ + ROUND((k), _x, _y, 6); \ + ROUND((k), _y, _x, 7); \ + ROUND((k), _x, _y, 8); \ + ROUND((k), _y, _x, 9); \ + ROUND((k), _x, _y, 10); \ + ROUND((k), _y, _x, 11); \ + ROUND((k), _x, _y, 12); \ + ROUND((k), _y, _x, 13); \ + ROUND((k), _x, _y, 14); \ + ROUND((k), _y, _x, 15); \ + (c) = _y ^ (k)->p[17]; \ + (d) = _x ^ (k)->p[16]; \ +} while (0) + +#define DBLK(k, a, b, c, d) do { \ + uint32 _x = (a); \ + uint32 _y = (b); \ + ROUND((k), _x, _y, 17); \ + ROUND((k), _y, _x, 16); \ + ROUND((k), _x, _y, 15); \ + ROUND((k), _y, _x, 14); \ + ROUND((k), _x, _y, 13); \ + ROUND((k), _y, _x, 12); \ + ROUND((k), _x, _y, 11); \ + ROUND((k), _y, _x, 10); \ + ROUND((k), _x, _y, 9); \ + ROUND((k), _y, _x, 8); \ + ROUND((k), _x, _y, 7); \ + ROUND((k), _y, _x, 6); \ + ROUND((k), _x, _y, 5); \ + ROUND((k), _y, _x, 4); \ + ROUND((k), _x, _y, 3); \ + ROUND((k), _y, _x, 2); \ + (c) = _y ^ (k)->p[0]; \ + (d) = _x ^ (k)->p[1]; \ +} while (0) + +/*----- Low-level encryption interface ------------------------------------*/ + +/* --- @blowfish_init@ --- * + * + * Arguments: @blowfish_ctx *k@ = pointer to key block to fill in + * @const void *buf@ = pointer to buffer of key material + * @size_t sz@ = size of key material + * + * Returns: --- + * + * Use: Initializes a Blowfish key buffer. Blowfish accepts + * a more-or-less arbitrary size key. + */ + +void blowfish_init(blowfish_ctx *k, const void *buf, size_t sz) +{ + /* --- Copy the initial value over --- */ + + memcpy(k, &ikey, sizeof(ikey)); + + /* --- Initialize the %$P$% array --- */ + + { + const octet *p = buf; + const octet *q = p + sz; + int i = 0, j = 0; + uint32 x = 0; + + while (i < 18) { + x = (x << 8) | U8(*p++); + if (p >= q) + p = buf; + if (++j >= 4) { + k->p[i++] ^= x; + x = 0; + j = 0; + } + } + + x = 0; + } + + /* --- Now mangle the complete array of keys --- */ + + { + uint32 b[2]; + int i; + + b[0] = b[1] = 0; + + for (i = 0; i < 18; i += 2) { + blowfish_eblk(k, b, b); + k->p[i] = b[0]; k->p[i + 1] = b[1]; + } + + for (i = 0; i < 256; i += 2) { + blowfish_eblk(k, b, b); + k->s0[i] = b[0]; k->s0[i + 1] = b[1]; + } + + for (i = 0; i < 256; i += 2) { + blowfish_eblk(k, b, b); + k->s1[i] = b[0]; k->s1[i + 1] = b[1]; + } + + for (i = 0; i < 256; i += 2) { + blowfish_eblk(k, b, b); + k->s2[i] = b[0]; k->s2[i + 1] = b[1]; + } + + for (i = 0; i < 256; i += 2) { + blowfish_eblk(k, b, b); + k->s3[i] = b[0]; k->s3[i + 1] = b[1]; + } + + BURN(b); + } +} + +/* --- @blowfish_eblk@, @blowfish_dblk@ --- * + * + * Arguments: @const blowfish_ctx *k@ = pointer to key block + * @const uint32 s[2]@ = pointer to source block + * @uint32 d[2]@ = pointer to destination block + * + * Returns: --- + * + * Use: Low-level block encryption and decryption. + */ + +void blowfish_eblk(const blowfish_ctx *k, const uint32 *s, uint32 *d) +{ + EBLK(k, s[0], s[1], d[0], d[1]); +} + +void blowfish_dblk(const blowfish_ctx *k, const uint32 *s, uint32 *d) +{ + DBLK(k, s[0], s[1], d[0], d[1]); +} + +BLKC_TEST(BLOWFISH, blowfish) + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/blowfish.h b/blowfish.h new file mode 100644 index 0000000..9f4fb56 --- /dev/null +++ b/blowfish.h @@ -0,0 +1,113 @@ +/* -*-c-*- + * + * $Id: blowfish.h,v 1.1 1999/09/03 08:41:11 mdw Exp $ + * + * The Blowfish block cipher + * + * (c) 1998 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: blowfish.h,v $ + * Revision 1.1 1999/09/03 08:41:11 mdw + * Initial import. + * + */ + +/*----- Notes on the Blowfish block cipher --------------------------------* + * + * Blowfish was invented by Bruce Schneier. The algorithm is unpatented and + * free for anyone to use. It's fast, simple, offers a big key, and is + * looking relatively bulletproof. It's also this author's block cipher of + * choice, for what little that's worth. The disadvantage is that Blowfish + * has a particularly heavyweight key schedule. + */ + +#ifndef BLOWFISH_H +#define BLOWFISH_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#include + +#include + +/*----- Magical numbers ---------------------------------------------------*/ + +#define BLOWFISH_BLKSZ 8 +#define BLOWFISH_KEYSZ 0 +#define BLOWFISH_CLASS (N, B, 64) + +/*----- Data structures ---------------------------------------------------*/ + +typedef struct blowfish_ctx { + uint32 p[18]; + uint32 s0[256], s1[256], s2[256], s3[256]; +} blowfish_ctx; + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @blowfish_init@ --- * + * + * Arguments: @blowfish_ctx *k@ = pointer to key block to fill in + * @const void *buf@ = pointer to buffer of key material + * @size_t sz@ = size of key material + * + * Returns: --- + * + * Use: Initializes a Blowfish key buffer. Blowfish accepts + * a more-or-less arbitrary size key. + */ + +extern void blowfish_init(blowfish_ctx */*k*/, + const void */*buf*/, size_t /*sz*/); + +/* --- @blowfish_eblk@, @blowfish_dblk@ --- * + * + * Arguments: @const blowfish_ctx *k@ = pointer to key block + * @const uint32 s[2]@ = pointer to source block + * @uint32 d[2]@ = pointer to destination block + * + * Returns: --- + * + * Use: Low-level block encryption and decryption. + */ + +extern void blowfish_eblk(const blowfish_ctx */*k*/, + const uint32 */*s*/, uint32 */*d*/); + +extern void blowfish_dblk(const blowfish_ctx */*k*/, + const uint32 */*s*/, uint32 */*d*/); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/cbc.h b/cbc.h new file mode 100644 index 0000000..66979de --- /dev/null +++ b/cbc.h @@ -0,0 +1,521 @@ +/* -*-c-*- + * + * $Id: cbc.h,v 1.1 1999/09/03 08:41:11 mdw Exp $ + * + * Ciphertext block chaining for block ciphers + * + * (c) 1999 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: cbc.h,v $ + * Revision 1.1 1999/09/03 08:41:11 mdw + * Initial import. + * + */ + +#ifndef CBC_H +#define CBC_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#include + +#include + +#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 + +#include "daftstory.h" + +/* --- @CBC_TEST@ --- * + * + * Arguments: @PRE@, @pre@ = prefixes for block cipher definitions + * + * Use: Standard test rig for CBC functions. + */ + +#define CBC_TEST(PRE, pre) \ + \ +/* --- Initial plaintext for the test --- */ \ + \ +static const octet text[] = TEXT; \ + \ +/* --- Key and IV to use --- */ \ + \ +static const octet key[] = KEY; \ +static const octet iv[] = IV; \ + \ +/* --- Buffers for encryption and decryption output --- */ \ + \ +static octet ct[sizeof(text)]; \ +static octet pt[sizeof(text)]; \ + \ +static void hexdump(const octet *p, size_t sz) \ +{ \ + const octet *q = p + sz; \ + for (sz = 0; p < q; p++, sz++) { \ + printf("%02x", *p); \ + if ((sz + 1) % PRE ## _BLKSZ == 0) \ + putchar(':'); \ + } \ +} \ + \ +int main(void) \ +{ \ + size_t sz = 0, rest; \ + pre ## _cbcctx ctx; \ + pre ## _ctx k; \ + int status = 0; \ + int done = 0; \ + \ + size_t keysz = PRE ## _KEYSZ ? \ + PRE ## _KEYSZ : strlen((const char *)key); \ + \ + fputs(#pre "-cbc: ", stdout); \ + \ + pre ## _init(&k, key, keysz); \ + pre ## _cbcsetkey(&ctx, &k); \ + \ + while (sz <= sizeof(text)) { \ + rest = sizeof(text) - sz; \ + memcpy(ct, text, sizeof(text)); \ + pre ## _cbcsetiv(&ctx, iv); \ + pre ## _cbcencrypt(&ctx, ct, ct, sz); \ + pre ## _cbcencrypt(&ctx, ct + sz, ct + sz, rest); \ + memcpy(pt, ct, sizeof(text)); \ + pre ## _cbcsetiv(&ctx, iv); \ + pre ## _cbcdecrypt(&ctx, pt, pt, sz); \ + pre ## _cbcdecrypt(&ctx, pt + sz, pt + sz, rest); \ + if (memcmp(pt, text, sizeof(text)) == 0) { \ + done++; \ + if (sizeof(text) < 40 || done % 8 == 0) \ + fputc('.', stdout); \ + if (done % 480 == 0) \ + fputs("\n\t", stdout); \ + fflush(stdout); \ + } else { \ + printf("\nError (sz = %lu)\n", (unsigned long)sz); \ + status = 1; \ + printf("\tplaintext = "); hexdump(text, sz); \ + printf(", "); hexdump(text + sz, rest); \ + fputc('\n', stdout); \ + printf("\tciphertext = "); hexdump(ct, sz); \ + printf(", "); hexdump(ct + sz, rest); \ + fputc('\n', stdout); \ + printf("\trecovered text = "); hexdump(pt, sz); \ + printf(", "); hexdump(pt + sz, rest); \ + fputc('\n', stdout); \ + fputc('\n', stdout); \ + } \ + if (sz < 63) \ + sz++; \ + else \ + sz += 9; \ + } \ + \ + fputs(status ? " failed\n" : " ok\n", stdout); \ + return (status); \ +} + +#else +# define CBC_TEST(PRE, pre) +#endif + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/cfb.h b/cfb.h new file mode 100644 index 0000000..0719934 --- /dev/null +++ b/cfb.h @@ -0,0 +1,477 @@ +/* -*-c-*- + * + * $Id: cfb.h,v 1.1 1999/09/03 08:41:11 mdw Exp $ + * + * Ciphertext feedback for block ciphers + * + * (c) 1999 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: cfb.h,v $ + * Revision 1.1 1999/09/03 08:41:11 mdw + * Initial import. + * + */ + +#ifndef CFB_H +#define CFB_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#include + +#include + +#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 + +#include "daftstory.h" + +/* --- @CFB_TEST@ --- * + * + * Arguments: @PRE@, @pre@ = prefixes for block cipher definitions + * + * Use: Standard test rig for CFB functions. + */ + +#define CFB_TEST(PRE, pre) \ + \ +/* --- Initial plaintext for the test --- */ \ + \ +static const octet text[] = TEXT; \ + \ +/* --- Key and IV to use --- */ \ + \ +static const octet key[] = KEY; \ +static const octet iv[] = IV; \ + \ +/* --- Buffers for encryption and decryption output --- */ \ + \ +static octet ct[sizeof(text)]; \ +static octet pt[sizeof(text)]; \ + \ +static void hexdump(const octet *p, size_t sz) \ +{ \ + const octet *q = p + sz; \ + for (sz = 0; p < q; p++, sz++) { \ + printf("%02x", *p); \ + if ((sz + 1) % PRE ## _BLKSZ == 0) \ + putchar(':'); \ + } \ +} \ + \ +int main(void) \ +{ \ + size_t sz = 0, rest; \ + pre ## _cfbctx ctx; \ + int status = 0; \ + int done = 0; \ + pre ## _ctx k; \ + \ + size_t keysz = PRE ## _KEYSZ ? \ + PRE ## _KEYSZ : strlen((const char *)key); \ + \ + fputs(#pre "-cfb: ", stdout); \ + \ + pre ## _init(&k, key, keysz); \ + pre ## _cfbsetkey(&ctx, &k); \ + \ + while (sz <= sizeof(text)) { \ + rest = sizeof(text) - sz; \ + memcpy(ct, text, sizeof(text)); \ + pre ## _cfbsetiv(&ctx, iv); \ + pre ## _cfbencrypt(&ctx, ct, ct, sz); \ + pre ## _cfbencrypt(&ctx, ct + sz, ct + sz, rest); \ + memcpy(pt, ct, sizeof(text)); \ + pre ## _cfbsetiv(&ctx, iv); \ + pre ## _cfbdecrypt(&ctx, pt, pt, rest); \ + pre ## _cfbdecrypt(&ctx, pt + rest, pt + rest, sz); \ + if (memcmp(pt, text, sizeof(text)) == 0) { \ + done++; \ + if (sizeof(text) < 40 || done % 8 == 0) \ + fputc('.', stdout); \ + if (done % 480 == 0) \ + fputs("\n\t", stdout); \ + fflush(stdout); \ + } else { \ + printf("\nError (sz = %lu)\n", (unsigned long)sz); \ + status = 1; \ + printf("\tplaintext = "); hexdump(text, sz); \ + printf(", "); hexdump(text + sz, rest); \ + fputc('\n', stdout); \ + printf("\tciphertext = "); hexdump(ct, sz); \ + printf(", "); hexdump(ct + sz, rest); \ + fputc('\n', stdout); \ + printf("\trecovered text = "); hexdump(pt, sz); \ + printf(", "); hexdump(pt + sz, rest); \ + fputc('\n', stdout); \ + fputc('\n', stdout); \ + } \ + if (sz < 63) \ + sz++; \ + else \ + sz += 9; \ + } \ + \ + fputs(status ? " failed\n" : " ok\n", stdout); \ + return (status); \ +} + +#else +# define CFB_TEST(PRE, pre) +#endif + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..4893b92 --- /dev/null +++ b/configure.in @@ -0,0 +1,95 @@ +dnl -*-fundamental-*- +dnl +dnl $Id: configure.in,v 1.1 1999/09/03 08:41:11 mdw Exp $ +dnl +dnl Autoconfiguration for Catacomb +dnl +dnl (c) 1999 Straylight/Edgeware +dnl + +dnl ----- Licensing notice -------------------------------------------------- +dnl +dnl This file is part of Catacomb. +dnl +dnl Catacomb is free software; you can redistribute it and/or modify +dnl it under the terms of the GNU Library General Public License as +dnl published by the Free Software Foundation; either version 2 of the +dnl License, or (at your option) any later version. +dnl +dnl Catacomb is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +dnl GNU Library General Public License for more details. +dnl +dnl You should have received a copy of the GNU Library General Public +dnl License along with Catacomb; if not, write to the Free +dnl Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, +dnl MA 02111-1307, USA. + +dnl ----- Revision history -------------------------------------------------- +dnl +dnl $Log: configure.in,v $ +dnl Revision 1.1 1999/09/03 08:41:11 mdw +dnl Initial import. +dnl +dnl Revision 1.1 1999/09/03 08:41:11 mdw +dnl Initial import. +dnl + +AM_INIT_AUTOMAKE(catacomb, 1.0.0pre1) + +AC_INIT(blkc.h) +AM_INIT_AUTOMAKE(catacomb, 1.0.0pre2) +AM_CONFIG_HEADER(config.h) + +dnl --- Make sure I can compile and build libraries --- + +AC_PROG_CC +AC_CHECK_PROG(AR, ar, ar) +AC_PROG_RANLIB +mdw_MLIB + +AC_PROG_YACC + +dnl --- Actually, I assume these exist anyway --- + +AC_CHECK_HEADERS(unistd.h) +AC_HEADER_STDC + +dnl --- Check for various important system types --- + +AC_TYPE_PID_T + +dnl --- Tedious check for ssize_t --- +dnl +dnl glibc-2 puts ssize_t in a strange place. + +AC_CACHE_CHECK(for ssize_t, cat_cv_type_ssize_t, +[AC_EGREP_CPP(ssize_t, +[#include +#if HAVE_UNISTD_H +#inlcude +#endif +#if STDC_HEADERS +#include +#include +#endif], +cat_cv_type_ssize_t=yes, cat_cv_type_ssize_t=no)]) +if test $cat_cv_type_ssize_t = no; then + AC_DEFINE(ssize_t, int) +fi +AC_TYPE_UID_T +AC_CHECK_TYPE(time_t, long) +mdw_TYPE_SSIZE_T + +dnl --- Can I call `initgroups'? --- +dnl +dnl This is used in noise-gathering. + +AC_CHECK_FUNCS(setgroups) +AC_OUTPUT(Makefile) +dnl --- Done --- + +AC_OUTPUT(Makefile catacomb-config) + +dnl ----- That's all, folks ------------------------------------------------- diff --git a/daftstory.h b/daftstory.h new file mode 100644 index 0000000..8715cdc --- /dev/null +++ b/daftstory.h @@ -0,0 +1,86 @@ +/* -*-c-*- + * + * $Id: daftstory.h,v 1.1 1999/09/03 08:41:11 mdw Exp $ + * + * Daft story for use in test encryptions + * + * (c) 1999 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: daftstory.h,v $ + * Revision 1.1 1999/09/03 08:41:11 mdw + * Initial import. + * + */ + +#ifndef DAFTSTORY_H +#define DAFTSTORY_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Macros ------------------------------------------------------------*/ + +/* --- Don't ask --- */ + +#ifdef SMALL_TEST +# define TEXT "A small piece of text for testing encryption." +#else +# define STORY "\ +Once upon a time there were a beautiful princess, a slightly nutty wizard,\n\ +and a watermelon. Now, the watermelon had decided that it probably wasn't\n\ +going to get very far with the princess unless it did something pretty\n\ +drastic. So it asked the wizard to turn it into a handsome prince.\n\ +\n\ +At least, this is the way that the wizard viewed the situation. He might\n\ +have just hallucinated it all; those mushrooms had looked ever so nice.\n\ +\n\ +Back to the point. The watermelon had expressed its desire not to be a\n\ +watermelon any more. And the wizard was probably tripping something quite\n\ +powerful. He hunted around a bit for his staff, and mumbled something\n\ +that film directors would think of as sounding appropriately arcane and\n\ +mystical (but was, in fact, just the ingredients list for an ancient\n\ +remedy for athlete's foot) and *pop*. Cooked watermelon. Yuk.\n\ +\n\ +Later in the year, the princess tripped over the hem of her dress, fell\n\ +down a spiral staircase, and died. The king ordered dressmakers to attach\n\ +safety warnings to long dresses.\n\ +\n\ +And the wizard? Who cares?\n\ +" +# define TEXT STORY STORY STORY +#endif + +#define KEY "Penguins rule OK, rhubarb cauliflower" +#define IV "EdgewareCatacomb, parsley, sage, rosemary and thyme" + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/des-base.c b/des-base.c new file mode 100644 index 0000000..73246b6 --- /dev/null +++ b/des-base.c @@ -0,0 +1,49 @@ +/* -*-c-*- + * + * $Id: des-base.c,v 1.1 1999/09/03 08:41:11 mdw Exp $ + * + * Common features for DES implementation + * + * (c) 1999 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: des-base.c,v $ + * Revision 1.1 1999/09/03 08:41:11 mdw + * Initial import. + * + */ + +/*----- Header files ------------------------------------------------------*/ + +#include + +#include "des-base.h" +#include "des_sp.h" + +/*----- Global variables --------------------------------------------------*/ + +uint32 des_sp[8][64] = DES_SP; + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/des-base.h b/des-base.h new file mode 100644 index 0000000..dd4fdb7 --- /dev/null +++ b/des-base.h @@ -0,0 +1,166 @@ +/* -*-c-*- + * + * $Id: des-base.h,v 1.1 1999/09/03 08:41:11 mdw Exp $ + * + * Common features for DES implementation + * + * (c) 1999 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: des-base.h,v $ + * Revision 1.1 1999/09/03 08:41:11 mdw + * Initial import. + * + */ + +#ifndef DES_BASE_H +#define DES_BASE_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#include + +/*----- External data -----------------------------------------------------*/ + +extern uint32 des_sp[8][64]; + +/*----- Macros ------------------------------------------------------------*/ + +/* --- @DES_ROUND@ --- * + * + * This is the basic DES round function. The inputs are the two subkey + * halves, and the left and right block halves. Note that the block halves + * are rotated left one place at this point. This wraps what's meant to be + * the top bit around to the bottom, so I get a clear run at the S-boxes. + */ + +#define DES_ROUND(ka, kb, x, y) do { \ + uint32 _t = (y) ^ (ka); \ + (x) ^= des_sp[7][(_t >> 0) & 0x3f] ^ \ + des_sp[5][(_t >> 8) & 0x3f] ^ \ + des_sp[3][(_t >> 16) & 0x3f] ^ \ + des_sp[1][(_t >> 24) & 0x3f]; \ + _t = ROR32((y), 4) ^ (kb); \ + (x) ^= des_sp[6][(_t >> 0) & 0x3f] ^ \ + des_sp[4][(_t >> 8) & 0x3f] ^ \ + des_sp[2][(_t >> 16) & 0x3f] ^ \ + des_sp[0][(_t >> 24) & 0x3f]; \ +} while (0) + +/* --- @DES_IP@, @DES_IPINV@ --- * + * + * The cryptographically useless initial and final permutations. The initial + * permutation also rotates the two block halves left by one place. This is + * undone by the inverse permutation at the end. + */ + +#define DES_IP(x, y) do { \ + uint32 _t; \ + _t = (y ^ (x >> 4)) & 0x0f0f0f0f; y ^= _t; x ^= _t << 4; \ + _t = (x ^ (x >> 18)) & 0x00003333; x ^= _t; x ^= _t << 18; \ + _t = (y ^ (y >> 18)) & 0x00003333; y ^= _t; y ^= _t << 18; \ + _t = (x ^ (x >> 9)) & 0x00550055; x ^= _t; x ^= _t << 9; \ + _t = (y ^ (y >> 9)) & 0x00550055; y ^= _t; y ^= _t << 9; \ + _t = (x ^ (x >> 24)) & 0x000000ff; x ^= _t; x ^= _t << 24; \ + _t = (y ^ (y >> 24)) & 0x000000ff; y ^= _t; y ^= _t << 24; \ + _t = (y ^ (x >> 16)) & 0x0000ffff; y ^= _t; x ^= _t << 16; \ + x = ROL32(x, 1); y = ROL32(y, 1); \ +} while (0) + +#define DES_IPINV(x, y) do { \ + uint32 _t; \ + x = ROR32(x, 1); y = ROR32(y, 1); \ + _t = (y ^ (x >> 16)) & 0x0000ffff; y ^= _t; x ^= _t << 16; \ + _t = (x ^ (x >> 24)) & 0x000000ff; x ^= _t; x ^= _t << 24; \ + _t = (y ^ (y >> 24)) & 0x000000ff; y ^= _t; y ^= _t << 24; \ + _t = (y ^ (x >> 4)) & 0x0f0f0f0f; y ^= _t; x ^= _t << 4; \ + _t = (x ^ (x >> 18)) & 0x00003333; x ^= _t; x ^= _t << 18; \ + _t = (y ^ (y >> 18)) & 0x00003333; y ^= _t; y ^= _t << 18; \ + _t = (x ^ (x >> 9)) & 0x00550055; x ^= _t; x ^= _t << 9; \ + _t = (y ^ (y >> 9)) & 0x00550055; y ^= _t; y ^= _t << 9; \ +} while (0) + +/* --- @DES_EBLK@, @DES_DBLK@ --- * + * + * Whole block encryption and decryption. + */ + +#define DES_EBLK(k, a, b, c, d) do { \ + const uint32 *_k = (k); \ + uint32 _x = (a), _y = (b); \ + DES_ROUND(_k[0], _k[1], _x, _y); _k += 2; \ + DES_ROUND(_k[0], _k[1], _y, _x); _k += 2; \ + DES_ROUND(_k[0], _k[1], _x, _y); _k += 2; \ + DES_ROUND(_k[0], _k[1], _y, _x); _k += 2; \ + DES_ROUND(_k[0], _k[1], _x, _y); _k += 2; \ + DES_ROUND(_k[0], _k[1], _y, _x); _k += 2; \ + DES_ROUND(_k[0], _k[1], _x, _y); _k += 2; \ + DES_ROUND(_k[0], _k[1], _y, _x); _k += 2; \ + DES_ROUND(_k[0], _k[1], _x, _y); _k += 2; \ + DES_ROUND(_k[0], _k[1], _y, _x); _k += 2; \ + DES_ROUND(_k[0], _k[1], _x, _y); _k += 2; \ + DES_ROUND(_k[0], _k[1], _y, _x); _k += 2; \ + DES_ROUND(_k[0], _k[1], _x, _y); _k += 2; \ + DES_ROUND(_k[0], _k[1], _y, _x); _k += 2; \ + DES_ROUND(_k[0], _k[1], _x, _y); _k += 2; \ + DES_ROUND(_k[0], _k[1], _y, _x); _k += 2; \ + (c) = _y; \ + (d) = _x; \ +} while (0) + +#define DES_DBLK(k, a, b, c, d) do { \ + const uint32 *_k = (k) + 32; \ + uint32 _x = (a), _y = (b); \ + _k -= 2; DES_ROUND(_k[0], _k[1], _x, _y); \ + _k -= 2; DES_ROUND(_k[0], _k[1], _y, _x); \ + _k -= 2; DES_ROUND(_k[0], _k[1], _x, _y); \ + _k -= 2; DES_ROUND(_k[0], _k[1], _y, _x); \ + _k -= 2; DES_ROUND(_k[0], _k[1], _x, _y); \ + _k -= 2; DES_ROUND(_k[0], _k[1], _y, _x); \ + _k -= 2; DES_ROUND(_k[0], _k[1], _x, _y); \ + _k -= 2; DES_ROUND(_k[0], _k[1], _y, _x); \ + _k -= 2; DES_ROUND(_k[0], _k[1], _x, _y); \ + _k -= 2; DES_ROUND(_k[0], _k[1], _y, _x); \ + _k -= 2; DES_ROUND(_k[0], _k[1], _x, _y); \ + _k -= 2; DES_ROUND(_k[0], _k[1], _y, _x); \ + _k -= 2; DES_ROUND(_k[0], _k[1], _x, _y); \ + _k -= 2; DES_ROUND(_k[0], _k[1], _y, _x); \ + _k -= 2; DES_ROUND(_k[0], _k[1], _x, _y); \ + _k -= 2; DES_ROUND(_k[0], _k[1], _y, _x); \ + (c) = _y; \ + (d) = _x; \ +} while (0) + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/des-mktab.c b/des-mktab.c new file mode 100644 index 0000000..07cff39 --- /dev/null +++ b/des-mktab.c @@ -0,0 +1,279 @@ +/* -*-c-*- + * + * $Id: des-mktab.c,v 1.1 1999/09/03 08:41:11 mdw Exp $ + * + * Build combined S-P tables for DES + * + * (c) 1999 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: des-mktab.c,v $ + * Revision 1.1 1999/09/03 08:41:11 mdw + * Initial import. + * + */ + +/*----- Header files ------------------------------------------------------*/ + +#include +#include +#include +#include + +#include + +/*----- Static variables --------------------------------------------------*/ + +/* --- S boxes --- */ + +static char s[8][4][16] = { + + /* --- S1 --- */ + + { { 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7 }, + { 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8 }, + { 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0 }, + { 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13 } }, + + /* --- S2 --- */ + + { { 15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10 }, + { 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5 }, + { 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15 }, + { 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9 } }, + + /* --- S3 --- */ + + { { 10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8 }, + { 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1 }, + { 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7 }, + { 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12 } }, + + /* --- S4 --- */ + + { { 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15 }, + { 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9 }, + { 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4 }, + { 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14 } }, + + /* --- S5 --- */ + + { { 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9 }, + { 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6 }, + { 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14 }, + { 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3 } }, + + /* --- S6 --- */ + + { { 12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11 }, + { 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8 }, + { 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6 }, + { 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13 } }, + + /* --- S7 --- */ + + { { 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1 }, + { 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6 }, + { 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2 }, + { 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12 } }, + + /* --- S8 --- */ + + { { 13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7 }, + { 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2 }, + { 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8 }, + { 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11 } } +}; + +/* --- P table --- */ + +static char p[32] = { + 16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10, + 2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25 +}; + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @unique@ --- * + * + * Arguments: @const char *t@ = pointer to table + * @int base@ = base of the data + * @int sz@ = number of elements + * @const char *name@ = name of this table + * @...@ = things to fill in + * + * Returns: Zero if it failed, nonzero if it didn't. + * + * Use: Validates a table. All the elements must be in range and + * unique. + */ + +static int unique(char *t, int base, int sz, const char *name, ...) +{ + char u[32]; + char nbuf[128]; + int i; + int ok = 1; + + { + va_list ap; + va_start(ap, name); + vsprintf(nbuf, name, ap); + va_end(ap); + } + + if (sz > sizeof(u)) { + fprintf(stderr, "internal error: table `%s' too large\n", nbuf); + exit(EXIT_FAILURE); + } + memset(u, 0, sizeof(u)); + for (i = 0; i < sz; i++) { + int x = t[i] - base; + if (x >= sz) { + fprintf(stderr, "validation error: %i too big (index %i) in %s\n", + x + base, i, nbuf); + ok = 0; + } else if (u[x]) { + fprintf(stderr, "validation error: duplicate %i (index %i) in %s\n", + x + base, i, nbuf); + ok = 0; + } + u[x] = 1; + } + for (i = 0; i < sz; i++) { + if (!u[i]) { + fprintf(stderr, "validation error: missing %i in %s\n", + i + base, nbuf); + ok = 0; + } + } + + return (ok); +} + +/* --- @validate@ --- * + * + * Arguments: --- + * + * Returns: Only if everything's OK. + * + * Use: Validates the tables. A bit. Not much at all... + */ + +static void validate(void) +{ + int i, j; + int ok = 1; + + for (i = 0; i < 8; i++) for (j = 0; j < 4; j++) + if (!unique(s[i][j], 0, 16, "sbox %i, row %i", i, j)) ok = 0; + if (!unique(p, 1, 32, "p")) ok = 0; + if (!ok) + exit(EXIT_FAILURE); +} + +/* --- @permute@ --- * + * + * Arguments: @unsigned long x@ = value to permute + * + * Returns: Permuted version of @x@. + * + * Use: Permutes a number. The result is the input value after + * having been spewed through the @P@ permutation, and then + * (and this is important) rotated left one place. + */ + +static unsigned long permute(unsigned long x) +{ + unsigned long y = 0; + unsigned i; + + for (i = 0; i < 32; i++) { + if (x & (1 << (32 - p[i]))) + y |= (1 << (31 - i)); + } + return (ROL32(y, 1)); +} + +/* --- @mangle@ --- * + * + * Arguments: @const char s[4][16]@ = an s-box + * @unsigned long ss[64]@ = output buffer + * @int bitoff@ = bit offset to use + * + * Returns: --- + * + * Use: Mangles the s-box. Specifically, the bizarre indexing is + * transformed into something sensible, and the result is + * permuted according to the @p@ table. + */ + +static void mangle(const char s[4][16], unsigned long *ss, int bitoff) +{ + unsigned i; + for (i = 0; i < 64; i++) { + unsigned row = ((i & 0x20) >> 4) | (i & 0x01); + unsigned col = (i & 0x1e) >> 1; + ss[i] = permute(s[row][col] << bitoff); + } +} + +/* --- @main@ --- */ + +int main(void) +{ + int i, j; + unsigned long ss[64]; + const char *sep; + + validate(); + + fputs("\ +/* DES SP table (generated) */\n\ +\n\ +#define DES_SP { \\\n\ +", stdout); + for (i = 0; i < 8; i++) { + mangle(s[i], ss, 28 - 4 * i); + printf("\ + \\\n\ + /* --- SP[%i] --- */ \\\n\ + \\\n\ +", i); + sep = " { "; + for (j = 0; j < 64; j++) { + printf("%s0x%08lx", sep, ss[j]); + if (j % 4 == 3) + sep = ", \\\n "; + else + sep = ", "; + } + printf(" }%s \\\n", i == 7 ? "" : ","); + } + fputs("}\n", stdout); + return (0); +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/des.c b/des.c new file mode 100644 index 0000000..cb73a8e --- /dev/null +++ b/des.c @@ -0,0 +1,263 @@ +/* -*-c-*- + * + * $Id: des.c,v 1.1 1999/09/03 08:41:11 mdw Exp $ + * + * The Data Encryption Standard + * + * (c) 1999 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: des.c,v $ + * Revision 1.1 1999/09/03 08:41:11 mdw + * Initial import. + * + */ + +/*----- Header files ------------------------------------------------------*/ + +#include +#include +#include +#include + +#include + +#include "blkc.h" +#include "des-base.h" +#include "des.h" + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @permute@ --- * + * + * Arguments: @const char *p@ = pointer to permutation table + * @uint32 a, b@ = source value to permute + * @uint32 *d@ = destination for value + * + * Returns: --- + * + * Use: Performs a 64-bit permutation. The table is given in the + * normal (but bizarre) DES bit numbering system. That's not to + * say that the tables in this source file are like the normal + * DES tables, because they're not. + */ + +static void permute(const char *p, uint32 a, uint32 b, uint32 *d) +{ + uint32 x = 0, y = 0; + int i; + + for (i = 0; i < 32; i++) { + int q = p[i]; + uint32 t; + if (!q) + continue; + else if (q <= 32) + t = a; + else { + t = b; + q -= 32; + } + if (t & (1 << (32 - q))) + x |= (1 << (31 - i)); + } + + p += 32; + + for (i = 0; i < 32; i++) { + int q = p[i]; + uint32 t; + if (!q) + continue; + else if (q <= 32) + t = a; + else { + t = b; + q -= 32; + } + if (t & (1 << (32 - q))) + y |= (1 << (31 - i)); + } + + d[0] = x; + d[1] = y; +} + +/* --- @des_init@ --- * + * + * Arguments: @des_ctx *k@ = pointer to key block + * @const void *buf@ = pointer to key buffer + * @size_t sz@ = size of key material + * + * Returns: --- + * + * Use: Initializes a DES key buffer. The key buffer may be either 7 + * or 8 bytes long. If it's 8 bytes, the key is assumed to be + * padded with parity bits in the low order bit of each octet. + * These are stripped out without checking prior to the actual + * key scheduling. + */ + +void des_init(des_ctx *k, const void *buf, size_t sz) +{ + uint32 x, y; + uint32 *kp = k->k; + int i; + + /* --- @pc1@ --- * + * + * This cryptographically useless permutation is used to mangle the key + * before it's subjected to the key schedule proper. I've not actually + * messed it about much except for inserting padding at the beginning of + * the two halves of the key. + */ + + static const char pc1[] = { + 0, 0, 0, 0, + 57, 49, 41, 33, 25, 17, 9, + 1, 58, 50, 42, 34, 26, 18, + 10, 2, 59, 51, 43, 35, 27, + 19, 11, 3, 60, 52, 44, 36, + 0, 0, 0, 0, + 63, 55, 47, 39, 31, 23, 15, + 7, 62, 54, 46, 38, 30, 22, + 14, 6, 61, 53, 45, 37, 29, + 21, 13, 5, 28, 20, 12, 4 + }; + + /* --- @pc2@ --- * + * + * This irritating but necessary permutation mangles the key between the + * simple rotation-based schedule and the actual XOR with which it modifies + * the behaviour of the cipher. + * + * This version of the table doesn't look much like the original. This is + * because some parts of the world have been permuted in order to make + * things simpler for the round function. In particular, everything is + * rotated left one place to avoid problems with the wraparound of the + * expansion permutation, and the key is split between odd and even S-boxes + * rather than high and low ones. That's without the complication of the + * padding bits in the representation of the 56-bit proto-key. + */ + + static const char pc2[] = { + 0, 0, 3 + 4, 28 + 4, 15 + 4, 6 + 4, 21 + 4, 10 + 4, /* S-box 2 */ + 0, 0, 16 + 4, 7 + 4, 27 + 4, 20 + 4, 13 + 4, 2 + 4, /* S-box 4 */ + 0, 0, 30 + 8, 40 + 8, 51 + 8, 45 + 8, 33 + 8, 48 + 8, /* S-box 6 */ + 0, 0, 46 + 8, 42 + 8, 50 + 8, 36 + 8, 29 + 8, 32 + 8, /* S-box 8 */ + 0, 0, 14 + 4, 17 + 4, 11 + 4, 24 + 4, 1 + 4, 5 + 4, /* S-box 1 */ + 0, 0, 23 + 4, 19 + 4, 12 + 4, 4 + 4, 26 + 4, 8 + 4, /* S-box 3 */ + 0, 0, 41 + 8, 52 + 8, 31 + 8, 37 + 8, 47 + 8, 55 + 8, /* S-box 5 */ + 0, 0, 44 + 8, 49 + 8, 39 + 8, 56 + 8, 34 + 8, 53 + 8 /* S-box 7 */ + }; + + /* --- @v@ --- * + * + * Contains the rotation amounts for the key halves. + */ + + static const char v[] = { + 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 + }; + + /* --- Extract the key into my registers --- * + * + * The 7 byte case is rather horrible. It expands the key to the 8 byte + * case before going any further. It could probably do with its own @pc1@ + * table. + */ + + assert(((void)"DES key must be 56 or 64 bits", sz == 7 || sz == 8)); + + if (sz == 8) { + const octet *p = buf; + x = LOAD32(p); y = LOAD32(p + 4); + } else { + const octet *p = buf; + x = LOAD32(p); + x = (x & 0xfe000000) | ((x & 0x01fffff0) >> 1); + x = (x & 0xfffe0000) | ((x & 0x0001fff8) >> 1); + x = (x & 0xfffffe00) | ((x & 0x000001fc) >> 1); + y = LOAD32(p + 3) << 1; /* Note: misaligned */ + y = (y & 0x000000fe) | ((y & 0x1fffff00) << 1); + y = (y & 0x0000fefe) | ((y & 0x3fff0000) << 1); + y = (y & 0x00fefefe) | ((y & 0x7f000000) << 1); + } + + /* --- Permute using the pointless PC1 --- */ + + { + uint32 ka[2]; + permute(pc1, x, y, ka); + x = ka[0]; y = ka[1]; + } + + /* --- Now for the key schedule proper --- */ + + for (i = 0; i < 16; i++) { + if (v[i] == 1) { + x = ((x << 1) | (x >> 27)) & 0x0fffffff; + y = ((y << 1) | (y >> 27)) & 0x0fffffff; + } else { + x = ((x << 2) | (x >> 26)) & 0x0fffffff; + y = ((y << 2) | (y >> 26)) & 0x0fffffff; + } + permute(pc2, x, y, kp); + kp += 2; + } +} + +/* --- @des_eblk@, @des_dblk@ --- * + * + * Arguments: @const des_ctx *k@ = pointer to key block + * @const uint32 s[2]@ = pointer to source block + * @uint32 d[2]@ = pointer to destination block + * + * Returns: --- + * + * Use: Low-level block encryption and decryption. + */ + +void des_eblk(const des_ctx *k, const uint32 *s, uint32 *d) +{ + uint32 x = s[0], y = s[1]; + DES_IP(x, y); + DES_EBLK(k->k, x, y, x, y); + DES_IPINV(x, y); + d[0] = x, d[1] = y; +} + +void des_dblk(const des_ctx *k, const uint32 *s, uint32 *d) +{ + uint32 x = s[0], y = s[1]; + DES_IP(x, y); + DES_DBLK(k->k, x, y, x, y); + DES_IPINV(x, y); + d[0] = x, d[1] = y; +} + +BLKC_TEST(DES, des) + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/des.h b/des.h new file mode 100644 index 0000000..857cec3 --- /dev/null +++ b/des.h @@ -0,0 +1,116 @@ +/* -*-c-*- + * + * $Id: des.h,v 1.1 1999/09/03 08:41:11 mdw Exp $ + * + * The Data Encryption Standard + * + * (c) 1999 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: des.h,v $ + * Revision 1.1 1999/09/03 08:41:11 mdw + * Initial import. + * + */ + +/*----- Notes on the Data Encryption Standard -----------------------------* + * + * Almost twenty years after it was first accepted, DES is still the standard + * block cipher. It's showing its age in its small key size and poor + * optimiziation for software implementations, but it's not really been badly + * dented by the intensive analysis thrown at it. + * + * This interface is here for compatibility with existing protocols, and + * because it's a trivial veneer over the base DES code which is used by the + * @des3@ interface which implements proper strong triple-DES. + */ + +#ifndef DES_H +#define DES_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#include + +#include + +/*----- Magical numbers ---------------------------------------------------*/ + +#define DES_BLKSZ 8 +#define DES_KEYSZ 7 +#define DES_CLASS (N, B, 64) + +/*----- Data structures ---------------------------------------------------*/ + +typedef struct des_ctx { + uint32 k[32]; +} des_ctx; + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @des_init@ --- * + * + * Arguments: @des_ctx *k@ = pointer to key block + * @const void *buf@ = pointer to key buffer + * @size_t sz@ = size of key material + * + * Returns: --- + * + * Use: Initializes a DES key buffer. The key buffer may be either 7 + * or 8 bytes long. If it's 8 bytes, the key is assumed to be + * padded with parity bits in the low order bit of each octet. + * These are stripped out without checking prior to the actual + * key scheduling. + */ + +extern void des_init(des_ctx */*k*/, const void */*buf*/, size_t /*sz*/); + +/* --- @des_eblk@, @des_dblk@ --- * + * + * Arguments: @const des_ctx *k@ = pointer to key block + * @const uint32 s[2]@ = pointer to source block + * @uint32 d[2]@ = pointer to destination block + * + * Returns: --- + * + * Use: Low-level block encryption and decryption. + */ + +extern void des_eblk(const des_ctx */*k*/, + const uint32 */*s*/, uint32 */*d*/); +extern void des_dblk(const des_ctx */*k*/, + const uint32 */*s*/, uint32 */*d*/); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/des3.c b/des3.c new file mode 100644 index 0000000..281d8ea --- /dev/null +++ b/des3.c @@ -0,0 +1,123 @@ +/* -*-c-*- + * + * $Id: des3.c,v 1.1 1999/09/03 08:41:11 mdw Exp $ + * + * Implementation of double- and triple-DES + * + * (c) 1999 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: des3.c,v $ + * Revision 1.1 1999/09/03 08:41:11 mdw + * Initial import. + * + */ + +/*----- Header files ------------------------------------------------------*/ + +#include +#include +#include +#include + +#include + +#include "blkc.h" +#include "des-base.h" +#include "des.h" +#include "des3.h" + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @des3_init@ --- * + * + * Arguments: @des3_ctx *k@ = pointer to key block + * @const void *buf@ = pointer to key buffer + * @size_t sz@ = size of key material + * + * Returns: --- + * + * Use: Initializes a DES key buffer. The key buffer may have length + * 7, 8, 14, 16, 21, or 24. These correspond to one, two or + * three DES keys, either packed or unpacked (i.e., still + * containing parity bits). + */ + +void des3_init(des3_ctx *k, const void *buf, size_t sz) +{ + size_t step; + const octet *p = buf; + + assert(((void)("des3 key length must be one of 7, 8, 14, 16, 21 or 24"), + (sz % 7 == 0 || sz % 8 == 0) && sz < 25)); + + if (sz % 7 == 0) + step = 7; + else + step = 8; + + des_init(&k->a, p, step); + if (sz > 8) p += step; + des_init(&k->b, p, step); + if (sz > 16) p += step; else p = buf; + des_init(&k->c, p, step); +} + +/* --- @des3_eblk@, @des3_dblk@ --- * + * + * Arguments: @const des3_ctx *k@ = pointer to key block + * @const uint32 s[2]@ = pointer to source block + * @uint32 d[2]@ = pointer to destination block + * + * Returns: --- + * + * Use: Low-level block encryption and decryption. + */ + +void des3_eblk(const des3_ctx *k, const uint32 *s, uint32 *d) +{ + uint32 x = s[0], y = s[1]; + DES_IP(x, y); + DES_EBLK(k->a.k, x, y, x, y); + DES_DBLK(k->b.k, x, y, x, y); + DES_EBLK(k->c.k, x, y, x, y); + DES_IPINV(x, y); + d[0] = x, d[1] = y; +} + +void des3_dblk(const des3_ctx *k, const uint32 *s, uint32 *d) +{ + uint32 x = s[0], y = s[1]; + DES_IP(x, y); + DES_DBLK(k->c.k, x, y, x, y); + DES_EBLK(k->b.k, x, y, x, y); + DES_DBLK(k->a.k, x, y, x, y); + DES_IPINV(x, y); + d[0] = x, d[1] = y; +} + +BLKC_TEST(DES3, des3) + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/des3.h b/des3.h new file mode 100644 index 0000000..ce57ac9 --- /dev/null +++ b/des3.h @@ -0,0 +1,116 @@ +/* -*-c-*- + * + * $Id: des3.h,v 1.1 1999/09/03 08:41:11 mdw Exp $ + * + * Implementation of double- and triple-DES + * + * (c) 1999 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: des3.h,v $ + * Revision 1.1 1999/09/03 08:41:11 mdw + * Initial import. + * + */ + +#ifndef DES3_H +#define DES3_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Notes on double- and triple-DES -----------------------------------* + * + * The normal recommendation for using DES now is `triple DES'. + * Conventionally, this involves an encrypt-decrypt-encrypt sequence, + * although whether the first and last operations use the same key is + * unfortunately not agreed upon. This interface handles both cases (and the + * single-key one too, although the simple @des@ calls are quicker for that). + */ + +/*----- Header files ------------------------------------------------------*/ + +#include + +#include + +#ifndef DES_H +# include "des.h" +#endif + +/*----- Magical numbers ---------------------------------------------------*/ + +#define DES3_BLKSZ 8 +#define DES3_KEYSZ 21 +#define DES3_CLASS (N, B, 64) + +/*----- Data structures ---------------------------------------------------*/ + +typedef struct des3_ctx { + des_ctx a, b, c; +} des3_ctx; + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @des3_init@ --- * + * + * Arguments: @des3_ctx *k@ = pointer to key block + * @const void *buf@ = pointer to key buffer + * @size_t sz@ = size of key material + * + * Returns: --- + * + * Use: Initializes a DES key buffer. The key buffer may have length + * 7, 8, 14, 16, 21, or 24. These correspond to one, two or + * three DES keys, either packed or unpacked (i.e., still + * containing parity bits). + */ + +extern void des3_init(des3_ctx */*k*/, const void */*buf*/, size_t /*sz*/); + +/* --- @des3_eblk@, @des_dblk@ --- * + * + * Arguments: @const des3_ctx *k@ = pointer to key block + * @const uint32 s[2]@ = pointer to source block + * @uint32 d[2]@ = pointer to destination block + * + * Returns: --- + * + * Use: Low-level block encryption and decryption. + */ + +extern void des3_eblk(const des3_ctx */*k*/, + const uint32 */*s*/, uint32 */*d*/); +extern void des3_dblk(const des3_ctx */*k*/, + const uint32 */*s*/, uint32 */*d*/); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/ecb.h b/ecb.h new file mode 100644 index 0000000..1027591 --- /dev/null +++ b/ecb.h @@ -0,0 +1,440 @@ +/* -*-c-*- + * + * $Id: ecb.h,v 1.1 1999/09/03 08:41:12 mdw Exp $ + * + * Ciphertext block chaining for block ciphers + * + * (c) 1999 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: ecb.h,v $ + * Revision 1.1 1999/09/03 08:41:12 mdw + * Initial import. + * + */ + +#ifndef ECB_H +#define ECB_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#include +#include + +#include + +#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 + +#include "daftstory.h" + +/* --- @ECB_TEST@ --- * + * + * Arguments: @PRE@, @pre@ = prefixes for block cipher definitions + * + * Use: Standard test rig for ECB functions. + */ + +#define ECB_TEST(PRE, pre) \ + \ +/* --- Initial plaintext for the test --- */ \ + \ +static const octet text[] = TEXT; \ + \ +/* --- Key and IV to use --- */ \ + \ +static const octet key[] = KEY; \ +static const octet iv[] = IV; \ + \ +/* --- Buffers for encryption and decryption output --- */ \ + \ +static octet ct[sizeof(text)]; \ +static octet pt[sizeof(text)]; \ + \ +static void hexdump(const octet *p, size_t sz) \ +{ \ + const octet *q = p + sz; \ + for (sz = 0; p < q; p++, sz++) { \ + printf("%02x", *p); \ + if ((sz + 1) % PRE ## _BLKSZ == 0) \ + putchar(':'); \ + } \ +} \ + \ +int main(void) \ +{ \ + size_t sz = 0, rest; \ + pre ## _ecbctx ctx; \ + int status = 0; \ + int done = 0; \ + \ + size_t keysz = PRE ## _KEYSZ ? \ + PRE ## _KEYSZ : strlen((const char *)key); \ + \ + fputs(#pre "-ecb: ", stdout); \ + \ + pre ## _ecbinit(&ctx, key, keysz, iv); \ + \ + while (sz <= sizeof(text)) { \ + rest = sizeof(text) - sz; \ + if ((sz != 0 && sz < PRE ## _BLKSZ) || \ + (rest != 0 && rest < PRE ## _BLKSZ)) \ + goto next; \ + memcpy(ct, text, sizeof(text)); \ + pre ## _ecbencrypt(&ctx, ct, ct, sz); \ + pre ## _ecbencrypt(&ctx, ct + sz, ct + sz, rest); \ + memcpy(pt, ct, sizeof(text)); \ + pre ## _ecbdecrypt(&ctx, pt, pt, sz); \ + pre ## _ecbdecrypt(&ctx, pt + sz, pt + sz, rest); \ + if (memcmp(pt, text, sizeof(text)) == 0) { \ + done++; \ + if (sizeof(text) < 40 || done % 8 == 0) \ + fputc('.', stdout); \ + if (done % 480 == 0) \ + fputs("\n\t", stdout); \ + fflush(stdout); \ + } else { \ + printf("\nError (sz = %lu)\n", (unsigned long)sz); \ + status = 1; \ + printf("\tplaintext = "); hexdump(text, sz); \ + printf(", "); hexdump(text + sz, rest); \ + fputc('\n', stdout); \ + printf("\tciphertext = "); hexdump(ct, sz); \ + printf(", "); hexdump(ct + sz, rest); \ + fputc('\n', stdout); \ + printf("\trecovered text = "); hexdump(pt, sz); \ + printf(", "); hexdump(pt + sz, rest); \ + fputc('\n', stdout); \ + fputc('\n', stdout); \ + } \ + next: \ + if (sz < 63) \ + sz++; \ + else \ + sz += 9; \ + } \ + \ + fputs(status ? " failed\n" : " ok\n", stdout); \ + return (status); \ +} + +#else +# define ECB_TEST(PRE, pre) +#endif + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/genmodes b/genmodes new file mode 100755 index 0000000..48175ce --- /dev/null +++ b/genmodes @@ -0,0 +1,50 @@ +#! /bin/sh + +baselist=$1 +extlist=$2 + +for i in $baselist; do + I=`echo "$i" | tr -d '\n' | tr a-z A-Z | tr -c a-zA-Z0-9 _` + for j in $extlist; do + J=`echo "$j" | tr -d '\n' | tr a-z A-Z | tr -c a-zA-Z0-9 _` + guard="${I}_${J}_H" + cat >$i-$j.h <$i-$j.c < + +#include + +/*----- 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 +#include + +#define HASH_TEST(PRE, pre) \ + \ +static int verify(dstr *v) \ +{ \ + pre##_ctx ctx; \ + int ok = 1; \ + int i; \ + octet *p; \ + int szs[] = { 1, 7, 192, -1, 0 }, *ip; \ + size_t sz; \ + dstr d; \ + \ + dstr_create(&d); \ + dstr_ensure(&d, PRE##_HASHSZ); \ + d.len = PRE##_HASHSZ; \ + \ + for (ip = szs; *ip; ip++) { \ + i = *ip; \ + sz = v[0].len; \ + if (i == -1) \ + i = sz; \ + if (i > sz) \ + continue; \ + p = (octet *)v[0].buf; \ + pre##_init(&ctx); \ + while (sz) { \ + if (i > sz) \ + i = sz; \ + pre##_hash(&ctx, p, i); \ + p += i; \ + sz -= i; \ + } \ + pre##_done(&ctx, d.buf); \ + if (memcmp(d.buf, v[1].buf, PRE##_HASHSZ) != 0) { \ + printf("\nfail:\n\tstep = %i\n\tinput = `%s'\n\texpected = ", \ + *ip, v[0].buf); \ + type_hex.dump(&v[1], stdout); \ + fputs("\n\tcomputed = ", stdout); \ + type_hex.dump(&d, stdout); \ + putchar('\n'); \ + ok = 0; \ + } \ + } \ + \ + dstr_destroy(&d); \ + return (ok); \ +} \ + \ +static test_chunk defs[] = { \ + { #pre, verify, { &type_string, &type_hex, 0 } }, \ + { 0, 0, { 0 } } \ +}; \ + \ +int main(int argc, char *argv[]) \ +{ \ + ego(argv[0]); \ + test_run(argc, argv, defs, SRCDIR"/tests/" #pre); \ + return (0); \ +} + +#else +# define HASH_TEST(PRE, pre) +#endif + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/hmac.h b/hmac.h new file mode 100644 index 0000000..8849258 --- /dev/null +++ b/hmac.h @@ -0,0 +1,321 @@ +/* -*-c-*- + * + * $Id: hmac.h,v 1.1 1999/09/03 08:41:12 mdw Exp $ + * + * Generic code for HMAC and NMAC + * + * (c) 1998 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: hmac.h,v $ + * Revision 1.1 1999/09/03 08:41:12 mdw + * Initial import. + * + */ + +/*----- Notes on the HMAC and NMAC constructions --------------------------* + * + * Designed by Mihir Bellare, Ran Canetti and Hugo Krawczyk, NMAC is a + * method for constructing keyed message authentication algorithms from + * unkeyed hash functions. HMAC is an alternative formulation which doesn't + * require low-level access to the hash function's implementation. NMAC was + * designed to allow MD5 has a suitable underlying hash function, even though + * doubts were already being raised about its collision resistance. + */ + +#ifndef HMAC_H +#define HMAC_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#include +#include + +#include + +#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 +#include +#include + +#define HMAC_TEST(PRE, pre) \ + \ +static int macverify(dstr *v) \ +{ \ + pre##_macctx cctx; \ + pre##_mackey ckey; \ + int ok = 1; \ + int i; \ + octet *p; \ + int szs[] = { 1, 7, 192, -1, 0 }, *ip; \ + size_t csz; \ + dstr d; \ + \ + dstr_create(&d); \ + dstr_ensure(&d, PRE##_HASHSZ); \ + d.len = PRE##_HASHSZ; \ + \ + pre##_hmac(&ckey, v[1].buf, v[1].len); \ + \ + for (ip = szs; *ip; ip++) { \ + i = *ip; \ + csz = v[0].len; \ + if (i == -1) \ + i = csz; \ + if (i > csz) \ + continue; \ + p = (octet *)v[0].buf; \ + pre##_macinit(&cctx, &ckey); \ + while (csz) { \ + if (i > csz) \ + i = csz; \ + pre##_mac(&cctx, p, i); \ + p += i; \ + csz -= i; \ + } \ + pre##_macdone(&cctx, d.buf); \ + if (memcmp(d.buf, v[2].buf, PRE##_HASHSZ) != 0) { \ + printf("\nfail:\n\tstep = %i\n\tinput = `%s'\n\tkey = ", \ + *ip, v[0].buf); \ + type_hex.dump(&v[1], stdout); \ + fputs("\n\texpected = ", stdout); \ + type_hex.dump(&v[2], stdout); \ + fputs("\n\tcomputed = ", stdout); \ + type_hex.dump(&d, stdout); \ + putchar('\n'); \ + ok = 0; \ + } \ + } \ + \ + dstr_destroy(&d); \ + return (ok); \ +} \ + \ +static test_chunk macdefs[] = { \ + { #pre "-hmac", macverify, \ + { &type_string, &type_hex, &type_hex, 0 } }, \ + { 0, 0, { 0 } } \ +}; \ + \ +int main(int argc, char *argv[]) \ +{ \ + ego(argv[0]); \ + test_run(argc, argv, macdefs, SRCDIR"/tests/" #pre); \ + return (0); \ +} + +#else +# define HMAC_TEST(PRE, pre) +#endif + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/idea.c b/idea.c new file mode 100644 index 0000000..f554bf4 --- /dev/null +++ b/idea.c @@ -0,0 +1,276 @@ +/* -*-c-*- + * + * $Id: idea.c,v 1.1 1999/09/03 08:41:12 mdw Exp $ + * + * Implementation of the IDEA cipher + * + * (c) 1999 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: idea.c,v $ + * Revision 1.1 1999/09/03 08:41:12 mdw + * Initial import. + * + */ + +/*----- Header files ------------------------------------------------------*/ + +#include +#include +#include +#include + +#include + +#include "blkc.h" +#include "idea.h" + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @inv@ --- * + * + * Arguments: @uint16 n@ = number to invert + * + * Returns: Multiplicative inverse of @n@ %$\pmod{2^{16} + 1}$%. + * + * Use: Computes multiplicative inverses. This is handy for the + * decryption key scheduling. + */ + +static uint16 inv(uint16 n) +{ + uint32 m = 0x10001; + uint32 a = 1, b = 0; + + for (;;) { + uint32 q, r, t; + if (!(r = m % n)) + break; + q = m / n; + m = n; n = r; + t = a; a = b - q * a; b = t; + } + if (a > MASK16) + a += 1; + return (U16(a)); +} + +/* --- @MUL@ --- * + * + * Arguments @x@ and @y@ are two 32-bit values to multiply. On exit, @x@ is + * the product of the two arguments. The result is not normalized back to 16 + * bits; the arguments are not expected to be normalized. + */ + +#define MUL(x, y) do { \ + uint32 _mx, _my = (y); \ + if ((_mx = U16(x)) == 0) \ + (x) = 1 - _my; \ + else if (_my == 0) \ + (x) = 1 - _mx; \ + else { \ + _my *= _mx; \ + _mx = U16(_my); _my >>= 16; \ + if (_mx < _my) \ + (x) = _mx - _my + 1; \ + else \ + (x) = _mx - _my; \ + } \ +} while (0) + +/* --- @idea_init@ --- * + * + * Arguments: @idea_ctx *k@ = pointer to key block + * @const void *buf@ = pointer to key buffer + * @size_t sz@ = size of key material + * + * Returns: --- + * + * Use: Initializes an IDEA key buffer. The buffer must be exactly + * 16 bytes in size, because IDEA is only defined with a key + * size of 128 bits. + */ + +void idea_init(idea_ctx *k, const void *buf, size_t sz) +{ + assert(((void)"IDEA key must be 128 bits", sz == IDEA_KEYSZ)); + + /* --- Unpack the encryption key --- */ + + { + const octet *p = buf; + uint16 *q = k->e; + uint32 a = LOAD32(p + 0); + uint32 b = LOAD32(p + 4); + uint32 c = LOAD32(p + 8); + uint32 d = LOAD32(p + 12); + int i; + + /* --- Main unpacking loop --- */ + + for (i = 0; i < 6; i++) { + + /* --- Spit out the next 8 subkeys --- */ + + q[0] = U16(a >> 16); + q[1] = U16(a >> 0); + q[2] = U16(b >> 16); + q[3] = U16(b >> 0); + q[4] = U16(c >> 16); + q[5] = U16(c >> 0); + q[6] = U16(d >> 16); + q[7] = U16(d >> 0); + q += 8; + + /* --- Rotate and permute the subkeys --- */ + + { + uint32 t = a; + a = U32((a << 25) | (b >> 7)); + b = U32((b << 25) | (c >> 7)); + c = U32((c << 25) | (d >> 7)); + d = U32((d << 25) | (t >> 7)); + } + } + + /* --- Write out the tail-enders --- */ + + q[0] = U16(a >> 16); + q[1] = U16(a >> 0); + q[2] = U16(b >> 16); + q[3] = U16(b >> 0); + } + + /* --- Convert this into the decryption key --- */ + + { + uint16 *p = k->e + 52; + uint16 *q = k->d; + int i; + + /* --- Translate the main round keys --- */ + + for (i = 0; i < 8; i++) { + p -= 6; + q[4] = p[0]; + q[5] = p[1]; + q[0] = inv(p[2]); + q[3] = inv(p[5]); + if (i) { + q[1] = 0x10000 - p[4]; + q[2] = 0x10000 - p[3]; + } else { + q[1] = 0x10000 - p[3]; + q[2] = 0x10000 - p[4]; + } + q += 6; + } + + /* --- Translate the tail-enders --- */ + + p -= 4; + q[0] = inv(p[0]); + q[1] = 0x10000 - p[1]; + q[2] = 0x10000 - p[2]; + q[3] = inv(p[3]); + } +} + +/* --- @ROUND@ --- */ + +#define MIX(k, a, b, c, d) do { \ + MUL(a, (k)[0]); \ + (b) += (k)[1]; \ + (c) += (k)[2]; \ + MUL(d, (k)[3]); \ +} while (0) + +#define MA(k, a, b, c, d) do { \ + unsigned _u = (a) ^ (c); \ + unsigned _v = (b) ^ (d); \ + MUL(_u, (k)[4]); \ + _v += _u; \ + MUL(_v, (k)[5]); \ + _u += _v; \ + (a) ^= _v; \ + (b) ^= _u; \ + (c) ^= _v; \ + (d) ^= _u; \ +} while (0); + +#define ROUND(k, a, b, c, d) do { \ + MIX((k), (a), (b), (c), (d)); \ + MA((k), (a), (b), (c), (d)); \ + (k) += 6; \ +} while (0) + +/* --- Encryption --- */ + +#define EBLK(k, a, b, c, d) do { \ + unsigned _a = U16(a >> 16); \ + unsigned _b = U16(a >> 0); \ + unsigned _c = U16(b >> 16); \ + unsigned _d = U16(b >> 0); \ + const uint16 *_k = (k); \ + \ + ROUND(_k, _a, _b, _c, _d); \ + ROUND(_k, _a, _c, _b, _d); \ + ROUND(_k, _a, _b, _c, _d); \ + ROUND(_k, _a, _c, _b, _d); \ + ROUND(_k, _a, _b, _c, _d); \ + ROUND(_k, _a, _c, _b, _d); \ + ROUND(_k, _a, _b, _c, _d); \ + ROUND(_k, _a, _c, _b, _d); \ + MIX (_k, _a, _c, _b, _d); \ + (c) = (U16(_a) << 16) | U16(_c); \ + (d) = (U16(_b) << 16) | U16(_d); \ +} while (0) + +#define DBLK(k, a, b) EBLK((k), (a), (b)) + +/* --- @idea_eblk@, @idea_dblk@ --- * + * + * Arguments: @const idea_ctx *k@ = pointer to a key block + * @const uint32 s[2]@ = pointer to source block + * @uint32 d[2]@ = pointer to destination block + * + * Returns: --- + * + * Use: Low-level block encryption and decryption. + */ + +void idea_eblk(const idea_ctx *k, const uint32 *s, uint32 *d) +{ + EBLK(k->e, s[0], s[1], d[0], d[1]); +} + +void idea_dblk(const idea_ctx *k, const uint32 *s, uint32 *d) +{ + EBLK(k->d, s[0], s[1], d[0], d[1]); +} + +BLKC_TEST(IDEA, idea) + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/idea.h b/idea.h new file mode 100644 index 0000000..5d26a57 --- /dev/null +++ b/idea.h @@ -0,0 +1,119 @@ +/* -*-c-*- + * + * $Id: idea.h,v 1.1 1999/09/03 08:41:12 mdw Exp $ + * + * Implementation of the IDEA cipher + * + * (c) 1999 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: idea.h,v $ + * Revision 1.1 1999/09/03 08:41:12 mdw + * Initial import. + * + */ + +/*----- Notes on the IDEA block cipher ------------------------------------* + * + * IDEA was invented by James Massey and Xuejia Lai. The fundamental idea + * underlying the cipher is combining incompatible group operations. The + * algorithm's main claim to fame is that it is the symmetric cipher in PGP + * version 2. + * + * The IDEA algorithm is allegedly patented by Ascom Tech A.G., even in the + * UK and Europe. Ascom are willing to grant licences for use in software + * which forbids commercial use, but this is not compatible with Free + * Software Foundation ideology. The author recommends against the use of + * the IDEA cipher entirely. Blowfish is conceptually simpler, appears more + * concervative, offers a larger keyspace, runs faster, and is in the public + * domain. + */ + +#ifndef IDEA_H +#define IDEA_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#include + +#include + +/*----- Magical numbers ---------------------------------------------------*/ + +#define IDEA_BLKSZ 8 +#define IDEA_KEYSZ 16 +#define IDEA_CLASS (N, B, 64) + +/*----- Data structures ---------------------------------------------------*/ + +typedef struct idea_ctx { + uint16 e[52]; + uint16 d[52]; +} idea_ctx; + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @idea_init@ --- * + * + * Arguments: @idea_ctx *k@ = pointer to key block + * @const void *buf@ = pointer to key buffer + * @size_t sz@ = size of key material + * + * Returns: --- + * + * Use: Initializes an IDEA key buffer. The buffer must be exactly + * 16 bytes in size, because IDEA is only defined with a key + * size of 128 bits. + */ + +extern void idea_init(idea_ctx */*k*/, const void */*buf*/, size_t /*sz*/); + +/* --- @idea_eblk@, @idea_dblk@ --- * + * + * Arguments: @const idea_ctx *k@ = pointer to a key block + * @const uint32 s[2]@ = pointer to source block + * @uint32 d[2]@ = pointer to destination block + * + * Returns: --- + * + * Use: Low-level block encryption and decryption. + */ + +extern void idea_eblk(const idea_ctx */*k*/, + const uint32 */*s*/, uint32 */*d*/); +extern void idea_dblk(const idea_ctx */*k*/, + const uint32 */*s*/, uint32 */*d*/); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/key.1 b/key.1 new file mode 100644 index 0000000..cbac9bc --- /dev/null +++ b/key.1 @@ -0,0 +1,245 @@ +.\" -*-nroff-*- +.TH key 1 "5 June 1999" Catacomb +.SH NAME +key \- simple key management system +.SH SYNOPSIS +.B key +.RB [ \-k +.IR keyring ] +.I command +.PP +where +.I command +is one of: +.PP +.B add +.RB [ \-b +.IR bits ] +.RB [ \-e +.IR expire ] +.RB [ \-c +.IR comment ] +.I type +.IR attr ... +.br +.B expire +.IR keyid ... +.br +.B delete +.IR keyid ... +.br +.B setattr +.I keyid +.IR attr ... +.br +.B list +.RB [ \-qv ] +.br +.B tidy +.br +.B extract +.I file +.IR keyid ... +.br +.B merge +.I file +.SH DESCRIPTION +The +.B key +command performs useful operations on Catacomb keyring files. It +provides a number of subcommands, by which the various operations may be +carried out. +.SS "Global options" +Before the command name, +.I "global options" +may be given. The following global options are supported: +.TP +.B "\-h, \-\-help" +Writes a brief summary of +.BR key 's +various options to standard output, and +returns a successful exit status. +.TP +.B "\-v, \-\-version" +Writes the program's version number to standard output, and returns a +successful exit status. +.TP +.B "\-u, \-\-usage" +Writes a very terse command line summary to standard output, and returns +a successful exit status. +.TP +.BI "\-k, \-\-keyring=" file +Names the keyring file which +.B key +is to process. The default keyring, used if this option doesn't specify +one, is the file named +.B keyring +in the current directory. The keyring must be stored in a regular file: +pipes, sockets, devices etc. are not allowed. +The +.B key +program attempts to lock the keyring before accessing it, using +.BR fcntl (2) +locking. It will however time out after a short while (10 seconds) and +report a failure. +.SS Concepts +In addition to the actual key data itself, a Catacomb key has a number +of other pieces of information attached to it: +.TP +.B keyid +Every key has a 32-bit identifying number, written in hexadecimal. The +keyid is derived from the actual key contents (although knowledge of a +key's keyid doesn't help one to guess the key itself). Applications use +keyids to refer to specific keys. A +.I deleted +key cannot be looked up by keyid. +.TP +.B type +A key's type string describes what the key may be used for. The type +string is arbitrary, except that it may not contain whitespace +characters. Applications use key types to obtain an arbitrary but +suitable key for some purpose. An +.I expired +key cannot be looked up by type, but may be looked up by keyid. +.TP +.B "expiry time" +Most keys expire after a certain amount of time. Once a key has +expired, it will no longer be chosen as a result of a lookup by key +type. However, it is not deleted until its deletion time is also +reached. +.TP +.B "deletion time" +A key's deletion time is the latest expiry time of any of the objects +which require that key. For example, a key used for authenticating +cryptographic cookies should have its deletion time set to the longest +expiry time of any of the cookies it can authenticate. A key is never +deleted until it has also expired. Once a key has expired +.I and +its deletion time is passed, it can no longer be referred to by +applications, and will be removed from the keyring next time it's +written to disk. +.TP +.B comment +A key may be given a comment when it's created. The comment is for the +benefit of users, and isn't interpreted by applications at all. +(Hopefully.) +.TP +.B attributes +A key as zero or more name/value pairs. The names and values are +arbitrary strings, except they may not contain null bytes. Some +attributes may have meaning for particular applications or key types; +others may be assigned global meanings in future. +.SH "COMMAND REFERENCE" +.SS add +The +.B add +command creates a new key and adds it to the keyring. The command +accepts the following options: +.TP +.BI "\-b, \-\-bits=" bits +The length of the key to generate, in bits. The default, if this option +is not supplied, is 128 bits. The bit length must be nonzero, and must +be a multiple of 8. +.TP +.BI "\-e, \-\-expire=" expire +The expiry date for the generated key. This may be the string +.RB ` forever ' +if the key should never expire automatically, or any date acceptable to +the +.BR getdate (3) +library function. Briefly, +.B getdate +understands absolute dates such as +.RB ` 1999-08-02 ' +or +.RB ` "August 2nd, 1999" ', +and (perhaps more usefully) relative dates such as +.RB ` "+2 weeks" '. +The default is to allow a 2 week expiry, which isn't useful. +.TP +.BI "\-c, \-\-comment=" comment +Sets a comment for the key. The default is not to attach a comment. +.PP +The key's type is given by the required +.I type +argument. Following the type are zero or more attributes, which are +attached to the key in the same way as for the +.B setattr +command. +.PP +The +.B key +program only generates random bitstrings, which are suitable for most +symmetric algorithms but not for public key cryptography. Generating +keys for more exotic algorithms is a feature which will be added later. +The keys are generated using the Catacomb random number generator, using +the +.B rand_goodbits +function. The Catacomb generator is believed to be strong. +.SS expire +Forces keys to immediately expire. An expired key is not chosen when a +program requests a key by its type. The keys to expire are listed by +their +.IR keyid s. +.SS delete +Deletes keys immediately. The keys to delete are listed by their +.IR keyid s. +Be careful when deleting keys. It might be a better idea +to expire keys rather than deleting them. +.SS setattr +Attaches attributes to a key. The key to which the attributes should be +attached is given by its +.IR keyid . +Each attribute has the form +.IB name = value\fR. +An attribute can be deleted by assigning it an empty value. Although +the keyring file format is capable of representing an attribute with an +empty value as distinct from a nonexistant attribute, this interface +does not allow empty attributes to be set. +.SS list +Lists the keys in the keyring. A couple of options are supported: +.TP +.B "\-v, \-\-verbose" +Increases the amount of information displayed for each key. Repeat for +a greater effect. +.TP +.B "\-q, \-\-quiet" +Decreases the amount of information displayed for each key. Each use +cancels a +.RB ` \-v ' +option. +.PP +By default, a single line of output is generated for each, showing +keyids, types, expiry and deletion dates, and comments. Additional +.RB ` \-v ' +options show more information, such as the exact time of day for expiry +and deletion, key attributes, and a hex dump of the actual key data. +.SS tidy +Simply reads the keyring from file and writes it back again. This has +the effect of removing any deleted keys from the file. +.SS extract +Writes a selection of keys to the named +.IR file , +which may be +.RB ` \- ' +to designate standard output. The keys to extract are listed by their +.IR keyid s. +The output is a valid keyring file. +.SS merge +Merges the keys from the named +.IR file , +which may be +.RB ` \- ' +to designate standard input, with the keyring. Keys already in the +keyring are not overwritten: you must explicitly remove them first if +you want them to be replaced during the merge. +.SH BUGS +The ability to generate keys for specific algorithms ought to be added, +for DES (setting the parity bits correctly), RSA, ElGamal and DSA, at +the very least. (None of these systems are actually implemented in +Catacomb at the moment, however.) +.SH "SEE ALSO" +.BR keyring (5). +.SH AUTHOR +Mark Wooding, + diff --git a/key.c b/key.c new file mode 100644 index 0000000..9ffa98b --- /dev/null +++ b/key.c @@ -0,0 +1,1077 @@ +/* -*-c-*- + * + * $Id: key.c,v 1.1 1999/09/03 08:41:12 mdw Exp $ + * + * Simple key management + * + * (c) 1999 Mark Wooding + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: key.c,v $ + * Revision 1.1 1999/09/03 08:41:12 mdw + * Initial import. + * + */ + +/*----- Header files ------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "key.h" + +/*----- Useful macros -----------------------------------------------------*/ + +#define KEY_PARANOID +#define NOTHING + +#ifdef KEY_PARANOID +# define KEY_WRITE(f, func, val) do { \ + if (!(f)->f & KF_WRITE) { \ + moan(#func " [caller error]: keyfile is readonly"); \ + errno = EROFS; \ + return val; \ + } \ + } while (0) +#else +# define KEY_WRITE(f, func) do { ; } while (0) +#endif + +#define KEY_MODIFY(f) do { (f)->f |= KF_MODIFIED; } while (0) + +#define KEY_LOAD(n) ((n) * 2) + +/*----- Sanity checking of values -----------------------------------------*/ + +/* --- @key_chktype@ --- * + * + * Arguments: @const char *type@ = pointer to a type string + * + * Returns: Zero if OK, -1 on error. + * + * Use: Checks whether a type string is OK. + */ + +int key_chktype(const char *type) +{ + if (!type || !*type) + goto fail; + while (*type) { + if (isspace((unsigned char)*type)) + goto fail; + type++; + } + return (0); + +fail: + errno = EINVAL; + return (-1); +} + +/* --- @key_chkcomment@ --- * + * + * Arguments: @const char *comment@ = pointer to a comment string + * + * Returns: Zero if OK, -1 on error. + * + * Use: Checks whether a comment string is OK. + */ + +int key_chkcomment(const char *c) +{ + if (!c) + return (0); + if (!*c) + goto fail; + while (*c) { + if (*c == '\n') + goto fail; + c++; + } + return (0); + +fail: + errno = EINVAL; + return (-1); +} + +/*----- Low-level fiddling ------------------------------------------------*/ + +/* --- @insert@ --- * + * + * Arguments: @key_file *f@ = pointer to file structure + * @const char *type@ = type of key to insert + * @const void *k@ = pointer to key data + * @size_t ksz@ = size of key data + * @time_t exp@ = expiry time for key + * @time_t del@ = deletion time for key + * + * Returns: Pointer to key block to fill in the rest of, or zero. + * + * Use: Inserts a key into a key file. + */ + +static key *insert(key_file *f, + const char *type, + const void *k, size_t ksz, + time_t exp, time_t del) +{ + uint32 id; + key *kk; + key_type *t; + + /* --- Sanity preservatives --- */ + + if (key_chktype(type)) + return (0); + + /* --- Insert into the id table --- */ + + { + hash_base **bin, **b; + + CRC32(id, 0, k, ksz); + bin = HASH_BIN(&f->byid, id); + for (b = bin; *b; b = &(*b)->next) { + if ((*b)->hash == id) { + errno = EEXIST; + return (0); + } + } + + kk = CREATE(key); + kk->_b.next = 0; + *b = &kk->_b; + kk->_b.hash = id; + } + + /* --- Extend the table --- */ + + if (f->idload > 0) + f->idload--; + else if (hash_extend(&f->byid)) + f->idload = KEY_LOAD(f->byid.mask / 2); + + /* --- Initialize the key block --- */ + + kk->id = id; + kk->k = sub_alloc(ksz); + memcpy(kk->k, k, ksz); + kk->ksz = ksz; + kk->type = xstrdup(type); + kk->exp = exp; + kk->del = del; + sym_create(&kk->a); + kk->c = 0; + + /* --- Insert into the type table --- */ + + { + unsigned found; + t = sym_find(&f->bytype, type, -1, sizeof(*t), &found); + if (!found) { + t->k = kk; + kk->next = 0; + } else { + key **p = &t->k; + if (exp != KEXP_FOREVER) { + while (*p && (*p)->exp != KEXP_EXPIRE && (*p)->exp > exp) + p = &(*p)->next; + } + kk->next = *p; + *p = kk; + } + } + + return (kk); +} + +/*----- Iteration and iterators -------------------------------------------*/ + +/* --- @key_mkiter@ --- * + * + * Arguments: @key_iter *i@ = pointer to iterator object + * @key_file *f@ = pointer to file structure + * + * Returns: --- + * + * Use: Initializes a key iterator. The keys are returned by + * @key_next@. + */ + +void key_mkiter(key_iter *i, key_file *f) +{ + HASH_MKITER(&i->i, &f->byid); + i->t = time(0); +} + +/* --- @key_next@ --- * + * + * Arguments: @key_iter *i@ = pointer to iterator object + * + * Returns: Pointer to next key, or null. + * + * Use: Returns the next key in some arbitrary sequence. + */ + +key *key_next(key_iter *i) +{ + hash_base *b; + key *k; + do { + HASH_NEXT(&i->i, b); + k = (key *)b; + } while (k && KEY_EXPIRED(i->t, k->exp) && KEY_DELETED(i->t, k->del)); + return (k); +} + +/* --- @key_mkattriter@ --- * + * + * Arguments: @key_attriter *i@ = pointer to attribute iterator + * @key_file *f@ = pointer to key file + * @key *k@ = pointer to key + * + * Returns: --- + * + * Use: Initializes an attribute iterator. The attributes are + * returned by @key_nextattr@. + */ + +void key_mkattriter(key_attriter *i, key_file *f, key *k) +{ + sym_mkiter(&i->i, &k->a); +} + +/* --- @key_nextattr@ --- * + * + * Arguments: @key_attriter *i@ = pointer to attribute iterator + * @const char **n, **v@ = pointers to name and value + * + * Returns: Zero if no attribute available, or nonzero if returned OK. + * + * Use: Returns the next attribute. + */ + +int key_nextattr(key_attriter *i, const char **n, const char **v) +{ + key_attr *a = sym_next(&i->i); + if (!a) + return (0); + *n = SYM_NAME(a); + *v = a->p; + return (1); +} + +/*----- Lookup ------------------------------------------------------------*/ + +/* --- @key_bytype@ --- * + * + * Arguments: @key_file *f@ = key file we want a key from + * @const char *type@ = type string for desired key + * + * Returns: Pointer to the best key to use, or null. + * + * Use: Looks up a key by its type. Returns the key with the latest + * expiry time. This function will not return an expired key. + */ + +key *key_bytype(key_file *f, const char *type) +{ + time_t now = time(0); + key *k; + key_type *t; + + if ((t = sym_find(&f->bytype, type, -1, 0, 0)) == 0) + return (0); + for (k = t->k; k && KEY_EXPIRED(now, k->exp); k = k->next) + ; + return (k); +} + +/* --- @key_byid@ --- * + * + * Arguments: @key_file *f@ = key file to find a key from + * @uint32 id@ = id to look for + * + * Returns: Key with matching id. + * + * Use: Returns a key given its id. This function will return an + * expired key, but not a deleted one. + */ + +key *key_byid(key_file *f, uint32 id) +{ + time_t t = time(0); + hash_base **bin, *b; + + bin = HASH_BIN(&f->byid, id); + for (b = *bin; b; b = b->next) { + if (b->hash == id) { + key *k = (key *)b; + if (KEY_EXPIRED(t, k->exp) && KEY_DELETED(t, k->del)) + return (0); + return (k); + } + } + return (0); +} + +/*----- Attributes --------------------------------------------------------*/ + +/* --- @key_getattr@ --- * + * + * Arguments: @key_file *f@ = pointer to file + * @key *k@ = pointer to key + * @const char *n@ = pointer to attribute name + * + * Returns: Pointer to attribute value, or null if not found. + * + * Use: Returns the value of a key attribute. + */ + +const char *key_getattr(key_file *f, key *k, const char *n) +{ + key_attr *a; + if ((a = sym_find(&k->a, n, -1, 0, 0)) == 0) + return (0); + return (a->p); +} + +/* --- @key_putattr@ --- * + * + * Arguments: @key_file *f@ = pointer to file + * @key *k@ = pointer to key + * @const char *n@ = pointer to attribute name + * @const char *v@ = pointer to attribute value or null + * + * Returns: --- + * + * Use: Inserts an attribute on a key. If an attribute with the same + * name already exists, it is deleted. Setting a null value + * removes the attribute. + */ + +void key_putattr(key_file *f, key *k, const char *n, const char *v) +{ + key_attr *a; + unsigned found; + + KEY_WRITE(f, key_putattr, NOTHING); + + if (v) { + a = sym_find(&k->a, n, -1, sizeof(*a), &found); + if (found) + free(a->p); + a->p = xstrdup(v); + } else if ((a = sym_find(&k->a, n, -1, 0, 0)) != 0) { + free(a->p); + sym_remove(&k->a, a); + } + + KEY_MODIFY(f); +} + +/* --- @key_setcomment@ --- * + * + * Arguments: @key_file *f@ = pointer to key file block + * @key *k@ = pointer to key block + * @const char *c@ = pointer to comment to set, or zero + * + * Returns: --- + * + * Use: Replaces the key's current comment with a new one. + */ + +void key_setcomment(key_file *f, key *k, const char *c) +{ + KEY_WRITE(f, key_setcomment, NOTHING); + if (key_chkcomment(c)) + return; + if (k->c) + free(k->c); + if (c) + k->c = xstrdup(c); + else + k->c = 0; + KEY_MODIFY(f); +} + +/*----- Low-level file I/O ------------------------------------------------*/ + +/* --- @key_merge@ --- * + * + * Arguments: @key_file *f@ = pointer to file structure + * @const char *file@ = name of file (for error messages) + * @FILE *fp@ = file handle to read from + * + * Returns: --- + * + * Use: Reads keys from a file, and inserts them into the file. + */ + +void key_merge(key_file *f, const char *file, FILE *fp) +{ + int line = 0; + dstr l = DSTR_INIT; + dstr d = DSTR_INIT; + dstr n = DSTR_INIT, v = DSTR_INIT; + + KEY_WRITE(f, key_merge, NOTHING); + + for (; dstr_putline(&l, fp) != EOF; DRESET(&l)) { + char *vf[6]; + char *p = l.buf; + key *k; + + /* --- Skip blank lines and comments --- * + * + * Quite what they're doing in what ought to be an automatically- + * maintained file I don't know. + */ + + line++; + while (isspace((unsigned char)*p)) + p++; + if (!*p || *p == '#') + continue; + + /* --- Break the line into fields --- * + * + * There are currently six fields of interest: + * + * * The key's type tag. + * * The actual key data itself. + * * The key expiry time. + * * The key deletion time. + * * The attributes field. + * * Any further comments. + * + * All but the last field can contain no spaces. + */ + + { + int n = str_split(p, vf, 5, &vf[5]); + if (n < 4) { + moan("key file `%s', line %i: too few fields", file, line); + continue; + } + } + + /* --- Decode various bits and insert the key --- */ + + { + base64_ctx b; + time_t exp, del; + + base64_init(&b); + base64_decode(&b, vf[1], strlen(vf[1]), &d); + base64_decode(&b, 0, 0, &d); + + exp = (time_t)atol(vf[2]); + del = (time_t)atol(vf[3]); + + if ((k = insert(f, vf[0], d.buf, d.len, exp, del)) == 0) + continue; + DRESET(&d); + } + + /* --- Parse up the attributes, if specified --- */ + + if (vf[4]) { + url_dctx uc; + for (url_initdec(&uc, vf[4]); url_dec(&uc, &n, &v); ) { + key_putattr(f, k, n.buf, v.buf); + DRESET(&n); DRESET(&v); + } + } + + /* --- Insert the comment --- */ + + if (vf[5]) + k->c = xstrdup(vf[5]); + } + + /* --- Extensive tidying up now required --- */ + + dstr_destroy(&l); + dstr_destroy(&d); + dstr_destroy(&n); + dstr_destroy(&v); + KEY_MODIFY(f); +} + +/* --- @key_extract@ --- * + * + * Arguments: @key_file *f@ = pointer to file structure + * @key *k@ = key to extract + * @FILE *fp@ = file to write on + * + * Returns: Zero if OK, EOF on error. + * + * Use: Extracts a key to an ouptut file. + */ + +int key_extract(key_file *f, key *k, FILE *fp) +{ + dstr d = DSTR_INIT; + + /* --- Encode the key and write the easy stuff --- */ + + { + base64_ctx b; + base64_init(&b); + b.indent = ""; + base64_encode(&b, k->k, k->ksz, &d); + base64_encode(&b, 0, 0, &d); + DPUTZ(&d); + fprintf(fp, "%s %s %li %li ", + k->type, d.buf, (long)k->exp, (long)k->del); + DRESET(&d); + } + + /* --- Output the attributes --- */ + + { + int none = 1; + sym_iter i; + key_attr *a; + url_ectx uc; + + url_initenc(&uc); + for (sym_mkiter(&i, &k->a); (a = sym_next(&i)) != 0; ) { + none = 0; + url_enc(&uc, &d, SYM_NAME(a), a->p); + } + if (none) + DPUTS(&d, "-"); + DWRITE(&d, fp); + } + + dstr_destroy(&d); + if (k->c) { + putc(' ', fp); + fputs(k->c, fp); + } + putc('\n', fp); + return (ferror(fp) ? EOF : 0); +} + +/* --- @fdcopy@ --- * + * + * Arguments: @int source@ = source file descriptor + * @int dest@ = destination file descriptor + * + * Returns: Zero if OK, nonzero otherwise. + * + * Use: Copies data from one file descriptor to another. + */ + +static int fdcopy(int source, int dest) +{ + char buf[4096]; + + if (lseek(source, 0, SEEK_SET) < 0|| + lseek(dest, 0, SEEK_SET) < 0 || + ftruncate(dest, 0) < 0) + return (-1); + for (;;) { + int n = read(source, buf, sizeof(buf)); + if (n < 0) + return (-1); + else if (n == 0) + break; + else if (write(dest, buf, n) < 0) + return (-1); + } + return (0); +} + +/* --- @key_write@ --- * + * + * Arguments: @key_file *f@ = pointer to key file block + * + * Returns: A @KWRITE_@ code indicating how well it worked. + * + * Use: Writes a key file's data back to the actual file. This code + * is extremely careful about error handling. It should usually + * be able to back out somewhere sensible, but it can tell when + * it's got itself into a real pickle and starts leaving well + * alone. + * + * Callers, please make sure that you ring alarm bells when this + * function returns @KWRITE_BROKEN@. + */ + +int key_write(key_file *f) +{ + dstr n_older = DSTR_INIT, n_old = DSTR_INIT, n_new = DSTR_INIT; + int rc = KWRITE_FAIL; + + if (!(f->f & KF_MODIFIED)) + return (KWRITE_OK); + + /* --- Write a new key file out --- * + * + * Check for an error after each key line. This ought to be enough. + * Checking after each individual byte write and @fprintf@ isn't much fun. + */ + + dstr_putf(&n_new, "%s.new", f->name); + + { + key *k; + key_iter i; + FILE *fp; + + if ((fp = fopen(n_new.buf, "w")) == 0) + goto fail_open; + + for (key_mkiter(&i, f); (k = key_next(&i)) != 0; ) { + if (key_extract(f, k, fp)) { + fclose(fp); + goto fail_write; + } + } + + if (fclose(fp)) + goto fail_write; + } + + /* --- Set up the other filenames --- */ + + dstr_putf(&n_older, "%s.older", f->name); + dstr_putf(&n_old, "%s.old", f->name); + + /* --- Move the current backup on one --- * + * + * If the `older' file exists, then we're in need of attention. + */ + + { + struct stat st; + if (stat(n_older.buf, &st) == 0 || errno != ENOENT) { + errno = EEXIST; + rc = KWRITE_BROKEN; + goto fail_shift; + } + if (rename(n_old.buf, n_older.buf) && errno != ENOENT) + goto fail_shift; + } + + /* --- Copy the current file to the backup --- */ + + { + int fd; + if ((fd = open(n_old.buf, O_WRONLY | O_CREAT | O_EXCL, 0600)) < 0) + goto fail_backup; + if (fdcopy(f->fd, fd)) { + close(fd); + goto fail_backup; + } + if (close(fd)) + goto fail_backup; + } + + /* --- Copy the newly created file to the current one --- * + * + * This is the dangerous bit. + */ + + { + int fd; + if ((fd = open(n_new.buf, O_RDONLY)) < 0) + goto fail_update; + if (fdcopy(fd, f->fd)) { + close(fd); + goto fail_update; + } + close(fd); + if (fsync(f->fd)) + goto fail_update; + } + + /* --- Clean up --- * + * + * Remove the `new' file and the `older' backup. Then we're done. + */ + + unlink(n_new.buf); + unlink(n_older.buf); + return (KWRITE_OK); + + /* --- Failure while writing the new key file --- * + * + * I need to copy the backup back. If that fails then I'm really stuffed. + * If not, then I might as well try to get the backups sorted back out + * again. + */ + +fail_update: + { + int fd; + int e = errno; + + if ((fd = open(n_old.buf, O_RDONLY)) < 0) + rc = KWRITE_BROKEN; + else if (fdcopy(fd, f->fd)) { + close(fd); + rc = KWRITE_BROKEN; + } else { + close(fd); + if (fsync(f->fd)) + rc = KWRITE_BROKEN; + } + + errno = e; + if (rc == KWRITE_BROKEN) + goto fail_shift; + } + /* Now drop through */ + + /* --- Failure while writing the new backup --- * + * + * The new backup isn't any use. Try to recover the old one. + */ + +fail_backup: + { + int e = errno; + unlink(n_old.buf); + if (rename(n_older.buf, n_old.buf) && errno != ENOENT) + rc = KWRITE_BROKEN; + errno = e; + } + /* Now drop through */ + + /* --- Failure while demoting the current backup --- * + * + * Leave the completed output file there for the operator in case he wants + * to clean up. + */ + +fail_shift: + dstr_destroy(&n_new); + dstr_destroy(&n_old); + dstr_destroy(&n_older); + return (rc); + +/* --- Failure during write of new data --- * + * + * Clean up the new file and return. These errors can never cause + * breakage. + */ + +fail_write: + unlink(n_new.buf); + fail_open: + dstr_destroy(&n_new); + return (rc); +} + +/*----- Opening and closing files -----------------------------------------*/ + +/* --- @key_open@ --- * + * + * Arguments: @key_file *f@ = pointer to file structure to initialize + * @const char *file@ = pointer to the file name + * @int how@ = opening options (@KOPEN_*@). + * + * Returns: Zero if it worked, nonzero otherwise. + * + * Use: Opens a key file, reads its contents, and stores them in a + * structure. The file is locked appropriately until closed + * using @key_close@. On an error, everything is cleared away + * tidily. If the file is opened with @KOPEN_WRITE@, it's + * created if necessary, with read and write permissions for its + * owner only. + */ + +int key_open(key_file *f, const char *file, int how) +{ + FILE *fp; + + /* --- Trivial bits of initialization --- */ + + f->f = 0; + f->name = xstrdup(file); + + /* --- Open the file and get the lock --- */ + + { + int of, lf; + const char *ff; + int fd; + + /* --- Lots of things depend on whether we're writing --- */ + + switch (how) { + case KOPEN_READ: + of = O_RDONLY; + lf = LOCK_NONEXCL; + ff = "r"; + break; + case KOPEN_WRITE: + of = O_RDWR | O_CREAT; + lf = LOCK_EXCL; + ff = "r+"; + break; + default: + errno = EINVAL; + return (-1); + } + + if ((fd = open(file, of, 0600)) < 0) + return (-1); + if (fcntl(fd, F_SETFD, 1) < 0 || + lock_file(fd, lf) < 0 || (fp = fdopen(fd, ff)) == 0) { + close(fd); + return (-1); + } + f->fd = fd; + } + + /* --- Read the file of keys into the table --- */ + + hash_create(&f->byid, 64); + f->idload = KEY_LOAD(64); + sym_create(&f->bytype); + f->f |= KF_WRITE; + key_merge(f, file, fp); + if (how == KOPEN_READ) + f->f &= ~(KF_WRITE | KF_MODIFIED); + else + f->f &= ~KF_MODIFIED; + + /* --- Close the file if only needed for reading --- */ + + if (how == KOPEN_READ) { + f->fp = 0; + fclose(fp); + } else + f->fp = fp; + + return (0); +} + +/* --- @key_close@ --- * + * + * Arguments: @key_file *f@ = pointer to key file block + * + * Returns: A @KWRITE_@ code indicating how it went. + * + * Use: Frees all the key data, writes any changes. Make sure that + * all hell breaks loose if this returns @KWRITE_BROKEN@. + */ + +int key_close(key_file *f) +{ + int e; + hash_base *b; + hash_iter i; + + if ((e = key_write(f)) != KWRITE_OK) + return (e); + + /* --- Free all the individual keys --- */ + + for (hash_mkiter(&i, &f->byid); (b = hash_next(&i)) != 0; ) { + sym_iter j; + key_attr *a; + key *k = (key *)b; + + sub_free(k->k, k->ksz); + free(k->type); + if (k->c) + free(k->c); + for (sym_mkiter(&j, &k->a); (a = sym_next(&j)) != 0; ) + free(a->p); + sym_destroy(&k->a); + DESTROY(k); + } + hash_destroy(&f->byid); + sym_destroy(&f->bytype); + + if (f->fp) + fclose(f->fp); + free(f->name); + return (KWRITE_OK); +} + +/*----- Miscellaneous functions -------------------------------------------*/ + +/* --- @key_new@ --- + * + * Arguments: @key_file *f@ = pointer to key file + * @const char *type@ = the type of this key + * @const void *k@ = pointer to key data + * @size_t ksz@ = size of key data + * @time_t exp@ = when the key expires + * @const char *c@ = textual comment to attach + * + * Returns: Key block containing new data, or null if it couldn't be + * done. + * + * Use: Attaches a new key to a key file. You must have a writable + * key file for this to work. + * + * The type is a key type string. This interface doesn't care + * about how type strings are formatted: it just treats them as + * opaque gobs of text. Clients are advised to choose some + * standard for representing key types, though. + * + * The key can be any old binary mess. + * + * The expiry time should either be a time in the future, or the + * magic value @KEXP_FOREVER@ which means `never expire this + * key'. Be careful with `forever' keys. If I were you, I'd + * use a more sophisticated key management system than this for + * them. + * + * The comment can be any old text not containing newlines or + * nulls. This interface doesn't impose any length restrictions + * on comment lengths. + */ + +key *key_new(key_file *f, const char *type, + const void *k, size_t ksz, + time_t exp, const char *c) +{ + key *kk; + time_t t = time(0); + + KEY_WRITE(f, key_new, 0); + + if (KEY_EXPIRED(t, exp) || + key_chktype(type) || key_chkcomment(c) || + (kk = insert(f, type, k, ksz, exp, KEXP_UNUSED)) == 0) + return (0); + if (c) + kk->c = xstrdup(c); + KEY_MODIFY(f); + return (kk); +} + +/* --- @key_delete@ --- * + * + * Arguments: @key_file *f@ = pointer to file block + * @key *k@ = key to delete + * + * Returns: --- + * + * Use: Removes the given key from the list. The key file must be + * writable. (Due to the horridness of the data structures, + * deleted keys aren't actually removed, just marked so that + * they can't be looked up or iterated over. One upshot of + * this is that they don't get written back to the file when + * it's closed.) + */ + +void key_delete(key_file *f, key *k) +{ + KEY_WRITE(f, key_delete, NOTHING); + k->exp = KEXP_EXPIRE; + k->del = KEXP_UNUSED; + KEY_MODIFY(f); +} + +/* --- @key_expire@ --- * + * + * Arguments: @key_file *f@ = pointer to file block + * @key *k@ = pointer to key block + * + * Returns: --- + * + * Use: Immediately marks the key as expired. It may be removed + * immediately, if it is no longer required, and will be removed + * by a tidy operation when it is no longer required. The key + * file must be writable. + */ + +void key_expire(key_file *f, key *k) +{ + KEY_WRITE(f, key_expire, NOTHING); + k->exp = KEXP_EXPIRE; + if (k->del == KEXP_FOREVER) + k->del = KEXP_UNUSED; + KEY_MODIFY(f); +} + +/* --- @key_used@ --- * + * + * Arguments: @key_file *f@ = pointer to key file + * @key *k@ = pointer to key block + * @time_t t@ = when key can be removed + * + * Returns: Zero if OK, nonzero on failure. + * + * Use: Marks a key as being required until a given time. Even + * though the key may expire before then (and won't be returned + * by type after that time), it will still be available when + * requested explicitly by id. The key file must be writable. + * + * The only (current) reason for failure is attempting to use + * a key which can expire for something which can't. + */ + +int key_used(key_file *f, key *k, time_t t) +{ + KEY_WRITE(f, key_used, -1); + if (t == KEXP_FOREVER) { + if (k->exp != KEXP_FOREVER) { + errno = EINVAL; + return (-1); + } + } else if (k->del >= t) + return (0); + + k->del = t; + KEY_MODIFY(f); + return (0); +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/key.h b/key.h new file mode 100644 index 0000000..b7eb12b --- /dev/null +++ b/key.h @@ -0,0 +1,462 @@ +/* -*-c-*- + * + * $Id: key.h,v 1.1 1999/09/03 08:41:12 mdw Exp $ + * + * Simple key management + * + * (c) 1999 Mark Wooding + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: key.h,v $ + * Revision 1.1 1999/09/03 08:41:12 mdw + * Initial import. + * + */ + +#ifndef KEY_H +#define KEY_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#include +#include + +#include +#include +#include + +/*----- Data structures ---------------------------------------------------*/ + +/* --- Key attributes --- * + * + * Each attribute is stored as a symbol in a symbol table. The value is + * the plain (not url-encoded) text to be written to the the file. If the + * value is binary data, then by this point it's base-64 encoded. + */ + +typedef struct key_attr { + sym_base _b; /* Symbol table data */ + char *p; /* Pointer to attribute value */ +} key_attr; + +/* --- Main key structure --- * + * + * Each key is stored in two symbol tables, one indexed by keyid, and the + * other indexed by type. Because many keys can have the same type, the type + * table contains a list of keys, sorted in descending order of expiry. + */ + +typedef struct key { + hash_base _b; /* Symbol table data */ + struct key *next; /* Next key of the same type */ + uint32 id; /* Key id used to name it */ + char *type; /* Textual key type */ + void *k; /* Actual key data */ + size_t ksz; /* Size of the key data */ + time_t exp, del; /* Expiry times for keys */ + sym_table a; /* Hashtable of key attributes */ + char *c; /* Any additional comments */ +} key; + +/* --- The keys-by-type entries --- */ + +typedef struct key_type { + sym_base _b; /* Symbol table data */ + key *k; /* Pointer to first key in list */ +} key_type; + +/* --- A key file --- */ + +typedef struct key_file { + FILE *fp; /* File pointer open on file */ + int fd; /* File descriptor open on file */ + char *name; /* Filename used to create it */ + unsigned f; /* Various useful flags */ + hash_table byid; /* Table of keys by keyid */ + sym_table bytype; /* Table of keys by type */ + size_t idload; /* Loading on id table */ +} key_file; + +/* --- Key file flags --- */ + +enum { + KF_WRITE = 1, /* File opened for writing */ + KF_MODIFIED = 2 /* File has been modified */ +}; + +/* --- Iterating over keys --- * + * + * Both of these are simple symbol table iterators, but they're made distinct + * types for the dubious benefits that type safety brings. + */ + +typedef struct { hash_iter i; time_t t; } key_iter; +typedef struct { sym_iter i; } key_attriter; + +/* --- File opening options --- */ + +enum { + KOPEN_READ, + KOPEN_WRITE +}; + +/* --- Various other magic numbers --- */ + +#define KEXP_UNUSED ((time_t)0) /* Key has never been used */ +#define KEXP_FOREVER ((time_t)-1) /* Never expire this key */ +#define KEXP_EXPIRE ((time_t)-2) /* Expire this key when unused */ + +/* --- Write attempt codes --- */ + +enum { + KWRITE_OK, /* Everything went fine */ + KWRITE_FAIL = -1, /* Close attempt failed */ + KWRITE_BROKEN = -2 /* Key ring needs manual fixing */ +}; + +/* --- Macros for testing expiry --- */ + +#define KEY_EXPIRED(now, exp) \ + ((exp) == KEXP_EXPIRE || ((exp) != KEXP_FOREVER && (exp) < (now))) + +#define KEY_DELETED(now, del) ((del) == KEXP_FOREVER || (del) < (now)) + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @key_chktype@ --- * + * + * Arguments: @const char *type@ = pointer to a type string + * + * Returns: Zero if OK, -1 on error. + * + * Use: Checks whether a type string is OK. + */ + +extern int key_chktype(const char */*type*/); + +/* --- @key_chkcomment@ --- * + * + * Arguments: @const char *comment@ = pointer to a comment string + * + * Returns: Zero if OK, -1 on error. + * + * Use: Checks whether a comment string is OK. + */ + +extern int key_chkcomment(const char */*c*/); + +/* --- @key_mkiter@ --- * + * + * Arguments: @key_iter *i@ = pointer to iterator object + * @key_file *f@ = pointer to file structure + * + * Returns: --- + * + * Use: Initializes a key iterator. The keys are returned by + * @key_next@. + */ + +extern void key_mkiter(key_iter */*i*/, key_file */*f*/); + +/* --- @key_next@ --- * + * + * Arguments: @key_iter *i@ = pointer to iterator object + * + * Returns: Pointer to next key, or null. + * + * Use: Returns the next key in some arbitrary sequence. + */ + +extern key *key_next(key_iter */*i*/); + +/* --- @key_mkattriter@ --- * + * + * Arguments: @key_attriter *i@ = pointer to attribute iterator + * @key_file *f@ = pointer to key file + * @key *k@ = pointer to key + * + * Returns: --- + * + * Use: Initializes an attribute iterator. The attributes are + * returned by @key_nextattr@. + */ + +extern void key_mkattriter(key_attriter */*i*/, key_file */*f*/, key */*k*/); + +/* --- @key_nextattr@ --- * + * + * Arguments: @key_attriter *i@ = pointer to attribute iterator + * @const char **n, **v@ = pointers to name and value + * + * Returns: Zero if no attribute available, or nonzero if returned OK. + * + * Use: Returns the next attribute. + */ + +extern int key_nextattr(key_attriter */*i*/, + const char **/*n*/, const char **/*v*/); + +/* --- @key_bytype@ --- * + * + * Arguments: @key_file *f@ = key file we want a key from + * @const char *type@ = type string for desired key + * + * Returns: Pointer to the best key to use, or null. + * + * Use: Looks up a key by its type. Returns the key with the latest + * expiry time. This function will not return an expired key. + */ + +extern key *key_bytype(key_file */*f*/, const char */*type*/); + +/* --- @key_byid@ --- * + * + * Arguments: @key_file *f@ = key file to find a key from + * @uint32 id@ = id to look for + * + * Returns: Key with matching id. + * + * Use: Returns a key given its id. This function will return an + * expired key, but not a deleted one. + */ + +extern key *key_byid(key_file */*f*/, uint32 /*id*/); + +/* --- @key_getattr@ --- * + * + * Arguments: @key_file *f@ = pointer to file + * @key *k@ = pointer to key + * @const char *n@ = pointer to attribute name + * + * Returns: Pointer to attribute value, or null if not found. + * + * Use: Returns the value of a key attribute. + */ + +extern const char *key_getattr(key_file */*f*/, key */*k*/, + const char */*n*/); + +/* --- @key_putattr@ --- * + * + * Arguments: @key_file *f@ = pointer to file + * @key *k@ = pointer to key + * @const char *n@ = pointer to attribute name + * @const char *v@ = pointer to attribute value + * + * Returns: --- + * + * Use: Inserts an attribute on a key. If an attribute with the same + * name already exists, it is deleted. + */ + +extern void key_putattr(key_file */*f*/, key */*k*/, + const char */*n*/, const char */*v*/); + +/* --- @key_setcomment@ --- * + * + * Arguments: @key_file *f@ = pointer to key file block + * @key *k@ = pointer to key block + * @const char *c@ = pointer to comment to set, or zero + * + * Returns: --- + * + * Use: Replaces the key's current comment with a new one. + */ + +extern void key_setcomment(key_file */*f*/, key */*k*/, const char */*c*/); + +/* --- @key_merge@ --- * + * + * Arguments: @key_file *f@ = pointer to file structure + * @const char *file@ = name of file (for error messages) + * @FILE *fp@ = file handle to read from + * + * Returns: --- + * + * Use: Reads keys from a file, and inserts them into the file. + */ + +extern void key_merge(key_file */*f*/, const char */*file*/, FILE */*fp*/); + +/* --- @key_extract@ --- * + * + * Arguments: @key_file *f@ = pointer to file structure + * @key *k@ = key to extract + * @FILE *fp@ = file to write on + * + * Returns: Zero if OK, EOF on error. + * + * Use: Extracts a key to an ouptut file. + */ + +extern int key_extract(key_file */*f*/, key */*k*/, FILE */*fp*/); + +/* --- @key_write@ --- * + * + * Arguments: @key_file *f@ = pointer to key file block + * + * Returns: A @KWRITE_@ code indicating how well it worked. + * + * Use: Writes a key file's data back to the actual file. This code + * is extremely careful about error handling. It should usually + * be able to back out somewhere sensible, but it can tell when + * it's got itself into a real pickle and starts leaving well + * alone. + * + * Callers, please make sure that you ring alarm bells when this + * function returns @KWRITE_BROKEN@. + */ + +extern int key_write(key_file */*f*/); + +/* --- @key_open@ --- * + * + * Arguments: @key_file *f@ = pointer to file structure to initialize + * @const char *file@ = pointer to the file name + * @int how@ = opening options (@KOPEN_*@). + * + * Returns: Zero if it worked, nonzero otherwise. + * + * Use: Opens a key file, reads its contents, and stores them in a + * structure. The file is locked appropriately until closed + * using @key_close@. On an error, everything is cleared away + * tidily. If the file is opened with @KOPEN_WRITE@, it's + * created if necessary, with read and write permissions for its + * owner only. + */ + +extern int key_open(key_file */*f*/, const char */*file*/, int /*how*/); + +/* --- @key_close@ --- * + * + * Arguments: @key_file *f@ = pointer to key file block + * + * Returns: A @KWRITE_@ code indicating how it went. + * + * Use: Frees all the key data, writes any changes. Make sure that + * all hell breaks loose if this returns @KWRITE_BROKEN@. + */ + +extern int key_close(key_file */*f*/); + +/* --- @key_new@ --- + * + * Arguments: @key_file *f@ = pointer to key file + * @const char *type@ = the type of this key + * @const void *k@ = pointer to key data + * @size_t ksz@ = size of key data + * @time_t exp@ = when the key expires + * @const char *c@ = textual comment to attach + * + * Returns: Key block containing new data, or null if it couldn't be + * done. + * + * Use: Attaches a new key to a key file. You must have a writable + * key file for this to work. + * + * The type is a key type string. This interface doesn't care + * about how type strings are formatted: it just treats them as + * opaque gobs of text. Clients are advised to choose some + * standard for representing key types, though. + * + * The key can be any old binary mess. + * + * The expiry time should either be a time in the future, or the + * magic value @KEXP_FOREVER@ which means `never expire this + * key'. Be careful with `forever' keys. If I were you, I'd + * use a more sophisticated key management system than this for + * them. + * + * The comment can be any old text not containing newlines or + * nulls. This interface doesn't impose any length restrictions + * on comment lengths. + */ + +extern key *key_new(key_file */*f*/, const char */*type*/, + const void */*k*/, size_t /*ksz*/, + time_t /*exp*/, const char */*c*/); + +/* --- @key_delete@ --- * + * + * Arguments: @key_file *f@ = pointer to file block + * @key *k@ = key to delete + * + * Returns: --- + * + * Use: Removes the given key from the list. The key file must be + * writable. (Due to the horridness of the data structures, + * deleted keys aren't actually removed, just marked so that + * they can't be looked up or iterated over. One upshot of + * this is that they don't get written back to the file when + * it's closed.) + */ + +extern void key_delete(key_file */*f*/, key */*k*/); + +/* --- @key_expire@ --- * + * + * Arguments: @key_file *f@ = pointer to file block + * @key *k@ = pointer to key block + * + * Returns: --- + * + * Use: Immediately marks the key as expired. It may be removed + * immediately, if it is no longer required, and will be removed + * by a tidy operation when it is no longer required. The key + * file must be writable. + */ + +extern void key_expire(key_file */*f*/, key */*k*/); + +/* --- @key_used@ --- * + * + * Arguments: @key_file *f@ = pointer to key file + * @key *k@ = pointer to key block + * @time_t t@ = when key can be removed + * + * Returns: Zero if OK, nonzero on failure. + * + * Use: Marks a key as being required until a given time. Even + * though the key may expire before then (and won't be returned + * by type after that time), it will still be available when + * requested explicitly by id. The key file must be writable. + * + * The only (current) reason for failure is attempting to use + * a key which can expire for something which can't. + */ + +extern int key_used(key_file */*f*/, key */*k*/, time_t /*t*/); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/keyring.5 b/keyring.5 new file mode 100644 index 0000000..a2eb7ae --- /dev/null +++ b/keyring.5 @@ -0,0 +1,54 @@ +.\" -*-nroff-*- +.TH keyring 5 "5 June 1999" Catacomb +.SH NAME +keyring \- description of Catacomb keyring files +.SH DESCRIPTION +Keyring files are line-oriented text files. It is recommended that +programs use only the provided interface for reading and modifying +keyring files, for consistency of locking and representation: this +description is provided for the benefit of administrators attempting to +understand or repair keyring files. +.PP +Lines containing only whitespace and lines whose first non-whitespace +character is +.RB ` # ' +are ignored, but are not written back to the file. Thus, the comment +facility is not particularly useful. +.PP +Each other line describes a key. Key descriptions consist of between 4 +and six whitespace-separated fields. The final comment field may +contain whitespace characters. The fields are, in order: +.TP +.B type +The key's type string, set when the key was created. +.TP +.B "key data" +The actual key, Base64 encoded, as described in RFC2045. +.TP +.B "expiry time" +The time at which this key expires, represented as an integer number of +seconds since 1970-01-01 00:00:00 UTC, not counting leap seconds. The +special value \-1 signifies that this key never expires. +.TP +.B "deletion time" +The time at which this key should be deleted, using the same +representation as the expiry time. The special value 0 signifies that +the key should be deleted on expiry. +.TP +.B attributes +The key's attributes, encoded using the `form-urlencoded' encoding +defined in RFC1866. This field is optional: if it is omitted, the key +has no attributes. Alternatively, if there are no attributes, this +field may be given as a single dash +.RB ` \- '. +.TP +.B comment +The comment field. This field is optional. It may contain whitespace. +It is deliberately not included as an attribute, since the urlencoded +nature of attributes makes them hard to read when perusing a keyring +file. +.PP +It is not envisaged that the file format will change in the future. Any +extensions will be made by defining new attributes. +.SH AUTHOR +Mark Wooding, diff --git a/keyutil.c b/keyutil.c new file mode 100644 index 0000000..adf8c50 --- /dev/null +++ b/keyutil.c @@ -0,0 +1,722 @@ +/* -*-c-*- + * + * $Id: keyutil.c,v 1.1 1999/09/03 08:41:12 mdw Exp $ + * + * Simple key manager program + * + * (c) 1999 Mark Wooding + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: keyutil.c,v $ + * Revision 1.1 1999/09/03 08:41:12 mdw + * Initial import. + * + */ + +/*----- Header files ------------------------------------------------------*/ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#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 : ""); + 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 : ""); + + /* --- 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(" "); + } + + /* --- If dumping requested, dump the raw key data in hex --- */ + + if (v > 2) { + unsigned char *p = k->k; + unsigned char *l = p + k->ksz; + size_t sz = 0; + + fputs("key:", stdout); + while (p < l) { + if (sz % 16 == 0) + fputs("\n\t", stdout); + else if (sz % 8 == 0) + fputs(" ", stdout); + else + fputc(' ', stdout); + printf("%02x", *p++); + sz++; + } + fputc('\n', stdout); + } + } + } + + doclose(&f); + return (0); +} + +/* --- @cmd_extract@ --- */ + +static int cmd_extract(int argc, char *argv[]) +{ + key_file f; + key *k; + uint32 id; + int i; + int rc = 0; + FILE *fp; + + if (argc < 3) + die(EXIT_FAILURE, "Usage: extract FILE KEYID..."); + if (strcmp(argv[1], "-") == 0) + fp = stdout; + else if (!(fp = fopen(argv[1], "w"))) { + die(EXIT_FAILURE, "couldn't open `%s' for writing: %s", + argv[1], strerror(errno)); + } + + doopen(&f, KOPEN_WRITE); + for (i = 2; i < argc; i++) { + id = (uint32)strtoul(argv[i], 0, 16); + if ((k = key_byid(&f, id)) != 0) + key_extract(&f, k, fp); + else { + moan("keyid %lx not found", (unsigned long)id); + rc = 1; + } + } + doclose(&f); + return (rc); +} + +/* --- @cmd_tidy@ --- */ + +static int cmd_tidy(int argc, char *argv[]) +{ + key_file f; + if (argc != 1) + die(EXIT_FAILURE, "usage: tidy"); + doopen(&f, KOPEN_WRITE); + f.f |= KF_MODIFIED; /* Nasty hack */ + doclose(&f); + return (0); +} + +/* --- @cmd_merge@ --- */ + +static int cmd_merge(int argc, char *argv[]) +{ + key_file f; + FILE *fp; + + if (argc != 2) + die(EXIT_FAILURE, "Usage: merge FILE"); + if (strcmp(argv[1], "-") == 0) + fp = stdin; + else if (!(fp = fopen(argv[1], "r"))) { + die(EXIT_FAILURE, "couldn't open `%s' for writing: %s", + argv[1], strerror(errno)); + } + + doopen(&f, KOPEN_WRITE); + key_merge(&f, argv[1], fp); + doclose(&f); + return (0); +} + +/*----- Main command table ------------------------------------------------*/ + +static struct cmd { + const char *name; + int (*cmd)(int /*argc*/, char */*argv*/[]); + const char *help; +} cmds[] = { + { "add", cmd_add, + "add [-b BITS] [-e EXPIRE] [-c COMMENT] TYPE [ATTR...]" }, + { "expire", cmd_expire, "expire KEYID..." }, + { "delete", cmd_delete, "delete KEYID..." }, + { "setattr", cmd_setattr, "setattr KEYID ATTR..." }, + { "comment", cmd_comment, "comment KEYID [COMMENT]" }, + { "list", cmd_list, "list [-qv]" }, + { "tidy", cmd_tidy, "tidy" }, + { "extract", cmd_extract, "extract FILE KEYID..." }, + { "merge", cmd_merge, "merge FILE" }, + { 0, 0, 0 } +}; + +typedef struct cmd cmd; + +/*----- Main code ---------------------------------------------------------*/ + +/* --- Helpful GNUy functions --- */ + +void usage(FILE *fp) +{ + fprintf(fp, "Usage: %s [-k file] command [args]\n", QUIS); +} + +void version(FILE *fp) +{ + fprintf(fp, "%s, Catacomb version " VERSION "\n", QUIS); +} + +void help(FILE *fp) +{ + cmd *c; + version(fp); + fputc('\n', fp); + usage(fp); + fputs("\n\ +Performs various simple key management operations. Command line options\n\ +recognized are:\n\ +\n\ +-h, --help Display this help text.\n\ +-v, --version Display version number.\n\ +-u, --usage Display short usage summary.\n\ +\n\ +-k, --keyring=FILE Read and write keys in FILE.\n\ +\n\ +The following commands are understood:\n\n", + fp); + for (c = cmds; c->name; c++) + fprintf(fp, "%s\n", c->help); +} + +/* --- @main@ --- * + * + * Arguments: @int argc@ = number of command line arguments + * @char *argv[]@ = array of command line arguments + * + * Returns: Nonzero on failure. + * + * Use: Main program. Performs simple key management functions. + */ + +int main(int argc, char *argv[]) +{ + unsigned f = 0; + + enum { + f_bogus = 1 + }; + + /* --- Initialization --- */ + + ego(argv[0]); + sub_init(); + + /* --- Parse command line options --- */ + + for (;;) { + static struct option opt[] = { + + /* --- Standard GNUy help options --- */ + + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'v' }, + { "usage", 0, 0, 'u' }, + + /* --- Real live useful options --- */ + + { "keyring", OPTF_ARGREQ, 0, 'k' }, + + /* --- Magic terminator --- */ + + { 0, 0, 0, 0 } + }; + int i = mdwopt(argc, argv, "+hvu k:", opt, 0, 0, 0); + + if (i < 0) + break; + switch (i) { + + /* --- GNU help options --- */ + case 'h': + help(stdout); + exit(0); + case 'v': + version(stdout); + exit(0); + case 'u': + usage(stdout); + exit(0); + + /* --- Real useful options --- */ + + case 'k': + keyfile = optarg; + break; + + /* --- Bogosity --- */ + + default: + f |= f_bogus; + break; + } + } + + /* --- Complain about excessive bogons --- */ + + if (f & f_bogus || optind == argc) { + usage(stderr); + exit(1); + } + + /* --- Dispatch to appropriate command handler --- */ + + argc -= optind; + argv += optind; + optind = 0; + + { + cmd *c, *chosen = 0; + size_t sz = strlen(argv[0]); + + for (c = cmds; c->name; c++) { + if (strncmp(argv[0], c->name, sz) == 0) { + if (c->name[sz] == 0) { + chosen = c; + break; + } else if (chosen) + die(EXIT_FAILURE, "ambiguous command name `%s'", argv[0]); + else + chosen = c; + } + } + if (!chosen) + die(EXIT_FAILURE, "unknown command name `%s'", argv[0]); + return (chosen->cmd(argc, argv)); + } +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/md4.c b/md4.c new file mode 100644 index 0000000..406c3a0 --- /dev/null +++ b/md4.c @@ -0,0 +1,260 @@ +/* -*-c-*- + * + * $Id: md4.c,v 1.1 1999/09/03 08:41:12 mdw Exp $ + * + * The MD4 message digest function + * + * (c) 1999 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: md4.c,v $ + * Revision 1.1 1999/09/03 08:41:12 mdw + * Initial import. + * + */ + +/*----- Header files ------------------------------------------------------*/ + +#include + +#include "hash.h" +#include "md4.h" + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @md4_compress@ --- * + * + * Arguments: @md4_ctx *ctx@ = pointer to context block + * @const void *sbuf@ = pointer to buffer of appropriate size + * + * Returns: --- + * + * Use: RIPEMD-160 compression function. + */ + +void md4_compress(md4_ctx *ctx, const void *sbuf) +{ + uint32 a, b, c, d; + uint32 buf[16]; + + /* --- Fetch the chaining variables --- */ + + a = ctx->a; + b = ctx->b; + c = ctx->c; + d = ctx->d; + + /* --- Fetch the buffer contents --- */ + + { + int i; + const octet *p; + + for (i = 0, p = sbuf; i < 16; i++, p += 4) + buf[i] = LOAD32_L(p); + } + + /* --- Definitions for round functions --- */ + +#define F(x, y, z) (((x) & (y)) | (~(x) & (z))) +#define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) + +#define T(w, x, y, z, i, r, k, f) do { \ + uint32 _t = w + f(x, y, z) + buf[i] + k; \ + w = ROL32(_t, r); \ +} while (0) + +#define FF(w, x, y, z, i, r) T(w, x, y, z, i, r, 0x00000000, F) +#define GG(w, x, y, z, i, r) T(w, x, y, z, i, r, 0x5a827999, G) +#define HH(w, x, y, z, i, r) T(w, x, y, z, i, r, 0x6ed9eba1, H) + + /* --- The main compression function --- */ + + FF(a, b, c, d, 0, 3); + FF(d, a, b, c, 1, 7); + FF(c, d, a, b, 2, 11); + FF(b, c, d, a, 3, 19); + FF(a, b, c, d, 4, 3); + FF(d, a, b, c, 5, 7); + FF(c, d, a, b, 6, 11); + FF(b, c, d, a, 7, 19); + FF(a, b, c, d, 8, 3); + FF(d, a, b, c, 9, 7); + FF(c, d, a, b, 10, 11); + FF(b, c, d, a, 11, 19); + FF(a, b, c, d, 12, 3); + FF(d, a, b, c, 13, 7); + FF(c, d, a, b, 14, 11); + FF(b, c, d, a, 15, 19); + + GG(a, b, c, d, 0, 3); + GG(d, a, b, c, 4, 5); + GG(c, d, a, b, 8, 9); + GG(b, c, d, a, 12, 13); + GG(a, b, c, d, 1, 3); + GG(d, a, b, c, 5, 5); + GG(c, d, a, b, 9, 9); + GG(b, c, d, a, 13, 13); + GG(a, b, c, d, 2, 3); + GG(d, a, b, c, 6, 5); + GG(c, d, a, b, 10, 9); + GG(b, c, d, a, 14, 13); + GG(a, b, c, d, 3, 3); + GG(d, a, b, c, 7, 5); + GG(c, d, a, b, 11, 9); + GG(b, c, d, a, 15, 13); + + HH(a, b, c, d, 0, 3); + HH(d, a, b, c, 8, 9); + HH(c, d, a, b, 4, 11); + HH(b, c, d, a, 12, 15); + HH(a, b, c, d, 2, 3); + HH(d, a, b, c, 10, 9); + HH(c, d, a, b, 6, 11); + HH(b, c, d, a, 14, 15); + HH(a, b, c, d, 1, 3); + HH(d, a, b, c, 9, 9); + HH(c, d, a, b, 5, 11); + HH(b, c, d, a, 13, 15); + HH(a, b, c, d, 3, 3); + HH(d, a, b, c, 11, 9); + HH(c, d, a, b, 7, 11); + HH(b, c, d, a, 15, 15); + + /* --- Update the chaining variables --- */ + + ctx->a += a; + ctx->b += b; + ctx->c += c; + ctx->d += d; +} + +/* --- @md4_init@ --- * + * + * Arguments: @md4_ctx *ctx@ = pointer to context block to initialize + * + * Returns: --- + * + * Use: Initializes a context block ready for hashing. + */ + +void md4_init(md4_ctx *ctx) +{ + ctx->a = 0x67452301; + ctx->b = 0xefcdab89; + ctx->c = 0x98badcfe; + ctx->d = 0x10325476; + ctx->off = 0; + ctx->count = 0; +} + +/* --- @md4_set@ --- * + * + * Arguments: @md4_ctx *ctx@ = pointer to context block + * @const void *buf@ = pointer to state buffer + * @unsigned long count@ = current count of bytes processed + * + * Returns: --- + * + * Use: Initializes a context block from a given state. This is + * useful in cases where the initial hash state is meant to be + * secret, e.g., for NMAC and HMAC support. + */ + +void md4_set(md4_ctx *ctx, const void *buf, unsigned long count) +{ + const octet *p = buf; + ctx->a = LOAD32_L(p + 0); + ctx->b = LOAD32_L(p + 4); + ctx->c = LOAD32_L(p + 8); + ctx->d = LOAD32_L(p + 12); + ctx->off = 0; + ctx->count = count; +} + +/* --- @md4_hash@ --- * + * + * Arguments: @md4_ctx *ctx@ = pointer to context block + * @const void *buf@ = buffer of data to hash + * @size_t sz@ = size of buffer to hash + * + * Returns: --- + * + * Use: Hashes a buffer of data. The buffer may be of any size and + * alignment. + */ + +void md4_hash(md4_ctx *ctx, const void *buf, size_t sz) +{ + HASH_BUFFER(MD4, md4, ctx, buf, sz); +} + +/* --- @md4_done@ --- * + * + * Arguments: @md4_ctx *ctx@ = pointer to context block + * @void *hash@ = pointer to output buffer + * + * Returns: --- + * + * Use: Returns the hash of the data read so far. + */ + +void md4_done(md4_ctx *ctx, void *hash) +{ + octet *p = hash; + HASH_MD5STRENGTH(MD4, md4, ctx); + STORE32_L(p + 0, ctx->a); + STORE32_L(p + 4, ctx->b); + STORE32_L(p + 8, ctx->c); + STORE32_L(p + 12, ctx->d); +} + +/* --- @md4_state@ --- * + * + * Arguments: @md4_ctx *ctx@ = pointer to context + * @void *state@ = pointer to buffer for current state + * + * Returns: Number of bytes written to the hash function so far. + * + * Use: Returns the current state of the hash function such that + * it can be passed to @md4_set@. + */ + +unsigned long md4_state(md4_ctx *ctx, void *state) +{ + octet *p = state; + STORE32_L(p + 0, ctx->a); + STORE32_L(p + 4, ctx->b); + STORE32_L(p + 8, ctx->c); + STORE32_L(p + 12, ctx->d); + return (ctx->count); +} + +/* --- Test rig --- */ + +HASH_TEST(MD4, md4) + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/md4.h b/md4.h new file mode 100644 index 0000000..1697814 --- /dev/null +++ b/md4.h @@ -0,0 +1,158 @@ +/* -*-c-*- + * + * $Id: md4.h,v 1.1 1999/09/03 08:41:12 mdw Exp $ + * + * The MD4 message digest function + * + * (c) 1998 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: md4.h,v $ + * Revision 1.1 1999/09/03 08:41:12 mdw + * Initial import. + * + */ + +/*----- Notes on the MD4 hash function ------------------------------------* + * + * MD4 was designed by Ron Rivest. It's now well and truly broken: not only + * have collisions been discovered, a slightly cut-down version has been + * shown to be non-preimage-resistant. On the other hand, MD4 is fast and + * makes a good heavy-duty checksum. Just don't rely on it being + * cryptographically strong, 'cos it ain't. + */ + +#ifndef MD4_H +#define MD4_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#include + +/*----- Magic numbers -----------------------------------------------------*/ + +#define MD4_BUFSZ 64 +#define MD4_HASHSZ 16 + +/*----- Data structures ---------------------------------------------------*/ + +typedef struct md4_ctx { + uint32 a, b, c, d; /* Chaining variables */ + unsigned long count; /* Byte count so far */ + int off; /* Offset into buffer */ + octet buf[MD4_BUFSZ]; /* Accumulation buffer */ +} md4_ctx; + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @md4_compress@ --- * + * + * Arguments: @md4_ctx *ctx@ = pointer to context block + * @const void *sbuf@ = pointer to buffer of appropriate size + * + * Returns: --- + * + * Use: MD4 compression function. + */ + +extern void md4_compress(md4_ctx */*ctx*/, const void */*sbuf*/); + +/* --- @md4_init@ --- * + * + * Arguments: @md4_ctx *ctx@ = pointer to context block to initialize + * + * Returns: --- + * + * Use: Initializes a context block ready for hashing. + */ + +extern void md4_init(md4_ctx */*ctx*/); + +/* --- @md4_set@ --- * + * + * Arguments: @md4_ctx *ctx@ = pointer to context block + * @const void *buf@ = pointer to state buffer + * @unsigned long count@ = current count of bytes processed + * + * Returns: --- + * + * Use: Initializes a context block from a given state. This is + * useful in cases where the initial hash state is meant to be + * secret, e.g., for NMAC and HMAC support. + */ + +extern void md4_set(md4_ctx */*ctx*/, const void */*buf*/, + unsigned long /*count*/); + +/* --- @md4_hash@ --- * + * + * Arguments: @md4_ctx *ctx@ = pointer to context block + * @const void *buf@ = buffer of data to hash + * @size_t sz@ = size of buffer to hash + * + * Returns: --- + * + * Use: Hashes a buffer of data. The buffer may be of any size and + * alignment. + */ + +extern void md4_hash(md4_ctx */*ctx*/, const void */*buf*/, size_t /*sz*/); + +/* --- @md4_done@ --- * + * + * Arguments: @md4_ctx *ctx@ = pointer to context block + * @void *hash@ = pointer to output buffer + * + * Returns: --- + * + * Use: Returns the hash of the data read so far. + */ + +extern void md4_done(md4_ctx */*ctx*/, void */*hash*/); + +/* --- @md4_state@ --- * + * + * Arguments: @md4_ctx *ctx@ = pointer to context + * @void *state@ = pointer to buffer for current state + * + * Returns: Number of bytes written to the hash function so far. + * + * Use: Returns the current state of the hash function such that + * it can be passed to @md4_set@. + */ + +extern unsigned long md4_state(md4_ctx */*ctx*/, void */*state*/); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/md5.c b/md5.c new file mode 100644 index 0000000..c6e2a2a --- /dev/null +++ b/md5.c @@ -0,0 +1,279 @@ +/* -*-c-*- + * + * $Id: md5.c,v 1.1 1999/09/03 08:41:12 mdw Exp $ + * + * The MD5 message digest function + * + * (c) 1998 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: md5.c,v $ + * Revision 1.1 1999/09/03 08:41:12 mdw + * Initial import. + * + */ + +/*----- Header files ------------------------------------------------------*/ + +#include + +#include "hash.h" +#include "md5.h" + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @md5_compress@ --- * + * + * Arguments: @md5_ctx *ctx@ = pointer to context block + * @const void *sbuf@ = pointer to buffer of appropriate size + * + * Returns: --- + * + * Use: MD5 compression function. + */ + +void md5_compress(md5_ctx *ctx, const void *sbuf) +{ + uint32 a, b, c, d; + uint32 buf[16]; + + /* --- Fetch the chaining variables --- */ + + a = ctx->a; + b = ctx->b; + c = ctx->c; + d = ctx->d; + + /* --- Fetch the buffer contents --- */ + + { + int i; + const octet *p; + + for (i = 0, p = sbuf; i < 16; i++, p += 4) + buf[i] = LOAD32_L(p); + } + + /* --- Definitions for round functions --- */ + +#define F(x, y, z) (((x) & (y)) | (~(x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | ~(z))) + +#define T(w, x, y, z, i, r, k, f) do { \ + uint32 _t = w + f(x, y, z) + buf[i] + k; \ + w = ROL32(_t, r) + x; \ +} while (0) + +#define FF(w, x, y, z, i, r, k) T(w, x, y, z, i, r, k, F) +#define GG(w, x, y, z, i, r, k) T(w, x, y, z, i, r, k, G) +#define HH(w, x, y, z, i, r, k) T(w, x, y, z, i, r, k, H) +#define II(w, x, y, z, i, r, k) T(w, x, y, z, i, r, k, I) + + /* --- The main compression function --- */ + + FF(a, b, c, d, 0, 7, 0xd76aa478); + FF(d, a, b, c, 1, 12, 0xe8c7b756); + FF(c, d, a, b, 2, 17, 0x242070db); + FF(b, c, d, a, 3, 22, 0xc1bdceee); + FF(a, b, c, d, 4, 7, 0xf57c0faf); + FF(d, a, b, c, 5, 12, 0x4787c62a); + FF(c, d, a, b, 6, 17, 0xa8304613); + FF(b, c, d, a, 7, 22, 0xfd469501); + FF(a, b, c, d, 8, 7, 0x698098d8); + FF(d, a, b, c, 9, 12, 0x8b44f7af); + FF(c, d, a, b, 10, 17, 0xffff5bb1); + FF(b, c, d, a, 11, 22, 0x895cd7be); + FF(a, b, c, d, 12, 7, 0x6b901122); + FF(d, a, b, c, 13, 12, 0xfd987193); + FF(c, d, a, b, 14, 17, 0xa679438e); + FF(b, c, d, a, 15, 22, 0x49b40821); + + GG(a, b, c, d, 1, 5, 0xf61e2562); + GG(d, a, b, c, 6, 9, 0xc040b340); + GG(c, d, a, b, 11, 14, 0x265e5a51); + GG(b, c, d, a, 0, 20, 0xe9b6c7aa); + GG(a, b, c, d, 5, 5, 0xd62f105d); + GG(d, a, b, c, 10, 9, 0x02441453); + GG(c, d, a, b, 15, 14, 0xd8a1e681); + GG(b, c, d, a, 4, 20, 0xe7d3fbc8); + GG(a, b, c, d, 9, 5, 0x21e1cde6); + GG(d, a, b, c, 14, 9, 0xc33707d6); + GG(c, d, a, b, 3, 14, 0xf4d50d87); + GG(b, c, d, a, 8, 20, 0x455a14ed); + GG(a, b, c, d, 13, 5, 0xa9e3e905); + GG(d, a, b, c, 2, 9, 0xfcefa3f8); + GG(c, d, a, b, 7, 14, 0x676f02d9); + GG(b, c, d, a, 12, 20, 0x8d2a4c8a); + + HH(a, b, c, d, 5, 4, 0xfffa3942); + HH(d, a, b, c, 8, 11, 0x8771f681); + HH(c, d, a, b, 11, 16, 0x6d9d6122); + HH(b, c, d, a, 14, 23, 0xfde5380c); + HH(a, b, c, d, 1, 4, 0xa4beea44); + HH(d, a, b, c, 4, 11, 0x4bdecfa9); + HH(c, d, a, b, 7, 16, 0xf6bb4b60); + HH(b, c, d, a, 10, 23, 0xbebfbc70); + HH(a, b, c, d, 13, 4, 0x289b7ec6); + HH(d, a, b, c, 0, 11, 0xeaa127fa); + HH(c, d, a, b, 3, 16, 0xd4ef3085); + HH(b, c, d, a, 6, 23, 0x04881d05); + HH(a, b, c, d, 9, 4, 0xd9d4d039); + HH(d, a, b, c, 12, 11, 0xe6db99e5); + HH(c, d, a, b, 15, 16, 0x1fa27cf8); + HH(b, c, d, a, 2, 23, 0xc4ac5665); + + II(a, b, c, d, 0, 6, 0xf4292244); + II(d, a, b, c, 7, 10, 0x432aff97); + II(c, d, a, b, 14, 15, 0xab9423a7); + II(b, c, d, a, 5, 21, 0xfc93a039); + II(a, b, c, d, 12, 6, 0x655b59c3); + II(d, a, b, c, 3, 10, 0x8f0ccc92); + II(c, d, a, b, 10, 15, 0xffeff47d); + II(b, c, d, a, 1, 21, 0x85845dd1); + II(a, b, c, d, 8, 6, 0x6fa87e4f); + II(d, a, b, c, 15, 10, 0xfe2ce6e0); + II(c, d, a, b, 6, 15, 0xa3014314); + II(b, c, d, a, 13, 21, 0x4e0811a1); + II(a, b, c, d, 4, 6, 0xf7537e82); + II(d, a, b, c, 11, 10, 0xbd3af235); + II(c, d, a, b, 2, 15, 0x2ad7d2bb); + II(b, c, d, a, 9, 21, 0xeb86d391); + + /* --- Update the chaining variables --- */ + + ctx->a += a; + ctx->b += b; + ctx->c += c; + ctx->d += d; +} + +/* --- @md5_init@ --- * + * + * Arguments: @md5_ctx *ctx@ = pointer to context block to initialize + * + * Returns: --- + * + * Use: Initializes a context block ready for hashing. + */ + +void md5_init(md5_ctx *ctx) +{ + ctx->a = 0x67452301; + ctx->b = 0xefcdab89; + ctx->c = 0x98badcfe; + ctx->d = 0x10325476; + ctx->off = 0; + ctx->count = 0; +} + +/* --- @md5_set@ --- * + * + * Arguments: @md5_ctx *ctx@ = pointer to context block + * @const void *buf@ = pointer to state buffer + * @unsigned long count@ = current count of bytes processed + * + * Returns: --- + * + * Use: Initializes a context block from a given state. This is + * useful in cases where the initial hash state is meant to be + * secret, e.g., for NMAC and HMAC support. + */ + +void md5_set(md5_ctx *ctx, const void *buf, unsigned long count) +{ + const octet *p = buf; + ctx->a = LOAD32_L(p + 0); + ctx->b = LOAD32_L(p + 4); + ctx->c = LOAD32_L(p + 8); + ctx->d = LOAD32_L(p + 12); + ctx->off = 0; + ctx->count = count; +} + +/* --- @md5_hash@ --- * + * + * Arguments: @md5_ctx *ctx@ = pointer to context block + * @const void *buf@ = buffer of data to hash + * @size_t sz@ = size of buffer to hash + * + * Returns: --- + * + * Use: Hashes a buffer of data. The buffer may be of any size and + * alignment. + */ + +void md5_hash(md5_ctx *ctx, const void *buf, size_t sz) +{ + HASH_BUFFER(MD5, md5, ctx, buf, sz); +} + +/* --- @md5_done@ --- * + * + * Arguments: @md5_ctx *ctx@ = pointer to context block + * @void *hash@ = pointer to output buffer + * + * Returns: --- + * + * Use: Returns the hash of the data read so far. + */ + +void md5_done(md5_ctx *ctx, void *hash) +{ + octet *p = hash; + HASH_MD5STRENGTH(MD5, md5, ctx); + STORE32_L(p + 0, ctx->a); + STORE32_L(p + 4, ctx->b); + STORE32_L(p + 8, ctx->c); + STORE32_L(p + 12, ctx->d); +} + +/* --- @md5_state@ --- * + * + * Arguments: @md5_ctx *ctx@ = pointer to context + * @void *state@ = pointer to buffer for current state + * + * Returns: Number of bytes written to the hash function so far. + * + * Use: Returns the current state of the hash function such that + * it can be passed to @md5_set@. + */ + +unsigned long md5_state(md5_ctx *ctx, void *state) +{ + octet *p = state; + STORE32_L(p + 0, ctx->a); + STORE32_L(p + 4, ctx->b); + STORE32_L(p + 8, ctx->c); + STORE32_L(p + 12, ctx->d); + return (ctx->count); +} + +/* --- Test code --- */ + +HASH_TEST(MD5, md5) + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/md5.h b/md5.h new file mode 100644 index 0000000..8c54e6d --- /dev/null +++ b/md5.h @@ -0,0 +1,159 @@ +/* -*-c-*- + * + * $Id: md5.h,v 1.1 1999/09/03 08:41:12 mdw Exp $ + * + * The MD5 message digest function + * + * (c) 1998 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: md5.h,v $ + * Revision 1.1 1999/09/03 08:41:12 mdw + * Initial import. + * + */ + +/*----- Notes on the MD5 hash function ------------------------------------* + * + * MD5 was designed by Ron Rivest. It was intended to be a more conservative + * design than the slightly earlier MD4, and indeed while MD4 has been pretty + * much demolished by subsequent cryptanalysis, MD5 is still standing + * shakily. It's provided here not because the author recommends its use but + * because it's a common standard, and is often handy in non-cryptographic + * applications. It's also still useful in constructions such as HMAC. + */ + +#ifndef MD5_H +#define MD5_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#include + +/*----- Magic numbers -----------------------------------------------------*/ + +#define MD5_BUFSZ 64 +#define MD5_HASHSZ 16 + +/*----- Data structures ---------------------------------------------------*/ + +typedef struct md5_ctx { + uint32 a, b, c, d; /* Chaining variables */ + unsigned long count; /* Byte count so far */ + int off; /* Offset into buffer */ + octet buf[MD5_BUFSZ]; /* Accumulation buffer */ +} md5_ctx; + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @md5_compress@ --- * + * + * Arguments: @md5_ctx *ctx@ = pointer to context block + * @const void *sbuf@ = pointer to buffer of appropriate size + * + * Returns: --- + * + * Use: MD5 compression function. + */ + +extern void md5_compress(md5_ctx */*ctx*/, const void */*sbuf*/); + +/* --- @md5_init@ --- * + * + * Arguments: @md5_ctx *ctx@ = pointer to context block to initialize + * + * Returns: --- + * + * Use: Initializes a context block ready for hashing. + */ + +extern void md5_init(md5_ctx */*ctx*/); + +/* --- @md5_set@ --- * + * + * Arguments: @md5_ctx *ctx@ = pointer to context block + * @const void *buf@ = pointer to state buffer + * @unsigned long count@ = current count of bytes processed + * + * Returns: --- + * + * Use: Initializes a context block from a given state. This is + * useful in cases where the initial hash state is meant to be + * secret, e.g., for NMAC and HMAC support. + */ + +extern void md5_set(md5_ctx */*ctx*/, const void */*buf*/, + unsigned long /*count*/); + +/* --- @md5_hash@ --- * + * + * Arguments: @md5_ctx *ctx@ = pointer to context block + * @const void *buf@ = buffer of data to hash + * @size_t sz@ = size of buffer to hash + * + * Returns: --- + * + * Use: Hashes a buffer of data. The buffer may be of any size and + * alignment. + */ + +extern void md5_hash(md5_ctx */*ctx*/, const void */*buf*/, size_t /*sz*/); + +/* --- @md5_done@ --- * + * + * Arguments: @md5_ctx *ctx@ = pointer to context block + * @void *hash@ = pointer to output buffer + * + * Returns: --- + * + * Use: Returns the hash of the data read so far. + */ + +extern void md5_done(md5_ctx */*ctx*/, void */*hash*/); + +/* --- @md5_state@ --- * + * + * Arguments: @md5_ctx *ctx@ = pointer to context + * @void *state@ = pointer to buffer for current state + * + * Returns: Number of bytes written to the hash function so far. + * + * Use: Returns the current state of the hash function such that + * it can be passed to @md5_set@. + */ + +extern unsigned long md5_state(md5_ctx */*ctx*/, void */*state*/); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/mp.c b/mp.c new file mode 100644 index 0000000..fb5d627 --- /dev/null +++ b/mp.c @@ -0,0 +1,1252 @@ +/* -*-c-*- + * + * $Id: mp.c,v 1.1 1999/09/03 08:41:12 mdw Exp $ + * + * Multiprecision arithmetic + * + * (c) 1998 Mark Wooding + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: mp.c,v $ + * Revision 1.1 1999/09/03 08:41:12 mdw + * Initial import. + * + */ + +/*----- Header files ------------------------------------------------------*/ + +#include +#include +#include +#include + +#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 +#include "dstr.h" +#include "testrig.h" + +/* --- Loading and storing numbers --- * + * + * The most reliable way I can think of for doing this is to convert both + * numbers (the MP and the original hexgorp) into binary text, and compare. + * This ought to be reliable, and it's sufficiently different from what the + * actual load and store routines are doing not to have any common bugs. + */ + +static void mpbindump(const mp *x, char *p) +{ + mp_bitscan sc; + for (mp_mkbitscan(&sc, x); mp_bstep(&sc); ) + *p++ = '0' + MP_BIT(&sc); + *p++ = 0; +} + +static void bindumpl(const octet *q, size_t sz, char *p) +{ + unsigned w; + int bits = 0; + + while (sz) { + w = *q++; + for (bits = 8; bits > 0; bits--) { + *p++ = '0' + (w & 1); + w >>= 1; + } + sz--; + } + *p++ = 0; +} + +static void bindumpb(const octet *q, size_t sz, char *p) +{ + unsigned w; + int bits = 0; + + q += sz; + while (sz) { + w = *--q; + for (bits = 8; bits > 0; bits--) { + *p++ = '0' + (w & 1); + w >>= 1; + } + sz--; + } + *p++ = 0; +} + +static int bincmp(const char *p, const char *q) +{ + for (;;) { + if (!*p && !*q) + return (0); + else if (!*p && *q == '0') + q++; + else if (*p == '0' && !*q) + p++; + else if (*p == *q) + p++, q++; + else + return (-1); + } +} + +static int lscheck(const char *reason, int w, dstr *d, mp *x, + const char *bufa, const char *bufb) +{ + if (bincmp(bufa, bufb)) { + printf("\nmismatch in %s (width = %i):\n" + "\tvalue = ", reason, w); + type_hex.dump(d, stdout); + printf("\n\tmp = "); + mp_dump(x, stdout); + printf("\n\texpected = %s\n" + "\tcalculated = %s\n", + bufb, bufa); + return (1); + } + return (0); +} + +static int loadstore(dstr *d) +{ + octet *p = (octet *)d[0].buf; + size_t sz = d[0].len; + char bufa[1024], bufb[1024]; + octet bufc[64]; + mp x; + int i; + int ok = 1; + + mp_wmax = 0x7ful; + for (i = 8; ok && i <= 32; i++) { + mpw_bits = i; + mpw_max = (mp_wmax << 1) + 1; + + mp_create(&x); mp_loadl(&x, p, sz); + mpbindump(&x, bufa); bindumpl(p, sz, bufb); + if (lscheck("mp_loadl", i, d, &x, bufa, bufb)) ok = 0; + mp_storeb(&x, bufc, sizeof(bufc)); + bindumpb(bufc, sizeof(bufc), bufa); + if (lscheck("mp_storeb", i, d, &x, bufa, bufb)) ok = 0; + mp_destroy(&x); + + mp_create(&x); mp_loadb(&x, p, sz); + mpbindump(&x, bufa); bindumpb(p, sz, bufb); + if (lscheck("mp_loadb", i, d, &x, bufa, bufb)) ok = 0; + mp_storel(&x, bufc, sizeof(bufc)); + bindumpl(bufc, sizeof(bufc), bufa); + if (lscheck("mp_storel", i, d, &x, bufa, bufb)) ok = 0; + mp_destroy(&x); + } + + mpw_max = 0xffff; + mpw_bits = 16; + return (ok); +} + +/* --- Comparison test --- */ + +static int compare(dstr *d) +{ + mp x, y; + int r, s; + int ok = 1; + + mp_create(&x); mp_create(&y); + mp_loadb(&x, (octet *)d[0].buf, d[0].len); + mp_loadb(&y, (octet *)d[1].buf, d[1].len); + r = *(int *)d[2].buf; + s = mp_ucmp(&x, &y); + + if (r != s) { + ok = 0; + printf("\nfailed compare:" + "\n\tx = "); + type_hex.dump(&d[0], stdout); + printf("\n\ty = "); + type_hex.dump(&d[1], stdout); + printf("\n\texpected %i; found %i\n", r, s); + } + + return (ok); +} + +/* --- Addition and subtraction test --- */ + +static int addsub(dstr *d) +{ + mp t, x, y, z; + int ok = 1; + + mp_create(&t); mp_create(&x); mp_create(&y); mp_create(&z); + mp_loadb(&x, (octet *)d[0].buf, d[0].len); + mp_loadb(&y, (octet *)d[1].buf, d[1].len); + mp_loadb(&z, (octet *)d[2].buf, d[2].len); + + mp_uadd(&t, &x, &y); + if (mp_ucmp(&t, &z)) { + ok = 0; + printf("\nfailed add:"); + printf("\n\tx = "); type_hex.dump(&d[0], stdout); + printf("\n\ty = "); type_hex.dump(&d[1], stdout); + printf("\n\tz = "); type_hex.dump(&d[2], stdout); + fputc('\n', stdout); + } + + mp_usub(&t, &z, &x); + if (mp_ucmp(&t, &y)) { + ok = 0; + printf("\nfailed subtract:"); + printf("\n\tz = "); type_hex.dump(&d[2], stdout); + printf("\n\tx = "); type_hex.dump(&d[0], stdout); + printf("\n\ty = "); type_hex.dump(&d[1], stdout); + fputc('\n', stdout); + } + + return (ok); +} + +/* --- Shifting --- */ + +static int shift(dstr *d) +{ + char bufa[1024], bufb[1024]; + mp x, y; + int n = *(int *)d[1].buf; + char *p; + int i; + int ok = 1; + + mp_create(&x); + mp_create(&y); + + mp_loadb(&x, (octet *)d[0].buf, d[0].len); + p = bufa; + for (i = 0; i < n; i++) + *p++ = '0'; + mpbindump(&x, p); + + mp_lsl(&y, &x, n); + mpbindump(&y, bufb); + if (lscheck("lsl", n, d, &x, bufb, bufa)) + ok = 0; + + mp_lsr(&y, &x, n); + mpbindump(&y, bufb); + if (lscheck("lsr", n, d, &x, bufb, bufa + 2 * n)) + ok = 0; + + return (ok); +} + +/* --- Test driver stub --- */ + +static test_chunk mp__defs[] = { + { "mp-loadstore", loadstore, { &type_hex, 0 } }, + { "mp-cmp", compare, { &type_hex, &type_hex, &type_int, 0 } }, + { "mp-addsub", addsub, { &type_hex, &type_hex, &type_hex, 0 } }, + { "mp-shift", shift, { &type_hex, &type_int, 0 } }, + { 0, 0, { 0 } } +}; + +int main(int argc, char *argv[]) +{ + test_run(argc, argv, mp__defs, SRCDIR"/tests/mp"); + return (0); +} + +#endif + +/*----- That's all, folks -------------------------------------------------*/ + +#endif diff --git a/mp.h b/mp.h new file mode 100644 index 0000000..fcccee2 --- /dev/null +++ b/mp.h @@ -0,0 +1,449 @@ +/* -*-c-*- + * + * $Id: mp.h,v 1.1 1999/09/03 08:41:12 mdw Exp $ + * + * Multiprecision arithmetic + * + * (c) 1999 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: mp.h,v $ + * Revision 1.1 1999/09/03 08:41:12 mdw + * Initial import. + * + */ + +#ifndef MP_H +#define MP_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#include +#include + +#ifndef MPTYPES_H +# include "mptypes.h" +#endif + +#ifndef MPX_H +# include "mpx.h" +#endif + +#ifndef MPSCAN_H +# include "mpscan.h" +#endif + +/*----- Data structures ---------------------------------------------------*/ + +typedef struct mp { + mpw *v; /* Vector of words */ + unsigned f; /* Various flags */ + size_t sz; /* Size allocated to word vector */ + size_t len; /* Length of current word vector */ +} mp; + +enum { + MPF_SIGN = 1, /* Sign bit */ + MPF_BURN = 2 /* Burn word vector after use */ +}; + +typedef struct mp_bitscan { + const mp *x; /* Pointer to target MP */ + size_t i; /* Index into MP vector */ + mpw w; /* Current value being examined */ + unsigned bits; /* Number of bits left in @w@ */ +} mp_bitscan; + +/*----- External variables ------------------------------------------------*/ + +extern mp mp_std; + +#define MP_ZERO (mp_std + 0) +#define MP_ONE (mp_std + 1) +#define MP_TWO (mp_std + 2) +#define MP_THREE (mp_std + 3) +#define MP_FOUR (mp_std + 4) +#define MP_FIVE (mp_std + 5) +#define MP_TEN (mp_std + 6) +#define MP_MONE (mp_std + 7) + +/*----- Memory allocation and low-level fiddling --------------------------*/ + +/* --- @mp_create@ --- * + * + * Arguments @mp *x@ = pointer to MP head + * + * Returns: --- + * + * Use: Initializes an MP ready for use. The initial value is zero. + */ + +#define MP_INIT { 0, 0, 0, 0 } + +extern void mp_create(mp */*x*/); + +/* --- @MP_BURN@ --- * + * + * Arguments: @x@ = pointer to MP head + * + * Use: Burns the contents of the MP, if it contains sensitive data. + */ + +#define MP_BURN(x) do { \ + mp *_y = (x) \ + if (_y->v && _y->f & mpf_burn) { \ + memset(_y->v, 0, _y->sz * sizeof(mpw)); \ + _y->f &= ~MPF_BURN; \ + } \ +} while (0) + +/* --- @mp_free@ --- * + * + * Arguments: @mp *x@ = pointer to MP head + * + * Returns: --- + * + * Use: Releases the memory used by an MP. + */ + +#define MP_DESTROY(x) do { \ + mp *_x = (x); \ + MP_BURN(_x); \ + if (_x->v) \ + free(_x->v); \ + _x->v = 0; \ + _x->f = 0; \ + _x->sz = 0; \ + _x->len = 0; \ +} while (0) + +extern void mp_free(mp */*x*/); + +/* --- @MP_ENSURE@ --- * + * + * Arguments: @x@ = pointer to MP head + * @len@ = length required (in words) + * + * Use: Ensures that the MP has enough memory to store a @len@-word + * value. + */ + +#define MP_ENSURE(x, len) do { \ + mp *_x = (x); \ + size_t _len = (len); \ + if (_x->sz < _len) \ + mp_resize(_x, _len); \ +} while (0) + +/* --- @mp_resize@ --- * + * + * Arguments: @mp *x@ = pointer to MP head + * @size_t sz@ = size required (in words) + * + * Returns: --- + * + * Use: Resizes the MP so that its word vector has space for + * exactly @sz@ words. + */ + +extern void mp_resize(mp */*x*/, size_t /*sz*/); + +/* --- @mp_norm@ --- * + * + * Arguments: @mp *x@ = pointer to MP head + * + * Returns: --- + * + * Use: `Normalizes' an MP. Fixes the @len@ field so that it's + * correct. Assumes that @len@ is either already correct or + * too large. + */ + +#define MP_NORM(x) do { \ + mp *_y = (x); \ + MPX_LEN(_y->len, _y->v, _y->len); \ +} while (0) + +extern void mp_norm(mp */*x*/); + +/* --- @mp_dump@ --- * + * + * Arguments: @mp *x@ = pointer to MP head + * @FILE *fp@ = pointer to stream to write on + * + * Returns: --- + * + * Use: Dumps an MP to a stream. + */ + +extern void mp_dump(mp */*x*/, FILE */*fp*/); + +/* --- @MP_PARANOID@ --- * + * + * Arguments: @x@ = pointer to MP head + * + * Use: Marks the MP as containing sensitive data which should be + * burnt when no longer required. + */ + +#define MP_PARANOID(x) ((x)->f |= MPF_BURN) + +/* --- @mp_copy@ --- * + * + * Arguments: @mp *d@ = pointer to MP head for destination + * @const mp *s@ = pointer to MP head for source + * + * Returns: --- + * + * Use: Copies an MP. + */ + +extern void mp_copy(mp */*d*/, const mp */*s*/); + +/* --- @mp_bits@ --- * + * + * Arguments: @mp *x@ = pointer to MP head + * + * Returns: Length of the number in bits. + * + * Use: Calculates the number of bits required to represent a number. + * The number must be normalized. + */ + +unsigned long mp_bits(mp */*x*/); + +/* --- @mp_octets@ --- * + * + * Arguments: @mp *x@ = pointer to MP head + * + * Returns: Length of number in octets. + * + * Use: Calculates the number of octets required to represent a + * number. The number must be normalized. + */ + +extern size_t mp_octets(mp */*x*/); + +/*----- Loading and storing as binary data --------------------------------*/ + +/* --- @mp_storel@ --- * + * + * Arguments: @mp *x@ = pointer to MP head + * @octet *p@ = pointer to octet array + * @size_t sz@ = size of octet array + * + * Returns: --- + * + * Use: Stores an MP in an octet array, least significant octet + * first. High-end octets are silently discarded if there + * isn't enough space for them. + */ + +extern void mp_storel(mp */*x*/, octet */*p*/, size_t /*sz*/); + +/* --- @mp_loadl@ --- * + * + * Arguments: @mp *x@ = pointer to MP head + * @const octet *p@ = pointer to octet array + * @size_t sz@ = size of octet array + * + * Returns: --- + * + * Use: Loads an MP in an octet array, least significant octet + * first. + */ + +extern void mp_loadl(mp */*x*/, const octet */*p*/, size_t /*sz*/); + +/* --- @mp_storeb@ --- * + * + * Arguments: @mp *x@ = pointer to MP head + * @octet *p@ = pointer to octet array + * @size_t sz@ = size of octet array + * + * Returns: --- + * + * Use: Stores an MP in an octet array, most significant octet + * first. High-end octets are silently discarded if there + * isn't enough space for them. + */ + +extern void mp_storeb(mp */*x*/, octet */*p*/, size_t /*sz*/); + +/* --- @mp_loadb@ --- * + * + * Arguments: @mp *x@ = pointer to MP head + * @const octet *p@ = pointer to octet array + * @size_t sz@ = size of octet array + * + * Returns: --- + * + * Use: Loads an MP in an octet array, most significant octet + * first. + */ + +extern void mp_loadb(mp */*x*/, const octet */*p*/, size_t /*sz*/); + +/*----- Iterating through bits --------------------------------------------*/ + +/* --- @mp_mkbitscan@ --- * + * + * Arguments: @mp_bitscan *sc@ = pointer to bitscan object + * @const mp *x@ = pointer to MP head + * + * Returns: --- + * + * Use: Initializes a bitscan object. + */ + +extern void mp_mkbitscan(mp_bitscan */*sc*/, const mp */*x*/); + +/* --- @mp_bstep@ --- * + * + * Arguments: @mp_bitscan *sc@ = pointer to bitscanner object + * + * Returns: Nonzero if there is another bit to read. + * + * Use: Steps on to the next bit, and tells the caller whether one + * exists. + */ + +extern int mp_bstep(mp_bitscan */*sc*/); + +/* --- @mp_bit@ --- * + * + * Arguments: @const mp_bitscan *sc@ = pointer to bitscanner + * + * Returns: Current bit value. + * + * Use: Returns the value of the current bit. + */ + +#define MP_BIT(sc) ((sc)->w & 1) + +extern int mp_bit(const mp_bitscan */*sc*/); + +/*----- Shifting ----------------------------------------------------------*/ + +/* --- @mp_lsl@ --- * + * + * Arguments: @mp *d@ = pointer to MP head of destination + * @const mp *x@ = pointer to MP head of source + * @size_t n@ = number of bits to shift + * + * Returns: --- + * + * Use: Shifts a number left by a given number of bit positions. + */ + +extern void mp_lsl(mp */*d*/, const mp */*x*/, size_t /*n*/); + +/* --- @mp_lsr@ --- * + * + * Arguments: @mp *d@ = pointer to MP head of destination + * @const mp *x@ = pointer to MP head of source + * @size_t n@ = number of bits to shift + * + * Returns: --- + * + * Use: Shifts a number right by a given number of bit positions. + */ + +extern void mp_lsr(mp */*d*/, const mp */*x*/, size_t /*n*/); + +/*----- Unsigned arithmetic -----------------------------------------------*/ + +/* --- @mp_uadd@ --- * + * + * Arguments: @const mp *d@ = pointers to MP head of destination + * @const mp *x, *y@ = pointers to MP heads of operands + * + * Returns: --- + * + * Use: Performs unsigned MP addition. + */ + +extern void mp_uadd(mp */*d*/, const mp */*x*/, const mp */*y*/); + +/* --- @mp_usub@ --- * + * + * Arguments: @const mp *d@ = pointers to MP head of destination + * @const mp *x, *y@ = pointers to MP heads of operands + * + * Returns: --- + * + * Use: Performs unsigned MP subtraction. + */ + +extern void mp_usub(mp */*d*/, const mp */*x*/, const mp */*y*/); + +/* --- @mp_ucmp@ --- * + * + * Arguments: @const mp *x, *y@ = pointers to MP heads of operands + * + * Returns: Less than, equal to, or greater than zero. + * + * Use: Performs unsigned MP comparison. + */ + +#define MP_UCMP(x, op, y) (mp_ucmp((x), (y)) op 0) + +extern int mp_ucmp(const mp */*x*/, const mp */*y*/); + +/* --- @mp_umul@ --- * + * + * Arguments: @mp *d@ = pointer to MP head of destination + * @const mp *x, *y@ = pointes to MP heads of operands + * + * Returns: --- + * + * Use: Performs unsigned MP multiplication. + */ + +extern void mp_umul(mp */*d*/, const mp */*x*/, const mp */*y*/); + +/* --- @mp_udiv@ --- * + * + * Arguments: @mp *q, *r@ = pointers to MP heads for quotient, remainder + * @const mp *x, *y@ = pointers to MP heads for operands + * + * Returns: --- + * + * Use: Performs unsigned MP division. + */ + +extern void mp_udiv(mp */*q*/, mp */*r*/, const mp */*x*/, const mp */*y*/); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/mpscan.c b/mpscan.c new file mode 100644 index 0000000..a49eddf --- /dev/null +++ b/mpscan.c @@ -0,0 +1,89 @@ +/* -*-c-*- + * + * $Id: mpscan.c,v 1.1 1999/09/03 08:41:12 mdw Exp $ + * + * Sequential bit scan of multiprecision integers + * + * (c) 1999 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: mpscan.c,v $ + * Revision 1.1 1999/09/03 08:41:12 mdw + * Initial import. + * + */ + +/*----- Header files ------------------------------------------------------*/ + +/*----- Data structures ---------------------------------------------------*/ + +/*----- Static variables --------------------------------------------------*/ + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @mpscan_initx@ --- * + * + * Arguments: @mpscan *m@ = pointer to bitscanner structure + * @const mpw *v@ = vector of words to scan + * @size_t len@ = length of vector in words + * + * Returns: --- + * + * Use: Initializes a bitscanner from a low-level vector-and-length + * representation of an integer. Initially no bit is ready; you + * must call @mpscan_step@ before anything useful will come + * out. + */ + +void mpscan_initx(mpscan *m, const mpw *v, size_t len) +{ + MPSCAN_INITX(m, v, len); +} + +/* --- @mpscan_step@ --- * + * + * Arguments: @mpscan *m@ = pointer to bitscanner + * + * Returns: Nonzero if there is another bit to read. + * + * Use: Steps on to the next bit in the integer. The macro version + * evaluates its argument multiple times. + */ + +int mpscan_step(mpscan *m) { return (MPSCAN_STEP(m)); } + +/* --- @mpscan_bit@ --- * + * + * Arguments: @const mpscan *m@ = pointer to bitscanner + * + * Returns: The value of the current bit. + * + * Use: Reads the value of the current bit looked at by a + * bitscanner. + */ + +int mpscan_bit(const mpscan *m) { return (MPSCAN_BIT(m)); } + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/mpscan.h b/mpscan.h new file mode 100644 index 0000000..6ec09cc --- /dev/null +++ b/mpscan.h @@ -0,0 +1,125 @@ +/* -*-c-*- + * + * $Id: mpscan.h,v 1.1 1999/09/03 08:41:12 mdw Exp $ + * + * Sequential bit scan of multiprecision integers + * + * (c) 1999 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: mpscan.h,v $ + * Revision 1.1 1999/09/03 08:41:12 mdw + * Initial import. + * + */ + +#ifndef MPSCAN_H +#define MPSCAN_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#ifndef MPTYPES_H +# include "mptypes.h" +#endif + +/*----- Data structures ---------------------------------------------------*/ + +typedef struct mpscan { + const mpw *v; /* Vector of words to scan */ + mpw w; /* Current word to scan */ + int bits; /* Number of bits left in @w@ */ + size_t len; /* Length of the vector in words */ +} mpscan; + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @mpscan_initx@ --- * + * + * Arguments: @mpscan *m@ = pointer to bitscanner structure + * @const mpw *v@ = vector of words to scan + * @size_t len@ = length of vector in words + * + * Returns: --- + * + * Use: Initializes a bitscanner from a low-level vector-and-length + * representation of an integer. Initially no bit is ready; you + * must call @mpscan_step@ before anything useful will come + * out. + */ + +#define MPSCAN_INITX(m_, v_, len_) do { \ + mpscan *_m = (m_); \ + _m->v = (v_); \ + _m->len = (len_); \ + _m->bits = 0; \ +} while (0) + +extern void mpscan_initx(mpscan */*m*/, const mpw */*v*/, size_t /*len*/); + +/* --- @mpscan_step@ --- * + * + * Arguments: @mpscan *m@ = pointer to bitscanner + * + * Returns: Nonzero if there is another bit to read. + * + * Use: Steps on to the next bit in the integer. The macro version + * evaluates its argument multiple times. + */ + +#define MPSCAN_STEP(m) \ + ((m)->bits ? ((m)->w >>= 1, \ + (m)->bits--, 1) : \ + (m)->len ? ((m)->len--, \ + (m)->w = *(m)->v++, \ + (m)->bits = MP_WBITS - 1, 1) : \ + 0) + +extern int mpscan_step(mpscan */*m*/); + +/* --- @mpscan_bit@ --- * + * + * Arguments: @const mpscan *m@ = pointer to bitscanner + * + * Returns: The value of the current bit. + * + * Use: Reads the value of the current bit looked at by a + * bitscanner. + */ + +#define MPSCAN_BIT(m) ((m)->w & 1) + +extern int mpscan_bit(const mpscan */*m*/); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/mptypes.h b/mptypes.h new file mode 100644 index 0000000..8513b6e --- /dev/null +++ b/mptypes.h @@ -0,0 +1,206 @@ +/* -*-c-*- + * + * $Id: mptypes.h,v 1.1 1999/09/03 08:41:12 mdw Exp $ + * + * Types for multiprecision arithmetic + * + * (c) 1999 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: mptypes.h,v $ + * Revision 1.1 1999/09/03 08:41:12 mdw + * Initial import. + * + */ + +#ifndef MPTYPES_H +#define MPTYPES_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#include +#include + +#include + +/*----- 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 + * file. This little hack makes the proper portable C9X names + * work as well. + */ + +#if defined(ULONG_LONG_MAX) && !defined(ULLONG_MAX) +# define LLONG_MIN LONG_LONG_MIN +# define LLONG_MAX LONG_LONG_MAX +# define ULLONG_MAX ULONG_LONG_MAX +#endif + +/* --- Decide on the actual types to use --- * + * + * The division trick here makes sure that one type is twice another without + * the risk of overflowing the arithmetic provided by the preprocessor. + */ + +#if ULONG_MAX / UINT_MAX >= UINT_MAX + typedef unsigned int mpw; + typedef unsigned long mpd; +# define MPW_MAX UINT_MAX +#elif ULLONG_MAX / UINT_MAX >= UINT_MAX + typedef unsigned int mpw; + typedef unsigned long long mpd; +# define MPW_MAX UINT_MAX +#elif UINT_MAX / USHRT_MAX >= USHRT_MAX + typedef unsigned short mpw; + typedef unsigned int mpd; +# define MPW_MAX USHRT_MAX +#elif ULONG_MAX / USHRT_MAX >= USHRT_MAX + typedef unsigned short mpw; + typedef unsigned long mpd; +# define MPW_MAX USHRT_MAX +#elif ULLONG_MAX / USHRT_MAX >= USHRT_MAX + typedef unsigned short mpw; + typedef unsigned long long mpd; +# define MPW_MAX USHRT_MAX +#else + typedef unsigned int mpw; + typedef unsigned int mpd; +# define MPD_MAX UINT_MAX +#endif + +/* --- Fix a value for @MPD_MAX@ --- * + * + * This will then be the target for finding the bit widths and things. + */ + +#ifdef MPW_MAX +# define MPD_MAX ((MPW_MAX + 1) * MPW_MAX + MW_WMAX) +#endif + +/* --- Find the width of @MPD_MAX@ in bits --- * + * + * It must be at least 16 bits wide, because the smallest type I bother to + * try is @unsigned short@. I only bother testing for up to 64 bits, and in + * power-of-two chunks, because I don't care about efficiency on more bizarre + * systems. + */ + +#if MPD_MAX <= 0xfffffffff +# if MPD_MAX == 0xffffffff +# define MPD_BITS 32 +# elif MPD_MAX >= 0x0fffffff +# define MPD_BITS 28 +# elif MPD_MAX >= 0x00ffffff +# define MPD_BITS 24 +# elif MPD_MAX >= 0x000fffff +# define MPD_BITS 20 +# elif MPD_MAX >= 0x0000ffff +# define MPD_BITS 16 +# else +# error "Abject failure deciding on type `mpw'" +#else +# if MPD_MAX / 0xffffffff < 0xffffffff +# define MPD_BITS 32 +# else +# define MPD_BITS 64 /* Slightly dodgy */ +#endif + +/* --- Now sort out the other magical values --- */ + +#undef MPD_MAX +#undef MPW_MAX + +#define MPW_BITS (MPD_BITS / 2) +#define MPD_MAX (((1 << (MPD_BITS - 1)) - 1) * 2 + 1) +#define MPW_MAX ((1 << MPW_BITS) - 1) + +#endif + +/*----- Macros for coercion and masking -----------------------------------*/ + +/* --- @MPW@ --- * + * + * Arguments: @x@ = an unsigned value + * + * Use: Expands to the value of @x@ masked and typecast to a + * multiprecision integer word. + */ + +#define MPW(x) ((mpw)((x) & MPW_MAX)) + +/* --- @MPWS@ --- * + * + * Arguments: @n@ = number of words + * + * Use: Expands to the number of bytes occupied by a given number of + * words. + */ + +#define MPWS(n) ((n) * sizeof(mpw)) + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/mpx.c b/mpx.c new file mode 100644 index 0000000..7ac5a8b --- /dev/null +++ b/mpx.c @@ -0,0 +1,644 @@ +/* -*-c-*- + * + * $Id: mpx.c,v 1.1 1999/09/03 08:41:12 mdw Exp $ + * + * Low-level multiprecision arithmetic + * + * (c) 1999 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: mpx.c,v $ + * Revision 1.1 1999/09/03 08:41:12 mdw + * Initial import. + * + */ + +/*----- Header files ------------------------------------------------------*/ + +#include +#include +#include + +#include + +#include "mptypes.h" +#include "mpx.h" + +/*----- Loading and storing -----------------------------------------------*/ + +/* --- @mpx_storel@ --- * + * + * Arguments: @const mpw *v, *vl@ = base and limit of source vector + * @octet *p@ = pointer to octet array + * @size_t sz@ = size of octet array + * + * Returns: --- + * + * Use: Stores an MP in an octet array, least significant octet + * first. High-end octets are silently discarded if there + * isn't enough space for them. + */ + +void mpx_storel(const mpw *v, const mpw *vl, octet *p, size_t sz) +{ + mpw n, w = 0; + octet *q = p + sz; + unsigned bits = 0; + + while (p < q) { + if (bits < 8) { + if (v >= vl) { + *p++ = U8(w); + break; + } + n = *v++; + *p++ = U8(w | n << bits); + w = n >> (8 - bits); + bits += MPW_BITS - 8; + } else { + *p++ = U8(w); + w >>= 8; + bits -= 8; + } + } + memset(p, 0, q - p); +} + +/* --- @mpx_loadl@ --- * + * + * Arguments: @mpw *v, *vl@ = base and limit of destination vector + * @const octet *p@ = pointer to octet array + * @size_t sz@ = size of octet array + * + * Returns: --- + * + * Use: Loads an MP in an octet array, least significant octet + * first. High-end octets are ignored if there isn't enough + * space for them. + */ + +void mpx_loadl(mpw *v, const mpw *vl, const octet *p, size_t sz) +{ + unsigned n; + const octet *q = p + sz; + unsigned bits = 0; + + if (v >= vl) + return; + while (p < q) { + n = U8(*p++); + w |= n << bits; + bits += 8; + if (bits >= MPW_BITS) { + *v++ = MPW(w); + w = n >> (MPW_BITS - bits + 8); + bits -= MPW_BITS; + if (v >= vl) + return; + } + } + *v++ = w; + MPX_ZERO(v, vl); +} + +/* --- @mpx_storeb@ --- * + * + * Arguments: @const mpw *v, *vl@ = base and limit of source vector + * @octet *p@ = pointer to octet array + * @size_t sz@ = size of octet array + * + * Returns: --- + * + * Use: Stores an MP in an octet array, most significant octet + * first. High-end octets are silently discarded if there + * isn't enough space for them. + */ + +void mpx_storeb(const mpw *v, const mpw *vl, octet *p, size_t sz); +{ + mpw n, w = 0; + octet *q = p + sz; + unsigned bits = 0; + + while (q > p) { + if (bits < 8) { + if (v >= vl) { + *--q = U8(w); + break; + } + n = *v++; + *--q = U8(w | n << bits); + w = n >> (8 - bits); + bits += MPW_BITS - 8; + } else { + *--q = U8(w); + w >>= 8; + bits -= 8; + } + } + memset(p, 0, q - p); +} + +/* --- @mpx_loadb@ --- * + * + * Arguments: @mpw *v, *vl@ = base and limit of destination vector + * @const octet *p@ = pointer to octet array + * @size_t sz@ = size of octet array + * + * Returns: --- + * + * Use: Loads an MP in an octet array, most significant octet + * first. High-end octets are ignored if there isn't enough + * space for them. + */ + +void mpx_loadb(mpw *v, const mpw *vl, const octet *p, size_t sz) +{ + unsigned n; + const octet *q = p + sz; + unsigned bits = 0; + + if (v >= vl) + return; + while (q > p) { + n = U8(*--q); + w |= n << bits; + bits += 8; + if (bits >= MPW_BITS) { + *v++ = MPW(w); + w = n >> (MPW_BITS - bits + 8); + bits -= MPW_BITS; + if (v >= vl) + return; + } + } + *v++ = w; + MPX_ZERO(v, vl); +} + +/*----- Logical shifting --------------------------------------------------*/ + +/* --- @mpx_lsl@ --- * + * + * Arguments: @mpw *dv, *dvl@ = destination vector base and limit + * @const mpw *av, *avl@ = source vector base and limit + * @size_t n@ = number of bit positions to shift by + * + * Returns: --- + * + * Use: Performs a logical shift left operation on an integer. + */ + +void mpx_lsl(mpw *dv, mpw *dvl, const mpw *av, const mpw *avl, size_t n) +{ + size_t nw; + unsigned nb; + + /* --- Trivial special case --- */ + + if (n == 0) + MPX_COPY(dv, dvl, av, avl); + + /* --- Single bit shifting --- */ + + else if (n == 1) { + mpw w = 0; + while (av < avl) { + mpw t; + if (dv >= dvl) + goto done; + t = *av++; + *dv++ = MPW((t << 1) | w); + w = t >> (MPW_BITS - 1); + } + if (dv >= dvl) + goto done; + *dv++ = MPW(w); + MPX_ZERO(dv, dvl); + } + + /* --- Break out word and bit shifts for more sophisticated work --- */ + + nw = n / MPW_BITS; + nb = n % MPW_BITS; + + /* --- Handle a shift by a multiple of the word size --- */ + + if (nb == 0) { + MPX_COPY(dv + nw, dvl, av, avl); + memset(dv, 0, MPWS(nw)); + } + + /* --- And finally the difficult case --- */ + + else { + mpw w; + size_t nr = MPW_BITS - nb; + + if (dv + nw >= dvl) { + MPX_ZERO(dv, dvl); + goto done; + } + memset(dv, 0, MPWS(nw)); + dv += nw; + w = *av++; + + while (av < avl) { + mpw t; + if (dv >= dvl) + goto done; + t = *av++; + *dv++ = MPW((w >> nr) | (t << nb)); + w = t; + } + + if (dv < dvl) { + *dv++ = MPW(w >> nr); + MPX_ZERO(dv, dvl); + } + } + +done:; +} + +/* --- @mpx_lsr@ --- * + * + * Arguments: @mpw *dv, *dvl@ = destination vector base and limit + * @const mpw *av, *avl@ = source vector base and limit + * @size_t n@ = number of bit positions to shift by + * + * Returns: --- + * + * Use: Performs a logical shift right operation on an integer. + */ + +void mpx_lsr(mpw *dv, mpw *dvl, const mpw *av, const mpw *avl, size_t n) +{ + size_t nw; + unsigned nb; + + /* --- Trivial special case --- */ + + if (n == 0) + MPX_COPY(dv, dvl, av, avl); + + /* --- Single bit shifting --- */ + + else if (n == 1) { + mpw w = *av++ >> 1; + while (av < avl) { + mpw t; + if (dv >= dvl) + goto done; + t = *av++; + *dv++ = MPW((t << (MPW_BITS - 1)) | w); + w = t >> 1; + } + if (dv >= dvl) + goto done; + *dv++ = MPW(w); + MPX_ZERO(dv, dvl); + } + + /* --- Break out word and bit shifts for more sophisticated work --- */ + + nw = n / MPW_BITS; + nb = n % MPW_BITS; + + /* --- Handle a shift by a multiple of the word size --- */ + + if (nb == 0) + MPX_COPY(dv, dvl, av + nw, avl); + + /* --- And finally the difficult case --- */ + + else { + mpw w; + size_t nr = MPW_BITS - nb; + + av += nw; + w = *av++; + while (av < avl) { + mpw t; + if (dv >= dvl) + goto done; + t = *av++; + *dv++ = MPW((w >> nb) | (t << nr)); + w = t; + } + if (dv < dvl) { + *dv++ = MPW(w >> nb); + MPX_ZERO(dv, dvl); + } + } + +done:; +} + +/*----- Unsigned arithmetic -----------------------------------------------*/ + +/* --- @mpx_ucmp@ --- * + * + * Arguments: @const mpw *av, *avl@ = first argument vector base and limit + * @const mpw *bv, *bvl@ = second argument vector base and limit + * + * Returns: Less than, equal to, or greater than zero depending on + * whether @a@ is less than, equal to or greater than @b@, + * respectively. + * + * Use: Performs an unsigned integer comparison. + */ + +int mpx_ucmp(const mpw *av, const mpw *avl, const mpw *bv, const mpw *bvl) +{ + MPX_SHRINK(av, avl); + MPX_SHRINK(bv, bvl); + + if (avl - av > bvl - bv) + return (+1); + else if (avl - av < bvl - bv) + return (-1); + else while (avl > av) { + mpw a = *--avl, b = *--bvl; + if (a > b) + return (+1); + else if (a < b) + return (-1); + } + return (0); +} + +/* --- @mpx_uadd@ --- * + * + * Arguments: @mpw *dv, *dvl@ = destination vector base and limit + * @const mpw *av, *avl@ = first addend vector base and limit + * @const mpw *bv, *bvl@ = second addend vector base and limit + * + * Returns: --- + * + * Use: Performs unsigned integer addition. If the result overflows + * the destination vector, high-order bits are discarded. This + * means that two's complement addition happens more or less for + * free, although that's more a side-effect than anything else. + * The result vector may be equal to either or both source + * vectors, but may not otherwise overlap them. + */ + +void mpx_uadd(mpw *dv, mpw *dvl, const mpw *av, const mpw *avl, + const mpw *bv, const mpw *bvl) +{ + mpw c = 0; + + while (av < avl || bv < bvl) { + mpw a, b; + mpd x; + if (dv >= dvl) + return; + a = (av < avl) ? *av++ : 0; + b = (bv < bvl) ? *bv++ : 0; + x = (mpd)a + (mpd)b + c; + *dv++ = MPW(x); + c = x >> MPW_BITS; + } + if (dv < dvl) { + *dv++ = c; + MPX_ZERO(dv, dvl); + } +} + +/* --- @mpx_usub@ --- * + * + * Arguments: @mpw *dv, *dvl@ = destination vector base and limit + * @const mpw *av, *avl@ = first argument vector base and limit + * @const mpw *bv, *bvl@ = second argument vector base and limit + * + * Returns: --- + * + * Use: Performs unsigned integer subtraction. If the result + * overflows the destination vector, high-order bits are + * discarded. This means that two's complement subtraction + * happens more or less for free, althuogh that's more a side- + * effect than anything else. The result vector may be equal to + * either or both source vectors, but may not otherwise overlap + * them. + */ + +void mpx_usub(mpw *dv, mpw *dvl, const mpw *av, const mpw *avl, + const mpw *bv, const mpw *bvl) +{ + mpw c = 0; + + while (av < avl || bv < bvl) { + mpw a, b; + mpd x; + if (dv >= dvl) + return; + a = (av < avl) ? *av++ : 0; + b = (bv < bvl) ? *bv++ : 0; + x = (mpd)a - (mpd)b + c; + *dv++ = MPW(x); + if (c >> MPW_BITS) + c = MPW(~0u); + } + c = c ? ~0u : 0; + while (dv < dvl) + *dv++ = c +} + +/* --- @mpx_umul@ --- * + * + * Arguments: @mpw *dv, *dvl@ = destination vector base and limit + * @const mpw *av, *avl@ = multiplicand vector base and limit + * @const mpw *bv, *bvl@ = multiplier vector base and limit + * + * Returns: --- + * + * Use: Performs unsigned integer multiplication. If the result + * overflows the desination vector, high-order bits are + * discarded. The result vector may not overlap the argument + * vectors in any way. + */ + +void mpx_umul(mpw *dv, mpw *dvl, const mpw *av, const mpw *avl, + const mpw *bv, const mpw *bvl) +{ + /* --- This is probably worthwhile on a multiply --- */ + + MPX_SHRINK(av, avl); + MPX_SHRINK(bv, bvl); + + /* --- Deal with a multiply by zero --- */ + + if (bv == bvl) { + MPX_COPY(dv, dvl, bv, bvl); + return; + } + + /* --- Do the initial multiply and initialize the accumulator --- */ + + MPX_UMULN(dv, dvl, av, avl, *bv++); + + /* --- Do the remaining multiply/accumulates --- */ + + while (bv < bvl) { + mpw m = *bv++; + mpw c = ; + const mpw *avv = av; + mpw *dvv = ++dv; + + while (avv < avl) { + mpd x; + if (dvv >= dvl) + goto next; + x = *dvv + m * *av++ + c; + *dv++ = MPW(x); + c = x >> MPW_BITS; + } + if (dvv < dvl) + *dvv++ = MPW(c); + next:; + } +} + +/* --- @mpx_udiv@ --- * + * + * Arguments: @mpw *qv, *qvl@ = quotient vector base and limit + * @mpw *rv, *rvl@ = dividend/remainder vector base and limit + * @const mpw *dv, *dvl@ = divisor vector base and limit + * + * Returns: --- + * + * Use: Performs unsigned integer division. If the result overflows + * the quotient vector, high-order bits are discarded. (Clearly + * the remainder vector can't overflow.) The various vectors + * may not overlap in any way. Yes, I know it's a bit odd + * requiring the dividend to be in the result position but it + * does make some sense really. The remainder must have + * headroom for at least two extra words. + */ + +void mpx_udiv(mpw *qv, mpw *qvl, mpw *rv, mpw *rvl, + const mpw *dv, const mpw *dvl) +{ + mpw spare[2]; + unsigned norm = 0; + size_t scale; + mpw d, dd; + + /* --- Initialize the quotient --- */ + + MPX_ZERO(qv, qvl); + + /* --- Normalize the divisor --- * + * + * The algorithm requires that the divisor be at least two digits long. + * This is easy to fix. + */ + + MPX_SHRINK(dv, dvl); + + assert(((void)"division by zero in mpx_udiv", dv < dvl)); + + d = dvl[-1]; + if (dv + 1 == dvl) { + spare[0] = 0; + spare[1] = d; + dv = spare; + dvl = spare + 2; + norm += MPW_BITS; + } + + while (d < MPW_MAX / 2) { + d <<= 1; + norm += 1; + } + dd = dvl[-2]; + + /* --- Normalize the dividend/remainder to match --- */ + + mpx_lsl(rv, rvl, rv, rvl, norm); + MPX_SHRINK(rv, rvl); + + /* --- Work out the relative scales --- */ + + { + size_t rvn = rvl - rv; + size_t dvn = dvn - dv; + + /* --- If the divisor is clearly larger, notice this --- */ + + if (dvn > rvn) { + mpx_lsr(rv, rvl, rv, rvl, norm); + return; + } + + scale = rvn - dvn; + } + + /* --- Calculate the most significant quotient digit --- * + * + * Because the divisor has its top bit set, this can only happen once. The + * pointer arithmetic is a little contorted, to make sure that the + * behaviour is defined. + */ + + if (MPX_UCMP(rv + scale, rvl, >=, dv, dvl)) { + mpx_usub(rv + scale, rvl, rv + scale, rvl, dv, dvl); + if (qvl - qv > scale) + qv[scale] = 1; + } + + /* --- Now for the main loop --- */ + + { + mpw *rvv; + mpw r; + + scale--; + rvv = rvl - 2; + r = rvv[1]; + + while (scale) { + mpw q, rr; + + /* --- Get an estimate for the next quotient digit --- */ + + rr = *rvv--; + if (r == d) + q = MPW_MAX; + else { + mpd rx = (r << MPW_BITS) | rr; + q = MPW(rx / d); + } + + /* --- Refine the estimate --- */ + + { + mpd yh = (mpd)d * q; + mpd yl = (mpd)dd * q; + +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/mpx.h b/mpx.h new file mode 100644 index 0000000..edaea71 --- /dev/null +++ b/mpx.h @@ -0,0 +1,440 @@ +/* -*-c-*- + * + * $Id: mpx.h,v 1.1 1999/09/03 08:41:12 mdw Exp $ + * + * Low level multiprecision arithmetic + * + * (c) 1999 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: mpx.h,v $ + * Revision 1.1 1999/09/03 08:41:12 mdw + * Initial import. + * + */ + +#ifndef MPX_H +#define MPX_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- The idea ----------------------------------------------------------* + * + * This file provides functions and macros which work on vectors of words as + * unsigned multiprecision integers. The interface works in terms of base + * and limit pointers (i.e., a pointer to the start of a vector, and a + * pointer just past its end) rather than base pointer and length, because + * that requires more arithmetic and state to work on. + * + * The interfaces are slightly bizarre in other ways. Try to use the + * higher-level functions where you can: they're rather better designed to + * actually be friendly and useful. + */ + +/*----- Header files ------------------------------------------------------*/ + +#include + +#ifndef MPTYPES_H +# include "mptypes.h" +#endif + +/*----- General manipulation ----------------------------------------------*/ + +/* --- @MPX_SHRINK@ --- * + * + * Arguments: @const mpw *v@ = pointer to vector of words + * @const mpw *vl@ = (updated) current limit of vector + * + * Use: Shrinks down the limit of a multiprecision integer vector. + */ + +#define MPX_SHRINK(v, vl) do { \ + const mpw *_v = (v), *_vl = (vl); \ + while (_vl > _v && *--_vl) \ + ; \ + (vl) = _vl; \ +} while (0) + +/* --- @MPX_BITS@ --- * + * + * Arguments: @unsigned long b@ = result variable + * @const mpw *v@ = pointer to array of words + * @const mpw *vl@ = limit of vector (from @MPX_SHRINK@) + * + * Use: Calculates the number of bits in a multiprecision value. + */ + +#define MPX_BITS(b, v, vl) do { \ + const mpw *_v = (v), *_vl = (vl); \ + if (_v == _vl) \ + (b) = 0; \ + else { \ + unsigned long _b = MPW_BITS * (_vl - _v - 1) + 1; \ + mpw _w = _vl[-1]; \ + unsigned _k = MPW_BITS / 2; \ + while (_k) { \ + if (_w >> _k) { \ + _w >>= _k; \ + _b += _k; \ + } \ + _k >>= 1; \ + } \ + (b) = _b; \ + } \ +} while (0) + +/* --- @MPX_OCTETS@ --- * + * + * Arguments: @size_t o@ = result variable + * @const mpw *v@ = pointer to array of words + * @size_t len@ = length of the array (from @MPX_LEN@) + * + * Use: Calculates the number of octets in a multiprecision value. + */ + +#define MPX_OCTETS(o, v, len) do { \ + const mpw *_v = (v), *_vl = (vl); \ + if (_v == _vl) \ + (o) = 0; \ + else { \ + _size_t _o = (MPW_BITS / 8) * (_vl - _v - 1); \ + mpw _w = _vl[-1]; \ + unsigned _k = MPW_BITS / 2; \ + while (_k > 3) { \ + if (_w >> _k) { \ + _w >>= _k; \ + _o += _k - 3; \ + } \ + _k >>= 1; \ + } \ + if (_w) \ + _o++; \ + (o) = _o; \ + } \ +} while (0) + +/* --- @MPX_COPY@ --- * + * + * Arguments: @dv, dvl@ = destination vector base and limit + * @av, avl@ = source vector base and limit + * + * Use: Copies a multiprecision integer. + */ + +#define MPX_COPY(dv, dvl, av, dvl) do { \ + mpw *_dv = (dv); \ + size_t _dn = (dvl) - _dv; \ + const mpw *_av = (av); \ + size_t _an = (avl) - _av; \ + if (_av == _dv) { \ + if (_dvl > _avl) \ + memset(_avl, 0, MPWS(_dn - _an)); \ + } else if (_an >= _dn) \ + memmove(_dv, _av, MPWS(_dn)); \ + else { \ + memmove(_dv, _av, MPWS(_an)); \ + memset(_dv + _an, 0, MPWS(_dn - _an)); \ + } \ +} while (0) + +/* --- @MPX_ZERO@ --- * + * + * Arguments: @v, vl@ = base and limit of vector to clear + * + * Use: Zeroes the area between the two vector pointers. + */ + +#define MPX_ZERO(v, vl) { \ + mpw *_v = (v), *_vl = (vl); \ + memset(_v, 0, MPWS(_vl - _v)); \ +} while (0) + +/*----- Loading and storing -----------------------------------------------*/ + +/* --- @mpx_storel@ --- * + * + * Arguments: @const mpw *v, *vl@ = base and limit of source vector + * @octet *p@ = pointer to octet array + * @size_t sz@ = size of octet array + * + * Returns: --- + * + * Use: Stores an MP in an octet array, least significant octet + * first. High-end octets are silently discarded if there + * isn't enough space for them. + */ + +extern void mpx_storel(const mpw */*v*/, const mpw */*vl*/, + octet */*p*/, size_t /*sz*/); + +/* --- @mpx_loadl@ --- * + * + * Arguments: @mpw *v, *vl@ = base and limit of destination vector + * @const octet *p@ = pointer to octet array + * @size_t sz@ = size of octet array + * + * Returns: --- + * + * Use: Loads an MP in an octet array, least significant octet + * first. High-end octets are ignored if there isn't enough + * space for them. + */ + +extern void mpx_loadl(mpw */*v*/, mpw */*vl*/, + const octet */*p*/, size_t /*sz*/); + +/* --- @mpx_storeb@ --- * + * + * Arguments: @const mpw *v, *vl@ = base and limit of source vector + * @octet *p@ = pointer to octet array + * @size_t sz@ = size of octet array + * + * Returns: --- + * + * Use: Stores an MP in an octet array, most significant octet + * first. High-end octets are silently discarded if there + * isn't enough space for them. + */ + +extern void mpx_storeb(const mpw */*v*/, const mpw */*vl*/, + octet */*p*/, size_t /*sz*/); + +/* --- @mpx_loadb@ --- * + * + * Arguments: @mpw *v, *vl@ = base and limit of destination vector + * @const octet *p@ = pointer to octet array + * @size_t sz@ = size of octet array + * + * Returns: --- + * + * Use: Loads an MP in an octet array, most significant octet + * first. High-end octets are ignored if there isn't enough + * space for them. + */ + +extern void mpx_loadb(mpw */*v*/, mpw */*vl*/, + const octet */*p*/, size_t /*sz*/); + +/*----- Logical shifting --------------------------------------------------*/ + +/* --- @mpx_lsl@ --- * + * + * Arguments: @mpw *dv, *dvl@ = destination vector base and limit + * @const mpw *av, *avl@ = source vector base and limit + * @size_t n@ = number of bit positions to shift by + * + * Returns: --- + * + * Use: Performs a logical shift left operation on an integer. + */ + +extern void mpx_lsl(mpw */*dv*/, mpw */*dvl*/, + const mpw */*av*/, const mpw */*avl*/, + size_t /*n*/); + +/* --- @mpx_lsr@ --- * + * + * Arguments: @mpw *dv, *dvl@ = destination vector base and limit + * @const mpw *av, *avl@ = source vector base and limit + * @size_t n@ = number of bit positions to shift by + * + * Returns: --- + * + * Use: Performs a logical shift right operation on an integer. + */ + +extern void mpx_lsr(mpw */*dv*/, mpw */*dvl*/, + const mpw */*av*/, const mpw */*avl*/, + size_t /*n*/); + +/*----- Unsigned arithmetic -----------------------------------------------*/ + +/* --- @mpx_ucmp@ --- * + * + * Arguments: @const mpw *av, *avl@ = first argument vector base and limit + * @const mpw *bv, *bvl@ = second argument vector base and limit + * + * Returns: Less than, equal to, or greater than zero depending on + * whether @a@ is less than, equal to or greater than @b@, + * respectively. + * + * Use: Performs an unsigned integer comparison. + */ + +#define MPX_UCMP(av, avl, op, dv, dvl) \ + (mpx_ucmp((av), (avl), (dv), (dvl)) op 0) + +extern int mpx_ucmp(const mpw */*av*/, const mpw */*avl*/, + const mpw */*bv*/, const mpw */*bvl*/); + +/* --- @mpx_uadd@ --- * + * + * Arguments: @mpw *dv, *dvl@ = destination vector base and limit + * @const mpw *av, *avl@ = first addend vector base and limit + * @const mpw *bv, *bvl@ = second addend vector base and limit + * + * Returns: --- + * + * Use: Performs unsigned integer addition. If the result overflows + * the destination vector, high-order bits are discarded. This + * means that two's complement addition happens more or less for + * free, although that's more a side-effect than anything else. + * The result vector may be equal to either or both source + * vectors, but may not otherwise overlap them. + */ + +extern void mpx_uadd(mpw */*dv*/, mpw */*dvl*/, + const mpw */*av*/, const mpw */*avl*/, + const mpw */*bv*/, const mpw */*bvl*/); + +/* --- @mpx_usub@ --- * + * + * Arguments: @mpw *dv, *dvl@ = destination vector base and limit + * @const mpw *av, *avl@ = first argument vector base and limit + * @const mpw *bv, *bvl@ = second argument vector base and limit + * + * Returns: --- + * + * Use: Performs unsigned integer subtraction. If the result + * overflows the destination vector, high-order bits are + * discarded. This means that two's complement subtraction + * happens more or less for free, althuogh that's more a side- + * effect than anything else. The result vector may be equal to + * either or both source vectors, but may not otherwise overlap + * them. + */ + +extern void mpx_usub(mpw */*dv*/, mpw */*dvl*/, + const mpw */*av*/, const mpw */*avl*/, + const mpw */*bv*/, const mpw */*bvl*/); + +/* --- @MPX_UMULN@ --- * + * + * Arguments: @dv, dvl@ = destination vector base and limit + * @av, avl@ = multiplicand vector base and limit + * @m@ = multiplier + * + * Use: Multiplies a multiprecision integer by a single-word value. + * The destination and source may be equal. The destination + * is completely cleared after use. + */ + +#define MPX_UMULN(dv, dvl, av, avl, m) do { \ + mpw *_dv = (dv), *_dvl = (dvl); \ + const mpw *_av = (av), *_avl = (avl); \ + mpw _c = 0; \ + mpd _m = (m); \ + \ + while (_av < _avl) { \ + mpd _x; \ + if (_dv >= _dvl) \ + break; \ + _x = _m * *_av++ + c; \ + *_dv++ = MPW(_x); \ + _c = _x >> MPW_BITS; \ + } \ + if (_dv < _dvl) { \ + *_dv++ = MPW(_c); \ + MPX_ZERO(_dv, _dvl); \ + } \ +} while (0) + +/* --- @MPX_UMLAN@ --- * + * + * Arguments: @dv, dvl@ = destination/accumulator vector base and limit + * @av, avl@ = multiplicand vector base and limit + * @m@ = multiplier + * + * Use: Multiplies a multiprecision integer by a single-word value + * and adds the result to an accumulator. + */ + +#define MPX_UMLAN(dv, dvl, av, avl, m) do { \ + mpw *_dv = (dv), *_dvl = (dvl); \ + const mpw *_av = (av), *_avl = (avl); \ + mpw _c = 0; \ + mpd _m = (m); \ + \ + while (_av < _avl) { \ + mpd _x; \ + if (_dv >= _dvl) \ + break; \ + _x = *_dv + _m * *_av++ + _c; \ + *_dv++ = MPW(_x); \ + _c = _x >> MPW_BITS; \ + } \ + if (_dv < _dvl) { \ + *_dv++ = MPW(_c); \ + MPX_ZERO(_dv, _dvl); \ + } \ +} while (0) + +/* --- @mpx_umul@ --- * + * + * Arguments: @mpw *dv, *dvl@ = destination vector base and limit + * @const mpw *av, *avl@ = multiplicand vector base and limit + * @const mpw *bv, *bvl@ = multiplier vector base and limit + * + * Returns: --- + * + * Use: Performs unsigned integer multiplication. If the result + * overflows the desination vector, high-order bits are + * discarded. The result vector may not overlap the argument + * vectors in any way. + */ + +extern void mpx_umul(mpw */*dv*/, mpw */*dvl*/, + const mpw */*av*/, const mpw */*avl*/, + const mpw */*bv*/, const mpw */*bvl*/); + +/* --- @mpx_udiv@ --- * + * + * Arguments: @mpw *qv, *qvl@ = quotient vector base and limit + * @mpw *rv, *rvl@ = dividend/remainder vector base and limit + * @const mpw *dv, *dvl@ = divisor vector base and limit + * + * Returns: --- + * + * Use: Performs unsigned integer division. If the result overflows + * the quotient vector, high-order bits are discarded. (Clearly + * the remainder vector can't overflow.) The various vectors + * may not overlap in any way. Yes, I know it's a bit odd + * requiring the dividend to be in the result position but it + * does make some sense really. + */ + +extern void mpx_udiv(mpw */*qv*/, mpw */*qvl*/, mpw */*rv*/, mpw */*rvl*/, + const mpw */*dv*/, const mpw */*dvl*/); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/noise.c b/noise.c new file mode 100644 index 0000000..094447c --- /dev/null +++ b/noise.c @@ -0,0 +1,396 @@ +/* -*-c-*- + * + * $Id: noise.c,v 1.1 1999/09/03 08:41:12 mdw Exp $ + * + * Acquisition of environmental noise (Unix specific) + * + * (c) 1998 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: noise.c,v $ + * Revision 1.1 1999/09/03 08:41:12 mdw + * Initial import. + * + */ + +/*----- Header files ------------------------------------------------------*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#ifdef HAVE_SETGROUPS +# include +#endif + +#include +#include + +#include "noise.h" +#include "paranoia.h" +#include "rand.h" + +/*----- Magical numbers ---------------------------------------------------*/ + +#define NOISE_KIDLIFE 100000 /* @noise_filter@ child lifetime */ +#define MILLION 1000000 /* One million */ + +/*----- Noise source definition -------------------------------------------*/ + +rand_source noise_source = { noise_acquire, noise_timer }; + +/*----- Static variables --------------------------------------------------*/ + +/* --- Timer differences --- */ + +static unsigned long noise_last; /* Last time recorded */ +static unsigned long noise_diff; /* Last first order difference */ + +/* --- Setuid program handling --- */ + +static uid_t noise_uid; /* Uid to set to spawn processes */ +static gid_t noise_gid; /* Gid to set to spawn processes */ + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @bitcount@ --- * + * + * Arguments: @unsigned long x@ = a word containing bits + * + * Returns: The number of bits set in the word. + */ + +static int bitcount(unsigned long x) +{ + char ctab[] = { 0, 1, 1, 2, 1, 2, 2, 3, + 1, 2, 2, 3, 2, 3, 3, 4 }; + int count = 0; + while (x) { + count += ctab[x & 0xfu]; + x >>= 4; + } + return (count); +} + +/* --- @timer@ --- * + * + * Arguments: @rand_pool *r@ = pointer to randomness pool + * @struct timeval *tv@ = pointer to time block + * + * Returns: Nonzer if some randomness was contributed. + * + * Use: Low-level timer contributor. + */ + +static int timer(rand_pool *r, struct timeval *tv) +{ + unsigned long x, d, dd; + int de, dde; + int ret; + + x = tv->tv_usec + MILLION * tv->tv_sec; + d = x ^ noise_last; + dd = d ^ noise_diff; + noise_diff = d; + de = bitcount(d); + dde = bitcount(dd); + rand_add(r, tv, sizeof(*tv), de <= dde ? de : dde); + ret = (de || dde); + BURN(tv); x = d = dd = de = dde = 0; + return (ret); +} + +/* --- @noise_timer@ --- * + * + * Arguments: @rand_pool *r@ = pointer to a randomness pool + * + * Returns: Nonzero if some randomness was contributed. + * + * Use: Contributes the current time to the randomness pool. + * A guess at the number of useful bits contributed is made, + * based on first and second order bit differences. This isn't + * ever-so reliable, but it's better than nothing. + */ + +int noise_timer(rand_pool *r) +{ + struct timeval tv; + gettimeofday(&tv, 0); + return (timer(r, &tv)); +} + +/* --- @noise_devrandom@ --- * + * + * Arguments: @rand_pool *r@ = pointer to a randomness pool + * + * Returns: Nonzero if some randomness was contributed. + * + * Use: Attempts to obtain some randomness from the system entropy + * pool. All bits from the device are assumed to be good. + */ + +int noise_devrandom(rand_pool *r) +{ + int fd; + octet buf[RAND_POOLSZ]; + ssize_t len; + int ret = 0; + + /* --- Be nice to other clients of the random device --- * + * + * Attempt to open the device nonblockingly. If that works, take up to + * one bufferful and then close again. If there's no data to be read, + * then that's tough and we go away again, on the grounds that the device + * needs to get some more entropy from somewhere. + */ + + if ((fd = open("/dev/random", O_RDONLY | O_NONBLOCK)) >= 0) { + if ((len = read(fd, buf, sizeof(buf))) > 0) { + rand_add(r, buf, len, len * 8); + BURN(buf); + ret = 1; + } + close(fd); + } + noise_timer(r); + return (ret); +} + +/* --- @noise_setid@ --- * + * + * Arguments: @uid_t uid@ = uid to set + * @gid_t gid@ = gid to set + * + * Returns: --- + * + * Use: Sets the user and group ids to be used by @noise_filter@ + * when running child processes. This is useful to avoid + * giving shell commands (even carefully written ones) undue + * privileges. + */ + +void noise_setid(uid_t uid, gid_t gid) +{ + noise_uid = uid; + noise_gid = gid; +} + +/* --- @noise_filter@ --- * + * + * Arguments: @rand_pool *r@ = pointer to a randomness pool + * @int good@ = number of good bits per 1024 bits + * @const char *c@ = shell command to run + * + * Returns: Nonzero if some randomness was contributed. + * + * Use: Attempts to execute a shell command, and dump it into the + * randomness pool. A very rough estimate of the number of + * good bits is made, based on the size of the command's output. + * This function calls @waitpid@, so be careful. Before execing + * the command, the process uid and gid are set to the values + * given to @noise_setid@, and an attempt is made to reset the + * list of supplementary groups. The environment passed to + * the command has been severly lobotimized. If the command + * fails to complete within a short time period, it is killed. + * Paranoid use of close-on-exec flags for file descriptors is + * recommended. + */ + +int noise_filter(rand_pool *r, int good, const char *c) +{ + char buf[4096]; + pid_t kid; + int fd[2]; + struct timeval dead; + int ret = 0; + const char *env[] = { + "PATH=/bin:/usr/bin:/sbin:/usr/sbin:/etc", + 0 + }; + + /* --- Remember when this business started --- */ + + gettimeofday(&dead, 0); + timer(r, &dead); + + /* --- Create a pipe --- */ + + if (pipe(fd)) + return (ret); + + /* --- Fork a child off --- */ + + kid = fork(); + if (kid < 0) { + close(fd[0]); + close(fd[1]); + return (ret); + } + + /* --- Handle the child end of the deal --- */ + + fflush(0); + if (kid == 0) { + int f; + + /* --- Set the pipe as standard output, close standard input --- */ + + close(0); close(1); close(2); + + if (fd[1] != 1) { + if (dup2(fd[1], 1) < 0) _exit(127); + close(fd[1]); + } + + if ((f = open("/dev/null", O_RDONLY)) != 0 || + (f = open("/dev/null", O_WRONLY)) != 2) + _exit(127); + + /* --- Play games with uids --- */ + + if (noise_gid != NOISE_NOSETGID) { + setgid(noise_gid); + setegid(noise_gid); +#ifdef HAVE_SETGROUPS + setgroups(1, &noise_gid); +#endif + } + + if (noise_uid != NOISE_NOSETUID) { + setuid(noise_uid); + seteuid(noise_uid); + } + + /* --- Start the process up --- */ + + execle("/bin/sh", "-c", c, (char *)0, env); + _exit(127); + } + + /* --- Sort out my end of the deal --- */ + + close(fd[1]); + + /* --- Decide on the deadline --- */ + + TV_ADDL(&dead, &dead, 0, NOISE_KIDLIFE); + + /* --- Now read, and think --- */ + + for (;;) { + struct timeval now, diff; + fd_set rd; + + gettimeofday(&now, 0); + timer(r, &now); + if (TV_CMP(&now, >, &dead)) + break; + TV_SUB(&diff, &dead, &now); + + FD_ZERO(&rd); + FD_SET(fd[0], &rd); + + if (select(fd[0] + 1, &rd, 0, 0, &diff) < 0) + break; + if (FD_ISSET(fd[0], &rd)) { + ssize_t sz; + int goodbits; + + if ((sz = read(fd[0], buf, sizeof(buf))) <= 0) + break; + goodbits = (sz * good) / 128; + rand_add(r, buf, sz, goodbits); + ret = 1; + } + } + + /* --- We've finished with it: kill the child process --- * + * + * This is morally questionable. On the other hand, I don't want to be + * held up in the @waitpid@ call if I can possibly help it. Maybe a + * double-fork is worth doing here. + */ + + close(fd[0]); + BURN(buf); + noise_timer(r); + kill(kid, SIGKILL); + waitpid(kid, 0, 0); + return (ret); +} + +/* --- @noise_acquire@ --- * + * + * Arguments: @rand_pool *r@ = pointer to a randomness pool + * + * Returns: --- + * + * Use: Acquires some randomness from somewhere. + */ + +void noise_acquire(rand_pool *r) +{ + if (!noise_devrandom(r)) { + + /* --- Output of `ps' --- * + * + * This is a hopefully cheap way of getting a bit of noise. I'm guessing + * the good bit ratio based on about 90 bytes per line of output, and + * each line containing maybe 12 bits worth of interesting data. (Some + * quick experiments with gzip seem to bear this idea out.) So, 12 good + * bits per 90 bytes of output gives slightly more than 17 good bits per + * 1024 bits of output. So I'll be a pessimist and say 16 bits. + */ + + (void) + (noise_filter(r, 16, "ps alxww") || + noise_filter(r, 16, "ps -elf")); + + /* --- Output of `netstat' --- * + * + * Another possibly cheap way of getting noise. My output has about + * 72 bytes per line of output. My wild guesses are telling me that + * there are probably only about four good bits per line (gzip tells + * me there's six, but I want to underestimate). Four bits per 72 bytes + * is 7 good bits per 1024 bits of output. Pessimist that I am, I'll + * call it six. + */ + + noise_filter(r, 6, "netstat -n"); + } +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/noise.h b/noise.h new file mode 100644 index 0000000..cadb747 --- /dev/null +++ b/noise.h @@ -0,0 +1,145 @@ +/* -*-c-*- + * + * $Id: noise.h,v 1.1 1999/09/03 08:41:12 mdw Exp $ + * + * Acquisition of environmental noise (Unix specific) + * + * (c) 1998 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: noise.h,v $ + * Revision 1.1 1999/09/03 08:41:12 mdw + * Initial import. + * + */ + +#ifndef NOISE_H +#define NOISE_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#include + +#ifndef RAND_H +# include "rand.h" +#endif + +/*----- Noise source definition -------------------------------------------*/ + +extern rand_source noise_source; + +/*----- Magic numbers -----------------------------------------------------*/ + +#define NOISE_NOSETUID ((uid_t)-1) +#define NOISE_NOSETGID ((gid_t)-1) + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @noise_timer@ --- * + * + * Arguments: @rand_pool *r@ = pointer to a randomness pool + * + * Returns: Nonzero if some randomness was contributed. + * + * Use: Contributes the current time to the randomness pool. + * A guess at the number of useful bits contributed is made, + * based on first and second order bit differences. This isn't + * ever-so reliable, but it's better than nothing. + */ + +extern int noise_timer(rand_pool */*r*/); + +/* --- @noise_devrandom@ --- * + * + * Arguments: @rand_pool *r@ = pointer to a randomness pool + * + * Returns: Nonzero if some randomness was contributed. + * + * Use: Attempts to obtain some randomness from the system entropy + * pool. All bits from the device are assumed to be good. + */ + +extern int noise_devrandom(rand_pool */*r*/); + +/* --- @noise_setid@ --- * + * + * Arguments: @uid_t uid@ = uid to set + * @gid_t gid@ = gid to set + * + * Returns: --- + * + * Use: Sets the user and group ids to be used by @noise_filter@ + * when running child processes. This is useful to avoid + * giving shell commands (even carefully written ones) undue + * privileges. + */ + +extern void noise_setid(uid_t /*uid*/, gid_t /*gid*/); + +/* --- @noise_filter@ --- * + * + * Arguments: @rand_pool *r@ = pointer to a randomness pool + * @int good@ = number of good bits per 1024 bits + * @const char *c@ = shell command to run + * + * Returns: Nonzero if some randomness was contributed. + * + * Use: Attempts to execute a shell command, and dump it into the + * randomness pool. A very rough estimate of the number of + * good bits is made, based on the size of the command's output. + * This function calls @waitpid@, so be careful. Before execing + * the command, the process uid and gid are set to the values + * given to @noise_setid@, and an attempt is made to reset the + * list of supplementary groups. The environment passed to + * the command has been severly lobotimized. If the command + * fails to complete within a short time period, it is killed. + * Paranoid use of close-on-exec flags for file descriptors is + * recommended. + */ + +extern int noise_filter(rand_pool */*r*/, int /*good*/, const char */*c*/); + +/* --- @noise_acquire@ --- * + * + * Arguments: @rand_pool *r@ = pointer to a randomness pool + * + * Returns: --- + * + * Use: Acquires some randomness from somewhere. + */ + +extern void noise_acquire(rand_pool */*r*/); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/ofb.h b/ofb.h new file mode 100644 index 0000000..85ff08c --- /dev/null +++ b/ofb.h @@ -0,0 +1,406 @@ +/* -*-c-*- + * + * $Id: ofb.h,v 1.1 1999/09/03 08:41:12 mdw Exp $ + * + * Output feedback for block ciphers + * + * (c) 1999 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: ofb.h,v $ + * Revision 1.1 1999/09/03 08:41:12 mdw + * Initial import. + * + */ + +#ifndef OFB_H +#define OFB_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#include + +#include + +#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 + +#include "daftstory.h" + +/* --- @OFB_TEST@ --- * + * + * Arguments: @PRE@, @pre@ = prefixes for block cipher definitions + * + * Use: Standard test rig for OFB functions. + */ + +#define OFB_TEST(PRE, pre) \ + \ +/* --- Initial plaintext for the test --- */ \ + \ +static const octet text[] = TEXT; \ + \ +/* --- Key and IV to use --- */ \ + \ +static const octet key[] = KEY; \ +static const octet iv[] = IV; \ + \ +/* --- Buffers for encryption and decryption output --- */ \ + \ +static octet ct[sizeof(text)]; \ +static octet pt[sizeof(text)]; \ + \ +static void hexdump(const octet *p, size_t sz) \ +{ \ + const octet *q = p + sz; \ + for (sz = 0; p < q; p++, sz++) { \ + printf("%02x", *p); \ + if ((sz + 1) % PRE ## _BLKSZ == 0) \ + putchar(':'); \ + } \ +} \ + \ +int main(void) \ +{ \ + size_t sz = 0, rest; \ + pre ## _ofbctx ctx; \ + int status = 0; \ + int done = 0; \ + pre ## _ctx k; \ + \ + size_t keysz = PRE ## _KEYSZ ? \ + PRE ## _KEYSZ : strlen((const char *)key); \ + \ + fputs(#pre "-ofb: ", stdout); \ + \ + pre ## _init(&k, key, keysz); \ + pre ## _ofbsetkey(&ctx, &k); \ + \ + while (sz <= sizeof(text)) { \ + rest = sizeof(text) - sz; \ + memcpy(ct, text, sizeof(text)); \ + pre ## _ofbsetiv(&ctx, iv); \ + pre ## _ofbencrypt(&ctx, ct, ct, sz); \ + pre ## _ofbencrypt(&ctx, ct + sz, ct + sz, rest); \ + memcpy(pt, ct, sizeof(text)); \ + pre ## _ofbsetiv(&ctx, iv); \ + pre ## _ofbencrypt(&ctx, pt, pt, rest); \ + pre ## _ofbencrypt(&ctx, pt + rest, pt + rest, sz); \ + if (memcmp(pt, text, sizeof(text)) == 0) { \ + done++; \ + if (sizeof(text) < 40 || done % 8 == 0) \ + fputc('.', stdout); \ + if (done % 480 == 0) \ + fputs("\n\t", stdout); \ + fflush(stdout); \ + } else { \ + printf("\nError (sz = %lu)\n", (unsigned long)sz); \ + status = 1; \ + printf("\tplaintext = "); hexdump(text, sz); \ + printf(", "); hexdump(text + sz, rest); \ + fputc('\n', stdout); \ + printf("\tciphertext = "); hexdump(ct, sz); \ + printf(", "); hexdump(ct + sz, rest); \ + fputc('\n', stdout); \ + printf("\trecovered text = "); hexdump(pt, sz); \ + printf(", "); hexdump(pt + sz, rest); \ + fputc('\n', stdout); \ + fputc('\n', stdout); \ + } \ + if (sz < 63) \ + sz++; \ + else \ + sz += 9; \ + } \ + \ + fputs(status ? " failed\n" : " ok\n", stdout); \ + return (status); \ +} + +#else +# define OFB_TEST(PRE, pre) +#endif + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/papers/.cvsignore b/papers/.cvsignore new file mode 100644 index 0000000..4c052b9 --- /dev/null +++ b/papers/.cvsignore @@ -0,0 +1,4 @@ +rand.aux +rand.dvi +rand.log +rand.toc diff --git a/papers/crypto.sty b/papers/crypto.sty new file mode 100644 index 0000000..7c0dcb7 --- /dev/null +++ b/papers/crypto.sty @@ -0,0 +1 @@ +\endinput diff --git a/papers/rand.tex b/papers/rand.tex new file mode 100644 index 0000000..77a986c --- /dev/null +++ b/papers/rand.tex @@ -0,0 +1,179 @@ +%%% -*-latex-*- +%%% +%%% $Id: rand.tex,v 1.1 1999/09/03 08:41:13 mdw Exp $ +%%% +%%% Description of Catacomb's random number generator +%%% +%%% (c) 1999 Straylight/Edgeware +%%% + +%%%----- Licensing notice --------------------------------------------------- +%%% +%%% This file is part of Catacomb. +%%% +%%% Catacomb is free software; you can redistribute it and/or modify +%%% it under the terms of the GNU Library General Public License as +%%% published by the Free Software Foundation; either version 2 of the +%%% License, or (at your option) any later version. +%%% +%%% Catacomb is distributed in the hope that it will be useful, +%%% but WITHOUT ANY WARRANTY; without even the implied warranty of +%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +%%% GNU Library General Public License for more details. +%%% +%%% You should have received a copy of the GNU Library General Public +%%% License along with Catacomb; if not, write to the Free +%%% Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, +%%% MA 02111-1307, USA. + +%%%----- Revision history --------------------------------------------------- +%%% +%%% $Log: rand.tex,v $ +%%% Revision 1.1 1999/09/03 08:41:13 mdw +%%% Initial import. +%%% + +%%%----- Header ------------------------------------------------------------- + +\documentclass[a4paper, article, 10pt, notitlepage, numbering]{strayman} +\usepackage[palatino, helvetica, courier, maths=cmr]{mdwfonts} +\usepackage{mdwtab, mathenv} +\usepackage[T1]{fontenc} +\usepackage{cmtt, url} +\usepackage[tpic, all]{xy} +\usepackage{mathbbol} +% \usepackage{crypto} + +\def\mdw{{\normalfont[{\bfseries\itshape mdw}]}} +\urlstyle{tt} +\def\email{\begingroup\urlstyle{rm}\Url} +\urldef\myemail\email{mdw@nsict.org} +\def\Z{\mathbb{Z}} +\let\assign\leftarrow +\let\xor\oplus +\let\bigxor\bigoplus + +\title{The Catacomb random number generator} +\author{Mark Wooding, \myemail} + +%%%----- The main document -------------------------------------------------- + +\begin{document} + +\maketitle + +\begin{abstract} + The author describes the random number generator used in the + Straylight/\-Edgeware `Catacomb' library. While the generator is + superficially similar to (for example) the Linux and OpenBSD random number + generators, it introduces a number of its own innovations which improve + both security and performance. + + The Catacomb generator uses an optional secret key, which can provide + additional security against forward state compromise extension. It uses a + catastrophic reseeding operation to prevent a compromise yielding + information about past generator states. This operation works on + arbitrary-sized blocks of data, so the generator's output buffer can be + large. This minimizes the effect of the reseeding overhead. +\end{abstract} + +\tableofcontents + + +\section{The one-way transformation} + +The most novel part of the generator\footnote{I believe this construction to +be novel. If I'm wrong, let me know.} is the one-way transformation which is +used to allow pooled input data to affect the output buffer. + +Let $H$ be some collision-resistant hash function, and let $E_k$ be a +symmetric cipher with key $k$. Then I can define the one-way transformation +$T$ by +\[ T(x) = E_{H(x)}(x) \] + +I believe, although formal proof seems difficult, that an adversary in +posession of $T(x)$ and a portion of the original $x$ cannot reconstruct the +remainder of $x$ without breaking one of the cryptographic primitives (which +I assume is `difficult') or performing an exhaustive search of one of: the +space of the unknown portion of $x$, the range of the hash function $H$, or +the keyspace of the cipher $E$. + +A similar feat of cryptanalysis or exhaustive search seems necessary to work +in a forwards direction: given partial knowledge of both $x$ and $T(x)$, the +adversary cannot work out the remainder of either without trying every +possibility for one or the other unknown portions, or working through the +hash- or keyspace. + +A keyed version of $T$ may be defined, given a keyed hash (or MAC) $H_k$: +\[ T_k(x) = E_{H_k(x)}(x) \] +If this is done, the adversary cannot work forwards even with \emph{complete} +knowledge of $B$, or performing one of the obvious exhaustive searches. + + +\section{Description of the generator} + +The generator is divided into two parts: an \emph{input pool} which +accumulates random input data from the environment, and an \emph{output +buffer} which contains data to be passed to clients of the generator on +request. + +New information is contributed to the generator by mixing it with the input +pool, using a mixing function derived from the Linux random number generator +\cite{linux:devrandom}. The mixing function views the input pool as eight +parallel shift registers. Input data is added one octet at a time. Each bit +of an input octet is mixed with a different shift register. + +Formally, let $I$ be the input pool, with size $n_I$ bytes; let $P(x) = a_0 + +a_1 x + a_2 x^2 + \cdots + a_{n_I} x^{n_I}$ be a primitive polynomial in +$\mathrm{GF}(2^{n_I})$ with degree $n_I$; let $i$ be an integer such that $0 +\le i < n_I$, and $r$ be an integer such that $0 \le r < 8$; and let $x$ be +an input byte. The result of mixing $x$ with the pool $I$ is calculated as +follows: +\begin{eqlines*} + \begin{spliteqn*} + I'[8j + b] = + \begin{cases} + x\bigl[(r + b) \bmod 8\bigr] \xor + \bigxor_{0 \le k < n_I} + a_k I\bigl[8\bigl((j + k) \bmod n_I\bigr) + b\bigr] & if $i = j$ \\ + I[j + b] & otherwise + \end{cases} \\ + \textrm{for all integers $j$ and $b$ where $0 \le j < n_I$ and + $0 \le b < 8$} + \end{spliteqn*} + \\ + I \assign I' \qquad + i \assign (i + 1) \bmod n_I \qquad + r \assign (r + 5) \bmod 8 +\end{eqlines*} +Initially, $i$ and $r$ are both zero. The use of 8-bit bytes above is +arbitrary. + +Newly added data doesn't affect the output buffer until a `gating' operation +is performed. This uses the one-way transformation described earlier over +the entire generator state. + +Data requested by clients of the generator is read from the output buffer +$O$. Initially the buffer contains zeroes. + +\begin{thebibliography}{99} + +\bibitem{cp:rand} + J.~Kelsey, B.~Schneier, D.~Wagner, and C.~Hall, ``Cryptographic Attacks on + Pseudorandom Number Generators'', \emph{Fast Software Encryption, Fifth + International Workshop Proceedings (March 1998)}, Springer-Verlag, 1998, + pp. 168--188, \url{http://www.counterpane.com/pseudorandom_number.html} + +\bibitem{linux:devrandom} + T.~Ts'o, ``A string random number generator'', Linux sources, + \path{drivers/char/random.c}. + +\bibitem{mdw:devrandom} + M.~Wooding, ``Linux \path{/dev/random} generator security'', Usenet article + posted to \mtt{sci.crypt}, July 1998. + +\end{thebibliography} + +%%%----- That's all, folks -------------------------------------------------- + +\end{document} \ No newline at end of file diff --git a/paranoia.h b/paranoia.h new file mode 100644 index 0000000..b2d19d8 --- /dev/null +++ b/paranoia.h @@ -0,0 +1,59 @@ +/* -*-c-*- + * + * $Id: paranoia.h,v 1.1 1999/09/03 08:41:12 mdw Exp $ + * + * Macros and functions for cryptographic paranoia + * + * (c) 1998 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: paranoia.h,v $ + * Revision 1.1 1999/09/03 08:41:12 mdw + * Initial import. + * + */ + +#ifndef PARANOIA_H +#define PARANOIA_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#include + +/*----- Macros ------------------------------------------------------------*/ + +#define BURN(x) (memset(&(x), 0, sizeof(x))) + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/rand.c b/rand.c new file mode 100644 index 0000000..036ba4d --- /dev/null +++ b/rand.c @@ -0,0 +1,392 @@ +/* -*-c-*- + * + * $Id: rand.c,v 1.1 1999/09/03 08:41:12 mdw Exp $ + * + * Secure random number generator + * + * (c) 1998 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: rand.c,v $ + * Revision 1.1 1999/09/03 08:41:12 mdw + * Initial import. + * + */ + +/*----- Header files ------------------------------------------------------*/ + +#include +#include + +#include + +#include "blowfish-cbc.h" +#include "paranoia.h" +#include "rand.h" +#include "rmd160.h" +#include "rmd160-hmac.h" + +/*----- Static variables --------------------------------------------------*/ + +static rand_pool pool; /* Default random pool */ + +/*----- Macros ------------------------------------------------------------*/ + +#define RAND_RESOLVE(r) do { \ + if ((r) == RAND_GLOBAL) \ + (r) = &pool; \ +} while (0) + +#define TIMER(r) do { \ + if ((r)->s && (r)->s->timer) \ + (r)->s->timer(r); \ +} while (0) + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @rand_init@ --- * + * + * Arguments: @rand_pool *r@ = pointer to a randomness pool + * + * Returns: --- + * + * Use: Initializes a randomness pool. The pool doesn't start out + * very random: that's your job to sort out. + */ + +void rand_init(rand_pool *r) +{ + RAND_RESOLVE(r); + memset(r->pool, 0, sizeof(r->pool)); + memset(r->buf, 0, sizeof(r->buf)); + r->i = 0; + r->irot = 0; + r->ibits = r->obits = 0; + r->o = RAND_SECSZ; + r->s = 0; + rmd160_hmac(&r->k, 0, 0); + rand_gate(r); +} + +/* --- @rand_noisesrc@ --- * + * + * Arguments: @rand_pool *r@ = pointer to a randomness pool + * @const rand_source *s@ = pointer to source definition + * + * Returns: --- + * + * Use: Sets a noise source for a randomness pool. When the pool's + * estimate of good random bits falls to zero, the @getnoise@ + * function is called, passing the pool handle as an argument. + * It is expected to increase the number of good bits by at + * least one, because it'll be called over and over again until + * there are enough bits to satisfy the caller. The @timer@ + * function is called frequently throughout the generator's + * operation. + */ + +void rand_noisesrc(rand_pool *r, const rand_source *s) +{ + RAND_RESOLVE(r); + r->s = s; +} + +/* --- @rand_key@ --- * + * + * Arguments: @rand_pool *r@ = pointer to a randomness pool + * @const void *k@ = pointer to key data + * @size_t sz@ = size of key data + * + * Returns: --- + * + * Use: Sets the secret key for a randomness pool. The key is used + * when mixing in new random bits. + */ + +void rand_key(rand_pool *r, const void *k, size_t sz) +{ + RAND_RESOLVE(r); + rmd160_hmac(&r->k, k, sz); +} + +/* --- @rand_add@ --- * + * + * Arguments: @rand_pool *r@ = pointer to a randomness pool + * @const void *p@ = pointer a buffer of data to add + * @size_t sz@ = size of the data buffer + * @unsigned goodbits@ = number of good bits estimated in buffer + * + * Returns: --- + * + * Use: Mixes the data in the buffer with the contents of the + * pool. The estimate of the number of good bits is added to + * the pool's own count. The mixing operation is not + * cryptographically strong. However, data in the input pool + * isn't output directly, only through the one-way gating + * operation, so that shouldn't matter. + */ + +void rand_add(rand_pool *r, const void *p, size_t sz, unsigned goodbits) +{ + const octet *c = p; + int i, rot, mid; + +#if RAND_POOLSZ != 1279 +# error Polynomial in rand_add is out of date. Fix it. +#endif + + RAND_RESOLVE(r); + + i = r->i; rot = r->irot; mid = i + 418; + if (mid >= RAND_POOLSZ) mid -= RAND_POOLSZ; + + while (sz) { + octet o = *c++; + r->pool[i] ^= (ROL8(o, rot) ^ r->pool[mid]); + rot = (rot + 5) & 7; + i++; if (i >= RAND_POOLSZ) i -= RAND_POOLSZ; + mid++; if (mid >= RAND_POOLSZ) mid -= RAND_POOLSZ; + sz--; + } + + r->i = i; + r->irot = rot; + r->ibits += goodbits; + if (r->ibits > RAND_IBITS) + r->ibits = RAND_IBITS; +} + +/* --- @rand_goodbits@ --- * + * + * Arguments: @rand_pool *r@ = pointer to a randomness pool + * + * Returns: Estimate of the number of good bits remaining in the pool. + */ + +unsigned rand_goodbits(rand_pool *r) +{ + RAND_RESOLVE(r); + return (r->ibits + r->obits); +} + +/* --- @rand_gate@ --- * + * + * Arguments: @rand_pool *r@ = pointer to a randomness pool + * + * Returns: --- + * + * Use: Mixes up the entire state of the generator in a nonreversible + * way. + */ + +void rand_gate(rand_pool *r) +{ + octet mac[RMD160_HASHSZ]; + + RAND_RESOLVE(r); + TIMER(r); + + /* --- Hash up all the data in the pool --- */ + + { + rmd160_macctx mc; + + rmd160_macinit(&mc, &r->k); + rmd160_mac(&mc, r->pool, sizeof(r->pool)); + rmd160_mac(&mc, r->buf, sizeof(r->buf)); + rmd160_macdone(&mc, mac); + BURN(mc); + } + + /* --- Now mangle all of the data based on the hash --- */ + + { + blowfish_cbcctx bc; + + blowfish_cbcinit(&bc, mac, sizeof(mac), 0); + blowfish_cbcencrypt(&bc, r->pool, r->pool, sizeof(r->pool)); + blowfish_cbcencrypt(&bc, r->buf, r->buf, sizeof(r->buf)); + BURN(bc); + } + + /* --- Reset the various state variables --- */ + + r->o = RAND_SECSZ; + r->obits += r->ibits; + if (r->obits > RAND_OBITS) { + r->ibits = r->obits - r->ibits; + r->obits = RAND_OBITS; + } else + r->ibits = 0; + TIMER(r); +} + +/* --- @rand_stretch@ --- * + * + * Arguments: @rand_pool *r@ = pointer to a randomness pool + * + * Returns: --- + * + * Use: Stretches the contents of the output buffer by transforming + * it in a nonreversible way. This doesn't add any entropy + * worth speaking about, but it works well enough when the + * caller doesn't care about that sort of thing. + */ + +void rand_stretch(rand_pool *r) +{ + octet mac[RMD160_HASHSZ]; + + RAND_RESOLVE(r); + TIMER(r); + + /* --- Hash up all the data in the buffer --- */ + + { + rmd160_macctx mc; + + rmd160_macinit(&mc, &r->k); + rmd160_mac(&mc, r->pool, sizeof(r->pool)); + rmd160_mac(&mc, r->buf, sizeof(r->buf)); + rmd160_macdone(&mc, mac); + BURN(mc); + } + + /* --- Now mangle the buffer based on that hash --- */ + + { + blowfish_cbcctx bc; + + blowfish_cbcinit(&bc, mac, sizeof(mac), 0); + blowfish_cbcencrypt(&bc, r->buf, r->buf, sizeof(r->buf)); + BURN(bc); + } + + /* --- Reset the various state variables --- */ + + r->o = RAND_SECSZ; + TIMER(r); +} + +/* --- @rand_get@ --- * + * + * Arguments: @rand_pool *r@ = pointer to a randomness pool + * @void *p@ = pointer to output buffer + * @size_t sz@ = size of output buffer + * + * Returns: --- + * + * Use: Gets random data from the pool. The pool's contents can't be + * determined from the output of this function; nor can the + * output data be determined from a knowledge of the data input + * to the pool wihtout also having knowledge of the secret key. + * The good bits counter is decremented, although no special + * action is taken if it reaches zero. + */ + +void rand_get(rand_pool *r, void *p, size_t sz) +{ + octet *o = p; + + RAND_RESOLVE(r); + TIMER(r); + + if (!sz) + return; + for (;;) { + if (r->o + sz <= RAND_BUFSZ) { + memcpy(o, r->buf + r->o, sz); + r->o += sz; + break; + } else { + size_t chunk = RAND_BUFSZ - r->o; + if (chunk) { + memcpy(o, r->buf + r->o, chunk); + sz -= chunk; + o += chunk; + } + rand_stretch(r); + } + } + + if (r->obits > sz * 8) + r->obits -= sz * 8; + else + r->obits = 0; +} + +/* --- @rand_getgood@ --- * + * + * Arguments: @rand_pool *r@ = pointer to a randomness pool + * @void *p@ = pointer to output buffer + * @size_t sz@ = size of output buffer + * + * Returns: --- + * + * Use: Gets random data from the pool. The pool's contents can't be + * determined from the output of this function; nor can the + * output data be determined from a knowledge of the data input + * to the pool wihtout also having knowledge of the secret key. + * If a noise source is attached to the pool in question, it is + * called to replenish the supply of good bits in the pool; + * otherwise this call is equivalent to @rand_get@. + */ + +void rand_getgood(rand_pool *r, void *p, size_t sz) +{ + octet *o = p; + + RAND_RESOLVE(r); + + if (!sz) + return; + if (!r->s || !r->s->getnoise) { + rand_get(r, p, sz); + return; + } + TIMER(r); + + while (sz) { + size_t chunk = sz; + + if (chunk * 8 > r->obits) { + if (chunk * 8 > r->ibits + r->obits) + do r->s->getnoise(r); while (r->ibits + r->obits < 128); + rand_gate(r); + if (chunk * 8 > r->obits) + chunk = r->obits / 8; + } + + if (chunk + r->o > RAND_BUFSZ) + chunk = RAND_BUFSZ - r->o; + + memcpy(o, r->buf + r->o, chunk); + r->obits -= chunk * 8; + o += chunk; + sz -= chunk; + } +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/rand.h b/rand.h new file mode 100644 index 0000000..8ccd38c --- /dev/null +++ b/rand.h @@ -0,0 +1,270 @@ +/* -*-c-*- + * + * $Id: rand.h,v 1.1 1999/09/03 08:41:12 mdw Exp $ + * + * Secure random number generator + * + * (c) 1999 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: rand.h,v $ + * Revision 1.1 1999/09/03 08:41:12 mdw + * Initial import. + * + */ + +/*----- Notes on the random number generator ------------------------------* + * + * The algorithm is one of the author's own devising. It may therefore be + * worth a certain amount of skepticism. However, I've thought about this + * method for over a year before actually considering it worth implementing. + * With a little bit of luck, it should have received some peer review by the + * time this code is actually properly released, and it'll be worth a bit + * more confidence. My earlier generator was very similar in structure to + * the Linux /dev/random device. This generator is intended to address + * concerns I expressed about the Linux generator in a Usenet article to + * sci.crypt. + * + * The generator is divided into two parts: an input pool and an outpu + * buffer. New random data is placed into the pool in the way described + * below, which is shamelessly stolen from the Linux /dev/random generator. + * The only interaction that the pool has on the output buffer is through the + * keyed `gating' operation, which mixes up and redistributes all of the + * generator's state in an irreversible manner. Random bytes, when + * requested, are extracted from the output buffer in a linear fashion. + * + * The input pool is best seen as being eight shift registers in parallel. + * Data is added to the pool one octet at a time. Each bit of a new octet is + * added to a different shift register, by adding it (mod 2) with other bits + * according to the coefficients of a primitive polynomial. Each new byte is + * rotated before being added into the pool, in a half-hearted attempt to + * protect against biases in the input data (e.g., top bits being clear on + * ASCII text). + * + * The gating operation takes a keyed hash of the entire generator state, + * uses it as the key for a symmetric cipher, and encrypts the state. The + * key is then discarded. The result is that every ouptut bit of the + * operation depends in a complex way on every input bit, but the operation + * cannot be reversed. + * + * As an added wrinkle, 160 bits of the output buffer are never actually + * output. They are used in the gating operation only, as an extra item that + * an adversary has to guess before predicting generator output. + */ + +#ifndef RAND_H +#define RAND_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#include + +#include "rmd160-hmac.h" + +/*----- Magic numbers -----------------------------------------------------*/ + +#define RAND_POOLSZ 1279 /* Input pool size in bytes */ +#define RAND_BUFSZ 1024 /* Output buffer size in bytes */ +#define RAND_SECSZ 20 /* Secret octets in output buffer */ + +#define RAND_IBITS (RAND_POOLSZ * 8) +#define RAND_OBITS (RAND_BUFSZ * 8) + +/*----- Data structures ---------------------------------------------------*/ + +/* --- A random number generator pool --- */ + +typedef struct rand_pool { + octet pool[RAND_POOLSZ]; /* Actual contents of the pool */ + unsigned i; /* Current index into pool */ + unsigned irot; /* Current rotation applied */ + unsigned ibits; /* Number of good bits in pool */ + octet buf[RAND_BUFSZ]; /* Random octet output buffer */ + unsigned o; /* Current index into buffer */ + unsigned obits; /* Number of good bits in buffer */ + rmd160_mackey k; /* Secret key for this pool */ + const struct rand_source *s; /* System-specific noise source */ +} rand_pool; + +#define RAND_GLOBAL ((rand_pool *)0) /* The global randomness pool */ + +/* --- A noise source --- */ + +typedef struct rand_source { + void (*getnoise)(rand_pool */*r*/); /* Acquire more noise */ + int (*timer)(rand_pool */*r*/); /* Get noise from current time */ +} rand_source; + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @rand_init@ --- * + * + * Arguments: @rand_pool *r@ = pointer to a randomness pool + * + * Returns: --- + * + * Use: Initializes a randomness pool. The pool doesn't start out + * very random: that's your job to sort out. + */ + +extern void rand_init(rand_pool */*r*/); + +/* --- @rand_noisesrc@ --- * + * + * Arguments: @rand_pool *r@ = pointer to a randomness pool + * @const rand_source *s@ = pointer to source definition + * + * Returns: --- + * + * Use: Sets a noise source for a randomness pool. When the pool's + * estimate of good random bits falls to zero, the @getnoise@ + * function is called, passing the pool handle as an argument. + * It is expected to increase the number of good bits by at + * least one, because it'll be called over and over again until + * there are enough bits to satisfy the caller. The @timer@ + * function is called frequently throughout the generator's + * operation. + */ + +extern void rand_noisesrc(rand_pool */*r*/, const rand_source */*s*/); + +/* --- @rand_key@ --- * + * + * Arguments: @rand_pool *r@ = pointer to a randomness pool + * @const void *k@ = pointer to key data + * @size_t sz@ = size of key data + * + * Returns: --- + * + * Use: Sets the secret key for a randomness pool. The key is used + * when mixing in new random bits. + */ + +extern void rand_key(rand_pool */*r*/, const void */*k*/, size_t /*sz*/); + +/* --- @rand_add@ --- * + * + * Arguments: @rand_pool *r@ = pointer to a randomness pool + * @const void *p@ = pointer a buffer of data to add + * @size_t sz@ = size of the data buffer + * @unsigned goodbits@ = number of good bits estimated in buffer + * + * Returns: --- + * + * Use: Mixes the data in the buffer with the contents of the + * pool. The estimate of the number of good bits is added to + * the pool's own count. The mixing operation is not + * cryptographically strong. However, data in the input pool + * isn't output directly, only through the one-way gating + * operation, so that shouldn't matter. + */ + +extern void rand_add(rand_pool */*r*/, + const void */*p*/, size_t /*sz*/, + unsigned /*goodbits*/); + +/* --- @rand_goodbits@ --- * + * + * Arguments: @rand_pool *r@ = pointer to a randomness pool + * + * Returns: Estimate of the number of good bits remaining in the pool. + */ + +extern unsigned rand_goodbits(rand_pool */*r*/); + +/* --- @rand_gate@ --- * + * + * Arguments: @rand_pool *r@ = pointer to a randomness pool + * + * Returns: --- + * + * Use: Mixes up the entire state of the generator in a nonreversible + * way. + */ + +extern void rand_gate(rand_pool */*r*/); + +/* --- @rand_stretch@ --- * + * + * Arguments: @rand_pool *r@ = pointer to a randomness pool + * + * Returns: --- + * + * Use: Stretches the contents of the output buffer by transforming + * it in a nonreversible way. This doesn't add any entropy + * worth speaking about, but it works well enough when the + * caller doesn't care about that sort of thing. + */ + +extern void rand_stretch(rand_pool */*r*/); + +/* --- @rand_get@ --- * + * + * Arguments: @rand_pool *r@ = pointer to a randomness pool + * @void *p@ = pointer to output buffer + * @size_t sz@ = size of output buffer + * + * Returns: --- + * + * Use: Gets random data from the pool. The pool's contents can't be + * determined from the output of this function; nor can the + * output data be determined from a knowledge of the data input + * to the pool wihtout also having knowledge of the secret key. + * The good bits counter is decremented, although no special + * action is taken if it reaches zero. + */ + +extern void rand_get(rand_pool */*r*/, void */*p*/, size_t /*sz*/); + +/* --- @rand_getgood@ --- * + * + * Arguments: @rand_pool *r@ = pointer to a randomness pool + * @void *p@ = pointer to output buffer + * @size_t sz@ = size of output buffer + * + * Returns: --- + * + * Use: Gets random data from the pool. The pool's contents can't be + * determined from the output of this function; nor can the + * output data be determined from a knowledge of the data input + * to the pool wihtout also having knowledge of the secret key. + * If a noise source is attached to the pool in question, it is + * called to replenish the supply of good bits in the pool; + * otherwise this call is equivalent to @rand_get@. + */ + +extern void rand_getgood(rand_pool */*r*/, void */*p*/, size_t /*sz*/); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/rc4.c b/rc4.c new file mode 100644 index 0000000..6417635 --- /dev/null +++ b/rc4.c @@ -0,0 +1,197 @@ +/* -*-c-*- + * + * $Id: rc4.c,v 1.1 1999/09/03 08:41:12 mdw Exp $ + * + * The alleged RC4 stream cipher + * + * (c) 1999 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: rc4.c,v $ + * Revision 1.1 1999/09/03 08:41:12 mdw + * Initial import. + * + */ + +/*----- Header files ------------------------------------------------------*/ + +#include +#include + +#include + +#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 +#include + +#include +#include + +static int v_encrypt(dstr *v) +{ + rc4_ctx ctx; + dstr d = DSTR_INIT; + int ok = 1; + + rc4_init(&ctx, v[0].buf, v[0].len); + dstr_ensure(&d, v[1].len); + d.len = v[1].len; + rc4_encrypt(&ctx, v[1].buf, d.buf, d.len); + + if (memcmp(v[2].buf, d.buf, d.len) != 0) { + ok = 0; + printf("\nfail encryption:" + "\n\tkey = "); + type_hex.dump(&v[0], stdout); + printf("\n\tplaintext = "); type_hex.dump(&v[1], stdout); + printf("\n\texpected = "); type_hex.dump(&v[2], stdout); + printf("\n\tcalculated = "); type_hex.dump(&d, stdout); + putchar('\n'); + } + + return (ok); +} + +static int v_generate(dstr *v) +{ + rc4_ctx ctx; + dstr d = DSTR_INIT; + int ok = 1; + + rc4_init(&ctx, v[0].buf, v[0].len); + rc4_encrypt(&ctx, 0, 0, *(int *)v[1].buf); + dstr_ensure(&d, v[2].len); + d.len = v[2].len; + rc4_encrypt(&ctx, 0, d.buf, d.len); + + if (memcmp(v[2].buf, d.buf, d.len) != 0) { + ok = 0; + printf("\nfail generation:" + "\n\tkey = "); + type_hex.dump(&v[0], stdout); + printf("\n\tskip len = %i", *(int *)v[1].buf); + printf("\n\texpected = "); type_hex.dump(&v[2], stdout); + printf("\n\tcalculated = "); type_hex.dump(&d, stdout); + putchar('\n'); + } + + return (ok); +} + +static test_chunk defs[] = { + { "rc4-encrypt", v_encrypt, { &type_hex, &type_hex, &type_hex, 0 } }, + { "rc4-generate", v_generate, { &type_hex, &type_int, &type_hex, 0 } }, + { 0, 0, { 0 } } +}; + +int main(int argc, char *argv[]) +{ + test_run(argc, argv, defs, SRCDIR"/tests/rc4"); + return (0); +} + +#endif + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/rc4.h b/rc4.h new file mode 100644 index 0000000..adaf731 --- /dev/null +++ b/rc4.h @@ -0,0 +1,160 @@ +/* -*-c-*- + * + * $Id: rc4.h,v 1.1 1999/09/03 08:41:12 mdw Exp $ + * + * The alleged RC4 stream cipher + * + * (c) 1999 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: rc4.h,v $ + * Revision 1.1 1999/09/03 08:41:12 mdw + * Initial import. + * + */ + +/*----- Notes on RC4 ------------------------------------------------------* + * + * RC4 is a stream cipher desgigned by Ron Rivest. For a while RC4 was a + * trade secret of RSA Data Security, Inc., but somehow source code for a + * cipher which interworks with RC4 was posted to the Cypherpunks mailing + * list. + */ + +#ifndef RC4_H +#define RC4_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#include + +#include + +/*----- Data structures ---------------------------------------------------*/ + +typedef struct rc4_ctx { + unsigned i, j; /* Indices into the @S@ table */ + unsigned f; /* Flags word */ + octet s[256]; /* The ever-changing @S@ table */ +} rc4_ctx; + +#define RC4F_OPEN 1u + +/*----- Macros ------------------------------------------------------------*/ + +/* --- @RC4_OPEN@ --- * + * + * Arguments: @ctx@ = pointer to an RC4 context + * @guts@ = code to perform within the RC4 context + * + * Use: Performs some code within an RC4 context. Some of the + * parameters are extracted from the context and held in local + * variables for speed. Multiple calls to @RC4_BYTE@ may be + * made within the open context. A context must only be + * opened once at a time. + */ + +#define RC4_OPEN(ctx, guts) do { \ + unsigned _rc4_i = (ctx)->i; \ + unsigned _rc4_j = (ctx)->j; \ + octet *_rc4_s = (ctx)->s; \ + \ + assert(((void)"RC4 context may only be opened once at a time", \ + ((ctx)->f & RC4F_OPEN) == 0)); \ + (ctx)->f |= RC4F_OPEN; \ + \ + guts \ + \ + (ctx)->f &= ~RC4F_OPEN; \ + (ctx)->i = _rc4_i; \ + (ctx)->j = _rc4_j; \ +} while (0) + +/* --- @RC4_BYTE@ --- * + * + * Arguments: @x@ = output variable to set + * + * Use: Extracts an octet from the lexically innermost open RC4 + * context and places it in the variable @x@. + */ + +#define RC4_BYTE(x) do { \ + unsigned _si, _sj; \ + _rc4_i = (_rc4_i + 1) & 0xff; \ + _si = _rc4_s[_rc4_i]; \ + _rc4_j = (_rc4_j + _si) & 0xff; \ + _sj = _rc4_s[_rc4_j]; \ + _rc4_s[_rc4_i] = _sj; \ + _rc4_s[_rc4_j] = _si; \ + (x) = _rc4_s[(_si + _sj) & 0xff]; \ +} while (0) + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @rc4_init@ --- * + * + * Arguments: @rc4_ctx *ctx@ = pointer to context to initialize + * @const void *k@ = pointer to key data to use + * @size_t sz@ = size of the key data + * + * Returns: --- + * + * Use: Initializes an RC4 context ready for use. + */ + +extern void rc4_init(rc4_ctx */*ctx*/, const void */*k*/, size_t /*sz*/); + +/* --- @rc4_encrypt@ --- * + * + * Arguments: @rc4_ctx *ctx@ = pointer to context to use + * @const void *src@ = pointer to the source block + * @void *dest@ = pointer to the destination block + * @size_t sz@ = size of the block + * + * Returns: --- + * + * Use: Encrypts or decrypts a block of data. The destination may + * be null to just grind the generator around for a while. It's + * recommended that you say `@rc4_encrypt(&ctx, 0, 0, 1024)@' + * after initializing a new context, to prevent keystream + * guessing attacks. The source may be null to just extract a + * big lump of data from the generator. + */ + +extern void rc4_encrypt(rc4_ctx */*ctx*/, + const void */*src*/, void */*dest*/, + size_t /*sz*/); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/rc5.c b/rc5.c new file mode 100644 index 0000000..5d03be7 --- /dev/null +++ b/rc5.c @@ -0,0 +1,235 @@ +/* -*-c-*- + * + * $Id: rc5.c,v 1.1 1999/09/03 08:41:12 mdw Exp $ + * + * The RC5-32/12 block cipher + * + * (c) 1999 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: rc5.c,v $ + * Revision 1.1 1999/09/03 08:41:12 mdw + * Initial import. + * + */ + +/*----- Header files ------------------------------------------------------*/ + +#include +#include +#include + +#include +#include + +#include "blkc.h" +#include "rc5.h" + +/*----- Internal magical constants ----------------------------------------*/ + +#define RC5_ROUNDS 12 + +#define T ((RC5_ROUNDS + 1) * 2) +#define P 0xb7e15163 +#define Q 0x9e3779b9 + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @rc5_init@ --- * + * + * Arguments: @rc5_ctx *k@ = pointer to a key block + * @const void *sbuf@ = pointer to key material + * @size_t sz@ = size of the key material + * + * Returns: --- + * + * Use: Initializes an RC5 key block. + */ + +void rc5_init(rc5_ctx *k, const void *sbuf, size_t sz) +{ + uint32 *l; + size_t w; + + /* --- Set up the @L@ table --- * + * + * This is slightly unfortunately defined. + */ + + assert(((void)"RC5 does not support zero-length keys", sz != 0)); + + { + int i; + const octet *p = sbuf; + + /* --- Create the buffer --- */ + + w = (sz + 3) / 4; + l = xmalloc(w * sizeof(uint32)); + + /* --- Extract the key material --- */ + + for (i = 0; sz > 3; i++) { + l[i] = LOAD32_L(p); + p += 4; + sz -= 4; + } + + /* --- Fix up the tail end --- */ + + if (sz) { + uint32 x = U8(*p++); + if (sz > 1) x |= (U8(*p++) << 8); + if (sz > 2) x |= (U8(*p++) << 16); + l[i] = x; + } + } + + /* --- Initialize the @S@ table --- */ + + { + int i; + + k->s[0] = P; + for (i = 1; i < T; i++) + k->s[i] = k->s[i - 1] + Q; + } + + /* --- Mix in the key --- */ + + { + int m = 3 * (w > T ? w : T); + int i, j, c; + uint32 a, b; + + for (c = i = j = a = b = 0; c < m; c++) { + uint32 x; + + x = k->s[i] + a + b; + k->s[i] = a = ROL32(x, 3); + i++; if (i >= T) i = 0; + + x = l[j] + a + b; + l[j] = b = ROL32(x, a + b); + j++; if (j >= w) j = 0; + } + } + + free(l); +} + +/* --- @EROUND@, @DROUND@ --- */ + +#define EROUND(x, y, k) do { \ + uint32 _x; \ + _x = x ^ y; x = ROL32(_x, y) + k[0]; \ + _x = y ^ x; y = ROL32(_x, x) + k[1]; \ + k += 2; \ +} while (0) + +#define DROUND(x, y, k) do { \ + uint32 _x; \ + k -= 2; \ + _x = y - k[1]; y = ROR32(_x, x) ^ x; \ + _x = x - k[0]; x = ROR32(_x, y) ^ y; \ +} while (0) + +/* --- @EBLK@, @DBLK@ --- */ + +#define EBLK(a, b, c, d, k) do { \ + uint32 _l, _r; \ + const uint32 *_k = (k)->s; \ + \ + _l = (a) + _k[0]; \ + _r = (b) + _k[1]; \ + _k += 2; \ + \ + EROUND(_l, _r, _k); \ + EROUND(_l, _r, _k); \ + EROUND(_l, _r, _k); \ + EROUND(_l, _r, _k); \ + EROUND(_l, _r, _k); \ + EROUND(_l, _r, _k); \ + EROUND(_l, _r, _k); \ + EROUND(_l, _r, _k); \ + EROUND(_l, _r, _k); \ + EROUND(_l, _r, _k); \ + EROUND(_l, _r, _k); \ + EROUND(_l, _r, _k); \ + (c) = _l; \ + (d) = _r; \ +} while (0) + +#define DBLK(a, b, c, d, k) do { \ + uint32 _l, _r; \ + const uint32 *_k = (k)->s + T; \ + \ + _l = (a); \ + _r = (b); \ + \ + DROUND(_l, _r, _k); \ + DROUND(_l, _r, _k); \ + DROUND(_l, _r, _k); \ + DROUND(_l, _r, _k); \ + DROUND(_l, _r, _k); \ + DROUND(_l, _r, _k); \ + DROUND(_l, _r, _k); \ + DROUND(_l, _r, _k); \ + DROUND(_l, _r, _k); \ + DROUND(_l, _r, _k); \ + DROUND(_l, _r, _k); \ + DROUND(_l, _r, _k); \ + \ + _k -= 2; \ + (d) = _r - _k[1]; \ + (c) = _l - _k[0]; \ +} while (0) + +/* --- @rc5_eblk@, @rc5_dblk@ --- * + * + * Arguments: @const rc5_ctx *k@ = pointer to RC5 context block + * @const uint32 s[2]@ = pointer to source block + * @uint32 *d[2]@ = pointer to destination block + * + * Returns: --- + * + * Use: Low level block encryption and decryption. + */ + +void rc5_eblk(const rc5_ctx *k, const uint32 *s, uint32 *d) +{ + EBLK(s[0], s[1], d[0], d[1], k); +} + +void rc5_dblk(const rc5_ctx *k, const uint32 *s, uint32 *d) +{ + DBLK(s[0], s[1], d[0], d[1], k); +} + +/* --- Test rig --- */ + +BLKC_TEST(RC5, rc5) + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/rc5.h b/rc5.h new file mode 100644 index 0000000..23e6080 --- /dev/null +++ b/rc5.h @@ -0,0 +1,99 @@ +/* -*-c-*- + * + * $Id: rc5.h,v 1.1 1999/09/03 08:41:12 mdw Exp $ + * + * The RC5-32/12 block cipher + * + * (c) 1999 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: rc5.h,v $ + * Revision 1.1 1999/09/03 08:41:12 mdw + * Initial import. + * + */ + +#ifndef RC5_H +#define RC5_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#include + +/*----- Magic numbers -----------------------------------------------------*/ + +#define RC5_ROUNDS 12 +#define RC5_KEYSZ 0 +#define RC5_BLKSZ 8 +#define RC5_CLASS (N, L, 64) + +/*----- Data structures ---------------------------------------------------*/ + +typedef struct rc5_ctx { + uint32 s[2 * (RC5_ROUNDS + 1)]; +} rc5_ctx; + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @rc5_init@ --- * + * + * Arguments: @rc5_ctx *k@ = pointer to a key block + * @const void *sbuf@ = pointer to key material + * @size_t sz@ = size of the key material + * + * Returns: --- + * + * Use: Initializes an RC5 key block. + */ + +extern void rc5_init(rc5_ctx */*k*/, const void */*sbuf*/, size_t /*sz*/); + +/* --- @rc5_eblk@, @rc5_dblk@ --- * + * + * Arguments: @const rc5_ctx *k@ = pointer to RC5 context block + * @const uint32 s[2]@ = pointer to source block + * @uint32 *d[2]@ = pointer to destination block + * + * Returns: --- + * + * Use: Low level block encryption and decryption. + */ + +extern void rc5_eblk(const rc5_ctx */*k*/, + const uint32 */*s*/, uint32 */*d*/); +extern void rc5_dblk(const rc5_ctx */*k*/, + const uint32 */*s*/, uint32 */*d*/); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/rmd160.c b/rmd160.c new file mode 100644 index 0000000..a42c21c --- /dev/null +++ b/rmd160.c @@ -0,0 +1,402 @@ +/* -*-c-*- + * + * $Id: rmd160.c,v 1.1 1999/09/03 08:41:12 mdw Exp $ + * + * The RIPEMD-160 message digest function + * + * (c) 1998 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: rmd160.c,v $ + * Revision 1.1 1999/09/03 08:41:12 mdw + * Initial import. + * + */ + +/*----- Header files ------------------------------------------------------*/ + +#include + +#include "hash.h" +#include "rmd160.h" + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @rmd160_compress@ --- * + * + * Arguments: @rmd160_ctx *ctx@ = pointer to context block + * @const void *sbuf@ = pointer to buffer of appropriate size + * + * Returns: --- + * + * Use: RIPEMD-160 compression function. + */ + +void rmd160_compress(rmd160_ctx *ctx, const void *sbuf) +{ + uint32 a, b, c, d, e; + uint32 A, B, C, D, E; + uint32 buf[16]; + + /* --- Fetch the chaining variables --- */ + + a = A = ctx->a; + b = B = ctx->b; + c = C = ctx->c; + d = D = ctx->d; + e = E = ctx->e; + + /* --- Fetch the buffer contents --- */ + + { + int i; + const octet *p; + + for (i = 0, p = sbuf; i < 16; i++, p += 4) + buf[i] = LOAD32_L(p); + } + + /* --- Definitions for round functions --- */ + +#define F(x, y, z) ((x) ^ (y) ^ (z)) +#define G(x, y, z) (((x) & (y)) | (~(x) & (z))) +#define H(x, y, z) (((x) | ~(y)) ^ (z)) +#define I(x, y, z) (((x) & (z)) | ((y) & ~(z))) +#define J(x, y, z) ((x) ^ ((y) | ~(z))) + +#define T(v, w, x, y, z, i, r, f, k) do { \ + uint32 _t = v + f(w, x, y) + buf[i] + k; \ + v = ROL32(_t, r) + z; x = ROL32(x, 10); \ +} while (0) + +#define F1(v, w, x, y, z, i, r) T(v, w, x, y, z, i, r, F, 0x00000000) +#define G1(v, w, x, y, z, i, r) T(v, w, x, y, z, i, r, G, 0x5a827999) +#define H1(v, w, x, y, z, i, r) T(v, w, x, y, z, i, r, H, 0x6ed9eba1) +#define I1(v, w, x, y, z, i, r) T(v, w, x, y, z, i, r, I, 0x8f1bbcdc) +#define J1(v, w, x, y, z, i, r) T(v, w, x, y, z, i, r, J, 0xa953fd4e) + +#define F2(v, w, x, y, z, i, r) T(v, w, x, y, z, i, r, J, 0x50a28be6) +#define G2(v, w, x, y, z, i, r) T(v, w, x, y, z, i, r, I, 0x5c4dd124) +#define H2(v, w, x, y, z, i, r) T(v, w, x, y, z, i, r, H, 0x6d703ef3) +#define I2(v, w, x, y, z, i, r) T(v, w, x, y, z, i, r, G, 0x7a6d76e9) +#define J2(v, w, x, y, z, i, r) T(v, w, x, y, z, i, r, F, 0x00000000) + + /* --- First the left hand side --- */ + + F1(a, b, c, d, e, 0, 11); + F1(e, a, b, c, d, 1, 14); + F1(d, e, a, b, c, 2, 15); + F1(c, d, e, a, b, 3, 12); + F1(b, c, d, e, a, 4, 5); + F1(a, b, c, d, e, 5, 8); + F1(e, a, b, c, d, 6, 7); + F1(d, e, a, b, c, 7, 9); + F1(c, d, e, a, b, 8, 11); + F1(b, c, d, e, a, 9, 13); + F1(a, b, c, d, e, 10, 14); + F1(e, a, b, c, d, 11, 15); + F1(d, e, a, b, c, 12, 6); + F1(c, d, e, a, b, 13, 7); + F1(b, c, d, e, a, 14, 9); + F1(a, b, c, d, e, 15, 8); + + G1(e, a, b, c, d, 7, 7); + G1(d, e, a, b, c, 4, 6); + G1(c, d, e, a, b, 13, 8); + G1(b, c, d, e, a, 1, 13); + G1(a, b, c, d, e, 10, 11); + G1(e, a, b, c, d, 6, 9); + G1(d, e, a, b, c, 15, 7); + G1(c, d, e, a, b, 3, 15); + G1(b, c, d, e, a, 12, 7); + G1(a, b, c, d, e, 0, 12); + G1(e, a, b, c, d, 9, 15); + G1(d, e, a, b, c, 5, 9); + G1(c, d, e, a, b, 2, 11); + G1(b, c, d, e, a, 14, 7); + G1(a, b, c, d, e, 11, 13); + G1(e, a, b, c, d, 8, 12); + + H1(d, e, a, b, c, 3, 11); + H1(c, d, e, a, b, 10, 13); + H1(b, c, d, e, a, 14, 6); + H1(a, b, c, d, e, 4, 7); + H1(e, a, b, c, d, 9, 14); + H1(d, e, a, b, c, 15, 9); + H1(c, d, e, a, b, 8, 13); + H1(b, c, d, e, a, 1, 15); + H1(a, b, c, d, e, 2, 14); + H1(e, a, b, c, d, 7, 8); + H1(d, e, a, b, c, 0, 13); + H1(c, d, e, a, b, 6, 6); + H1(b, c, d, e, a, 13, 5); + H1(a, b, c, d, e, 11, 12); + H1(e, a, b, c, d, 5, 7); + H1(d, e, a, b, c, 12, 5); + + I1(c, d, e, a, b, 1, 11); + I1(b, c, d, e, a, 9, 12); + I1(a, b, c, d, e, 11, 14); + I1(e, a, b, c, d, 10, 15); + I1(d, e, a, b, c, 0, 14); + I1(c, d, e, a, b, 8, 15); + I1(b, c, d, e, a, 12, 9); + I1(a, b, c, d, e, 4, 8); + I1(e, a, b, c, d, 13, 9); + I1(d, e, a, b, c, 3, 14); + I1(c, d, e, a, b, 7, 5); + I1(b, c, d, e, a, 15, 6); + I1(a, b, c, d, e, 14, 8); + I1(e, a, b, c, d, 5, 6); + I1(d, e, a, b, c, 6, 5); + I1(c, d, e, a, b, 2, 12); + + J1(b, c, d, e, a, 4, 9); + J1(a, b, c, d, e, 0, 15); + J1(e, a, b, c, d, 5, 5); + J1(d, e, a, b, c, 9, 11); + J1(c, d, e, a, b, 7, 6); + J1(b, c, d, e, a, 12, 8); + J1(a, b, c, d, e, 2, 13); + J1(e, a, b, c, d, 10, 12); + J1(d, e, a, b, c, 14, 5); + J1(c, d, e, a, b, 1, 12); + J1(b, c, d, e, a, 3, 13); + J1(a, b, c, d, e, 8, 14); + J1(e, a, b, c, d, 11, 11); + J1(d, e, a, b, c, 6, 8); + J1(c, d, e, a, b, 15, 5); + J1(b, c, d, e, a, 13, 6); + + /* --- And then the right hand side --- */ + + F2(A, B, C, D, E, 5, 8); + F2(E, A, B, C, D, 14, 9); + F2(D, E, A, B, C, 7, 9); + F2(C, D, E, A, B, 0, 11); + F2(B, C, D, E, A, 9, 13); + F2(A, B, C, D, E, 2, 15); + F2(E, A, B, C, D, 11, 15); + F2(D, E, A, B, C, 4, 5); + F2(C, D, E, A, B, 13, 7); + F2(B, C, D, E, A, 6, 7); + F2(A, B, C, D, E, 15, 8); + F2(E, A, B, C, D, 8, 11); + F2(D, E, A, B, C, 1, 14); + F2(C, D, E, A, B, 10, 14); + F2(B, C, D, E, A, 3, 12); + F2(A, B, C, D, E, 12, 6); + + G2(E, A, B, C, D, 6, 9); + G2(D, E, A, B, C, 11, 13); + G2(C, D, E, A, B, 3, 15); + G2(B, C, D, E, A, 7, 7); + G2(A, B, C, D, E, 0, 12); + G2(E, A, B, C, D, 13, 8); + G2(D, E, A, B, C, 5, 9); + G2(C, D, E, A, B, 10, 11); + G2(B, C, D, E, A, 14, 7); + G2(A, B, C, D, E, 15, 7); + G2(E, A, B, C, D, 8, 12); + G2(D, E, A, B, C, 12, 7); + G2(C, D, E, A, B, 4, 6); + G2(B, C, D, E, A, 9, 15); + G2(A, B, C, D, E, 1, 13); + G2(E, A, B, C, D, 2, 11); + + H2(D, E, A, B, C, 15, 9); + H2(C, D, E, A, B, 5, 7); + H2(B, C, D, E, A, 1, 15); + H2(A, B, C, D, E, 3, 11); + H2(E, A, B, C, D, 7, 8); + H2(D, E, A, B, C, 14, 6); + H2(C, D, E, A, B, 6, 6); + H2(B, C, D, E, A, 9, 14); + H2(A, B, C, D, E, 11, 12); + H2(E, A, B, C, D, 8, 13); + H2(D, E, A, B, C, 12, 5); + H2(C, D, E, A, B, 2, 14); + H2(B, C, D, E, A, 10, 13); + H2(A, B, C, D, E, 0, 13); + H2(E, A, B, C, D, 4, 7); + H2(D, E, A, B, C, 13, 5); + + I2(C, D, E, A, B, 8, 15); + I2(B, C, D, E, A, 6, 5); + I2(A, B, C, D, E, 4, 8); + I2(E, A, B, C, D, 1, 11); + I2(D, E, A, B, C, 3, 14); + I2(C, D, E, A, B, 11, 14); + I2(B, C, D, E, A, 15, 6); + I2(A, B, C, D, E, 0, 14); + I2(E, A, B, C, D, 5, 6); + I2(D, E, A, B, C, 12, 9); + I2(C, D, E, A, B, 2, 12); + I2(B, C, D, E, A, 13, 9); + I2(A, B, C, D, E, 9, 12); + I2(E, A, B, C, D, 7, 5); + I2(D, E, A, B, C, 10, 15); + I2(C, D, E, A, B, 14, 8); + + J2(B, C, D, E, A, 12, 8); + J2(A, B, C, D, E, 15, 5); + J2(E, A, B, C, D, 10, 12); + J2(D, E, A, B, C, 4, 9); + J2(C, D, E, A, B, 1, 12); + J2(B, C, D, E, A, 5, 5); + J2(A, B, C, D, E, 8, 14); + J2(E, A, B, C, D, 7, 6); + J2(D, E, A, B, C, 6, 8); + J2(C, D, E, A, B, 2, 13); + J2(B, C, D, E, A, 13, 6); + J2(A, B, C, D, E, 14, 5); + J2(E, A, B, C, D, 0, 15); + J2(D, E, A, B, C, 3, 13); + J2(C, D, E, A, B, 9, 11); + J2(B, C, D, E, A, 11, 11); + + /* --- Recombine the two halves --- */ + + { + uint32 + tmp = ctx->b + c + D; + ctx->b = ctx->c + d + E; + ctx->c = ctx->d + e + A; + ctx->d = ctx->e + a + B; + ctx->e = ctx->a + b + C; + ctx->a = tmp; + } +} + +/* --- @rmd160_init@ --- * + * + * Arguments: @rmd160_ctx *ctx@ = pointer to context block to initialize + * + * Returns: --- + * + * Use: Initializes a context block ready for hashing. + */ + +void rmd160_init(rmd160_ctx *ctx) +{ + ctx->a = 0x67452301; + ctx->b = 0xefcdab89; + ctx->c = 0x98badcfe; + ctx->d = 0x10325476; + ctx->e = 0xc3d2e1f0; + ctx->off = 0; + ctx->count = 0; +} + +/* --- @rmd160_set@ --- * + * + * Arguments: @rmd160_ctx *ctx@ = pointer to context block + * @const void *buf@ = pointer to state buffer + * @unsigned long count@ = current count of bytes processed + * + * Returns: --- + * + * Use: Initializes a context block from a given state. This is + * useful in cases where the initial hash state is meant to be + * secret, e.g., for NMAC and HMAC support. + */ + +void rmd160_set(rmd160_ctx *ctx, const void *buf, unsigned long count) +{ + const octet *p = buf; + ctx->a = LOAD32_L(p + 0); + ctx->b = LOAD32_L(p + 4); + ctx->c = LOAD32_L(p + 8); + ctx->d = LOAD32_L(p + 12); + ctx->e = LOAD32_L(p + 16); + ctx->off = 0; + ctx->count = count; +} + +/* --- @rmd160_hash@ --- * + * + * Arguments: @rmd160_ctx *ctx@ = pointer to context block + * @const void *buf@ = buffer of data to hash + * @size_t sz@ = size of buffer to hash + * + * Returns: --- + * + * Use: Hashes a buffer of data. The buffer may be of any size and + * alignment. + */ + +void rmd160_hash(rmd160_ctx *ctx, const void *buf, size_t sz) +{ + HASH_BUFFER(RMD160, rmd160, ctx, buf, sz); +} + +/* --- @rmd160_done@ --- * + * + * Arguments: @rmd160_ctx *ctx@ = pointer to context block + * @void *hash@ = pointer to output buffer + * + * Returns: --- + * + * Use: Returns the hash of the data read so far. + */ + +void rmd160_done(rmd160_ctx *ctx, void *hash) +{ + octet *p = hash; + HASH_MD5STRENGTH(RMD160, rmd160, ctx); + STORE32_L(p + 0, ctx->a); + STORE32_L(p + 4, ctx->b); + STORE32_L(p + 8, ctx->c); + STORE32_L(p + 12, ctx->d); + STORE32_L(p + 16, ctx->e); +} + +/* --- @rmd160_state@ --- * + * + * Arguments: @rmd160_ctx *ctx@ = pointer to context + * @void *state@ = pointer to buffer for current state + * + * Returns: Number of bytes written to the hash function so far. + * + * Use: Returns the current state of the hash function such that + * it can be passed to @rmd160_set@. + */ + +unsigned long rmd160_state(rmd160_ctx *ctx, void *state) +{ + octet *p = state; + STORE32_L(p + 0, ctx->a); + STORE32_L(p + 4, ctx->b); + STORE32_L(p + 8, ctx->c); + STORE32_L(p + 12, ctx->d); + STORE32_L(p + 16, ctx->e); + return (ctx->count); +} + +/* --- Test code --- */ + +HASH_TEST(RMD160, rmd160) + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/rmd160.h b/rmd160.h new file mode 100644 index 0000000..5aa9579 --- /dev/null +++ b/rmd160.h @@ -0,0 +1,160 @@ +/* -*-c-*- + * + * $Id: rmd160.h,v 1.1 1999/09/03 08:41:12 mdw Exp $ + * + * The RIPEMD-160 message digest function + * + * (c) 1998 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: rmd160.h,v $ + * Revision 1.1 1999/09/03 08:41:12 mdw + * Initial import. + * + */ + +/*----- Notes on the RIPEMD-160 hash function -----------------------------* + * + * RIPEMD-160 was invented by Hans Dobbertin, Antoon Bosselaers and Bart + * Preneel. It's a strengthened version of the original RIPEMD hash + * function, fixing a vulnerability discovered by Hans Dobbertin. The + * RIPEMD-160 design team appears well respected in the cryptographic + * community. The author finds them more plausible than SHA-1, which is the + * best alternative hash function. + */ + +#ifndef RMD160_H +#define RMD160_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#include + +/*----- Magic numbers -----------------------------------------------------*/ + +#define RMD160_BUFSZ 64 +#define RMD160_HASHSZ 20 + +/*----- Data structures ---------------------------------------------------*/ + +typedef struct rmd160_ctx { + uint32 a, b, c, d, e; /* Chaining variables */ + unsigned long count; /* Byte count so far */ + int off; /* Offset into buffer */ + octet buf[RMD160_BUFSZ]; /* Accumulation buffer */ +} rmd160_ctx; + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @rmd160_compress@ --- * + * + * Arguments: @rmd160_ctx *ctx@ = pointer to context block + * @const void *sbuf@ = pointer to buffer of appropriate size + * + * Returns: --- + * + * Use: RIPEMD-160 compression function. + */ + +extern void rmd160_compress(rmd160_ctx */*ctx*/, const void */*sbuf*/); + +/* --- @rmd160_init@ --- * + * + * Arguments: @rmd160_ctx *ctx@ = pointer to context block to initialize + * + * Returns: --- + * + * Use: Initializes a context block ready for hashing. + */ + +extern void rmd160_init(rmd160_ctx */*ctx*/); + +/* --- @rmd160_set@ --- * + * + * Arguments: @rmd160_ctx *ctx@ = pointer to context block + * @const void *buf@ = pointer to state buffer + * @unsigned long count@ = current count of bytes processed + * + * Returns: --- + * + * Use: Initializes a context block from a given state. This is + * useful in cases where the initial hash state is meant to be + * secret, e.g., for NMAC and HMAC support. + */ + +extern void rmd160_set(rmd160_ctx */*ctx*/, + const void */*buf*/, unsigned long /*count*/); + +/* --- @rmd160_hash@ --- * + * + * Arguments: @rmd160_ctx *ctx@ = pointer to context block + * @const void *buf@ = buffer of data to hash + * @size_t sz@ = size of buffer to hash + * + * Returns: --- + * + * Use: Hashes a buffer of data. The buffer may be of any size and + * alignment. + */ + +extern void rmd160_hash(rmd160_ctx */*ctx*/, + const void */*buf*/, size_t /*sz*/); + +/* --- @rmd160_done@ --- * + * + * Arguments: @rmd160_ctx *ctx@ = pointer to context block + * @void *hash@ = pointer to output buffer + * + * Returns: --- + * + * Use: Returns the hash of the data read so far. + */ + +extern void rmd160_done(rmd160_ctx */*ctx*/, void */*hash*/); + +/* --- @rmd160_state@ --- * + * + * Arguments: @rmd160_ctx *ctx@ = pointer to context + * @void *state@ = pointer to buffer for current state + * + * Returns: Number of bytes written to the hash function so far. + * + * Use: Returns the current state of the hash function such that + * it can be passed to @rmd160_set@. + */ + +extern unsigned long rmd160_state(rmd160_ctx */*ctx*/, void */*state*/); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/setup b/setup new file mode 100755 index 0000000..3c35243 --- /dev/null +++ b/setup @@ -0,0 +1,11 @@ +#! /bin/sh + +set -e +mklinks +ln -s ../mLib/build mLib +mkaclocal +autoheader +m4 Makefile.m4 >Makefile.am +autoconf +automake +mkdir build diff --git a/sha.c b/sha.c new file mode 100644 index 0000000..a5a3bf3 --- /dev/null +++ b/sha.c @@ -0,0 +1,307 @@ +/* -*-c-*- + * + * $Id: sha.c,v 1.1 1999/09/03 08:41:12 mdw Exp $ + * + * Implementation of the SHA-1 hash function + * + * (c) 1999 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: sha.c,v $ + * Revision 1.1 1999/09/03 08:41:12 mdw + * Initial import. + * + */ + +/*----- Header files ------------------------------------------------------*/ + +#include + +#include "hash.h" +#include "sha.h" + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @sha_compress@ --- * + * + * Arguments: @sha_ctx *ctx@ = pointer to context block + * @const void *sbuf@ = pointer to buffer of appropriate size + * + * Returns: --- + * + * Use: SHA-1 compression function. + */ + +void sha_compress(sha_ctx *ctx, const void *sbuf) +{ + uint32 a, b, c, d, e; + uint32 buf[80]; + + /* --- Fetch the chaining variables --- */ + + a = ctx->a; + b = ctx->b; + c = ctx->c; + d = ctx->d; + e = ctx->e; + + /* --- Fetch and expand the buffer contents --- */ + + { + int i; + const octet *p; + + for (i = 0, p = sbuf; i < 16; i++, p += 4) + buf[i] = LOAD32(p); + for (i = 16; i < 80; i++) { + uint32 x = buf[i - 3] ^ buf[i - 8] ^ buf[i - 14] ^ buf[i - 16]; + buf[i] = ROL32(x, 1); + } + } + + /* --- Definitions for round functions --- */ + +#define F(x, y, z) (((x) & (y)) | (~(x) & (z))) +#define G(x, y, z) ((x) ^ (y) ^ (z)) +#define H(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z))) + +#define T(v, w, x, y, z, i, f, k) do { \ + z = ROL32(v, 5) + f(w, x, y) + z + buf[i] + k; \ + w = ROR32(w, 2); \ +} while (0) + +#define FF(v, w, x, y, z, i) T(v, w, x, y, z, i, F, 0x5a827999) +#define GG(v, w, x, y, z, i) T(v, w, x, y, z, i, G, 0x6ed9eba1) +#define HH(v, w, x, y, z, i) T(v, w, x, y, z, i, H, 0x8f1bbcdc) +#define II(v, w, x, y, z, i) T(v, w, x, y, z, i, G, 0xca62c1d6) + + /* --- The main compression function --- */ + + FF(a, b, c, d, e, 0); + FF(e, a, b, c, d, 1); + FF(d, e, a, b, c, 2); + FF(c, d, e, a, b, 3); + FF(b, c, d, e, a, 4); + FF(a, b, c, d, e, 5); + FF(e, a, b, c, d, 6); + FF(d, e, a, b, c, 7); + FF(c, d, e, a, b, 8); + FF(b, c, d, e, a, 9); + FF(a, b, c, d, e, 10); + FF(e, a, b, c, d, 11); + FF(d, e, a, b, c, 12); + FF(c, d, e, a, b, 13); + FF(b, c, d, e, a, 14); + FF(a, b, c, d, e, 15); + FF(e, a, b, c, d, 16); + FF(d, e, a, b, c, 17); + FF(c, d, e, a, b, 18); + FF(b, c, d, e, a, 19); + + GG(a, b, c, d, e, 20); + GG(e, a, b, c, d, 21); + GG(d, e, a, b, c, 22); + GG(c, d, e, a, b, 23); + GG(b, c, d, e, a, 24); + GG(a, b, c, d, e, 25); + GG(e, a, b, c, d, 26); + GG(d, e, a, b, c, 27); + GG(c, d, e, a, b, 28); + GG(b, c, d, e, a, 29); + GG(a, b, c, d, e, 30); + GG(e, a, b, c, d, 31); + GG(d, e, a, b, c, 32); + GG(c, d, e, a, b, 33); + GG(b, c, d, e, a, 34); + GG(a, b, c, d, e, 35); + GG(e, a, b, c, d, 36); + GG(d, e, a, b, c, 37); + GG(c, d, e, a, b, 38); + GG(b, c, d, e, a, 39); + + HH(a, b, c, d, e, 40); + HH(e, a, b, c, d, 41); + HH(d, e, a, b, c, 42); + HH(c, d, e, a, b, 43); + HH(b, c, d, e, a, 44); + HH(a, b, c, d, e, 45); + HH(e, a, b, c, d, 46); + HH(d, e, a, b, c, 47); + HH(c, d, e, a, b, 48); + HH(b, c, d, e, a, 49); + HH(a, b, c, d, e, 50); + HH(e, a, b, c, d, 51); + HH(d, e, a, b, c, 52); + HH(c, d, e, a, b, 53); + HH(b, c, d, e, a, 54); + HH(a, b, c, d, e, 55); + HH(e, a, b, c, d, 56); + HH(d, e, a, b, c, 57); + HH(c, d, e, a, b, 58); + HH(b, c, d, e, a, 59); + + II(a, b, c, d, e, 60); + II(e, a, b, c, d, 61); + II(d, e, a, b, c, 62); + II(c, d, e, a, b, 63); + II(b, c, d, e, a, 64); + II(a, b, c, d, e, 65); + II(e, a, b, c, d, 66); + II(d, e, a, b, c, 67); + II(c, d, e, a, b, 68); + II(b, c, d, e, a, 69); + II(a, b, c, d, e, 70); + II(e, a, b, c, d, 71); + II(d, e, a, b, c, 72); + II(c, d, e, a, b, 73); + II(b, c, d, e, a, 74); + II(a, b, c, d, e, 75); + II(e, a, b, c, d, 76); + II(d, e, a, b, c, 77); + II(c, d, e, a, b, 78); + II(b, c, d, e, a, 79); + + /* --- Update the chaining variables --- */ + + ctx->a += a; + ctx->b += b; + ctx->c += c; + ctx->d += d; + ctx->e += e; +} + +/* --- @sha_init@ --- * + * + * Arguments: @sha_ctx *ctx@ = pointer to context block to initialize + * + * Returns: --- + * + * Use: Initializes a context block ready for hashing. + */ + +void sha_init(sha_ctx *ctx) +{ + ctx->a = 0x67452301; + ctx->b = 0xefcdab89; + ctx->c = 0x98badcfe; + ctx->d = 0x10325476; + ctx->e = 0xc3d2e1f0; + ctx->off = 0; + ctx->count = 0; +} + +/* --- @sha_set@ --- * + * + * Arguments: @sha_ctx *ctx@ = pointer to context block + * @const void *buf@ = pointer to state buffer + * @unsigned long count@ = current count of bytes processed + * + * Returns: --- + * + * Use: Initializes a context block from a given state. This is + * useful in cases where the initial hash state is meant to be + * secret, e.g., for NMAC and HMAC support. + */ + +void sha_set(sha_ctx *ctx, const void *buf, unsigned long count) +{ + const octet *p = buf; + ctx->a = LOAD32(p + 0); + ctx->b = LOAD32(p + 4); + ctx->c = LOAD32(p + 8); + ctx->d = LOAD32(p + 12); + ctx->e = LOAD32(p + 16); + ctx->off = 0; + ctx->count = count; +} + +/* --- @sha_hash@ --- * + * + * Arguments: @sha_ctx *ctx@ = pointer to context block + * @const void *buf@ = buffer of data to hash + * @size_t sz@ = size of buffer to hash + * + * Returns: --- + * + * Use: Hashes a buffer of data. The buffer may be of any size and + * alignment. + */ + +void sha_hash(sha_ctx *ctx, const void *buf, size_t sz) +{ + HASH_BUFFER(SHA, sha, ctx, buf, sz); +} + +/* --- @sha_done@ --- * + * + * Arguments: @sha_ctx *ctx@ = pointer to context block + * @void *hash@ = pointer to output buffer + * + * Returns: --- + * + * Use: Returns the hash of the data read so far. + */ + +void sha_done(sha_ctx *ctx, void *hash) +{ + octet *p = hash; + HASH_PAD(SHA, sha, ctx, 0x80, 0, 8); + STORE32(ctx->buf + SHA_BUFSZ - 8, ctx->count >> 29); + STORE32(ctx->buf + SHA_BUFSZ - 4, ctx->count << 3); + sha_compress(ctx, ctx->buf); + STORE32(p + 0, ctx->a); + STORE32(p + 4, ctx->b); + STORE32(p + 8, ctx->c); + STORE32(p + 12, ctx->d); + STORE32(p + 16, ctx->e); +} + +/* --- @sha_state@ --- * + * + * Arguments: @sha_ctx *ctx@ = pointer to context + * @void *state@ = pointer to buffer for current state + * + * Returns: Number of bytes written to the hash function so far. + * + * Use: Returns the current state of the hash function such that + * it can be passed to @sha_set@. + */ + +unsigned long sha_state(sha_ctx *ctx, void *state) +{ + octet *p = state; + STORE32(p + 0, ctx->a); + STORE32(p + 4, ctx->b); + STORE32(p + 8, ctx->c); + STORE32(p + 12, ctx->d); + STORE32(p + 16, ctx->e); + return (ctx->count); +} + +/* --- Test code --- */ + +HASH_TEST(SHA, sha) + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/sha.h b/sha.h new file mode 100644 index 0000000..8f72def --- /dev/null +++ b/sha.h @@ -0,0 +1,157 @@ +/* -*-c-*- + * + * $Id: sha.h,v 1.1 1999/09/03 08:41:12 mdw Exp $ + * + * Implementation of the SHA-1 hash function + * + * (c) 1999 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: sha.h,v $ + * Revision 1.1 1999/09/03 08:41:12 mdw + * Initial import. + * + */ + +/*----- Notes on the SHA-1 hash function ----------------------------------* + * + * SHA (Secure Hash Algorithm) was designed by the NSA, for use with the + * Digital Signature Algorithm. It has gained wide acceptance since then, + * and is probably now most people's collision-resistant function of choice. + * The author prefers RIPEMD-160, for no particularly good reasons. + */ + +#ifndef SHA_H +#define SHA_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#include + +/*----- Magic numbers -----------------------------------------------------*/ + +#define SHA_BUFSZ 64 +#define SHA_HASHSZ 20 + +/*----- Data structures ---------------------------------------------------*/ + +typedef struct sha_ctx { + uint32 a, b, c, d, e; /* Chaining variables */ + unsigned long count; /* Byte count so far */ + int off; /* Offset into buffer */ + octet buf[SHA_BUFSZ]; /* Accumulation buffer */ +} sha_ctx; + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @sha_compress@ --- * + * + * Arguments: @sha_ctx *ctx@ = pointer to context block + * @const void *sbuf@ = pointer to buffer of appropriate size + * + * Returns: --- + * + * Use: SHA compression function. + */ + +extern void sha_compress(sha_ctx */*ctx*/, const void */*sbuf*/); + +/* --- @sha_init@ --- * + * + * Arguments: @sha_ctx *ctx@ = pointer to context block to initialize + * + * Returns: --- + * + * Use: Initializes a context block ready for hashing. + */ + +extern void sha_init(sha_ctx */*ctx*/); + +/* --- @sha_set@ --- * + * + * Arguments: @sha_ctx *ctx@ = pointer to context block + * @const void *buf@ = pointer to state buffer + * @unsigned long count@ = current count of bytes processed + * + * Returns: --- + * + * Use: Initializes a context block from a given state. This is + * useful in cases where the initial hash state is meant to be + * secret, e.g., for NMAC and HMAC support. + */ + +extern void sha_set(sha_ctx */*ctx*/, const void */*buf*/, + unsigned long /*count*/); + +/* --- @sha_hash@ --- * + * + * Arguments: @sha_ctx *ctx@ = pointer to context block + * @const void *buf@ = buffer of data to hash + * @size_t sz@ = size of buffer to hash + * + * Returns: --- + * + * Use: Hashes a buffer of data. The buffer may be of any size and + * alignment. + */ + +extern void sha_hash(sha_ctx */*ctx*/, const void */*buf*/, size_t /*sz*/); + +/* --- @sha_done@ --- * + * + * Arguments: @sha_ctx *ctx@ = pointer to context block + * @void *hash@ = pointer to output buffer + * + * Returns: --- + * + * Use: Returns the hash of the data read so far. + */ + +extern void sha_done(sha_ctx */*ctx*/, void */*hash*/); + +/* --- @sha_state@ --- * + * + * Arguments: @sha_ctx *ctx@ = pointer to context + * @void *state@ = pointer to buffer for current state + * + * Returns: Number of bytes written to the hash function so far. + * + * Use: Returns the current state of the hash function such that + * it can be passed to @sha_set@. + */ + +extern unsigned long sha_state(sha_ctx */*ctx*/, void */*state*/); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/tests/blowfish b/tests/blowfish new file mode 100644 index 0000000..612dc7d --- /dev/null +++ b/tests/blowfish @@ -0,0 +1,124 @@ +# Test vectors for Blowfish +# +# $Id: blowfish,v 1.1 1999/09/03 08:41:14 mdw Exp $ + +# --- Main block cipher --- +# +# Taken from Bruce Schneier's web pages, I think... + +blowfish { + 0000000000000000 0000000000000000 4ef997456198dd78; + ffffffffffffffff ffffffffffffffff 51866fd5b85ecb8a; + 3000000000000000 1000000000000001 7d856f9a613063f2; + 1111111111111111 1111111111111111 2466dd878b963c9d; + 0123456789abcdef 1111111111111111 61f9c3802281b096; + 1111111111111111 0123456789abcdef 7d0cc630afda1ec7; + 0000000000000000 0000000000000000 4ef997456198dd78; + fedcba9876543210 0123456789abcdef 0aceab0fc6a0a28d; + 7ca110454a1a6e57 01a1d6d039776742 59c68245eb05282b; + 0131d9619dc1376e 5cd54ca83def57da b1b8cc0b250f09a0; + 07a1133e4a0b2686 0248d43806f67172 1730e5778bea1da4; + 3849674c2602319e 51454b582ddf440a a25e7856cf2651eb; + 04b915ba43feb5b6 42fd443059577fa2 353882b109ce8f1a; + 0113b970fd34f2ce 059b5e0851cf143a 48f4d0884c379918; + 0170f175468fb5e6 0756d8e0774761d2 432193b78951fc98; + 43297fad38e373fe 762514b829bf486a 13f04154d69d1ae5; + 07a7137045da2a16 3bdd119049372802 2eedda93ffd39c79; + 04689104c2fd3b2f 26955f6835af609a d887e0393c2da6e3; + 37d06bb516cb7546 164d5e404f275232 5f99d04f5b163969; + 1f08260d1ac2465e 6b056e18759f5cca 4a057a3b24d3977b; + 584023641aba6176 004bd6ef09176062 452031c1e4fada8e; + 025816164629b007 480d39006ee762f2 7555ae39f59b87bd; + 49793ebc79b3258f 437540c8698f3cfa 53c55f9cb49fc019; + 4fb05e1515ab73a7 072d43a077075292 7a8e7bfa937e89a3; + 49e95d6d4ca229bf 02fe55778117f12a cf9c5d7a4986adb5; + 018310dc409b26d6 1d9d5c5018f728c2 d1abb290658bc778; + 1c587f1c13924fef 305532286d6f295a 55cb3774d13ef201; + 0101010101010101 0123456789abcdef fa34ec4847b268b2; + 1f1f1f1f0e0e0e0e 0123456789abcdef a790795108ea3cae; + e0fee0fef1fef1fe 0123456789abcdef c39e072d9fac631d; + 0000000000000000 ffffffffffffffff 014933e0cdaff6e4; + ffffffffffffffff 0000000000000000 f21e9a77b71c49bc; + 0123456789abcdef 0000000000000000 245946885754369a; + fedcba9876543210 ffffffffffffffff 6b5c5a9c5d9e0a5a; +} + +# --- Key schedule test --- +# +# From wherever the previous tests came from. + +blowfish-sched { + + f0 + fedcba9876543210 f9ad597c49db005e; + + f0e1 + fedcba9876543210 e91d21c1d961a6d6; + + f0e1d2 + fedcba9876543210 e9c2b70a1bc65cf3; + + f0e1d2c3 + fedcba9876543210 be1e639408640f05; + + f0e1d2c3b4 + fedcba9876543210 b39e44481bdb1e6e; + + f0e1d2c3b4a5 + fedcba9876543210 9457aa83b1928c0d; + + f0e1d2c3b4a596 + fedcba9876543210 8bb77032f960629d; + + f0e1d2c3b4a59687 + fedcba9876543210 e87a244e2cc85e82; + + f0e1d2c3b4a5968778 + fedcba9876543210 15750e7a4f4ec577; + + f0e1d2c3b4a596877869 + fedcba9876543210 122ba70b3ab64ae0; + + f0e1d2c3b4a5968778695a + fedcba9876543210 3a833c9affc537f6; + + f0e1d2c3b4a5968778695a4b + fedcba9876543210 9409da87a90f6bf2; + + f0e1d2c3b4a5968778695a4b3c + fedcba9876543210 884f80625060b8b4; + + f0e1d2c3b4a5968778695a4b3c2d + fedcba9876543210 1f85031c19e11968; + + f0e1d2c3b4a5968778695a4b3c2d1e + fedcba9876543210 79d9373a714ca34f; + + f0e1d2c3b4a5968778695a4b3c2d1e0f + fedcba9876543210 93142887ee3be15c; + + f0e1d2c3b4a5968778695a4b3c2d1e0f00 + fedcba9876543210 03429e838ce2d14b; + + f0e1d2c3b4a5968778695a4b3c2d1e0f0011 + fedcba9876543210 a4299e27469ff67b; + + f0e1d2c3b4a5968778695a4b3c2d1e0f001122 + fedcba9876543210 afd5aed1c1bc96a8; + + f0e1d2c3b4a5968778695a4b3c2d1e0f00112233 + fedcba9876543210 10851c0e3858da9f; + + f0e1d2c3b4a5968778695a4b3c2d1e0f0011223344 + fedcba9876543210 e6f51ed79b9db21f; + + f0e1d2c3b4a5968778695a4b3c2d1e0f001122334455 + fedcba9876543210 64a6e14afd36b46f; + + f0e1d2c3b4a5968778695a4b3c2d1e0f00112233445566 + fedcba9876543210 80c7d7d45a5479ad; + + f0e1d2c3b4a5968778695a4b3c2d1e0f0011223344556677 + fedcba9876543210 05044b62fa52d080; + +} diff --git a/tests/des b/tests/des new file mode 100644 index 0000000..c4fbba1 --- /dev/null +++ b/tests/des @@ -0,0 +1,19 @@ +# Test vectors for DES +# +# $Id: des,v 1.1 1999/09/03 08:41:14 mdw Exp $ + +des { + # --- 7-byte keys --- + + 00451338957377 4e6f772069732074 3fa40e8a984d4815; + b6c74cbf60c1fd 328da675ff5abd2c cd3e9f9b670671d1; + + # --- 8-byte keys --- + + 0123456789abcdef 4e6f772069732074 3fa40e8a984d4815; + 0022446688aaccee 4e6f772069732074 3fa40e8a984d4815; + 0123456789abcdef 68652074696d6520 6a271787ab8883f9; + 0123456789abcdef 666f7220616c6c20 893d51ec4b563b53; + 0123456789abcdef 0123456789abcde7 c95744256a5ed31d; + b763d297f70606fb 328da675ff5abd2c cd3e9f9b670671d1; +} diff --git a/tests/des3 b/tests/des3 new file mode 100644 index 0000000..0f8b3d1 --- /dev/null +++ b/tests/des3 @@ -0,0 +1,43 @@ +# Test vectors for double and triple DES +# +# $Id: des3,v 1.1 1999/09/03 08:41:14 mdw Exp $ + +des3 { + # --- Some simple single-DES things --- + + 00451338957377 4e6f772069732074 3fa40e8a984d4815; + b6c74cbf60c1fd 328da675ff5abd2c cd3e9f9b670671d1; + + 0123456789abcdef 4e6f772069732074 3fa40e8a984d4815; + 0123456789abcdef 68652074696d6520 6a271787ab8883f9; + 0123456789abcdef 666f7220616c6c20 893d51ec4b563b53; + 0123456789abcdef 0123456789abcde7 c95744256a5ed31d; + + 0045133895737700451338957377 4e6f772069732074 3fa40e8a984d4815; + b6c74cbf60c1fdb6c74cbf60c1fd 328da675ff5abd2c cd3e9f9b670671d1; + + 0123456789abcdef0123456789abcdef 4e6f772069732074 3fa40e8a984d4815; + 0123456789abcdef0123456789abcdef 68652074696d6520 6a271787ab8883f9; + 0123456789abcdef0123456789abcdef 666f7220616c6c20 893d51ec4b563b53; + 0123456789abcdef0123456789abcdef 0123456789abcde7 c95744256a5ed31d; + + 004513389573770045133895737700451338957377 + 4e6f772069732074 3fa40e8a984d4815; + b6c74cbf60c1fdb6c74cbf60c1fdb6c74cbf60c1fd + 328da675ff5abd2c cd3e9f9b670671d1; + + 0123456789abcdef0123456789abcdef0123456789abcdef + 4e6f772069732074 3fa40e8a984d4815; + 0123456789abcdef0123456789abcdef0123456789abcdef + 68652074696d6520 6a271787ab8883f9; + 0123456789abcdef0123456789abcdef0123456789abcdef + 666f7220616c6c20 893d51ec4b563b53; + 0123456789abcdef0123456789abcdef0123456789abcdef + 0123456789abcde7 c95744256a5ed31d; + + # --- Genuine longer keys --- + + 0123456789abcdeffedcba9876543210 0123456789abcde7 7f1d0a77826b8aff; + 0123456789abcdeffedcba987654321089abcdef01234567 + 0123456789abcde7 de0b7c06ae5e0ed5; +} diff --git a/tests/idea b/tests/idea new file mode 100644 index 0000000..f6a7675 --- /dev/null +++ b/tests/idea @@ -0,0 +1,9 @@ +# Test vectors for IDEA +# +# $Id: idea,v 1.1 1999/09/03 08:41:14 mdw Exp $ + +# --- Thin on the ground, these are --- + +idea { + 00010002000300040005000600070008 0000000100020003 11fbed2b01986de5; +} diff --git a/tests/md4 b/tests/md4 new file mode 100644 index 0000000..551b6a3 --- /dev/null +++ b/tests/md4 @@ -0,0 +1,60 @@ +# Test vectors for MD4 hash function +# +# $Id: md4,v 1.1 1999/09/03 08:41:14 mdw Exp $ + +# --- Basic hash function --- +# +# Test vectors from RFC1186 and some others I generated using the reference +# implementation. + +md4 { + + "" 31d6cfe0d16ae931b73c59d7e0c089c0; + "a" bde52cb31de33e46245e05fbdbd6fb24; + "abc" a448017aaf21d8525fc10ae87aa6729d; + "message digest" d9130a8164549fe818874806e1c7014b; + "abcdefghijklmnopqrstuvwxyz" + d79e1c308aa5bbcdeea8ed63df412da9; + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" + 043f8582f241db351ce627e153e7f0e4; +} + +# --- HMAC mode --- +# +# No test vectors available. The HMAC implementation has not been tested +# against an external reference. However, MD4 isn't strong enough to make +# a realistic MAC anyway, and use of md4-mac is thoroughly deprecated. The +# HMAC code is autogenerated anyway, and ought to be reliable and correct. +# +# These test vectors are here to spot changes in behaviour rather than ensure +# interoperability. + +md4-hmac { + "Hi There" + 0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b + 90a79458f58f437e21f169cdba283da6; + + "what do ya want for nothing?" + 4a656665 + be192c588a8e914d8a59b474a828128f; + + "ÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝ" + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + 75e5fb6e71ca6dcdd9fca269a9a3cd9c; + + "ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ" + 0102030405060708090a0b0c0d0e0f10111213141516171819 + fb14cddf9efe11ad24033fc70f37bb9e; + + "Test With Truncation" + 0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c + 6306262f9ba0e83f9ce3f15aafc23be8; + + "Test Using Larger Than Block-Size Key - Hash Key First" + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + 545b8f2577657042df628fbb98430d5f; + + "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data" + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + 0192f3442ad5d1ea5268306ab0d4962e; +} diff --git a/tests/md5 b/tests/md5 new file mode 100644 index 0000000..05f0bf1 --- /dev/null +++ b/tests/md5 @@ -0,0 +1,84 @@ +# Test vectors for MD5 hash function +# +# $Id: md5,v 1.1 1999/09/03 08:41:14 mdw Exp $ + +# --- Basic hash function --- +# +# Test vectors from RFC1321 and some others I generated using the reference +# implementation. + +md5 { + + "" d41d8cd98f00b204e9800998ecf8427e; + "a" 0cc175b9c0f1b6a831c399e269772661; + "abc" 900150983cd24fb0d6963f7d28e17f72; + "message digest" f96b697d7cb7938d525a2f31aaf161d0; + "abcdefghijklmnopqrstuvwxyz" + c3fcd3d76192e4007dfb496cca67e13b; + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" + d174ab98d277d9f5a5611c2c9f419d9f; + "12345678901234567890123456789012345678901234567890123456789012345678901234567890" + 57edf4a22be3c955ac49da2e2107b67a; + + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +" 2b65a7ba4acd105ef2cb9adebd9f0bfa; + +} + +# --- HMAC mode --- +# +# Test vectors from RFC2104 and RFC2202. + +md5-hmac { + + "Hi There" + 0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b + 9294727a3638bb1c13f48ef8158bfc9d; + + "what do ya want for nothing?" + 4a656665 + 750c783e6ab0b503eaa86e310a5db738; + + "ÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝ" + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + 56be34521d144c88dbb8c733f0e8b3f6; + + "ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ" + 0102030405060708090a0b0c0d0e0f10111213141516171819 + 697eaf0aca3a3aea3a75164746ffaa79; + + "Test With Truncation" + 0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c + 56461ef2342edc00f9bab995690efd4c; + + "Test Using Larger Than Block-Size Key - Hash Key First" + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + 6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd; + + "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data" + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + 6f630fad67cda0ee1fb1f562db3aa53e; + +} diff --git a/tests/rc4 b/tests/rc4 new file mode 100644 index 0000000..374430d --- /dev/null +++ b/tests/rc4 @@ -0,0 +1,28 @@ +# Test vectors for RC4 +# +# $Id: rc4,v 1.1 1999/09/03 08:41:14 mdw Exp $ + +# --- Note about these test vectors --- +# +# These test vectors come from two places: the Usenet article confirming that +# the alleged RC4 source is interoperable with the BSAFE version, and some +# tests I performed with the RC4-in-a-signature Perl version. Some testing +# with the SSLeay version shows that for an all-zero key I'm compatible, but +# with any other I'm not. Since I pass the Usenet test vectors (which I +# found in the SSLeay sources!), and I'm compatible with at least one other +# implementation, I'm happy that I'm in the right here. + +rc4-encrypt { + 0123456789abcdef 0123456789abcdef 75b7878099e0c596; + 0123456789abcdef + 010101010101010101010101010101010101010101 + 7595c3e6114a09780c4ad452338e1ffd9a1be9498f; +} + +rc4-generate { + 00 0 de188941a3375d3a8a061e67576e926d; + 01fe23dc45ba6798 0 643ded26ae29c15cda4f2b96d66baf2c; + 08192a3b4c5d6e7f 1024 abb3a32a2ba4d409752923aa4032be16; + 0123456789abcdef 0 7494c2e7104b0879; + ef012345 0 d6a141a7ec3c38dfbd61; +} diff --git a/tests/rc5 b/tests/rc5 new file mode 100644 index 0000000..340354f --- /dev/null +++ b/tests/rc5 @@ -0,0 +1,17 @@ +# Test vectors for RC5-32/12 +# +# $Id: rc5,v 1.1 1999/09/03 08:41:14 mdw Exp $ + +# --- Main cipher test vectors --- +# +# Taken from `The RC5 Encryption Algorithm' by Rivest. Important: the +# test vectors given in the paper are given as 32-bit words, not bytes, so +# since RC5 is little-endian they need to be swapped here. + +rc5 { + 00000000000000000000000000000000 0000000000000000 21a5dbee154b8f6d; + 915f4619be41b2516355a50110a9ce91 21a5dbee154b8f6d f7c013ac5b2b8952; + 783348e75aeb0f2fd7b169bb8dc16787 f7c013ac5b2b8952 2f42b3b70369fc92; + dc49db1375a5584f6485b413b5f12baf 2f42b3b70369fc92 65c178b284d197cc; + 5269f149d41ba0152497574d7f153125 65c178b284d197cc eb44e415da319824; +} diff --git a/tests/rmd160 b/tests/rmd160 new file mode 100644 index 0000000..b1b0fc0 --- /dev/null +++ b/tests/rmd160 @@ -0,0 +1,59 @@ +# Test vectors for RIPEMD-160 +# +# $Id: rmd160,v 1.1 1999/09/03 08:41:14 mdw Exp $ + +# --- Basic hash function --- +# +# Taken from the authors' web pages. + +rmd160 { + "" 9c1185a5c5e9fc54612808977ee8f548b2258d31; + "a" 0bdc9d2d256b3ee9daae347be6f4dc835a467ffe; + "abc" 8eb208f7e05d987a9b044a8e98c6b087f15a0bfc; + "message digest" 5d0689ef49d2fae572b881b123a85ffa21595f36; + "abcdefghijklmnopqrstuvwxyz" + f71c27109c692c1b56bbdceb5b9d2865b3708dbc; + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + 12a053384a9c0c88e405a06c27dcf49ada62eb2b; + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" + b0e20b6e3116640286ed3a87a5713079b21f5189; + "12345678901234567890123456789012345678901234567890123456789012345678901234567890" + 9b752e45573d4b39f4dbd3323cab82bf63326bfb; + +} + +# --- HMAC mode --- +# +# Test vectors from RFC2286. + +rmd160-hmac { + + "Hi There" + 0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b + 24cb4bd67d20fc1a5d2ed7732dcc39377f0a5668; + + "what do ya want for nothing?" + 4a656665 + dda6c0213a485a9e24f4742064a7f033b43c4069; + + "ÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝ" + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + b0b105360de759960ab4f35298e116e295d8e7c1; + + "ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ" + 0102030405060708090a0b0c0d0e0f10111213141516171819 + d5ca862f4d21d5e610e18b4cf1beb97a4365ecf4; + + "Test With Truncation" + 0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c + 7619693978f91d90539ae786500ff3d8e0518e39; + + "Test Using Larger Than Block-Size Key - Hash Key First" + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + 6466ca07ac5eac29e1bd523e5ada7605b791fd8b; + + "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data" + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + 69ea60798d71616cce5fd0871e23754cd75d5a0a; + +} \ No newline at end of file diff --git a/tests/sha b/tests/sha new file mode 100644 index 0000000..15c94e0 --- /dev/null +++ b/tests/sha @@ -0,0 +1,83 @@ +# Test vectors for the SHA-1 hash function +# +# $Id: sha,v 1.1 1999/09/03 08:41:14 mdw Exp $ + +# --- Basic hash function --- +# +# These were generated using the SSLeay implementation of SHA-1. + +sha { + + "" da39a3ee5e6b4b0d3255bfef95601890afd80709; + "a" 86f7e437faa5a7fce15d1ddcb9eaeaea377667b8; + "abc" a9993e364706816aba3e25717850c26c9cd0d89d; + "message digest" c12252ceda8be8994d5fa0290a47231c1d16aae3; + "abcdefghijklmnopqrstuvwxyz" + 32d10c7b8cf96570ca04ce37f2a19d84240d3a89; + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" + 761c457bf73b14d27e9e9265c46f4b4dda11f940; + "12345678901234567890123456789012345678901234567890123456789012345678901234567890" + 50abf5706a150990a08b2c5ea40fa0e585554732; + + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 +" f74d36bf17ee23c46ec166a48a24da6ab999eaea; + +} + +# --- HMAC mode --- +# +# Test vectors from RFC2202. + +sha-hmac { + + "Hi There" + 0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b + b617318655057264e28bc0b6fb378c8ef146be00; + + "what do ya want for nothing?" + 4a656665 + effcdf6ae5eb2fa2d27416d5f184df9c259a7c79; + + "ÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝ" + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + 125d7342b9ac11cd91a39af48aa17b4f63f175d3; + + "ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ" + 0102030405060708090a0b0c0d0e0f10111213141516171819 + 4c9007f4026250c6bc8414f9bf50c86c2d7235da; + + "Test With Truncation" + 0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c + 4c1a03424b55e07fe7f27be1d58bb9324a9a5a04; + + "Test Using Larger Than Block-Size Key - Hash Key First" + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + aa4ae5e15272d00e95705637ce8a3b55ed402112; + + "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data" + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + e8e99d0f45237d786d6bbaa7965c7808bbff1a91; + +}