1# otp.tcl - Copyright (C) 2006 Pat Thoyts <patthoyts@users.sourceforge.net>
2#
3# Tcl implementation of RFC 2289: A One-Time Password System
4#
5# -------------------------------------------------------------------------
6# See the file "license.terms" for information on usage and redistribution
7# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
8# -------------------------------------------------------------------------
9
10
11package require Tcl 8.2;                # tcl minimum version
12
13namespace eval ::otp {
14    variable version 1.0.0
15    variable rcsid {$Id: otp.tcl,v 1.2 2006/09/02 22:30:17 patthoyts Exp $}
16
17    namespace export otp-md4 otp-md5 otp-sha1 otp-rmd160
18
19    variable Words {
20        "A"     "ABE"   "ACE"   "ACT"   "AD"    "ADA"   "ADD"
21        "AGO"   "AID"   "AIM"   "AIR"   "ALL"   "ALP"   "AM"    "AMY"
22        "AN"    "ANA"   "AND"   "ANN"   "ANT"   "ANY"   "APE"   "APS"
23        "APT"   "ARC"   "ARE"   "ARK"   "ARM"   "ART"   "AS"    "ASH"
24        "ASK"   "AT"    "ATE"   "AUG"   "AUK"   "AVE"   "AWE"   "AWK"
25        "AWL"   "AWN"   "AX"    "AYE"   "BAD"   "BAG"   "BAH"   "BAM"
26        "BAN"   "BAR"   "BAT"   "BAY"   "BE"    "BED"   "BEE"   "BEG"
27        "BEN"   "BET"   "BEY"   "BIB"   "BID"   "BIG"   "BIN"   "BIT"
28        "BOB"   "BOG"   "BON"   "BOO"   "BOP"   "BOW"   "BOY"   "BUB"
29        "BUD"   "BUG"   "BUM"   "BUN"   "BUS"   "BUT"   "BUY"   "BY"
30        "BYE"   "CAB"   "CAL"   "CAM"   "CAN"   "CAP"   "CAR"   "CAT"
31        "CAW"   "COD"   "COG"   "COL"   "CON"   "COO"   "COP"   "COT"
32        "COW"   "COY"   "CRY"   "CUB"   "CUE"   "CUP"   "CUR"   "CUT"
33        "DAB"   "DAD"   "DAM"   "DAN"   "DAR"   "DAY"   "DEE"   "DEL"
34        "DEN"   "DES"   "DEW"   "DID"   "DIE"   "DIG"   "DIN"   "DIP"
35        "DO"    "DOE"   "DOG"   "DON"   "DOT"   "DOW"   "DRY"   "DUB"
36        "DUD"   "DUE"   "DUG"   "DUN"   "EAR"   "EAT"   "ED"    "EEL"
37        "EGG"   "EGO"   "ELI"   "ELK"   "ELM"   "ELY"   "EM"    "END"
38        "EST"   "ETC"   "EVA"   "EVE"   "EWE"   "EYE"   "FAD"   "FAN"
39        "FAR"   "FAT"   "FAY"   "FED"   "FEE"   "FEW"   "FIB"   "FIG"
40        "FIN"   "FIR"   "FIT"   "FLO"   "FLY"   "FOE"   "FOG"   "FOR"
41        "FRY"   "FUM"   "FUN"   "FUR"   "GAB"   "GAD"   "GAG"   "GAL"
42        "GAM"   "GAP"   "GAS"   "GAY"   "GEE"   "GEL"   "GEM"   "GET"
43        "GIG"   "GIL"   "GIN"   "GO"    "GOT"   "GUM"   "GUN"   "GUS"
44        "GUT"   "GUY"   "GYM"   "GYP"   "HA"    "HAD"   "HAL"   "HAM"
45        "HAN"   "HAP"   "HAS"   "HAT"   "HAW"   "HAY"   "HE"    "HEM"
46        "HEN"   "HER"   "HEW"   "HEY"   "HI"    "HID"   "HIM"   "HIP"
47        "HIS"   "HIT"   "HO"   "HOB"   "HOC"   "HOE"   "HOG"   "HOP"
48        "HOT"   "HOW"   "HUB"   "HUE"   "HUG"   "HUH"   "HUM"   "HUT"
49        "I"     "ICY"   "IDA"   "IF"    "IKE"   "ILL"   "INK"   "INN"
50        "IO"    "ION"   "IQ"   "IRA"   "IRE"   "IRK"   "IS"    "IT"
51        "ITS"   "IVY"   "JAB"   "JAG"   "JAM"   "JAN"   "JAR"   "JAW"
52        "JAY"   "JET"   "JIG"   "JIM"   "JO"    "JOB"   "JOE"   "JOG"
53        "JOT"   "JOY"   "JUG"   "JUT"   "KAY"   "KEG"   "KEN"   "KEY"
54        "KID"   "KIM"   "KIN"   "KIT"   "LA"    "LAB"   "LAC"   "LAD"
55        "LAG"   "LAM"   "LAP"   "LAW"   "LAY"   "LEA"   "LED"   "LEE"
56        "LEG"   "LEN"   "LEO"   "LET"   "LEW"   "LID"   "LIE"   "LIN"
57        "LIP"   "LIT"   "LO"   "LOB"   "LOG"   "LOP"   "LOS"   "LOT"
58        "LOU"   "LOW"   "LOY"   "LUG"   "LYE"   "MA"    "MAC"   "MAD"
59        "MAE"   "MAN"   "MAO"   "MAP"   "MAT"   "MAW"   "MAY"   "ME"
60        "MEG"   "MEL"   "MEN"   "MET"   "MEW"   "MID"   "MIN"   "MIT"
61        "MOB"   "MOD"   "MOE"   "MOO"   "MOP"   "MOS"   "MOT"   "MOW"
62        "MUD"   "MUG"   "MUM"   "MY"    "NAB"   "NAG"   "NAN"   "NAP"
63        "NAT"   "NAY"   "NE"   "NED"   "NEE"   "NET"   "NEW"   "NIB"
64        "NIL"   "NIP"   "NIT"   "NO"    "NOB"   "NOD"   "NON"   "NOR"
65        "NOT"   "NOV"   "NOW"   "NU"    "NUN"   "NUT"   "O"     "OAF"
66        "OAK"   "OAR"   "OAT"   "ODD"   "ODE"   "OF"    "OFF"   "OFT"
67        "OH"    "OIL"   "OK"   "OLD"   "ON"    "ONE"   "OR"    "ORB"
68        "ORE"   "ORR"   "OS"   "OTT"   "OUR"   "OUT"   "OVA"   "OW"
69        "OWE"   "OWL"   "OWN"   "OX"    "PA"    "PAD"   "PAL"   "PAM"
70        "PAN"   "PAP"   "PAR"   "PAT"   "PAW"   "PAY"   "PEA"   "PEG"
71        "PEN"   "PEP"   "PER"   "PET"   "PEW"   "PHI"   "PI"    "PIE"
72        "PIN"   "PIT"   "PLY"   "PO"    "POD"   "POE"   "POP"   "POT"
73        "POW"   "PRO"   "PRY"   "PUB"   "PUG"   "PUN"   "PUP"   "PUT"
74        "QUO"   "RAG"   "RAM"   "RAN"   "RAP"   "RAT"   "RAW"   "RAY"
75        "REB"   "RED"   "REP"   "RET"   "RIB"   "RID"   "RIG"   "RIM"
76        "RIO"   "RIP"   "ROB"   "ROD"   "ROE"   "RON"   "ROT"   "ROW"
77        "ROY"   "RUB"   "RUE"   "RUG"   "RUM"   "RUN"   "RYE"   "SAC"
78        "SAD"   "SAG"   "SAL"   "SAM"   "SAN"   "SAP"   "SAT"   "SAW"
79        "SAY"   "SEA"   "SEC"   "SEE"   "SEN"   "SET"   "SEW"   "SHE"
80        "SHY"   "SIN"   "SIP"   "SIR"   "SIS"   "SIT"   "SKI"   "SKY"
81        "SLY"   "SO"    "SOB"   "SOD"   "SON"   "SOP"   "SOW"   "SOY"
82        "SPA"   "SPY"   "SUB"   "SUD"   "SUE"   "SUM"   "SUN"   "SUP"
83        "TAB"   "TAD"   "TAG"   "TAN"   "TAP"   "TAR"   "TEA"   "TED"
84        "TEE"   "TEN"   "THE"   "THY"   "TIC"   "TIE"   "TIM"   "TIN"
85        "TIP"   "TO"    "TOE"   "TOG"   "TOM"   "TON"   "TOO"   "TOP"
86        "TOW"   "TOY"   "TRY"   "TUB"   "TUG"   "TUM"   "TUN"   "TWO"
87        "UN"    "UP"    "US"   "USE"   "VAN"   "VAT"   "VET"   "VIE"
88        "WAD"   "WAG"   "WAR"   "WAS"   "WAY"   "WE"    "WEB"   "WED"
89        "WEE"   "WET"   "WHO"   "WHY"   "WIN"   "WIT"   "WOK"   "WON"
90        "WOO"   "WOW"   "WRY"   "WU"    "YAM"   "YAP"   "YAW"   "YE"
91        "YEA"   "YES"   "YET"   "YOU"   "ABED"  "ABEL"  "ABET"  "ABLE"
92        "ABUT"  "ACHE"  "ACID"  "ACME"  "ACRE"  "ACTA"  "ACTS"  "ADAM"
93        "ADDS"  "ADEN"  "AFAR"  "AFRO"  "AGEE"  "AHEM"  "AHOY"  "AIDA"
94        "AIDE"  "AIDS"  "AIRY"  "AJAR"  "AKIN"  "ALAN"  "ALEC"  "ALGA"
95        "ALIA"  "ALLY"  "ALMA"  "ALOE"  "ALSO"  "ALTO"  "ALUM"  "ALVA"
96        "AMEN"  "AMES"  "AMID"  "AMMO"  "AMOK"  "AMOS"  "AMRA"  "ANDY"
97        "ANEW"  "ANNA"  "ANNE"  "ANTE"  "ANTI"  "AQUA"  "ARAB"  "ARCH"
98        "AREA"  "ARGO"  "ARID"  "ARMY"  "ARTS"  "ARTY"  "ASIA"  "ASKS"
99        "ATOM"  "AUNT"  "AURA"  "AUTO"  "AVER"  "AVID"  "AVIS"  "AVON"
100        "AVOW"  "AWAY"  "AWRY"  "BABE"  "BABY"  "BACH"  "BACK"  "BADE"
101        "BAIL"  "BAIT"  "BAKE"  "BALD"  "BALE"  "BALI"  "BALK"  "BALL"
102        "BALM"  "BAND"  "BANE"  "BANG"  "BANK"  "BARB"  "BARD"  "BARE"
103        "BARK"  "BARN"  "BARR"  "BASE"  "BASH"  "BASK"  "BASS"  "BATE"
104        "BATH"  "BAWD"  "BAWL"  "BEAD"  "BEAK"  "BEAM"  "BEAN"  "BEAR"
105        "BEAT"  "BEAU"  "BECK"  "BEEF"  "BEEN"  "BEER"  "BEET"  "BELA"
106        "BELL"  "BELT"  "BEND"  "BENT"  "BERG"  "BERN"  "BERT"  "BESS"
107        "BEST"  "BETA"  "BETH"  "BHOY"  "BIAS"  "BIDE"  "BIEN"  "BILE"
108        "BILK"  "BILL"  "BIND"  "BING"  "BIRD"  "BITE"  "BITS"  "BLAB"
109        "BLAT"  "BLED"  "BLEW"  "BLOB"  "BLOC"  "BLOT"  "BLOW"  "BLUE"
110        "BLUM"  "BLUR"  "BOAR"  "BOAT"  "BOCA"  "BOCK"  "BODE"  "BODY"
111        "BOGY"  "BOHR"  "BOIL"  "BOLD"  "BOLO"  "BOLT"  "BOMB"  "BONA"
112        "BOND"  "BONE"  "BONG"  "BONN"  "BONY"  "BOOK"  "BOOM"  "BOON"
113        "BOOT"  "BORE"  "BORG"  "BORN"  "BOSE"  "BOSS"  "BOTH"  "BOUT"
114        "BOWL"  "BOYD"  "BRAD"  "BRAE"  "BRAG"  "BRAN"  "BRAY"  "BRED"
115        "BREW"  "BRIG"  "BRIM"  "BROW"  "BUCK"  "BUDD"  "BUFF"  "BULB"
116        "BULK"  "BULL"  "BUNK"  "BUNT"  "BUOY"  "BURG"  "BURL"  "BURN"
117        "BURR"  "BURT"  "BURY"  "BUSH"  "BUSS"  "BUST"  "BUSY"  "BYTE"
118        "CADY"  "CAFE"  "CAGE"  "CAIN"  "CAKE"  "CALF"  "CALL"  "CALM"
119        "CAME"  "CANE"  "CANT"  "CARD"  "CARE"  "CARL"  "CARR"  "CART"
120        "CASE"  "CASH"  "CASK"  "CAST"  "CAVE"  "CEIL"  "CELL"  "CENT"
121        "CERN"  "CHAD"  "CHAR"  "CHAT"  "CHAW"  "CHEF"  "CHEN"  "CHEW"
122        "CHIC"  "CHIN"  "CHOU"  "CHOW"  "CHUB"  "CHUG"  "CHUM"  "CITE"
123        "CITY"  "CLAD"  "CLAM"  "CLAN"  "CLAW"  "CLAY"  "CLOD"  "CLOG"
124        "CLOT"  "CLUB"  "CLUE"  "COAL"  "COAT"  "COCA"  "COCK"  "COCO"
125        "CODA"  "CODE"  "CODY"  "COED"  "COIL"  "COIN"  "COKE"  "COLA"
126        "COLD"  "COLT"  "COMA"  "COMB"  "COME"  "COOK"  "COOL"  "COON"
127        "COOT"  "CORD"  "CORE"  "CORK"  "CORN"  "COST"  "COVE"  "COWL"
128        "CRAB"  "CRAG"  "CRAM"  "CRAY"  "CREW"  "CRIB"  "CROW"  "CRUD"
129        "CUBA"  "CUBE"  "CUFF"  "CULL"  "CULT"  "CUNY"  "CURB"  "CURD"
130        "CURE"  "CURL"  "CURT"  "CUTS"  "DADE"  "DALE"  "DAME"  "DANA"
131        "DANE"  "DANG"  "DANK"  "DARE"  "DARK"  "DARN"  "DART"  "DASH"
132        "DATA"  "DATE"  "DAVE"  "DAVY"  "DAWN"  "DAYS"  "DEAD"  "DEAF"
133        "DEAL"  "DEAN"  "DEAR"  "DEBT"  "DECK"  "DEED"  "DEEM"  "DEER"
134        "DEFT"  "DEFY"  "DELL"  "DENT"  "DENY"  "DESK"  "DIAL"  "DICE"
135        "DIED"  "DIET"  "DIME"  "DINE"  "DING"  "DINT"  "DIRE"  "DIRT"
136        "DISC"  "DISH"  "DISK"  "DIVE"  "DOCK"  "DOES"  "DOLE"  "DOLL"
137        "DOLT"  "DOME"  "DONE"  "DOOM"  "DOOR"  "DORA"  "DOSE"  "DOTE"
138        "DOUG"  "DOUR"  "DOVE"  "DOWN"  "DRAB"  "DRAG"  "DRAM"  "DRAW"
139        "DREW"  "DRUB"  "DRUG"  "DRUM"  "DUAL"  "DUCK"  "DUCT"  "DUEL"
140        "DUET"  "DUKE"  "DULL"  "DUMB"  "DUNE"  "DUNK"  "DUSK"  "DUST"
141        "DUTY"  "EACH"  "EARL"  "EARN"  "EASE"  "EAST"  "EASY"  "EBEN"
142        "ECHO"  "EDDY"  "EDEN"  "EDGE"  "EDGY"  "EDIT"  "EDNA"  "EGAN"
143        "ELAN"  "ELBA"  "ELLA"  "ELSE"  "EMIL"  "EMIT"  "EMMA"  "ENDS"
144        "ERIC"  "EROS"  "EVEN"  "EVER"  "EVIL"  "EYED"  "FACE"  "FACT"
145        "FADE"  "FAIL"  "FAIN"  "FAIR"  "FAKE"  "FALL"  "FAME"  "FANG"
146        "FARM"  "FAST"  "FATE"  "FAWN"  "FEAR"  "FEAT"  "FEED"  "FEEL"
147        "FEET"  "FELL"  "FELT"  "FEND"  "FERN"  "FEST"  "FEUD"  "FIEF"
148        "FIGS"  "FILE"  "FILL"  "FILM"  "FIND"  "FINE"  "FINK"  "FIRE"
149        "FIRM"  "FISH"  "FISK"  "FIST"  "FITS"  "FIVE"  "FLAG"  "FLAK"
150        "FLAM"  "FLAT"  "FLAW"  "FLEA"  "FLED"  "FLEW"  "FLIT"  "FLOC"
151        "FLOG"  "FLOW"  "FLUB"  "FLUE"  "FOAL"  "FOAM"  "FOGY"  "FOIL"
152        "FOLD"  "FOLK"  "FOND"  "FONT"  "FOOD"  "FOOL"  "FOOT"  "FORD"
153        "FORE"  "FORK"  "FORM"  "FORT"  "FOSS"  "FOUL"  "FOUR"  "FOWL"
154        "FRAU"  "FRAY"  "FRED"  "FREE"  "FRET"  "FREY"  "FROG"  "FROM"
155        "FUEL"  "FULL"  "FUME"  "FUND"  "FUNK"  "FURY"  "FUSE"  "FUSS"
156        "GAFF"  "GAGE"  "GAIL"  "GAIN"  "GAIT"  "GALA"  "GALE"  "GALL"
157        "GALT"  "GAME"  "GANG"  "GARB"  "GARY"  "GASH"  "GATE"  "GAUL"
158        "GAUR"  "GAVE"  "GAWK"  "GEAR"  "GELD"  "GENE"  "GENT"  "GERM"
159        "GETS"  "GIBE"  "GIFT"  "GILD"  "GILL"  "GILT"  "GINA"  "GIRD"
160        "GIRL"  "GIST"  "GIVE"  "GLAD"  "GLEE"  "GLEN"  "GLIB"  "GLOB"
161        "GLOM"  "GLOW"  "GLUE"  "GLUM"  "GLUT"  "GOAD"  "GOAL"  "GOAT"
162        "GOER"  "GOES"  "GOLD"  "GOLF"  "GONE"  "GONG"  "GOOD"  "GOOF"
163        "GORE"  "GORY"  "GOSH"  "GOUT"  "GOWN"  "GRAB"  "GRAD"  "GRAY"
164        "GREG"  "GREW"  "GREY"  "GRID"  "GRIM"  "GRIN"  "GRIT"  "GROW"
165        "GRUB"  "GULF"  "GULL"  "GUNK"  "GURU"  "GUSH"  "GUST"  "GWEN"
166        "GWYN"  "HAAG"  "HAAS"  "HACK"  "HAIL"  "HAIR"  "HALE"  "HALF"
167        "HALL"  "HALO"  "HALT"  "HAND"  "HANG"  "HANK"  "HANS"  "HARD"
168        "HARK"  "HARM"  "HART"  "HASH"  "HAST"  "HATE"  "HATH"  "HAUL"
169        "HAVE"  "HAWK"  "HAYS"  "HEAD"  "HEAL"  "HEAR"  "HEAT"  "HEBE"
170        "HECK"  "HEED"  "HEEL"  "HEFT"  "HELD"  "HELL"  "HELM"  "HERB"
171        "HERD"  "HERE"  "HERO"  "HERS"  "HESS"  "HEWN"  "HICK"  "HIDE"
172        "HIGH"  "HIKE"  "HILL"  "HILT"  "HIND"  "HINT"  "HIRE"  "HISS"
173        "HIVE"  "HOBO"  "HOCK"  "HOFF"  "HOLD"  "HOLE"  "HOLM"  "HOLT"
174        "HOME"  "HONE"  "HONK"  "HOOD"  "HOOF"  "HOOK"  "HOOT"  "HORN"
175        "HOSE"  "HOST"  "HOUR"  "HOVE"  "HOWE"  "HOWL"  "HOYT"  "HUCK"
176        "HUED"  "HUFF"  "HUGE"  "HUGH"  "HUGO"  "HULK"  "HULL"  "HUNK"
177        "HUNT"  "HURD"  "HURL"  "HURT"  "HUSH"  "HYDE"  "HYMN"  "IBIS"
178        "ICON"  "IDEA"  "IDLE"  "IFFY"  "INCA"  "INCH"  "INTO"  "IONS"
179        "IOTA"  "IOWA"  "IRIS"  "IRMA"  "IRON"  "ISLE"  "ITCH"  "ITEM"
180        "IVAN"  "JACK"  "JADE"  "JAIL"  "JAKE"  "JANE"  "JAVA"  "JEAN"
181        "JEFF"  "JERK"  "JESS"  "JEST"  "JIBE"  "JILL"  "JILT"  "JIVE"
182        "JOAN"  "JOBS"  "JOCK"  "JOEL"  "JOEY"  "JOHN"  "JOIN"  "JOKE"
183        "JOLT"  "JOVE"  "JUDD"  "JUDE"  "JUDO"  "JUDY"  "JUJU"  "JUKE"
184        "JULY"  "JUNE"  "JUNK"  "JUNO"  "JURY"  "JUST"  "JUTE"  "KAHN"
185        "KALE"  "KANE"  "KANT"  "KARL"  "KATE"  "KEEL"  "KEEN"  "KENO"
186        "KENT"  "KERN"  "KERR"  "KEYS"  "KICK"  "KILL"  "KIND"  "KING"
187        "KIRK"  "KISS"  "KITE"  "KLAN"  "KNEE"  "KNEW"  "KNIT"  "KNOB"
188        "KNOT"  "KNOW"  "KOCH"  "KONG"  "KUDO"  "KURD"  "KURT"  "KYLE"
189        "LACE"  "LACK"  "LACY"  "LADY"  "LAID"  "LAIN"  "LAIR"  "LAKE"
190        "LAMB"  "LAME"  "LAND"  "LANE"  "LANG"  "LARD"  "LARK"  "LASS"
191        "LAST"  "LATE"  "LAUD"  "LAVA"  "LAWN"  "LAWS"  "LAYS"  "LEAD"
192        "LEAF"  "LEAK"  "LEAN"  "LEAR"  "LEEK"  "LEER"  "LEFT"  "LEND"
193        "LENS"  "LENT"  "LEON"  "LESK"  "LESS"  "LEST"  "LETS"  "LIAR"
194        "LICE"  "LICK"  "LIED"  "LIEN"  "LIES"  "LIEU"  "LIFE"  "LIFT"
195        "LIKE"  "LILA"  "LILT"  "LILY"  "LIMA"  "LIMB"  "LIME"  "LIND"
196        "LINE"  "LINK"  "LINT"  "LION"  "LISA"  "LIST"  "LIVE"  "LOAD"
197        "LOAF"  "LOAM"  "LOAN"  "LOCK"  "LOFT"  "LOGE"  "LOIS"  "LOLA"
198        "LONE"  "LONG"  "LOOK"  "LOON"  "LOOT"  "LORD"  "LORE"  "LOSE"
199        "LOSS"  "LOST"  "LOUD"  "LOVE"  "LOWE"  "LUCK"  "LUCY"  "LUGE"
200        "LUKE"  "LULU"  "LUND"  "LUNG"  "LURA"  "LURE"  "LURK"  "LUSH"
201        "LUST"  "LYLE"  "LYNN"  "LYON"  "LYRA"  "MACE"  "MADE"  "MAGI"
202        "MAID"  "MAIL"  "MAIN"  "MAKE"  "MALE"  "MALI"  "MALL"  "MALT"
203        "MANA"  "MANN"  "MANY"  "MARC"  "MARE"  "MARK"  "MARS"  "MART"
204        "MARY"  "MASH"  "MASK"  "MASS"  "MAST"  "MATE"  "MATH"  "MAUL"
205        "MAYO"  "MEAD"  "MEAL"  "MEAN"  "MEAT"  "MEEK"  "MEET"  "MELD"
206        "MELT"  "MEMO"  "MEND"  "MENU"  "MERT"  "MESH"  "MESS"  "MICE"
207        "MIKE"  "MILD"  "MILE"  "MILK"  "MILL"  "MILT"  "MIMI"  "MIND"
208        "MINE"  "MINI"  "MINK"  "MINT"  "MIRE"  "MISS"  "MIST"  "MITE"
209        "MITT"  "MOAN"  "MOAT"  "MOCK"  "MODE"  "MOLD"  "MOLE"  "MOLL"
210        "MOLT"  "MONA"  "MONK"  "MONT"  "MOOD"  "MOON"  "MOOR"  "MOOT"
211        "MORE"  "MORN"  "MORT"  "MOSS"  "MOST"  "MOTH"  "MOVE"  "MUCH"
212        "MUCK"  "MUDD"  "MUFF"  "MULE"  "MULL"  "MURK"  "MUSH"  "MUST"
213        "MUTE"  "MUTT"  "MYRA"  "MYTH"  "NAGY"  "NAIL"  "NAIR"  "NAME"
214        "NARY"  "NASH"  "NAVE"  "NAVY"  "NEAL"  "NEAR"  "NEAT"  "NECK"
215        "NEED"  "NEIL"  "NELL"  "NEON"  "NERO"  "NESS"  "NEST"  "NEWS"
216        "NEWT"  "NIBS"  "NICE"  "NICK"  "NILE"  "NINA"  "NINE"  "NOAH"
217        "NODE"  "NOEL"  "NOLL"  "NONE"  "NOOK"  "NOON"  "NORM"  "NOSE"
218        "NOTE"  "NOUN"  "NOVA"  "NUDE"  "NULL"  "NUMB"  "OATH"  "OBEY"
219        "OBOE"  "ODIN"  "OHIO"  "OILY"  "OINT"  "OKAY"  "OLAF"  "OLDY"
220        "OLGA"  "OLIN"  "OMAN"  "OMEN"  "OMIT"  "ONCE"  "ONES"  "ONLY"
221        "ONTO"  "ONUS"  "ORAL"  "ORGY"  "OSLO"  "OTIS"  "OTTO"  "OUCH"
222        "OUST"  "OUTS"  "OVAL"  "OVEN"  "OVER"  "OWLY"  "OWNS"  "QUAD"
223        "QUIT"  "QUOD"  "RACE"  "RACK"  "RACY"  "RAFT"  "RAGE"  "RAID"
224        "RAIL"  "RAIN"  "RAKE"  "RANK"  "RANT"  "RARE"  "RASH"  "RATE"
225        "RAVE"  "RAYS"  "READ"  "REAL"  "REAM"  "REAR"  "RECK"  "REED"
226        "REEF"  "REEK"  "REEL"  "REID"  "REIN"  "RENA"  "REND"  "RENT"
227        "REST"  "RICE"  "RICH"  "RICK"  "RIDE"  "RIFT"  "RILL"  "RIME"
228        "RING"  "RINK"  "RISE"  "RISK"  "RITE"  "ROAD"  "ROAM"  "ROAR"
229        "ROBE"  "ROCK"  "RODE"  "ROIL"  "ROLL"  "ROME"  "ROOD"  "ROOF"
230        "ROOK"  "ROOM"  "ROOT"  "ROSA"  "ROSE"  "ROSS"  "ROSY"  "ROTH"
231        "ROUT"  "ROVE"  "ROWE"  "ROWS"  "RUBE"  "RUBY"  "RUDE"  "RUDY"
232        "RUIN"  "RULE"  "RUNG"  "RUNS"  "RUNT"  "RUSE"  "RUSH"  "RUSK"
233        "RUSS"  "RUST"  "RUTH"  "SACK"  "SAFE"  "SAGE"  "SAID"  "SAIL"
234        "SALE"  "SALK"  "SALT"  "SAME"  "SAND"  "SANE"  "SANG"  "SANK"
235        "SARA"  "SAUL"  "SAVE"  "SAYS"  "SCAN"  "SCAR"  "SCAT"  "SCOT"
236        "SEAL"  "SEAM"  "SEAR"  "SEAT"  "SEED"  "SEEK"  "SEEM"  "SEEN"
237        "SEES"  "SELF"  "SELL"  "SEND"  "SENT"  "SETS"  "SEWN"  "SHAG"
238        "SHAM"  "SHAW"  "SHAY"  "SHED"  "SHIM"  "SHIN"  "SHOD"  "SHOE"
239        "SHOT"  "SHOW"  "SHUN"  "SHUT"  "SICK"  "SIDE"  "SIFT"  "SIGH"
240        "SIGN"  "SILK"  "SILL"  "SILO"  "SILT"  "SINE"  "SING"  "SINK"
241        "SIRE"  "SITE"  "SITS"  "SITU"  "SKAT"  "SKEW"  "SKID"  "SKIM"
242        "SKIN"  "SKIT"  "SLAB"  "SLAM"  "SLAT"  "SLAY"  "SLED"  "SLEW"
243        "SLID"  "SLIM"  "SLIT"  "SLOB"  "SLOG"  "SLOT"  "SLOW"  "SLUG"
244        "SLUM"  "SLUR"  "SMOG"  "SMUG"  "SNAG"  "SNOB"  "SNOW"  "SNUB"
245        "SNUG"  "SOAK"  "SOAR"  "SOCK"  "SODA"  "SOFA"  "SOFT"  "SOIL"
246        "SOLD"  "SOME"  "SONG"  "SOON"  "SOOT"  "SORE"  "SORT"  "SOUL"
247        "SOUR"  "SOWN"  "STAB"  "STAG"  "STAN"  "STAR"  "STAY"  "STEM"
248        "STEW"  "STIR"  "STOW"  "STUB"  "STUN"  "SUCH"  "SUDS"  "SUIT"
249        "SULK"  "SUMS"  "SUNG"  "SUNK"  "SURE"  "SURF"  "SWAB"  "SWAG"
250        "SWAM"  "SWAN"  "SWAT"  "SWAY"  "SWIM"  "SWUM"  "TACK"  "TACT"
251        "TAIL"  "TAKE"  "TALE"  "TALK"  "TALL"  "TANK"  "TASK"  "TATE"
252        "TAUT"  "TEAL"  "TEAM"  "TEAR"  "TECH"  "TEEM"  "TEEN"  "TEET"
253        "TELL"  "TEND"  "TENT"  "TERM"  "TERN"  "TESS"  "TEST"  "THAN"
254        "THAT"  "THEE"  "THEM"  "THEN"  "THEY"  "THIN"  "THIS"  "THUD"
255        "THUG"  "TICK"  "TIDE"  "TIDY"  "TIED"  "TIER"  "TILE"  "TILL"
256        "TILT"  "TIME"  "TINA"  "TINE"  "TINT"  "TINY"  "TIRE"  "TOAD"
257        "TOGO"  "TOIL"  "TOLD"  "TOLL"  "TONE"  "TONG"  "TONY"  "TOOK"
258        "TOOL"  "TOOT"  "TORE"  "TORN"  "TOTE"  "TOUR"  "TOUT"  "TOWN"
259        "TRAG"  "TRAM"  "TRAY"  "TREE"  "TREK"  "TRIG"  "TRIM"  "TRIO"
260        "TROD"  "TROT"  "TROY"  "TRUE"  "TUBA"  "TUBE"  "TUCK"  "TUFT"
261        "TUNA"  "TUNE"  "TUNG"  "TURF"  "TURN"  "TUSK"  "TWIG"  "TWIN"
262        "TWIT"  "ULAN"  "UNIT"  "URGE"  "USED"  "USER"  "USES"  "UTAH"
263        "VAIL"  "VAIN"  "VALE"  "VARY"  "VASE"  "VAST"  "VEAL"  "VEDA"
264        "VEIL"  "VEIN"  "VEND"  "VENT"  "VERB"  "VERY"  "VETO"  "VICE"
265        "VIEW"  "VINE"  "VISE"  "VOID"  "VOLT"  "VOTE"  "WACK"  "WADE"
266        "WAGE"  "WAIL"  "WAIT"  "WAKE"  "WALE"  "WALK"  "WALL"  "WALT"
267        "WAND"  "WANE"  "WANG"  "WANT"  "WARD"  "WARM"  "WARN"  "WART"
268        "WASH"  "WAST"  "WATS"  "WATT"  "WAVE"  "WAVY"  "WAYS"  "WEAK"
269        "WEAL"  "WEAN"  "WEAR"  "WEED"  "WEEK"  "WEIR"  "WELD"  "WELL"
270        "WELT"  "WENT"  "WERE"  "WERT"  "WEST"  "WHAM"  "WHAT"  "WHEE"
271        "WHEN"  "WHET"  "WHOA"  "WHOM"  "WICK"  "WIFE"  "WILD"  "WILL"
272        "WIND"  "WINE"  "WING"  "WINK"  "WINO"  "WIRE"  "WISE"  "WISH"
273        "WITH"  "WOLF"  "WONT"  "WOOD"  "WOOL"  "WORD"  "WORE"  "WORK"
274        "WORM"  "WORN"  "WOVE"  "WRIT"  "WYNN"  "YALE"  "YANG"  "YANK"
275        "YARD"  "YARN"  "YAWL"  "YAWN"  "YEAH"  "YEAR"  "YELL"  "YOGA"
276        "YOKE"
277    }
278}
279
280# Encode 64 bits as words selected from the RFC 2289 dictionary.
281# See the RFC for details. Briefly the input is broken into 11 bit
282# chunks + 2bits of checksum and each chunk selects a word from the
283# 2048 word table.
284#
285proc ::otp::otp_encode {data} {
286    variable Words
287    if {[string length $data] != 8} {
288        set bc [expr {[string length $data] * 8}]
289        return -code error "invalid input: 64 bits of data\
290            required and $bc  bits provided"
291    }
292    binary scan $data II A B
293
294    set cksum 0
295    foreach w [list $A $B] {
296        for {set n 0} {$n < 32} {incr n 2} {
297            incr cksum [expr {($w >> $n) & 3}]
298        }
299    }
300
301    set W0 [expr { (($A & 0xFFE00000) >> 21) & 0x07ff}]
302    set W1 [expr { (($A & 0x001FFC00) >> 10)}]
303    set W2 [expr { (($A & 0x000003FF) << 1)  | (($B >> 31) & 0x1)}]
304    set W3 [expr {  ($B & 0x7FF00000) >> 20}]
305    set W4 [expr {  ($B & 0x000FFE00) >> 9}]
306    set W5 [expr { (($B & 0x000001FF) << 2) | ($cksum & 3)}]
307
308    foreach w [list $W0 $W1 $W2 $W3 $W4 $W5] {
309        lappend words [lindex $Words $w]
310    }
311
312    return $words
313}
314
315# Fold a 128 bit digest in little-endian format into a 64 bit
316# little-endian output
317proc ::otp::Fold64LE {digest} {
318    binary scan $digest iiii A B C D
319    set w0 [expr {($A ^ $C) & 0xffffffff}]
320    set w1 [expr {($B ^ $D) & 0xffffffff}]
321    binary format ii $w0 $w1
322}
323
324# Fold a160 bit big-endian digest (SHA-1) into a 64 bit
325# little-endian output
326proc ::otp::Fold160BE {digest} {
327    binary scan $digest IIIII A B C D E
328    set w0 [expr {(($A ^ $C) ^ $E) & 0xffffffff}]
329    set w1 [expr { ($B ^ $D)       & 0xffffffff}]
330    binary format ii $w0 $w1
331}
332
333# Fold a 160 bit little-endian digest into a 64 bit
334# little-endian output.
335proc ::otp::Fold160LE {digest} {
336    binary scan $digest iiiii A B C D E
337    set w0 [expr {(($A ^ $C) ^ $E) & 0xffffffff}]
338    set w1 [expr { ($B ^ $D)       & 0xffffffff}]
339    binary format ii $w0 $w1
340}
341
342# Description:
343#  Pop the nth element off a list. Used in options processing.
344#
345proc ::otp::Pop {varname {nth 0}} {
346    upvar $varname args
347    set r [lindex $args $nth]
348    set args [lreplace $args $nth $nth]
349    return $r
350}
351
352proc ::otp::otp {args} {
353    array set opts {-hash md5 -seed {} -count 0 -hex 0 -words 0}
354    while {[string match -* [set option [lindex $args 0]]]} {
355        switch -exact -- $option {
356            -hex   { set opts(-hex) 1}
357            -word -
358            -words { set opts(-words) 1 }
359            -hash  { set opts(-hash) [Pop args 1] }
360            -seed  { set opts(-seed) [Pop args 1] }
361            -count { set opts(-count) [Pop args 1] }
362            default {
363                if {[llength $args] == 1} { break }
364                if {[string compare $option "--"] == 0} { Pop args; break }
365                set err [join [lsort [array names opts]] ", "]
366                return -code error "bad option \"$option\":\
367                    must be one of $err"
368            }
369        }
370        Pop args
371    }
372
373    set data [lindex $args 0]
374
375    if {[string length $opts(-seed)] < 1 || [string length $opts(-seed)] > 16} {
376        return -code error "seed must be between 1 and 16 characters in length"
377    }
378    switch -exact -- $opts(-hash) {
379        md4  { set func ::md4::md4  ; set fold ::otp::Fold64LE }
380        md5  { set func ::md5::md5  ; set fold ::otp::Fold64LE }
381        sha1 { set func ::otp::sha1 ; set fold ::otp::Fold160BE }
382        rmd160 { set func ::ripemd::ripemd160 ; set fold ::otp::Fold160LE }
383        default {
384            return -code error "invalid hash type \"$opts(-hash)\":\
385                must be one of md4, md5, rmd160 or sha1"
386        }
387    }
388    # RFC 2289: Initial step
389    set S [$fold [$func [string tolower $opts(-seed)]$data]]
390
391    # RFC2289:6 Computation step
392    for {set n 0} {$n < $opts(-count)} {incr n} {
393        set S [$fold [$func $S]]
394    }
395
396    if {$opts(-hex)} {
397        binary scan $S H* S
398    } elseif {$opts(-words)} {
399        set S [otp_encode $S]
400    }
401    return $S
402}
403
404proc ::otp::otp-md4 {args} {
405    package require md4
406    return [eval [linsert $args 0 [namespace current]::otp -hash md4]]
407}
408
409proc ::otp::otp-md5 {args} {
410    package require md5
411    return [eval [linsert $args 0 [namespace current]::otp -hash md5]]
412}
413
414proc ::otp::otp-sha1 {args} {
415    package require sha1
416    interp alias {} ::otp::sha1 {} ::sha1::sha1 -bin
417    return [eval [linsert $args 0 [namespace current]::otp -hash sha1]]
418}
419
420proc ::otp::otp-rmd160 {args} {
421    package require ripemd160
422    return [eval [linsert $args 0 [namespace current]::otp -hash rmd160]]
423}
424
425# -------------------------------------------------------------------------
426
427package provide otp $::otp::version
428
429# -------------------------------------------------------------------------
430# Local Variables:
431#   mode: tcl
432#   indent-tabs-mode: nil
433# End:
434