summaryrefslogtreecommitdiff
path: root/lib/libskey
diff options
context:
space:
mode:
authorTheo de Raadt <deraadt@cvs.openbsd.org>1995-10-18 08:53:40 +0000
committerTheo de Raadt <deraadt@cvs.openbsd.org>1995-10-18 08:53:40 +0000
commitd6583bb2a13f329cf0332ef2570eb8bb8fc0e39c (patch)
treeece253b876159b39c620e62b6c9b1174642e070e /lib/libskey
initial import of NetBSD tree
Diffstat (limited to 'lib/libskey')
-rw-r--r--lib/libskey/Makefile7
-rw-r--r--lib/libskey/md4.c325
-rw-r--r--lib/libskey/md4.h71
-rw-r--r--lib/libskey/put.c2322
-rw-r--r--lib/libskey/shlib_version2
-rw-r--r--lib/libskey/skey.h77
-rw-r--r--lib/libskey/skeylogin.c366
-rw-r--r--lib/libskey/skeysubr.c317
8 files changed, 3487 insertions, 0 deletions
diff --git a/lib/libskey/Makefile b/lib/libskey/Makefile
new file mode 100644
index 00000000000..7e616b17dc6
--- /dev/null
+++ b/lib/libskey/Makefile
@@ -0,0 +1,7 @@
+# $Id: Makefile,v 1.1 1995/10/18 08:43:11 deraadt Exp $
+
+LIB= skey
+SRCS= skeylogin.c skeysubr.c md4.c put.c
+CFLAGS+= -DUSE_ECHO
+
+.include <bsd.lib.mk>
diff --git a/lib/libskey/md4.c b/lib/libskey/md4.c
new file mode 100644
index 00000000000..29a77c59bff
--- /dev/null
+++ b/lib/libskey/md4.c
@@ -0,0 +1,325 @@
+/*
+ * md4.c -- Implementation of MD4 Message Digest Algorithm
+ * Updated: 2/16/90 by Ronald L. Rivest
+ * Portability nits fixed and reformatted - 2/12/91 Phil Karn
+ *
+ * Copyright (C) 1990, RSA Data Security, Inc. All rights reserved.
+ *
+ * License to copy and use this software is granted provided that it
+ * is identified as the "RSA Data Security, Inc. MD4 Message-Digest
+ * Algorithm" in all material mentioning or referencing this software
+ * or this function.
+ *
+ * License is also granted to make and use derivative works provided
+ * that such works are identified as "derived from the RSA Data
+ * Security, Inc. MD4 Message-Digest Algorithm" in all material
+ * mentioning or referencing the derived work.
+ *
+ * RSA Data Security, Inc. makes no representations concerning either
+ * the merchantability of this software or the suitability of this
+ * software for any particular purpose. It is provided "as is"
+ * without express or implied warranty of any kind.
+ *
+ * These notices must be retained in any copies of any part of this
+ * documentation and/or software.
+ *
+ * $Id: md4.c,v 1.1 1995/10/18 08:43:11 deraadt Exp $
+ */
+
+/*
+ * To use MD4:
+ * -- Include md4.h in your program
+ * -- Declare an MDstruct MD to hold the state of the digest computation.
+ * -- Initialize MD using MDbegin(&MD)
+ * -- For each full block (64 bytes) X you wish to process, call
+ * MDupdate(&MD,X,512)
+ * (512 is the number of bits in a full block.)
+ * -- For the last block (less than 64 bytes) you wish to process,
+ * MDupdate(&MD,X,n)
+ * where n is the number of bits in the partial block. A partial
+ * block terminates the computation, so every MD computation should
+ * terminate by processing a partial block, even if it has n = 0.
+ * -- The message digest is available in MD.buffer[0] ... MD.buffer[3].
+ * (Least-significant byte of each word should be output first.)
+ * -- You can print out the digest using MDprint(&MD)
+ */
+
+/* Implementation notes:
+ * This implementation assumes that longs are 32-bit quantities.
+ * Note that on big endian machines the routine
+ * MDupdate modifies has a side-effect on its input array (the order of bytes
+ * in each word are reversed). If this is undesired a call to MDreverse(X) can
+ * reverse the bytes of X back into order after each call to MDupdate.
+ */
+/* Compile-time includes */
+#include <stdio.h>
+#include <machine/endian.h>
+#include "md4.h"
+
+static void MDblock __ARGS((MDptr, unsigned long *));
+
+/* Compile-time declarations of MD4 ``magic constants'' */
+#define I0 0x67452301 /* Initial values for MD buffer */
+#define I1 0xefcdab89
+#define I2 0x98badcfe
+#define I3 0x10325476
+#define C2 013240474631 /* round 2 constant = sqrt(2) in octal */
+#define C3 015666365641 /* round 3 constant = sqrt(3) in octal */
+/* C2 and C3 are from Knuth, The Art of Programming, Volume 2
+ * (Seminumerical Algorithms), Second Edition (1981), Addison-Wesley.
+ * Table 2, page 660.
+ */
+#define fs1 3 /* round 1 shift amounts */
+#define fs2 7
+#define fs3 11
+#define fs4 19
+#define gs1 3 /* round 2 shift amounts */
+#define gs2 5
+#define gs3 9
+#define gs4 13
+#define hs1 3 /* round 3 shift amounts */
+#define hs2 9
+#define hs3 11
+#define hs4 15
+
+
+/* Compile-time macro declarations for MD4.
+ * Note: The ``rot'' operator uses the variable ``tmp''.
+ * It assumes tmp is declared as unsigned long, so that the >>
+ * operator will shift in zeros rather than extending the sign bit.
+ */
+#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 rot(X,S) (tmp=X,(tmp<<S) | (tmp>>(32-S)))
+#define ff(A,B,C,D,i,s) A = rot((A + f(B,C,D) + X[i]),s)
+#define gg(A,B,C,D,i,s) A = rot((A + g(B,C,D) + X[i] + C2),s)
+#define hh(A,B,C,D,i,s) A = rot((A + h(B,C,D) + X[i] + C3),s)
+
+void MDreverse __ARGS((unsigned long *X));
+
+/* MDprint(MDp)
+ * Print message digest buffer MDp as 32 hexadecimal digits.
+ * Order is from low-order byte of buffer[0] to high-order byte of buffer[3].
+ * Each byte is printed with high-order hexadecimal digit first.
+ * This is a user-callable routine.
+ */
+void
+MDprint(MDp)
+MDptr MDp;
+{
+ int i,j;
+
+ for(i=0;i<4;i++)
+ for(j=0;j<32;j=j+8)
+ printf("%02lx",(MDp->buffer[i]>>j) & 0xFF);
+}
+
+/* MDbegin(MDp)
+ * Initialize message digest buffer MDp.
+ * This is a user-callable routine.
+ */
+void
+MDbegin(MDp)
+MDptr MDp;
+{
+ int i;
+
+ MDp->buffer[0] = I0;
+ MDp->buffer[1] = I1;
+ MDp->buffer[2] = I2;
+ MDp->buffer[3] = I3;
+ for(i=0;i<8;i++)
+ MDp->count[i] = 0;
+ MDp->done = 0;
+}
+
+/* MDreverse(X)
+ * Reverse the byte-ordering of every long in X.
+ * Assumes X is an array of 16 longs.
+ * The macro revx reverses the byte-ordering of the next word of X.
+ */
+#define revx { t = (*X << 16) | (*X >> 16); \
+ *X++ = ((t & 0xFF00FF00) >> 8) | ((t & 0x00FF00FF) << 8); }
+void
+MDreverse(X)
+unsigned long *X;
+{
+ register unsigned long t;
+
+ revx;
+ revx;
+ revx;
+ revx;
+ revx;
+ revx;
+ revx;
+ revx;
+ revx;
+ revx;
+ revx;
+ revx;
+ revx;
+ revx;
+ revx;
+ revx;
+}
+
+/* MDblock(MDp,X)
+ * Update message digest buffer MDp->buffer using 16-word data block X.
+ * Assumes all 16 words of X are full of data.
+ * Does not update MDp->count.
+ * This routine is not user-callable.
+ */
+static void
+MDblock(MDp,X)
+MDptr MDp;
+unsigned long *X;
+{
+ register unsigned long tmp, A, B, C, D;
+
+#if BYTE_ORDER == BIG_ENDIAN
+ MDreverse(X);
+#endif
+ A = MDp->buffer[0];
+ B = MDp->buffer[1];
+ C = MDp->buffer[2];
+ D = MDp->buffer[3];
+ /* Update the message digest buffer */
+ ff(A,B,C,D,0,fs1); /* Round 1 */
+ ff(D,A,B,C,1,fs2);
+ ff(C,D,A,B,2,fs3);
+ ff(B,C,D,A,3,fs4);
+ ff(A,B,C,D,4,fs1);
+ ff(D,A,B,C,5,fs2);
+ ff(C,D,A,B,6,fs3);
+ ff(B,C,D,A,7,fs4);
+ ff(A,B,C,D,8,fs1);
+ ff(D,A,B,C,9,fs2);
+ ff(C,D,A,B,10,fs3);
+ ff(B,C,D,A,11,fs4);
+ ff(A,B,C,D,12,fs1);
+ ff(D,A,B,C,13,fs2);
+ ff(C,D,A,B,14,fs3);
+ ff(B,C,D,A,15,fs4);
+ gg(A,B,C,D,0,gs1); /* Round 2 */
+ gg(D,A,B,C,4,gs2);
+ gg(C,D,A,B,8,gs3);
+ gg(B,C,D,A,12,gs4);
+ gg(A,B,C,D,1,gs1);
+ gg(D,A,B,C,5,gs2);
+ gg(C,D,A,B,9,gs3);
+ gg(B,C,D,A,13,gs4);
+ gg(A,B,C,D,2,gs1);
+ gg(D,A,B,C,6,gs2);
+ gg(C,D,A,B,10,gs3);
+ gg(B,C,D,A,14,gs4);
+ gg(A,B,C,D,3,gs1);
+ gg(D,A,B,C,7,gs2);
+ gg(C,D,A,B,11,gs3);
+ gg(B,C,D,A,15,gs4);
+ hh(A,B,C,D,0,hs1); /* Round 3 */
+ hh(D,A,B,C,8,hs2);
+ hh(C,D,A,B,4,hs3);
+ hh(B,C,D,A,12,hs4);
+ hh(A,B,C,D,2,hs1);
+ hh(D,A,B,C,10,hs2);
+ hh(C,D,A,B,6,hs3);
+ hh(B,C,D,A,14,hs4);
+ hh(A,B,C,D,1,hs1);
+ hh(D,A,B,C,9,hs2);
+ hh(C,D,A,B,5,hs3);
+ hh(B,C,D,A,13,hs4);
+ hh(A,B,C,D,3,hs1);
+ hh(D,A,B,C,11,hs2);
+ hh(C,D,A,B,7,hs3);
+ hh(B,C,D,A,15,hs4);
+ MDp->buffer[0] += A;
+ MDp->buffer[1] += B;
+ MDp->buffer[2] += C;
+ MDp->buffer[3] += D;
+}
+
+/* MDupdate(MDp,X,count)
+ * Input: MDp -- an MDptr
+ * X -- a pointer to an array of unsigned characters.
+ * count -- the number of bits of X to use.
+ * (if not a multiple of 8, uses high bits of last byte.)
+ * Update MDp using the number of bits of X given by count.
+ * This is the basic input routine for an MD4 user.
+ * The routine completes the MD computation when count < 512, so
+ * every MD computation should end with one call to MDupdate with a
+ * count less than 512. A call with count 0 will be ignored if the
+ * MD has already been terminated (done != 0), so an extra call with count
+ * 0 can be given as a ``courtesy close'' to force termination if desired.
+ */
+void
+MDupdate(MDp,X,count)
+MDptr MDp;
+unsigned char *X;
+unsigned int count;
+{
+ int i,bit,byte,mask;
+ unsigned long tmp;
+ unsigned char XX[64];
+ unsigned char *p;
+
+ /* return with no error if this is a courtesy close with count
+ * zero and MDp->done is true.
+ */
+ if(count == 0 && MDp->done)
+ return;
+ /* check to see if MD is already done and report error */
+ if(MDp->done){
+ printf("\nError: MDupdate MD already done.");
+ return;
+ }
+ /* Add count to MDp->count */
+ tmp = count;
+ p = MDp->count;
+ while(tmp){
+ tmp += *p;
+ *p++ = tmp;
+ tmp = tmp >> 8;
+ }
+ /* Process data */
+ if(count == 512){
+ /* Full block of data to handle */
+ MDblock(MDp,(unsigned long *)X);
+ } else if(count > 512){
+ /* Check for count too large */
+ printf("\nError: MDupdate called with illegal count value %ld.",count);
+ return;
+ } else {
+ /* partial block -- must be last block so finish up
+ * Find out how many bytes and residual bits there are
+ */
+ byte = count >> 3;
+ bit = count & 7;
+ /* Copy X into XX since we need to modify it */
+ for(i=0;i<=byte;i++)
+ XX[i] = X[i];
+ for(i=byte+1;i<64;i++)
+ XX[i] = 0;
+ /* Add padding '1' bit and low-order zeros in last byte */
+ mask = 1 << (7 - bit);
+ XX[byte] = (XX[byte] | mask) & ~( mask - 1);
+ /* If room for bit count, finish up with this block */
+ if(byte <= 55){
+ for(i=0;i<8;i++)
+ XX[56+i] = MDp->count[i];
+ MDblock(MDp,(unsigned long *)XX);
+ } else {
+ /* need to do two blocks to finish up */
+ MDblock(MDp,(unsigned long *)XX);
+ for(i=0;i<56;i++)
+ XX[i] = 0;
+ for(i=0;i<8;i++)
+ XX[56+i] = MDp->count[i];
+ MDblock(MDp,(unsigned long *)XX);
+ }
+ /* Set flag saying we're done with MD computation */
+ MDp->done = 1;
+ }
+}
+/* End of md4.c */
diff --git a/lib/libskey/md4.h b/lib/libskey/md4.h
new file mode 100644
index 00000000000..1564d3b76ac
--- /dev/null
+++ b/lib/libskey/md4.h
@@ -0,0 +1,71 @@
+/*
+ *
+ * md4.h -- Header file for implementation of MD4 Message Digest Algorithm
+ * Updated: 2/13/90 by Ronald L. Rivest
+ * Reformatted and de-linted - 2/12/91 Phil Karn
+ *
+ * Copyright (C) 1990, RSA Data Security, Inc. All rights reserved.
+ *
+ * License to copy and use this software is granted provided that it
+ * is identified as the "RSA Data Security, Inc. MD4 Message-Digest
+ * Algorithm" in all material mentioning or referencing this software
+ * or this function.
+ *
+ * License is also granted to make and use derivative works provided
+ * that such works are identified as "derived from the RSA Data
+ * Security, Inc. MD4 Message-Digest Algorithm" in all material
+ * mentioning or referencing the derived work.
+ *
+ * RSA Data Security, Inc. makes no representations concerning either
+ * the merchantability of this software or the suitability of this
+ * software for any particular purpose. It is provided "as is"
+ * without express or implied warranty of any kind.
+ *
+ * These notices must be retained in any copies of any part of this
+ * documentation and/or software.
+ *
+ * $Id: md4.h,v 1.1 1995/10/18 08:43:11 deraadt Exp $
+ */
+
+#ifdef __STDC__
+#define __ARGS(X) X /* For ANSI C */
+#else
+#define __ARGS(X) ()
+#endif
+
+/* MDstruct is the data structure for a message digest computation. */
+typedef struct {
+ unsigned long buffer[4];/* Holds 4-word result of MD computation */
+ unsigned char count[8]; /* Number of bits processed so far */
+ unsigned int done; /* Nonzero means MD computation finished */
+} MDstruct, *MDptr;
+
+/* MDbegin(MD)
+ * Input: MD -- an MDptr
+ * Initialize the MDstruct prepatory to doing a message digest computation.
+ */
+extern void MDbegin __ARGS((MDptr MDp));
+
+/* MDupdate(MD,X,count)
+ * Input: MD -- an MDptr
+ * X -- a pointer to an array of unsigned characters.
+ * count -- the number of bits of X to use (an unsigned int).
+ * Updates MD using the first ``count'' bits of X.
+ * The array pointed to by X is not modified.
+ * If count is not a multiple of 8, MDupdate uses high bits of last byte.
+ * This is the basic input routine for a user.
+ * The routine terminates the MD computation when count < 512, so
+ * every MD computation should end with one call to MDupdate with a
+ * count less than 512. Zero is OK for a count.
+ */
+extern void MDupdate __ARGS((MDptr MDp,unsigned char *X,unsigned int count));
+
+/* MDprint(MD)
+ * Input: MD -- an MDptr
+ * Prints message digest buffer MD as 32 hexadecimal digits.
+ * Order is from low-order byte of buffer[0] to high-order byte of buffer[3].
+ * Each byte is printed with high-order hexadecimal digit first.
+ */
+extern void MDprint __ARGS((MDptr MDp));
+
+/* End of md4.h */
diff --git a/lib/libskey/put.c b/lib/libskey/put.c
new file mode 100644
index 00000000000..1a493f1e35c
--- /dev/null
+++ b/lib/libskey/put.c
@@ -0,0 +1,2322 @@
+/* S/KEY v1.1b (put.c)
+ *
+ * Authors:
+ * Neil M. Haller <nmh@thumper.bellcore.com>
+ * Philip R. Karn <karn@chicago.qualcomm.com>
+ * John S. Walden <jsw@thumper.bellcore.com>
+ * Scott Chasin <chasin@crimelab.com>
+ *
+ * Dictionary lookup and extraction.
+ *
+ * $Id: put.c,v 1.1 1995/10/18 08:43:11 deraadt Exp $
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+#include "skey.h"
+
+static unsigned long extract __ARGS ((char *s, int start, int length));
+static void standard __ARGS ((char *word));
+static void insert __ARGS ((char *s, int x, int start, int length));
+static int wsrch __ARGS ((char *w, int low, int high));
+
+/* Dictionary for integer-word translations */
+char Wp[2048][4] = {
+ "A",
+ "ABE",
+ "ACE",
+ "ACT",
+ "AD",
+ "ADA",
+ "ADD",
+ "AGO",
+ "AID",
+ "AIM",
+ "AIR",
+ "ALL",
+ "ALP",
+ "AM",
+ "AMY",
+ "AN",
+ "ANA",
+ "AND",
+ "ANN",
+ "ANT",
+ "ANY",
+ "APE",
+ "APS",
+ "APT",
+ "ARC",
+ "ARE",
+ "ARK",
+ "ARM",
+ "ART",
+ "AS",
+ "ASH",
+ "ASK",
+ "AT",
+ "ATE",
+ "AUG",
+ "AUK",
+ "AVE",
+ "AWE",
+ "AWK",
+ "AWL",
+ "AWN",
+ "AX",
+ "AYE",
+ "BAD",
+ "BAG",
+ "BAH",
+ "BAM",
+ "BAN",
+ "BAR",
+ "BAT",
+ "BAY",
+ "BE",
+ "BED",
+ "BEE",
+ "BEG",
+ "BEN",
+ "BET",
+ "BEY",
+ "BIB",
+ "BID",
+ "BIG",
+ "BIN",
+ "BIT",
+ "BOB",
+ "BOG",
+ "BON",
+ "BOO",
+ "BOP",
+ "BOW",
+ "BOY",
+ "BUB",
+ "BUD",
+ "BUG",
+ "BUM",
+ "BUN",
+ "BUS",
+ "BUT",
+ "BUY",
+ "BY",
+ "BYE",
+ "CAB",
+ "CAL",
+ "CAM",
+ "CAN",
+ "CAP",
+ "CAR",
+ "CAT",
+ "CAW",
+ "COD",
+ "COG",
+ "COL",
+ "CON",
+ "COO",
+ "COP",
+ "COT",
+ "COW",
+ "COY",
+ "CRY",
+ "CUB",
+ "CUE",
+ "CUP",
+ "CUR",
+ "CUT",
+ "DAB",
+ "DAD",
+ "DAM",
+ "DAN",
+ "DAR",
+ "DAY",
+ "DEE",
+ "DEL",
+ "DEN",
+ "DES",
+ "DEW",
+ "DID",
+ "DIE",
+ "DIG",
+ "DIN",
+ "DIP",
+ "DO",
+ "DOE",
+ "DOG",
+ "DON",
+ "DOT",
+ "DOW",
+ "DRY",
+ "DUB",
+ "DUD",
+ "DUE",
+ "DUG",
+ "DUN",
+ "EAR",
+ "EAT",
+ "ED",
+ "EEL",
+ "EGG",
+ "EGO",
+ "ELI",
+ "ELK",
+ "ELM",
+ "ELY",
+ "EM",
+ "END",
+ "EST",
+ "ETC",
+ "EVA",
+ "EVE",
+ "EWE",
+ "EYE",
+ "FAD",
+ "FAN",
+ "FAR",
+ "FAT",
+ "FAY",
+ "FED",
+ "FEE",
+ "FEW",
+ "FIB",
+ "FIG",
+ "FIN",
+ "FIR",
+ "FIT",
+ "FLO",
+ "FLY",
+ "FOE",
+ "FOG",
+ "FOR",
+ "FRY",
+ "FUM",
+ "FUN",
+ "FUR",
+ "GAB",
+ "GAD",
+ "GAG",
+ "GAL",
+ "GAM",
+ "GAP",
+ "GAS",
+ "GAY",
+ "GEE",
+ "GEL",
+ "GEM",
+ "GET",
+ "GIG",
+ "GIL",
+ "GIN",
+ "GO",
+ "GOT",
+ "GUM",
+ "GUN",
+ "GUS",
+ "GUT",
+ "GUY",
+ "GYM",
+ "GYP",
+ "HA",
+ "HAD",
+ "HAL",
+ "HAM",
+ "HAN",
+ "HAP",
+ "HAS",
+ "HAT",
+ "HAW",
+ "HAY",
+ "HE",
+ "HEM",
+ "HEN",
+ "HER",
+ "HEW",
+ "HEY",
+ "HI",
+ "HID",
+ "HIM",
+ "HIP",
+ "HIS",
+ "HIT",
+ "HO",
+ "HOB",
+ "HOC",
+ "HOE",
+ "HOG",
+ "HOP",
+ "HOT",
+ "HOW",
+ "HUB",
+ "HUE",
+ "HUG",
+ "HUH",
+ "HUM",
+ "HUT",
+ "I",
+ "ICY",
+ "IDA",
+ "IF",
+ "IKE",
+ "ILL",
+ "INK",
+ "INN",
+ "IO",
+ "ION",
+ "IQ",
+ "IRA",
+ "IRE",
+ "IRK",
+ "IS",
+ "IT",
+ "ITS",
+ "IVY",
+ "JAB",
+ "JAG",
+ "JAM",
+ "JAN",
+ "JAR",
+ "JAW",
+ "JAY",
+ "JET",
+ "JIG",
+ "JIM",
+ "JO",
+ "JOB",
+ "JOE",
+ "JOG",
+ "JOT",
+ "JOY",
+ "JUG",
+ "JUT",
+ "KAY",
+ "KEG",
+ "KEN",
+ "KEY",
+ "KID",
+ "KIM",
+ "KIN",
+ "KIT",
+ "LA",
+ "LAB",
+ "LAC",
+ "LAD",
+ "LAG",
+ "LAM",
+ "LAP",
+ "LAW",
+ "LAY",
+ "LEA",
+ "LED",
+ "LEE",
+ "LEG",
+ "LEN",
+ "LEO",
+ "LET",
+ "LEW",
+ "LID",
+ "LIE",
+ "LIN",
+ "LIP",
+ "LIT",
+ "LO",
+ "LOB",
+ "LOG",
+ "LOP",
+ "LOS",
+ "LOT",
+ "LOU",
+ "LOW",
+ "LOY",
+ "LUG",
+ "LYE",
+ "MA",
+ "MAC",
+ "MAD",
+ "MAE",
+ "MAN",
+ "MAO",
+ "MAP",
+ "MAT",
+ "MAW",
+ "MAY",
+ "ME",
+ "MEG",
+ "MEL",
+ "MEN",
+ "MET",
+ "MEW",
+ "MID",
+ "MIN",
+ "MIT",
+ "MOB",
+ "MOD",
+ "MOE",
+ "MOO",
+ "MOP",
+ "MOS",
+ "MOT",
+ "MOW",
+ "MUD",
+ "MUG",
+ "MUM",
+ "MY",
+ "NAB",
+ "NAG",
+ "NAN",
+ "NAP",
+ "NAT",
+ "NAY",
+ "NE",
+ "NED",
+ "NEE",
+ "NET",
+ "NEW",
+ "NIB",
+ "NIL",
+ "NIP",
+ "NIT",
+ "NO",
+ "NOB",
+ "NOD",
+ "NON",
+ "NOR",
+ "NOT",
+ "NOV",
+ "NOW",
+ "NU",
+ "NUN",
+ "NUT",
+ "O",
+ "OAF",
+ "OAK",
+ "OAR",
+ "OAT",
+ "ODD",
+ "ODE",
+ "OF",
+ "OFF",
+ "OFT",
+ "OH",
+ "OIL",
+ "OK",
+ "OLD",
+ "ON",
+ "ONE",
+ "OR",
+ "ORB",
+ "ORE",
+ "ORR",
+ "OS",
+ "OTT",
+ "OUR",
+ "OUT",
+ "OVA",
+ "OW",
+ "OWE",
+ "OWL",
+ "OWN",
+ "OX",
+ "PA",
+ "PAD",
+ "PAL",
+ "PAM",
+ "PAN",
+ "PAP",
+ "PAR",
+ "PAT",
+ "PAW",
+ "PAY",
+ "PEA",
+ "PEG",
+ "PEN",
+ "PEP",
+ "PER",
+ "PET",
+ "PEW",
+ "PHI",
+ "PI",
+ "PIE",
+ "PIN",
+ "PIT",
+ "PLY",
+ "PO",
+ "POD",
+ "POE",
+ "POP",
+ "POT",
+ "POW",
+ "PRO",
+ "PRY",
+ "PUB",
+ "PUG",
+ "PUN",
+ "PUP",
+ "PUT",
+ "QUO",
+ "RAG",
+ "RAM",
+ "RAN",
+ "RAP",
+ "RAT",
+ "RAW",
+ "RAY",
+ "REB",
+ "RED",
+ "REP",
+ "RET",
+ "RIB",
+ "RID",
+ "RIG",
+ "RIM",
+ "RIO",
+ "RIP",
+ "ROB",
+ "ROD",
+ "ROE",
+ "RON",
+ "ROT",
+ "ROW",
+ "ROY",
+ "RUB",
+ "RUE",
+ "RUG",
+ "RUM",
+ "RUN",
+ "RYE",
+ "SAC",
+ "SAD",
+ "SAG",
+ "SAL",
+ "SAM",
+ "SAN",
+ "SAP",
+ "SAT",
+ "SAW",
+ "SAY",
+ "SEA",
+ "SEC",
+ "SEE",
+ "SEN",
+ "SET",
+ "SEW",
+ "SHE",
+ "SHY",
+ "SIN",
+ "SIP",
+ "SIR",
+ "SIS",
+ "SIT",
+ "SKI",
+ "SKY",
+ "SLY",
+ "SO",
+ "SOB",
+ "SOD",
+ "SON",
+ "SOP",
+ "SOW",
+ "SOY",
+ "SPA",
+ "SPY",
+ "SUB",
+ "SUD",
+ "SUE",
+ "SUM",
+ "SUN",
+ "SUP",
+ "TAB",
+ "TAD",
+ "TAG",
+ "TAN",
+ "TAP",
+ "TAR",
+ "TEA",
+ "TED",
+ "TEE",
+ "TEN",
+ "THE",
+ "THY",
+ "TIC",
+ "TIE",
+ "TIM",
+ "TIN",
+ "TIP",
+ "TO",
+ "TOE",
+ "TOG",
+ "TOM",
+ "TON",
+ "TOO",
+ "TOP",
+ "TOW",
+ "TOY",
+ "TRY",
+ "TUB",
+ "TUG",
+ "TUM",
+ "TUN",
+ "TWO",
+ "UN",
+ "UP",
+ "US",
+ "USE",
+ "VAN",
+ "VAT",
+ "VET",
+ "VIE",
+ "WAD",
+ "WAG",
+ "WAR",
+ "WAS",
+ "WAY",
+ "WE",
+ "WEB",
+ "WED",
+ "WEE",
+ "WET",
+ "WHO",
+ "WHY",
+ "WIN",
+ "WIT",
+ "WOK",
+ "WON",
+ "WOO",
+ "WOW",
+ "WRY",
+ "WU",
+ "YAM",
+ "YAP",
+ "YAW",
+ "YE",
+ "YEA",
+ "YES",
+ "YET",
+ "YOU",
+ "ABED",
+ "ABEL",
+ "ABET",
+ "ABLE",
+ "ABUT",
+ "ACHE",
+ "ACID",
+ "ACME",
+ "ACRE",
+ "ACTA",
+ "ACTS",
+ "ADAM",
+ "ADDS",
+ "ADEN",
+ "AFAR",
+ "AFRO",
+ "AGEE",
+ "AHEM",
+ "AHOY",
+ "AIDA",
+ "AIDE",
+ "AIDS",
+ "AIRY",
+ "AJAR",
+ "AKIN",
+ "ALAN",
+ "ALEC",
+ "ALGA",
+ "ALIA",
+ "ALLY",
+ "ALMA",
+ "ALOE",
+ "ALSO",
+ "ALTO",
+ "ALUM",
+ "ALVA",
+ "AMEN",
+ "AMES",
+ "AMID",
+ "AMMO",
+ "AMOK",
+ "AMOS",
+ "AMRA",
+ "ANDY",
+ "ANEW",
+ "ANNA",
+ "ANNE",
+ "ANTE",
+ "ANTI",
+ "AQUA",
+ "ARAB",
+ "ARCH",
+ "AREA",
+ "ARGO",
+ "ARID",
+ "ARMY",
+ "ARTS",
+ "ARTY",
+ "ASIA",
+ "ASKS",
+ "ATOM",
+ "AUNT",
+ "AURA",
+ "AUTO",
+ "AVER",
+ "AVID",
+ "AVIS",
+ "AVON",
+ "AVOW",
+ "AWAY",
+ "AWRY",
+ "BABE",
+ "BABY",
+ "BACH",
+ "BACK",
+ "BADE",
+ "BAIL",
+ "BAIT",
+ "BAKE",
+ "BALD",
+ "BALE",
+ "BALI",
+ "BALK",
+ "BALL",
+ "BALM",
+ "BAND",
+ "BANE",
+ "BANG",
+ "BANK",
+ "BARB",
+ "BARD",
+ "BARE",
+ "BARK",
+ "BARN",
+ "BARR",
+ "BASE",
+ "BASH",
+ "BASK",
+ "BASS",
+ "BATE",
+ "BATH",
+ "BAWD",
+ "BAWL",
+ "BEAD",
+ "BEAK",
+ "BEAM",
+ "BEAN",
+ "BEAR",
+ "BEAT",
+ "BEAU",
+ "BECK",
+ "BEEF",
+ "BEEN",
+ "BEER",
+ "BEET",
+ "BELA",
+ "BELL",
+ "BELT",
+ "BEND",
+ "BENT",
+ "BERG",
+ "BERN",
+ "BERT",
+ "BESS",
+ "BEST",
+ "BETA",
+ "BETH",
+ "BHOY",
+ "BIAS",
+ "BIDE",
+ "BIEN",
+ "BILE",
+ "BILK",
+ "BILL",
+ "BIND",
+ "BING",
+ "BIRD",
+ "BITE",
+ "BITS",
+ "BLAB",
+ "BLAT",
+ "BLED",
+ "BLEW",
+ "BLOB",
+ "BLOC",
+ "BLOT",
+ "BLOW",
+ "BLUE",
+ "BLUM",
+ "BLUR",
+ "BOAR",
+ "BOAT",
+ "BOCA",
+ "BOCK",
+ "BODE",
+ "BODY",
+ "BOGY",
+ "BOHR",
+ "BOIL",
+ "BOLD",
+ "BOLO",
+ "BOLT",
+ "BOMB",
+ "BONA",
+ "BOND",
+ "BONE",
+ "BONG",
+ "BONN",
+ "BONY",
+ "BOOK",
+ "BOOM",
+ "BOON",
+ "BOOT",
+ "BORE",
+ "BORG",
+ "BORN",
+ "BOSE",
+ "BOSS",
+ "BOTH",
+ "BOUT",
+ "BOWL",
+ "BOYD",
+ "BRAD",
+ "BRAE",
+ "BRAG",
+ "BRAN",
+ "BRAY",
+ "BRED",
+ "BREW",
+ "BRIG",
+ "BRIM",
+ "BROW",
+ "BUCK",
+ "BUDD",
+ "BUFF",
+ "BULB",
+ "BULK",
+ "BULL",
+ "BUNK",
+ "BUNT",
+ "BUOY",
+ "BURG",
+ "BURL",
+ "BURN",
+ "BURR",
+ "BURT",
+ "BURY",
+ "BUSH",
+ "BUSS",
+ "BUST",
+ "BUSY",
+ "BYTE",
+ "CADY",
+ "CAFE",
+ "CAGE",
+ "CAIN",
+ "CAKE",
+ "CALF",
+ "CALL",
+ "CALM",
+ "CAME",
+ "CANE",
+ "CANT",
+ "CARD",
+ "CARE",
+ "CARL",
+ "CARR",
+ "CART",
+ "CASE",
+ "CASH",
+ "CASK",
+ "CAST",
+ "CAVE",
+ "CEIL",
+ "CELL",
+ "CENT",
+ "CERN",
+ "CHAD",
+ "CHAR",
+ "CHAT",
+ "CHAW",
+ "CHEF",
+ "CHEN",
+ "CHEW",
+ "CHIC",
+ "CHIN",
+ "CHOU",
+ "CHOW",
+ "CHUB",
+ "CHUG",
+ "CHUM",
+ "CITE",
+ "CITY",
+ "CLAD",
+ "CLAM",
+ "CLAN",
+ "CLAW",
+ "CLAY",
+ "CLOD",
+ "CLOG",
+ "CLOT",
+ "CLUB",
+ "CLUE",
+ "COAL",
+ "COAT",
+ "COCA",
+ "COCK",
+ "COCO",
+ "CODA",
+ "CODE",
+ "CODY",
+ "COED",
+ "COIL",
+ "COIN",
+ "COKE",
+ "COLA",
+ "COLD",
+ "COLT",
+ "COMA",
+ "COMB",
+ "COME",
+ "COOK",
+ "COOL",
+ "COON",
+ "COOT",
+ "CORD",
+ "CORE",
+ "CORK",
+ "CORN",
+ "COST",
+ "COVE",
+ "COWL",
+ "CRAB",
+ "CRAG",
+ "CRAM",
+ "CRAY",
+ "CREW",
+ "CRIB",
+ "CROW",
+ "CRUD",
+ "CUBA",
+ "CUBE",
+ "CUFF",
+ "CULL",
+ "CULT",
+ "CUNY",
+ "CURB",
+ "CURD",
+ "CURE",
+ "CURL",
+ "CURT",
+ "CUTS",
+ "DADE",
+ "DALE",
+ "DAME",
+ "DANA",
+ "DANE",
+ "DANG",
+ "DANK",
+ "DARE",
+ "DARK",
+ "DARN",
+ "DART",
+ "DASH",
+ "DATA",
+ "DATE",
+ "DAVE",
+ "DAVY",
+ "DAWN",
+ "DAYS",
+ "DEAD",
+ "DEAF",
+ "DEAL",
+ "DEAN",
+ "DEAR",
+ "DEBT",
+ "DECK",
+ "DEED",
+ "DEEM",
+ "DEER",
+ "DEFT",
+ "DEFY",
+ "DELL",
+ "DENT",
+ "DENY",
+ "DESK",
+ "DIAL",
+ "DICE",
+ "DIED",
+ "DIET",
+ "DIME",
+ "DINE",
+ "DING",
+ "DINT",
+ "DIRE",
+ "DIRT",
+ "DISC",
+ "DISH",
+ "DISK",
+ "DIVE",
+ "DOCK",
+ "DOES",
+ "DOLE",
+ "DOLL",
+ "DOLT",
+ "DOME",
+ "DONE",
+ "DOOM",
+ "DOOR",
+ "DORA",
+ "DOSE",
+ "DOTE",
+ "DOUG",
+ "DOUR",
+ "DOVE",
+ "DOWN",
+ "DRAB",
+ "DRAG",
+ "DRAM",
+ "DRAW",
+ "DREW",
+ "DRUB",
+ "DRUG",
+ "DRUM",
+ "DUAL",
+ "DUCK",
+ "DUCT",
+ "DUEL",
+ "DUET",
+ "DUKE",
+ "DULL",
+ "DUMB",
+ "DUNE",
+ "DUNK",
+ "DUSK",
+ "DUST",
+ "DUTY",
+ "EACH",
+ "EARL",
+ "EARN",
+ "EASE",
+ "EAST",
+ "EASY",
+ "EBEN",
+ "ECHO",
+ "EDDY",
+ "EDEN",
+ "EDGE",
+ "EDGY",
+ "EDIT",
+ "EDNA",
+ "EGAN",
+ "ELAN",
+ "ELBA",
+ "ELLA",
+ "ELSE",
+ "EMIL",
+ "EMIT",
+ "EMMA",
+ "ENDS",
+ "ERIC",
+ "EROS",
+ "EVEN",
+ "EVER",
+ "EVIL",
+ "EYED",
+ "FACE",
+ "FACT",
+ "FADE",
+ "FAIL",
+ "FAIN",
+ "FAIR",
+ "FAKE",
+ "FALL",
+ "FAME",
+ "FANG",
+ "FARM",
+ "FAST",
+ "FATE",
+ "FAWN",
+ "FEAR",
+ "FEAT",
+ "FEED",
+ "FEEL",
+ "FEET",
+ "FELL",
+ "FELT",
+ "FEND",
+ "FERN",
+ "FEST",
+ "FEUD",
+ "FIEF",
+ "FIGS",
+ "FILE",
+ "FILL",
+ "FILM",
+ "FIND",
+ "FINE",
+ "FINK",
+ "FIRE",
+ "FIRM",
+ "FISH",
+ "FISK",
+ "FIST",
+ "FITS",
+ "FIVE",
+ "FLAG",
+ "FLAK",
+ "FLAM",
+ "FLAT",
+ "FLAW",
+ "FLEA",
+ "FLED",
+ "FLEW",
+ "FLIT",
+ "FLOC",
+ "FLOG",
+ "FLOW",
+ "FLUB",
+ "FLUE",
+ "FOAL",
+ "FOAM",
+ "FOGY",
+ "FOIL",
+ "FOLD",
+ "FOLK",
+ "FOND",
+ "FONT",
+ "FOOD",
+ "FOOL",
+ "FOOT",
+ "FORD",
+ "FORE",
+ "FORK",
+ "FORM",
+ "FORT",
+ "FOSS",
+ "FOUL",
+ "FOUR",
+ "FOWL",
+ "FRAU",
+ "FRAY",
+ "FRED",
+ "FREE",
+ "FRET",
+ "FREY",
+ "FROG",
+ "FROM",
+ "FUEL",
+ "FULL",
+ "FUME",
+ "FUND",
+ "FUNK",
+ "FURY",
+ "FUSE",
+ "FUSS",
+ "GAFF",
+ "GAGE",
+ "GAIL",
+ "GAIN",
+ "GAIT",
+ "GALA",
+ "GALE",
+ "GALL",
+ "GALT",
+ "GAME",
+ "GANG",
+ "GARB",
+ "GARY",
+ "GASH",
+ "GATE",
+ "GAUL",
+ "GAUR",
+ "GAVE",
+ "GAWK",
+ "GEAR",
+ "GELD",
+ "GENE",
+ "GENT",
+ "GERM",
+ "GETS",
+ "GIBE",
+ "GIFT",
+ "GILD",
+ "GILL",
+ "GILT",
+ "GINA",
+ "GIRD",
+ "GIRL",
+ "GIST",
+ "GIVE",
+ "GLAD",
+ "GLEE",
+ "GLEN",
+ "GLIB",
+ "GLOB",
+ "GLOM",
+ "GLOW",
+ "GLUE",
+ "GLUM",
+ "GLUT",
+ "GOAD",
+ "GOAL",
+ "GOAT",
+ "GOER",
+ "GOES",
+ "GOLD",
+ "GOLF",
+ "GONE",
+ "GONG",
+ "GOOD",
+ "GOOF",
+ "GORE",
+ "GORY",
+ "GOSH",
+ "GOUT",
+ "GOWN",
+ "GRAB",
+ "GRAD",
+ "GRAY",
+ "GREG",
+ "GREW",
+ "GREY",
+ "GRID",
+ "GRIM",
+ "GRIN",
+ "GRIT",
+ "GROW",
+ "GRUB",
+ "GULF",
+ "GULL",
+ "GUNK",
+ "GURU",
+ "GUSH",
+ "GUST",
+ "GWEN",
+ "GWYN",
+ "HAAG",
+ "HAAS",
+ "HACK",
+ "HAIL",
+ "HAIR",
+ "HALE",
+ "HALF",
+ "HALL",
+ "HALO",
+ "HALT",
+ "HAND",
+ "HANG",
+ "HANK",
+ "HANS",
+ "HARD",
+ "HARK",
+ "HARM",
+ "HART",
+ "HASH",
+ "HAST",
+ "HATE",
+ "HATH",
+ "HAUL",
+ "HAVE",
+ "HAWK",
+ "HAYS",
+ "HEAD",
+ "HEAL",
+ "HEAR",
+ "HEAT",
+ "HEBE",
+ "HECK",
+ "HEED",
+ "HEEL",
+ "HEFT",
+ "HELD",
+ "HELL",
+ "HELM",
+ "HERB",
+ "HERD",
+ "HERE",
+ "HERO",
+ "HERS",
+ "HESS",
+ "HEWN",
+ "HICK",
+ "HIDE",
+ "HIGH",
+ "HIKE",
+ "HILL",
+ "HILT",
+ "HIND",
+ "HINT",
+ "HIRE",
+ "HISS",
+ "HIVE",
+ "HOBO",
+ "HOCK",
+ "HOFF",
+ "HOLD",
+ "HOLE",
+ "HOLM",
+ "HOLT",
+ "HOME",
+ "HONE",
+ "HONK",
+ "HOOD",
+ "HOOF",
+ "HOOK",
+ "HOOT",
+ "HORN",
+ "HOSE",
+ "HOST",
+ "HOUR",
+ "HOVE",
+ "HOWE",
+ "HOWL",
+ "HOYT",
+ "HUCK",
+ "HUED",
+ "HUFF",
+ "HUGE",
+ "HUGH",
+ "HUGO",
+ "HULK",
+ "HULL",
+ "HUNK",
+ "HUNT",
+ "HURD",
+ "HURL",
+ "HURT",
+ "HUSH",
+ "HYDE",
+ "HYMN",
+ "IBIS",
+ "ICON",
+ "IDEA",
+ "IDLE",
+ "IFFY",
+ "INCA",
+ "INCH",
+ "INTO",
+ "IONS",
+ "IOTA",
+ "IOWA",
+ "IRIS",
+ "IRMA",
+ "IRON",
+ "ISLE",
+ "ITCH",
+ "ITEM",
+ "IVAN",
+ "JACK",
+ "JADE",
+ "JAIL",
+ "JAKE",
+ "JANE",
+ "JAVA",
+ "JEAN",
+ "JEFF",
+ "JERK",
+ "JESS",
+ "JEST",
+ "JIBE",
+ "JILL",
+ "JILT",
+ "JIVE",
+ "JOAN",
+ "JOBS",
+ "JOCK",
+ "JOEL",
+ "JOEY",
+ "JOHN",
+ "JOIN",
+ "JOKE",
+ "JOLT",
+ "JOVE",
+ "JUDD",
+ "JUDE",
+ "JUDO",
+ "JUDY",
+ "JUJU",
+ "JUKE",
+ "JULY",
+ "JUNE",
+ "JUNK",
+ "JUNO",
+ "JURY",
+ "JUST",
+ "JUTE",
+ "KAHN",
+ "KALE",
+ "KANE",
+ "KANT",
+ "KARL",
+ "KATE",
+ "KEEL",
+ "KEEN",
+ "KENO",
+ "KENT",
+ "KERN",
+ "KERR",
+ "KEYS",
+ "KICK",
+ "KILL",
+ "KIND",
+ "KING",
+ "KIRK",
+ "KISS",
+ "KITE",
+ "KLAN",
+ "KNEE",
+ "KNEW",
+ "KNIT",
+ "KNOB",
+ "KNOT",
+ "KNOW",
+ "KOCH",
+ "KONG",
+ "KUDO",
+ "KURD",
+ "KURT",
+ "KYLE",
+ "LACE",
+ "LACK",
+ "LACY",
+ "LADY",
+ "LAID",
+ "LAIN",
+ "LAIR",
+ "LAKE",
+ "LAMB",
+ "LAME",
+ "LAND",
+ "LANE",
+ "LANG",
+ "LARD",
+ "LARK",
+ "LASS",
+ "LAST",
+ "LATE",
+ "LAUD",
+ "LAVA",
+ "LAWN",
+ "LAWS",
+ "LAYS",
+ "LEAD",
+ "LEAF",
+ "LEAK",
+ "LEAN",
+ "LEAR",
+ "LEEK",
+ "LEER",
+ "LEFT",
+ "LEND",
+ "LENS",
+ "LENT",
+ "LEON",
+ "LESK",
+ "LESS",
+ "LEST",
+ "LETS",
+ "LIAR",
+ "LICE",
+ "LICK",
+ "LIED",
+ "LIEN",
+ "LIES",
+ "LIEU",
+ "LIFE",
+ "LIFT",
+ "LIKE",
+ "LILA",
+ "LILT",
+ "LILY",
+ "LIMA",
+ "LIMB",
+ "LIME",
+ "LIND",
+ "LINE",
+ "LINK",
+ "LINT",
+ "LION",
+ "LISA",
+ "LIST",
+ "LIVE",
+ "LOAD",
+ "LOAF",
+ "LOAM",
+ "LOAN",
+ "LOCK",
+ "LOFT",
+ "LOGE",
+ "LOIS",
+ "LOLA",
+ "LONE",
+ "LONG",
+ "LOOK",
+ "LOON",
+ "LOOT",
+ "LORD",
+ "LORE",
+ "LOSE",
+ "LOSS",
+ "LOST",
+ "LOUD",
+ "LOVE",
+ "LOWE",
+ "LUCK",
+ "LUCY",
+ "LUGE",
+ "LUKE",
+ "LULU",
+ "LUND",
+ "LUNG",
+ "LURA",
+ "LURE",
+ "LURK",
+ "LUSH",
+ "LUST",
+ "LYLE",
+ "LYNN",
+ "LYON",
+ "LYRA",
+ "MACE",
+ "MADE",
+ "MAGI",
+ "MAID",
+ "MAIL",
+ "MAIN",
+ "MAKE",
+ "MALE",
+ "MALI",
+ "MALL",
+ "MALT",
+ "MANA",
+ "MANN",
+ "MANY",
+ "MARC",
+ "MARE",
+ "MARK",
+ "MARS",
+ "MART",
+ "MARY",
+ "MASH",
+ "MASK",
+ "MASS",
+ "MAST",
+ "MATE",
+ "MATH",
+ "MAUL",
+ "MAYO",
+ "MEAD",
+ "MEAL",
+ "MEAN",
+ "MEAT",
+ "MEEK",
+ "MEET",
+ "MELD",
+ "MELT",
+ "MEMO",
+ "MEND",
+ "MENU",
+ "MERT",
+ "MESH",
+ "MESS",
+ "MICE",
+ "MIKE",
+ "MILD",
+ "MILE",
+ "MILK",
+ "MILL",
+ "MILT",
+ "MIMI",
+ "MIND",
+ "MINE",
+ "MINI",
+ "MINK",
+ "MINT",
+ "MIRE",
+ "MISS",
+ "MIST",
+ "MITE",
+ "MITT",
+ "MOAN",
+ "MOAT",
+ "MOCK",
+ "MODE",
+ "MOLD",
+ "MOLE",
+ "MOLL",
+ "MOLT",
+ "MONA",
+ "MONK",
+ "MONT",
+ "MOOD",
+ "MOON",
+ "MOOR",
+ "MOOT",
+ "MORE",
+ "MORN",
+ "MORT",
+ "MOSS",
+ "MOST",
+ "MOTH",
+ "MOVE",
+ "MUCH",
+ "MUCK",
+ "MUDD",
+ "MUFF",
+ "MULE",
+ "MULL",
+ "MURK",
+ "MUSH",
+ "MUST",
+ "MUTE",
+ "MUTT",
+ "MYRA",
+ "MYTH",
+ "NAGY",
+ "NAIL",
+ "NAIR",
+ "NAME",
+ "NARY",
+ "NASH",
+ "NAVE",
+ "NAVY",
+ "NEAL",
+ "NEAR",
+ "NEAT",
+ "NECK",
+ "NEED",
+ "NEIL",
+ "NELL",
+ "NEON",
+ "NERO",
+ "NESS",
+ "NEST",
+ "NEWS",
+ "NEWT",
+ "NIBS",
+ "NICE",
+ "NICK",
+ "NILE",
+ "NINA",
+ "NINE",
+ "NOAH",
+ "NODE",
+ "NOEL",
+ "NOLL",
+ "NONE",
+ "NOOK",
+ "NOON",
+ "NORM",
+ "NOSE",
+ "NOTE",
+ "NOUN",
+ "NOVA",
+ "NUDE",
+ "NULL",
+ "NUMB",
+ "OATH",
+ "OBEY",
+ "OBOE",
+ "ODIN",
+ "OHIO",
+ "OILY",
+ "OINT",
+ "OKAY",
+ "OLAF",
+ "OLDY",
+ "OLGA",
+ "OLIN",
+ "OMAN",
+ "OMEN",
+ "OMIT",
+ "ONCE",
+ "ONES",
+ "ONLY",
+ "ONTO",
+ "ONUS",
+ "ORAL",
+ "ORGY",
+ "OSLO",
+ "OTIS",
+ "OTTO",
+ "OUCH",
+ "OUST",
+ "OUTS",
+ "OVAL",
+ "OVEN",
+ "OVER",
+ "OWLY",
+ "OWNS",
+ "QUAD",
+ "QUIT",
+ "QUOD",
+ "RACE",
+ "RACK",
+ "RACY",
+ "RAFT",
+ "RAGE",
+ "RAID",
+ "RAIL",
+ "RAIN",
+ "RAKE",
+ "RANK",
+ "RANT",
+ "RARE",
+ "RASH",
+ "RATE",
+ "RAVE",
+ "RAYS",
+ "READ",
+ "REAL",
+ "REAM",
+ "REAR",
+ "RECK",
+ "REED",
+ "REEF",
+ "REEK",
+ "REEL",
+ "REID",
+ "REIN",
+ "RENA",
+ "REND",
+ "RENT",
+ "REST",
+ "RICE",
+ "RICH",
+ "RICK",
+ "RIDE",
+ "RIFT",
+ "RILL",
+ "RIME",
+ "RING",
+ "RINK",
+ "RISE",
+ "RISK",
+ "RITE",
+ "ROAD",
+ "ROAM",
+ "ROAR",
+ "ROBE",
+ "ROCK",
+ "RODE",
+ "ROIL",
+ "ROLL",
+ "ROME",
+ "ROOD",
+ "ROOF",
+ "ROOK",
+ "ROOM",
+ "ROOT",
+ "ROSA",
+ "ROSE",
+ "ROSS",
+ "ROSY",
+ "ROTH",
+ "ROUT",
+ "ROVE",
+ "ROWE",
+ "ROWS",
+ "RUBE",
+ "RUBY",
+ "RUDE",
+ "RUDY",
+ "RUIN",
+ "RULE",
+ "RUNG",
+ "RUNS",
+ "RUNT",
+ "RUSE",
+ "RUSH",
+ "RUSK",
+ "RUSS",
+ "RUST",
+ "RUTH",
+ "SACK",
+ "SAFE",
+ "SAGE",
+ "SAID",
+ "SAIL",
+ "SALE",
+ "SALK",
+ "SALT",
+ "SAME",
+ "SAND",
+ "SANE",
+ "SANG",
+ "SANK",
+ "SARA",
+ "SAUL",
+ "SAVE",
+ "SAYS",
+ "SCAN",
+ "SCAR",
+ "SCAT",
+ "SCOT",
+ "SEAL",
+ "SEAM",
+ "SEAR",
+ "SEAT",
+ "SEED",
+ "SEEK",
+ "SEEM",
+ "SEEN",
+ "SEES",
+ "SELF",
+ "SELL",
+ "SEND",
+ "SENT",
+ "SETS",
+ "SEWN",
+ "SHAG",
+ "SHAM",
+ "SHAW",
+ "SHAY",
+ "SHED",
+ "SHIM",
+ "SHIN",
+ "SHOD",
+ "SHOE",
+ "SHOT",
+ "SHOW",
+ "SHUN",
+ "SHUT",
+ "SICK",
+ "SIDE",
+ "SIFT",
+ "SIGH",
+ "SIGN",
+ "SILK",
+ "SILL",
+ "SILO",
+ "SILT",
+ "SINE",
+ "SING",
+ "SINK",
+ "SIRE",
+ "SITE",
+ "SITS",
+ "SITU",
+ "SKAT",
+ "SKEW",
+ "SKID",
+ "SKIM",
+ "SKIN",
+ "SKIT",
+ "SLAB",
+ "SLAM",
+ "SLAT",
+ "SLAY",
+ "SLED",
+ "SLEW",
+ "SLID",
+ "SLIM",
+ "SLIT",
+ "SLOB",
+ "SLOG",
+ "SLOT",
+ "SLOW",
+ "SLUG",
+ "SLUM",
+ "SLUR",
+ "SMOG",
+ "SMUG",
+ "SNAG",
+ "SNOB",
+ "SNOW",
+ "SNUB",
+ "SNUG",
+ "SOAK",
+ "SOAR",
+ "SOCK",
+ "SODA",
+ "SOFA",
+ "SOFT",
+ "SOIL",
+ "SOLD",
+ "SOME",
+ "SONG",
+ "SOON",
+ "SOOT",
+ "SORE",
+ "SORT",
+ "SOUL",
+ "SOUR",
+ "SOWN",
+ "STAB",
+ "STAG",
+ "STAN",
+ "STAR",
+ "STAY",
+ "STEM",
+ "STEW",
+ "STIR",
+ "STOW",
+ "STUB",
+ "STUN",
+ "SUCH",
+ "SUDS",
+ "SUIT",
+ "SULK",
+ "SUMS",
+ "SUNG",
+ "SUNK",
+ "SURE",
+ "SURF",
+ "SWAB",
+ "SWAG",
+ "SWAM",
+ "SWAN",
+ "SWAT",
+ "SWAY",
+ "SWIM",
+ "SWUM",
+ "TACK",
+ "TACT",
+ "TAIL",
+ "TAKE",
+ "TALE",
+ "TALK",
+ "TALL",
+ "TANK",
+ "TASK",
+ "TATE",
+ "TAUT",
+ "TEAL",
+ "TEAM",
+ "TEAR",
+ "TECH",
+ "TEEM",
+ "TEEN",
+ "TEET",
+ "TELL",
+ "TEND",
+ "TENT",
+ "TERM",
+ "TERN",
+ "TESS",
+ "TEST",
+ "THAN",
+ "THAT",
+ "THEE",
+ "THEM",
+ "THEN",
+ "THEY",
+ "THIN",
+ "THIS",
+ "THUD",
+ "THUG",
+ "TICK",
+ "TIDE",
+ "TIDY",
+ "TIED",
+ "TIER",
+ "TILE",
+ "TILL",
+ "TILT",
+ "TIME",
+ "TINA",
+ "TINE",
+ "TINT",
+ "TINY",
+ "TIRE",
+ "TOAD",
+ "TOGO",
+ "TOIL",
+ "TOLD",
+ "TOLL",
+ "TONE",
+ "TONG",
+ "TONY",
+ "TOOK",
+ "TOOL",
+ "TOOT",
+ "TORE",
+ "TORN",
+ "TOTE",
+ "TOUR",
+ "TOUT",
+ "TOWN",
+ "TRAG",
+ "TRAM",
+ "TRAY",
+ "TREE",
+ "TREK",
+ "TRIG",
+ "TRIM",
+ "TRIO",
+ "TROD",
+ "TROT",
+ "TROY",
+ "TRUE",
+ "TUBA",
+ "TUBE",
+ "TUCK",
+ "TUFT",
+ "TUNA",
+ "TUNE",
+ "TUNG",
+ "TURF",
+ "TURN",
+ "TUSK",
+ "TWIG",
+ "TWIN",
+ "TWIT",
+ "ULAN",
+ "UNIT",
+ "URGE",
+ "USED",
+ "USER",
+ "USES",
+ "UTAH",
+ "VAIL",
+ "VAIN",
+ "VALE",
+ "VARY",
+ "VASE",
+ "VAST",
+ "VEAL",
+ "VEDA",
+ "VEIL",
+ "VEIN",
+ "VEND",
+ "VENT",
+ "VERB",
+ "VERY",
+ "VETO",
+ "VICE",
+ "VIEW",
+ "VINE",
+ "VISE",
+ "VOID",
+ "VOLT",
+ "VOTE",
+ "WACK",
+ "WADE",
+ "WAGE",
+ "WAIL",
+ "WAIT",
+ "WAKE",
+ "WALE",
+ "WALK",
+ "WALL",
+ "WALT",
+ "WAND",
+ "WANE",
+ "WANG",
+ "WANT",
+ "WARD",
+ "WARM",
+ "WARN",
+ "WART",
+ "WASH",
+ "WAST",
+ "WATS",
+ "WATT",
+ "WAVE",
+ "WAVY",
+ "WAYS",
+ "WEAK",
+ "WEAL",
+ "WEAN",
+ "WEAR",
+ "WEED",
+ "WEEK",
+ "WEIR",
+ "WELD",
+ "WELL",
+ "WELT",
+ "WENT",
+ "WERE",
+ "WERT",
+ "WEST",
+ "WHAM",
+ "WHAT",
+ "WHEE",
+ "WHEN",
+ "WHET",
+ "WHOA",
+ "WHOM",
+ "WICK",
+ "WIFE",
+ "WILD",
+ "WILL",
+ "WIND",
+ "WINE",
+ "WING",
+ "WINK",
+ "WINO",
+ "WIRE",
+ "WISE",
+ "WISH",
+ "WITH",
+ "WOLF",
+ "WONT",
+ "WOOD",
+ "WOOL",
+ "WORD",
+ "WORE",
+ "WORK",
+ "WORM",
+ "WORN",
+ "WOVE",
+ "WRIT",
+ "WYNN",
+ "YALE",
+ "YANG",
+ "YANK",
+ "YARD",
+ "YARN",
+ "YAWL",
+ "YAWN",
+ "YEAH",
+ "YEAR",
+ "YELL",
+ "YOGA",
+ "YOKE"
+};
+
+/* Encode 8 bytes in 'c' as a string of English words.
+ * Returns a pointer to a static buffer
+ */
+char *
+ btoe (engout, c)
+ char *c, *engout;
+{
+ char cp[9]; /* add in room for the parity 2 bits */
+ int p, i;
+
+ engout[0] = '\0';
+ memcpy (cp, c, 8);
+ /* compute parity */
+ for (p = 0, i = 0; i < 64; i += 2)
+ p += extract (cp, i, 2);
+
+ cp[8] = (char) p << 6;
+
+ strncat (engout, &Wp[extract (cp, 0, 11)][0], 4);
+ strcat (engout, " ");
+ strncat (engout, &Wp[extract (cp, 11, 11)][0], 4);
+ strcat (engout, " ");
+ strncat (engout, &Wp[extract (cp, 22, 11)][0], 4);
+ strcat (engout, " ");
+ strncat (engout, &Wp[extract (cp, 33, 11)][0], 4);
+ strcat (engout, " ");
+ strncat (engout, &Wp[extract (cp, 44, 11)][0], 4);
+ strcat (engout, " ");
+ strncat (engout, &Wp[extract (cp, 55, 11)][0], 4);
+
+#ifdef notdef
+ printf ("engout is %s\n\r", engout);
+#endif
+ return (engout);
+}
+
+/* convert English to binary
+ * returns 1 OK - all good words and parity is OK
+ * 0 word not in data base
+ * -1 badly formed in put ie > 4 char word
+ * -2 words OK but parity is wrong
+ */
+int
+ etob (out, e)
+ char *out;
+ char *e;
+{
+ char *word;
+ int i, p, v, l, low, high;
+ char b[9];
+ char input[36];
+
+ if (e == NULL)
+ return -1;
+
+ strncpy (input, e, sizeof (input));
+ memset (b, 0, sizeof (b));
+ memset (out, 0, 8);
+ for (i = 0, p = 0; i < 6; i++, p += 11)
+ {
+ if ((word = strtok (i == 0 ? input : NULL, " ")) == NULL)
+ return -1;
+
+ l = strlen (word);
+ if (l > 4 || l < 1)
+ return -1;
+ else if (l < 4)
+ {
+ low = 0;
+ high = 570;
+ }
+ else
+ {
+ low = 571;
+ high = 2047;
+ }
+ standard (word);
+
+ if ((v = wsrch (word, low, high)) < 0)
+ return 0;
+
+ insert (b, v, p, 11);
+ }
+
+ /* now check the parity of what we got */
+ for (p = 0, i = 0; i < 64; i += 2)
+ p += extract (b, i, 2);
+
+ if ((p & 3) != extract (b, 64, 2))
+ return -2;
+
+ memcpy (out, b, 8);
+
+ return 1;
+}
+
+/* Display 8 bytes as a series of 16-bit hex digits */
+char *
+ put8 (out, s)
+ char *out;
+ char *s;
+{
+ sprintf (out, "%02X%02X %02X%02X %02X%02X %02X%02X",
+ s[0] & 0xff, s[1] & 0xff, s[2] & 0xff,
+ s[3] & 0xff, s[4] & 0xff, s[5] & 0xff,
+ s[6] & 0xff, s[7] & 0xff);
+ return out;
+}
+
+#ifdef notdef
+/* Encode 8 bytes in 'cp' as stream of ascii letters.
+ * Provided as a possible alternative to btoe()
+ */
+char *
+ btoc (cp)
+ char *cp;
+{
+ int i;
+ static char out[31];
+
+ /* code out put by characters 6 bits each added to 0x21 (!) */
+ for (i = 0; i <= 10; i++)
+ {
+ /* last one is only 4 bits not 6 */
+ out[i] = '!' + extract (cp, 6 * i, i >= 10 ? 4 : 6);
+ }
+ out[i] = '\0';
+ return (out);
+}
+
+#endif
+
+/* Internal subroutines for word encoding/decoding */
+
+/* Dictionary binary search */
+static int
+ wsrch (w, low, high)
+ char *w;
+ int low, high;
+{
+ int i, j;
+
+ for (;;)
+ {
+ i = (low + high) / 2;
+ if ((j = strncmp (w, Wp[i], 4)) == 0)
+ return i; /* Found it */
+ if (high == low + 1)
+ {
+ /* Avoid effects of integer truncation in /2 */
+ if (strncmp (w, Wp[high], 4) == 0)
+ return high;
+ else
+ return -1;
+ }
+ if (low >= high)
+ return -1; /* I don't *think* this can happen... */
+ if (j < 0)
+ high = i; /* Search lower half */
+ else
+ low = i; /* Search upper half */
+ }
+}
+static void
+ insert (s, x, start, length)
+ char *s;
+ int x;
+ int start, length;
+{
+ unsigned char cl;
+ unsigned char cc;
+ unsigned char cr;
+ unsigned long y;
+ int shift;
+
+ assert (length <= 11);
+ assert (start >= 0);
+ assert (length >= 0);
+ assert (start + length <= 66);
+
+ shift = ((8 - ((start + length) % 8)) % 8);
+ y = (long) x << shift;
+ cl = (y >> 16) & 0xff;
+ cc = (y >> 8) & 0xff;
+ cr = y & 0xff;
+ if (shift + length > 16)
+ {
+ s[start / 8] |= cl;
+ s[start / 8 + 1] |= cc;
+ s[start / 8 + 2] |= cr;
+ }
+ else if (shift + length > 8)
+ {
+ s[start / 8] |= cc;
+ s[start / 8 + 1] |= cr;
+ }
+ else
+ {
+ s[start / 8] |= cr;
+ }
+}
+
+static void
+ standard (word)
+ register char *word;
+{
+ while (*word)
+ {
+ if (!isascii (*word))
+ break;
+ if (islower (*word))
+ *word = toupper (*word);
+ if (*word == '1')
+ *word = 'L';
+ if (*word == '0')
+ *word = 'O';
+ if (*word == '5')
+ *word = 'S';
+ word++;
+ }
+}
+
+/* Extract 'length' bits from the char array 's' starting with bit 'start' */
+static unsigned long
+ extract (s, start, length)
+ char *s;
+ int start, length;
+{
+ unsigned char cl;
+ unsigned char cc;
+ unsigned char cr;
+ unsigned long x;
+
+ assert (length <= 11);
+ assert (start >= 0);
+ assert (length >= 0);
+ assert (start + length <= 66);
+
+ cl = s[start / 8];
+ cc = s[start / 8 + 1];
+ cr = s[start / 8 + 2];
+ x = ((long) (cl << 8 | cc) << 8 | cr);
+ x = x >> (24 - (length + (start % 8)));
+ x = (x & (0xffff >> (16 - length)));
+ return (x);
+}
diff --git a/lib/libskey/shlib_version b/lib/libskey/shlib_version
new file mode 100644
index 00000000000..97c9f92d6b8
--- /dev/null
+++ b/lib/libskey/shlib_version
@@ -0,0 +1,2 @@
+major=0
+minor=0
diff --git a/lib/libskey/skey.h b/lib/libskey/skey.h
new file mode 100644
index 00000000000..1cc7c4a4b1c
--- /dev/null
+++ b/lib/libskey/skey.h
@@ -0,0 +1,77 @@
+/*
+ * S/KEY v1.1b (skey.h)
+ *
+ * Authors:
+ * Neil M. Haller <nmh@thumper.bellcore.com>
+ * Philip R. Karn <karn@chicago.qualcomm.com>
+ * John S. Walden <jsw@thumper.bellcore.com>
+ *
+ * Modifications:
+ * Scott Chasin <chasin@crimelab.com>
+ *
+ * Main client header
+ *
+ * $Id: skey.h,v 1.1 1995/10/18 08:43:11 deraadt Exp $
+ */
+
+#if defined(__TURBOC__) || defined(__STDC__) || defined(LATTICE)
+#define ANSIPROTO 1
+#endif
+
+#ifndef __ARGS
+#ifdef ANSIPROTO
+#define __ARGS(x) x
+#else
+#define __ARGS(x) ()
+#endif
+#endif
+
+#ifdef SOLARIS
+#define setpriority(x,y,z) z
+#endif
+
+/* Server-side data structure for reading keys file during login */
+struct skey
+{
+ FILE *keyfile;
+ char buf[256];
+ char *logname;
+ int n;
+ char *seed;
+ char *val;
+ long recstart; /* needed so reread of buffer is efficient */
+
+
+};
+
+/* Client-side structure for scanning data stream for challenge */
+struct mc
+{
+ char buf[256];
+ int skip;
+ int cnt;
+};
+
+void f __ARGS ((char *x));
+int keycrunch __ARGS ((char *result, char *seed, char *passwd));
+char *btoe __ARGS ((char *engout, char *c));
+char *put8 __ARGS ((char *out, char *s));
+int etob __ARGS ((char *out, char *e));
+void rip __ARGS ((char *buf));
+int skeychallenge __ARGS ((struct skey * mp, char *name, char *ss));
+int skeylookup __ARGS ((struct skey * mp, char *name));
+int skeyverify __ARGS ((struct skey * mp, char *response));
+void sevenbit __ARGS ((char *s));
+void backspace __ARGS ((char *s));
+char *skipspace __ARGS ((char *s));
+char *readpass __ARGS ((char *buf, int n));
+char *readskey __ARGS ((char *buf, int n));
+int skey_authenticate __ARGS ((char *));
+int skey_passcheck __ARGS ((char *, char *));
+char *skey_keyinfo __ARGS ((char *));
+int skey_haskey __ARGS ((char *));
+int getskeyprompt __ARGS ((struct skey *, char *, char *));
+int atob8 __ARGS((char *, char *));
+int btoa8 __ARGS((char *, char *));
+int htoi __ARGS((char));
+
diff --git a/lib/libskey/skeylogin.c b/lib/libskey/skeylogin.c
new file mode 100644
index 00000000000..0c7a7feaa6a
--- /dev/null
+++ b/lib/libskey/skeylogin.c
@@ -0,0 +1,366 @@
+/* S/KEY v1.1b (skeylogin.c)
+ *
+ * Authors:
+ * Neil M. Haller <nmh@thumper.bellcore.com>
+ * Philip R. Karn <karn@chicago.qualcomm.com>
+ * John S. Walden <jsw@thumper.bellcore.com>
+ * Scott Chasin <chasin@crimelab.com>
+ *
+ * S/KEY verification check, lookups, and authentication.
+ *
+ * $Id: skeylogin.c,v 1.1 1995/10/18 08:43:11 deraadt Exp $
+ */
+
+#include <sys/param.h>
+#ifdef QUOTA
+#include <sys/quota.h>
+#endif
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/timeb.h>
+#include <sys/resource.h>
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <errno.h>
+
+#include "skey.h"
+
+#define _PATH_KEYFILE "/etc/skeykeys"
+
+char *skipspace __ARGS((char *));
+int skeylookup __ARGS((struct skey *, char *));
+
+/* Issue a skey challenge for user 'name'. If successful,
+ * fill in the caller's skey structure and return 0. If unsuccessful
+ * (e.g., if name is unknown) return -1.
+ *
+ * The file read/write pointer is left at the start of the
+ * record.
+ */
+int
+getskeyprompt(mp,name,prompt)
+ struct skey *mp;
+ char *name;
+ char *prompt;
+{
+ int rval;
+
+ sevenbit(name);
+ rval = skeylookup(mp,name);
+ strcpy(prompt,"s/key 55 latour1\n");
+ switch (rval) {
+ case -1: /* File error */
+ return -1;
+ case 0: /* Lookup succeeded, return challenge */
+ sprintf(prompt,"s/key %d %s\n",mp->n - 1,mp->seed);
+ return 0;
+ case 1: /* User not found */
+ fclose(mp->keyfile);
+ return -1;
+ }
+ return -1; /* Can't happen */
+}
+
+/* Return a skey challenge string for user 'name'. If successful,
+ * fill in the caller's skey structure and return 0. If unsuccessful
+ * (e.g., if name is unknown) return -1.
+ *
+ * The file read/write pointer is left at the start of the
+ * record.
+ */
+int
+skeychallenge(mp,name, ss)
+ struct skey *mp;
+ char *name;
+ char *ss;
+{
+ int rval;
+
+ rval = skeylookup(mp,name);
+ switch(rval){
+ case -1: /* File error */
+ return -1;
+ case 0: /* Lookup succeeded, issue challenge */
+ sprintf(ss, "s/key %d %s",mp->n - 1,mp->seed);
+ return 0;
+ case 1: /* User not found */
+ fclose(mp->keyfile);
+ return -1;
+ }
+ return -1; /* Can't happen */
+}
+
+/* Find an entry in the One-time Password database.
+ * Return codes:
+ * -1: error in opening database
+ * 0: entry found, file R/W pointer positioned at beginning of record
+ * 1: entry not found, file R/W pointer positioned at EOF
+ */
+int
+skeylookup(mp,name)
+ struct skey *mp;
+ char *name;
+{
+ int found;
+ int len;
+ long recstart;
+ char *cp;
+ struct stat statbuf;
+
+ /* See if the _PATH_KEYFILE exists, and create it if not */
+
+ if (stat(_PATH_KEYFILE,&statbuf) == -1 && errno == ENOENT) {
+ mp->keyfile = fopen(_PATH_KEYFILE,"w+");
+ if (mp->keyfile)
+ chmod(_PATH_KEYFILE, 0644);
+ } else {
+ /* Otherwise open normally for update */
+ mp->keyfile = fopen(_PATH_KEYFILE,"r+");
+ }
+ if (mp->keyfile == NULL)
+ return -1;
+
+ /* Look up user name in database */
+ len = strlen(name);
+ if( len > 8 ) len = 8; /* Added 8/2/91 - nmh */
+ found = 0;
+ while (!feof(mp->keyfile)) {
+ recstart = ftell(mp->keyfile);
+ mp->recstart = recstart;
+ if (fgets(mp->buf,sizeof(mp->buf),mp->keyfile) != mp->buf) {
+ break;
+ }
+ rip(mp->buf);
+ if (mp->buf[0] == '#')
+ continue; /* Comment */
+ if ((mp->logname = strtok(mp->buf," \t")) == NULL)
+ continue;
+ if ((cp = strtok(NULL," \t")) == NULL)
+ continue;
+ mp->n = atoi(cp);
+ if ((mp->seed = strtok(NULL," \t")) == NULL)
+ continue;
+ if ((mp->val = strtok(NULL," \t")) == NULL)
+ continue;
+ if (strlen(mp->logname) == len
+ && strncmp(mp->logname,name,len) == 0){
+ found = 1;
+ break;
+ }
+ }
+ if (found) {
+ fseek(mp->keyfile,recstart,0);
+ return 0;
+ } else
+ return 1;
+}
+/* Verify response to a s/key challenge.
+ *
+ * Return codes:
+ * -1: Error of some sort; database unchanged
+ * 0: Verify successful, database updated
+ * 1: Verify failed, database unchanged
+ *
+ * The database file is always closed by this call.
+ */
+int
+skeyverify(mp,response)
+ struct skey *mp;
+ char *response;
+{
+ char key[8];
+ char fkey[8];
+ char filekey[8];
+ time_t now;
+ struct tm *tm;
+ char tbuf[27];
+ char *cp;
+
+ time(&now);
+ tm = localtime(&now);
+ strftime(tbuf, sizeof(tbuf), " %b %d,%Y %T", tm);
+
+ if (response == NULL) {
+ fclose(mp->keyfile);
+ return -1;
+ }
+ rip(response);
+
+ /* Convert response to binary */
+ if (etob(key, response) != 1 && atob8(key, response) != 0) {
+ /* Neither english words or ascii hex */
+ fclose(mp->keyfile);
+ return -1;
+ }
+
+ /* Compute fkey = f(key) */
+ memcpy(fkey,key,sizeof(key));
+ fflush (stdout);
+
+ f(fkey);
+ /*
+ * in order to make the window of update as short as possible
+ * we must do the comparison here and if OK write it back
+ * other wise the same password can be used twice to get in
+ * to the system
+ */
+
+ setpriority(PRIO_PROCESS, 0, -4);
+
+ /* reread the file record NOW*/
+
+ fseek(mp->keyfile,mp->recstart,0);
+ if (fgets(mp->buf,sizeof(mp->buf),mp->keyfile) != mp->buf){
+ setpriority(PRIO_PROCESS, 0, 0);
+ fclose(mp->keyfile);
+ return -1;
+ }
+ rip(mp->buf);
+ mp->logname = strtok(mp->buf," \t");
+ cp = strtok(NULL," \t") ;
+ mp->seed = strtok(NULL," \t");
+ mp->val = strtok(NULL," \t");
+ /* And convert file value to hex for comparison */
+ atob8(filekey,mp->val);
+
+ /* Do actual comparison */
+ fflush (stdout);
+
+ if (memcmp(filekey,fkey,8) != 0){
+ /* Wrong response */
+ setpriority(PRIO_PROCESS, 0, 0);
+ fclose(mp->keyfile);
+ return 1;
+ }
+
+ /*
+ * Update key in database by overwriting entire record. Note
+ * that we must write exactly the same number of bytes as in
+ * the original record (note fixed width field for N)
+ */
+ btoa8(mp->val,key);
+ mp->n--;
+ fseek(mp->keyfile,mp->recstart,0);
+ fprintf(mp->keyfile, "%s %04d %-16s %s %-21s\n",
+ mp->logname,mp->n,mp->seed, mp->val, tbuf);
+
+ fclose(mp->keyfile);
+
+ setpriority(PRIO_PROCESS, 0, 0);
+ return 0;
+}
+
+
+/*
+ * skey_haskey ()
+ *
+ * Returns: 1 user doesnt exist, -1 fle error, 0 user exists.
+ *
+ */
+
+int
+skey_haskey (username)
+ char *username;
+{
+ int i;
+ struct skey skey;
+
+ return (skeylookup (&skey, username));
+}
+
+/*
+ * skey_keyinfo ()
+ *
+ * Returns the current sequence number and
+ * seed for the passed user.
+ *
+ */
+char *
+skey_keyinfo (username)
+ char *username;
+{
+ int i;
+ static char str [50];
+ struct skey skey;
+
+ i = skeychallenge (&skey, username, str);
+ if (i == -1)
+ return 0;
+
+ return str;
+}
+
+/*
+ * skey_passcheck ()
+ *
+ * Check to see if answer is the correct one to the current
+ * challenge.
+ *
+ * Returns: 0 success, -1 failure
+ *
+ */
+
+int
+skey_passcheck (username, passwd)
+ char *username, *passwd;
+{
+ int i;
+ struct skey skey;
+
+ i = skeylookup (&skey, username);
+ if (i == -1 || i == 1)
+ return -1;
+
+ if (skeyverify (&skey, passwd) == 0)
+ return skey.n;
+
+ return -1;
+}
+
+/*
+ * skey_authenticate ()
+ *
+ * Used when calling program will allow input of the user's
+ * response to the challenge.
+ *
+ * Returns: 0 success, -1 failure
+ *
+ */
+
+int
+skey_authenticate (username)
+ char *username;
+{
+ int i;
+ char pbuf[256], skeyprompt[50];
+ struct skey skey;
+
+ /* Attempt a S/Key challenge */
+ i = skeychallenge (&skey, username, skeyprompt);
+
+ if (i == -2)
+ return 0;
+
+ printf("[%s]\n", skeyprompt);
+ fflush(stdout);
+
+ printf("Response: ");
+ readskey(pbuf, sizeof (pbuf));
+ rip(pbuf);
+
+ /* Is it a valid response? */
+ if (i == 0 && skeyverify (&skey, pbuf) == 0) {
+ if (skey.n < 5) {
+ printf ("\nWarning! Key initialization needed soon. ");
+ printf ("(%d logins left)\n", skey.n);
+ }
+ return 0;
+ }
+ return -1;
+}
diff --git a/lib/libskey/skeysubr.c b/lib/libskey/skeysubr.c
new file mode 100644
index 00000000000..232927a1ed5
--- /dev/null
+++ b/lib/libskey/skeysubr.c
@@ -0,0 +1,317 @@
+/* S/KEY v1.1b (skeysubr.c)
+ *
+ * Authors:
+ * Neil M. Haller <nmh@thumper.bellcore.com>
+ * Philip R. Karn <karn@chicago.qualcomm.com>
+ * John S. Walden <jsw@thumper.bellcore.com>
+ *
+ * Modifications:
+ * Scott Chasin <chasin@crimelab.com>
+ *
+ * S/KEY misc routines.
+ *
+ * $Id: skeysubr.c,v 1.1 1995/10/18 08:43:11 deraadt Exp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <termios.h>
+
+#include "md4.h"
+#include "skey.h"
+
+struct termios newtty;
+struct termios oldtty;
+
+static void trapped __ARGS((int sig));
+static void set_term __ARGS((void));
+static void unset_term __ARGS((void));
+static void echo_off __ARGS((void));
+
+/* Crunch a key:
+ * concatenate the seed and the password, run through MD4 and
+ * collapse to 64 bits. This is defined as the user's starting key.
+ */
+int
+keycrunch(result,seed,passwd)
+char *result; /* 8-byte result */
+char *seed; /* Seed, any length */
+char *passwd; /* Password, any length */
+{
+ char *buf;
+ MDstruct md;
+ unsigned int buflen;
+ int i;
+ register long tmp;
+
+ buflen = strlen(seed) + strlen(passwd);
+ if ((buf = (char *)malloc(buflen+1)) == NULL)
+ return -1;
+ strcpy(buf,seed);
+ strcat(buf,passwd);
+
+ /* Crunch the key through MD4 */
+ sevenbit(buf);
+ MDbegin(&md);
+ MDupdate(&md,(unsigned char *)buf,8*buflen);
+
+ free(buf);
+
+ /* Fold result from 128 to 64 bits */
+ md.buffer[0] ^= md.buffer[2];
+ md.buffer[1] ^= md.buffer[3];
+
+ /* Default (but slow) code that will convert to
+ * little-endian byte ordering on any machine
+ */
+ for (i=0; i<2; i++) {
+ tmp = md.buffer[i];
+ *result++ = tmp;
+ tmp >>= 8;
+ *result++ = tmp;
+ tmp >>= 8;
+ *result++ = tmp;
+ tmp >>= 8;
+ *result++ = tmp;
+ }
+
+ return 0;
+}
+
+/* The one-way function f(). Takes 8 bytes and returns 8 bytes in place */
+void
+f(x)
+ char *x;
+{
+ MDstruct md;
+ register long tmp;
+
+ MDbegin(&md);
+ MDupdate(&md,(unsigned char *)x,64);
+
+ /* Fold 128 to 64 bits */
+ md.buffer[0] ^= md.buffer[2];
+ md.buffer[1] ^= md.buffer[3];
+
+ /* Default (but slow) code that will convert to
+ * little-endian byte ordering on any machine
+ */
+ tmp = md.buffer[0];
+ *x++ = tmp;
+ tmp >>= 8;
+ *x++ = tmp;
+ tmp >>= 8;
+ *x++ = tmp;
+ tmp >>= 8;
+ *x++ = tmp;
+
+ tmp = md.buffer[1];
+ *x++ = tmp;
+ tmp >>= 8;
+ *x++ = tmp;
+ tmp >>= 8;
+ *x++ = tmp;
+ tmp >>= 8;
+ *x = tmp;
+}
+
+/* Strip trailing cr/lf from a line of text */
+void
+rip(buf)
+ char *buf;
+{
+ char *cp;
+
+ if ((cp = strchr(buf,'\r')) != NULL)
+ *cp = '\0';
+
+ if ((cp = strchr(buf,'\n')) != NULL)
+ *cp = '\0';
+}
+
+char *
+readpass (buf,n)
+ char *buf;
+ int n;
+{
+ set_term();
+ echo_off();
+
+ fgets(buf, n, stdin);
+
+ rip(buf);
+ printf("\n");
+
+ sevenbit(buf);
+
+ unset_term();
+ return buf;
+}
+
+char *
+readskey(buf, n)
+ char *buf;
+ int n;
+{
+ fgets (buf, n, stdin);
+
+ rip(buf);
+ printf ("\n");
+
+ sevenbit (buf);
+
+ return buf;
+}
+
+static void
+set_term()
+{
+ fflush(stdout);
+ tcgetattr(fileno(stdin), &newtty);
+ tcgetattr(fileno(stdin), &oldtty);
+
+ signal (SIGINT, trapped);
+}
+
+static void
+echo_off()
+{
+ newtty.c_lflag &= ~(ICANON | ECHO | ECHONL);
+ newtty.c_cc[VMIN] = 1;
+ newtty.c_cc[VTIME] = 0;
+ newtty.c_cc[VINTR] = 3;
+
+ tcsetattr(fileno(stdin), TCSADRAIN, &newtty);
+}
+
+static void
+unset_term()
+{
+ tcsetattr(fileno(stdin), TCSADRAIN, &oldtty);
+}
+
+static void
+trapped(sig)
+ int sig;
+{
+ signal(SIGINT, trapped);
+ printf("^C\n");
+ unset_term();
+ exit(-1);
+}
+
+/* Convert 8-byte hex-ascii string to binary array
+ * Returns 0 on success, -1 on error
+ */
+int
+atob8(out, in)
+ register char *out, *in;
+{
+ register int i;
+ register int val;
+
+ if (in == NULL || out == NULL)
+ return -1;
+
+ for (i=0; i<8; i++) {
+ if ((in = skipspace(in)) == NULL)
+ return -1;
+ if ((val = htoi(*in++)) == -1)
+ return -1;
+ *out = val << 4;
+
+ if ((in = skipspace(in)) == NULL)
+ return -1;
+ if ((val = htoi(*in++)) == -1)
+ return -1;
+ *out++ |= val;
+ }
+ return 0;
+}
+
+/* Convert 8-byte binary array to hex-ascii string */
+int
+btoa8(out, in)
+ register char *out, *in;
+{
+ register int i;
+
+ if (in == NULL || out == NULL)
+ return -1;
+
+ for (i=0;i<8;i++) {
+ sprintf(out,"%02x",*in++ & 0xff);
+ out += 2;
+ }
+ return 0;
+}
+
+
+/* Convert hex digit to binary integer */
+int
+htoi(c)
+ register char c;
+{
+ if ('0' <= c && c <= '9')
+ return c - '0';
+ if ('a' <= c && c <= 'f')
+ return 10 + c - 'a';
+ if ('A' <= c && c <= 'F')
+ return 10 + c - 'A';
+ return -1;
+}
+
+char *
+skipspace(cp)
+ register char *cp;
+{
+ while (*cp == ' ' || *cp == '\t')
+ cp++;
+
+ if (*cp == '\0')
+ return NULL;
+ else
+ return cp;
+}
+
+/* removebackspaced over charaters from the string */
+void
+backspace(buf)
+char *buf;
+{
+ char bs = 0x8;
+ char *cp = buf;
+ char *out = buf;
+
+ while (*cp) {
+ if (*cp == bs) {
+ if (out == buf) {
+ cp++;
+ continue;
+ }
+ else {
+ cp++;
+ out--;
+ }
+ } else {
+ *out++ = *cp++;
+ }
+
+ }
+ *out = '\0';
+}
+
+/* sevenbit ()
+ *
+ * Make sure line is all seven bits.
+ */
+
+void
+sevenbit(s)
+ char *s;
+{
+ while (*s)
+ *s++ &= 0x7f;
+}