summaryrefslogtreecommitdiffstats
path: root/tcllib/modules/otp
diff options
context:
space:
mode:
Diffstat (limited to 'tcllib/modules/otp')
-rw-r--r--tcllib/modules/otp/ChangeLog59
-rw-r--r--tcllib/modules/otp/otp.man95
-rw-r--r--tcllib/modules/otp/otp.tcl430
-rw-r--r--tcllib/modules/otp/otp.test146
-rw-r--r--tcllib/modules/otp/pkgIndex.tcl3
5 files changed, 733 insertions, 0 deletions
diff --git a/tcllib/modules/otp/ChangeLog b/tcllib/modules/otp/ChangeLog
new file mode 100644
index 0000000..c6ecb57
--- /dev/null
+++ b/tcllib/modules/otp/ChangeLog
@@ -0,0 +1,59 @@
+2013-02-01 Andreas Kupries <andreas_kupries@users.sourceforge.net>
+
+ *
+ * Released and tagged Tcllib 1.15 ========================
+ *
+
+2011-12-13 Andreas Kupries <andreas_kupries@users.sourceforge.net>
+
+ *
+ * Released and tagged Tcllib 1.14 ========================
+ *
+
+2011-01-24 Andreas Kupries <andreas_kupries@users.sourceforge.net>
+
+ *
+ * Released and tagged Tcllib 1.13 ========================
+ *
+
+2009-12-07 Andreas Kupries <andreas_kupries@users.sourceforge.net>
+
+ *
+ * Released and tagged Tcllib 1.12 ========================
+ *
+
+2008-12-12 Andreas Kupries <andreas_kupries@users.sourceforge.net>
+
+ *
+ * Released and tagged Tcllib 1.11.1 ========================
+ *
+
+2008-10-16 Andreas Kupries <andreas_kupries@users.sourceforge.net>
+
+ *
+ * Released and tagged Tcllib 1.11 ========================
+ *
+
+2007-09-12 Andreas Kupries <andreas_kupries@users.sourceforge.net>
+
+ *
+ * Released and tagged Tcllib 1.10 ========================
+ *
+
+2007-03-21 Andreas Kupries <andreas_kupries@users.sourceforge.net>
+
+ * otp.man: Fixed all warnings due to use of now deprecated
+ commands. Added a section about how to give feedback.
+
+2006-10-03 Andreas Kupries <andreas_kupries@users.sourceforge.net>
+
+ *
+ * Released and tagged Tcllib 1.9 ========================
+ *
+
+2006-09-01 Pat Thoyts <patthoyts@users.sourceforge.net>
+
+ * modules/otp/otp.tcl: Initial version of RFC 2289 implementation
+ * modules/otp/otp.test: "A One-Time Password System"
+ * modules/otp/otp.man:
+
diff --git a/tcllib/modules/otp/otp.man b/tcllib/modules/otp/otp.man
new file mode 100644
index 0000000..cbc9ff4
--- /dev/null
+++ b/tcllib/modules/otp/otp.man
@@ -0,0 +1,95 @@
+[manpage_begin otp n 1.0.0]
+[see_also md4]
+[see_also md5]
+[see_also ripemd160]
+[see_also SASL]
+[see_also sha1]
+[keywords hashing]
+[keywords message-digest]
+[keywords password]
+[keywords {rfc 2289}]
+[keywords security]
+[moddesc {RFC 2289 A One-Time Password System}]
+[copyright {2006, Pat Thoyts <patthoyts@users.sourceforge.net>}]
+[titledesc {One-Time Passwords}]
+[category {Hashes, checksums, and encryption}]
+[require Tcl 8.2]
+[require otp [opt 1.0.0]]
+[description]
+[para]
+
+This package is an implementation in Tcl of the One-Time Password
+system as described in RFC 2289 (1). This system uses message-digest
+algorithms to sequentially hash a passphrase to create single-use
+passwords. The resulting data is then provided to the user as either
+hexadecimal digits or encoded using a dictionary of 2048 words. This
+system is used by OpenBSD for secure login and can be used as a SASL
+mechanism for authenticating users.
+
+[para]
+
+In this implementation we provide support for four algorithms that are
+included in the tcllib distribution: MD5 (2), MD4 (3), RIPE-MD160 (4)
+and SHA-1 (5).
+
+[section {COMMANDS}]
+
+[list_begin definitions]
+
+[call [cmd "::otp::otp-md4"] [opt "[arg -hex]"] [opt "[arg -words]"] \
+ [arg "-seed seed"] [arg "-count count"] [arg "data"]]
+
+[call [cmd "::otp::otp-md5"] [opt "[arg -hex]"] [opt "[arg -words]"] \
+ [arg "-seed seed"] [arg "-count count"] [arg "data"]]
+
+[call [cmd "::otp::otp-sha1"] [opt "[arg -hex]"] [opt "[arg -words]"] \
+ [arg "-seed seed"] [arg "-count count"] [arg "data"]]
+
+[call [cmd "::otp::otp-rmd160"] [opt "[arg -hex]"] [opt "[arg -words]"] \
+ [arg "-seed seed"] [arg "-count count"] [arg "data"]]
+
+[list_end]
+
+[section {EXAMPLES}]
+
+[example {
+% otp::otp-md5 -count 99 -seed host67821 "My Secret Pass Phrase"
+(binary gibberish)
+% otp::otp-md5 -words -count 99 -seed host67821 "My Secret Pass Phrase"
+SOON ARAB BURG LIMB FILE WAD
+% otp::otp-md5 -hex -count 99 -seed host67821 "My Secret Pass Phrase"
+e249b58257c80087
+}]
+
+[section {REFERENCES}]
+
+[list_begin enumerated]
+
+[enum]
+ Haller, N. et al., "A One-Time Password System", RFC 2289, February 1998.
+ [uri http://www.rfc-editor.org/rfc/rfc2289.txt]
+
+[enum]
+ Rivest, R., "The MD5 Message-Digest Algorithm", RFC 1321, MIT and
+ RSA Data Security, Inc, April 1992.
+ ([uri http://www.rfc-editor.org/rfc/rfc1321.txt])
+
+[enum]
+ Rivest, R., "The MD4 Message Digest Algorithm", RFC 1320, MIT,
+ April 1992. ([uri http://www.rfc-editor.org/rfc/rfc1320.txt])
+
+[enum]
+ H. Dobbertin, A. Bosselaers, B. Preneel,
+ "RIPEMD-160, a strengthened version of RIPEMD"
+ [uri http://www.esat.kuleuven.ac.be/~cosicart/pdf/AB-9601/AB-9601.pdf]
+
+[enum]
+ "Secure Hash Standard", National Institute of Standards
+ and Technology, U.S. Department Of Commerce, April 1995.
+ ([uri http://www.itl.nist.gov/fipspubs/fip180-1.htm])
+
+[list_end]
+
+[vset CATEGORY otp]
+[include ../doctools2base/include/feedback.inc]
+[manpage_end]
diff --git a/tcllib/modules/otp/otp.tcl b/tcllib/modules/otp/otp.tcl
new file mode 100644
index 0000000..dede029
--- /dev/null
+++ b/tcllib/modules/otp/otp.tcl
@@ -0,0 +1,430 @@
+# otp.tcl - Copyright (C) 2006 Pat Thoyts <patthoyts@users.sourceforge.net>
+#
+# Tcl implementation of RFC 2289: A One-Time Password System
+#
+# -------------------------------------------------------------------------
+# See the file "license.terms" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+# -------------------------------------------------------------------------
+
+
+package require Tcl 8.2; # tcl minimum version
+
+namespace eval ::otp {
+ namespace export otp-md4 otp-md5 otp-sha1 otp-rmd160
+
+ variable Words {
+ "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 64 bits as words selected from the RFC 2289 dictionary.
+# See the RFC for details. Briefly the input is broken into 11 bit
+# chunks + 2bits of checksum and each chunk selects a word from the
+# 2048 word table.
+#
+proc ::otp::otp_encode {data} {
+ variable Words
+ if {[string length $data] != 8} {
+ set bc [expr {[string length $data] * 8}]
+ return -code error "invalid input: 64 bits of data\
+ required and $bc bits provided"
+ }
+ binary scan $data II A B
+
+ set cksum 0
+ foreach w [list $A $B] {
+ for {set n 0} {$n < 32} {incr n 2} {
+ incr cksum [expr {($w >> $n) & 3}]
+ }
+ }
+
+ set W0 [expr { (($A & 0xFFE00000) >> 21) & 0x07ff}]
+ set W1 [expr { (($A & 0x001FFC00) >> 10)}]
+ set W2 [expr { (($A & 0x000003FF) << 1) | (($B >> 31) & 0x1)}]
+ set W3 [expr { ($B & 0x7FF00000) >> 20}]
+ set W4 [expr { ($B & 0x000FFE00) >> 9}]
+ set W5 [expr { (($B & 0x000001FF) << 2) | ($cksum & 3)}]
+
+ foreach w [list $W0 $W1 $W2 $W3 $W4 $W5] {
+ lappend words [lindex $Words $w]
+ }
+
+ return $words
+}
+
+# Fold a 128 bit digest in little-endian format into a 64 bit
+# little-endian output
+proc ::otp::Fold64LE {digest} {
+ binary scan $digest iiii A B C D
+ set w0 [expr {($A ^ $C) & 0xffffffff}]
+ set w1 [expr {($B ^ $D) & 0xffffffff}]
+ binary format ii $w0 $w1
+}
+
+# Fold a160 bit big-endian digest (SHA-1) into a 64 bit
+# little-endian output
+proc ::otp::Fold160BE {digest} {
+ binary scan $digest IIIII A B C D E
+ set w0 [expr {(($A ^ $C) ^ $E) & 0xffffffff}]
+ set w1 [expr { ($B ^ $D) & 0xffffffff}]
+ binary format ii $w0 $w1
+}
+
+# Fold a 160 bit little-endian digest into a 64 bit
+# little-endian output.
+proc ::otp::Fold160LE {digest} {
+ binary scan $digest iiiii A B C D E
+ set w0 [expr {(($A ^ $C) ^ $E) & 0xffffffff}]
+ set w1 [expr { ($B ^ $D) & 0xffffffff}]
+ binary format ii $w0 $w1
+}
+
+# Description:
+# Pop the nth element off a list. Used in options processing.
+#
+proc ::otp::Pop {varname {nth 0}} {
+ upvar $varname args
+ set r [lindex $args $nth]
+ set args [lreplace $args $nth $nth]
+ return $r
+}
+
+proc ::otp::otp {args} {
+ array set opts {-hash md5 -seed {} -count 0 -hex 0 -words 0}
+ while {[string match -* [set option [lindex $args 0]]]} {
+ switch -exact -- $option {
+ -hex { set opts(-hex) 1}
+ -word -
+ -words { set opts(-words) 1 }
+ -hash { set opts(-hash) [Pop args 1] }
+ -seed { set opts(-seed) [Pop args 1] }
+ -count { set opts(-count) [Pop args 1] }
+ default {
+ if {[llength $args] == 1} { break }
+ if {[string compare $option "--"] == 0} { Pop args; break }
+ set err [join [lsort [array names opts]] ", "]
+ return -code error "bad option \"$option\":\
+ must be one of $err"
+ }
+ }
+ Pop args
+ }
+
+ set data [lindex $args 0]
+
+ if {[string length $opts(-seed)] < 1 || [string length $opts(-seed)] > 16} {
+ return -code error "seed must be between 1 and 16 characters in length"
+ }
+ switch -exact -- $opts(-hash) {
+ md4 { set func ::md4::md4 ; set fold ::otp::Fold64LE }
+ md5 { set func ::md5::md5 ; set fold ::otp::Fold64LE }
+ sha1 { set func ::otp::sha1 ; set fold ::otp::Fold160BE }
+ rmd160 { set func ::ripemd::ripemd160 ; set fold ::otp::Fold160LE }
+ default {
+ return -code error "invalid hash type \"$opts(-hash)\":\
+ must be one of md4, md5, rmd160 or sha1"
+ }
+ }
+ # RFC 2289: Initial step
+ set S [$fold [$func [string tolower $opts(-seed)]$data]]
+
+ # RFC2289:6 Computation step
+ for {set n 0} {$n < $opts(-count)} {incr n} {
+ set S [$fold [$func $S]]
+ }
+
+ if {$opts(-hex)} {
+ binary scan $S H* S
+ } elseif {$opts(-words)} {
+ set S [otp_encode $S]
+ }
+ return $S
+}
+
+proc ::otp::otp-md4 {args} {
+ package require md4
+ return [eval [linsert $args 0 [namespace current]::otp -hash md4]]
+}
+
+proc ::otp::otp-md5 {args} {
+ package require md5
+ return [eval [linsert $args 0 [namespace current]::otp -hash md5]]
+}
+
+proc ::otp::otp-sha1 {args} {
+ package require sha1
+ interp alias {} ::otp::sha1 {} ::sha1::sha1 -bin
+ return [eval [linsert $args 0 [namespace current]::otp -hash sha1]]
+}
+
+proc ::otp::otp-rmd160 {args} {
+ package require ripemd160
+ return [eval [linsert $args 0 [namespace current]::otp -hash rmd160]]
+}
+
+# -------------------------------------------------------------------------
+
+package provide otp 1.0.0
+
+# -------------------------------------------------------------------------
+# Local Variables:
+# mode: tcl
+# indent-tabs-mode: nil
+# End:
diff --git a/tcllib/modules/otp/otp.test b/tcllib/modules/otp/otp.test
new file mode 100644
index 0000000..83d3ff2
--- /dev/null
+++ b/tcllib/modules/otp/otp.test
@@ -0,0 +1,146 @@
+# -*- tcl -*-
+# otp.test: tests the OTP implementation. Most of the tests are directly
+# copied from RFC 2289.
+#
+# This file contains a collection of tests for one or more of the Tcl
+# built-in commands. Sourcing this file into Tcl runs the tests and
+# generates output for errors. No output means no errors were found.
+#
+# Copyright (c) 2006 Patrick Thoyts
+#
+# RCS: @(#) $Id: otp.test,v 1.2 2006/10/09 21:41:41 andreas_kupries Exp $
+
+# -------------------------------------------------------------------------
+
+source [file join \
+ [file dirname [file dirname [file join [pwd] [info script]]]] \
+ devtools testutilities.tcl]
+
+testsNeedTcl 8.2
+testsNeedTcltest 1.0
+
+testing {
+ useLocal otp.tcl otp
+}
+
+# -------------------------------------------------------------------------
+
+set tests_md5 {
+ 0 "This is a test." "TeSt" 0 "9E876134D90499DD" "INCH SEA ANNE LONG AHEM TOUR"
+ 1 "This is a test." "TeSt" 1 "7965E05436F5029F" "EASE OIL FUM CURE AWRY AVIS"
+ 2 "This is a test." "TeSt" 99 "50FE1962C4965880" "BAIL TUFT BITS GANG CHEF THY"
+ 3 "AbCdEfGhIjK" "alpha1" 0 "87066DD9644BF206" "FULL PEW DOWN ONCE MORT ARC"
+ 4 "AbCdEfGhIjK" "alpha1" 1 "7CD34C1040ADD14B" "FACT HOOF AT FIST SITE KENT"
+ 5 "AbCdEfGhIjK" "alpha1" 99 "5AA37A81F212146C" "BODE HOP JAKE STOW JUT RAP"
+ 6 "OTP's are good" "correct" 0 "F205753943DE4CF9" "ULAN NEW ARMY FUSE SUIT EYED"
+ 7 "OTP's are good" "correct" 1 "DDCDAC956F234937" "SKIM CULT LOB SLAM POE HOWL"
+ 8 "OTP's are good" "correct" 99 "B203E28FA525BE47" "LONG IVY JULY AJAR BOND LEE"
+}
+
+foreach {ndx pass seed cnt hex wrds} $tests_md5 {
+ test otp-md5-hex-$ndx "otp-md5 check hex result" {
+ list [catch {
+ set res [::otp::otp-md5 -hex -seed $seed -count $cnt $pass]
+ string toupper $res
+ } msg] $msg
+ } [list 0 $hex]
+}
+
+foreach {ndx pass seed cnt hex wrds} $tests_md5 {
+ test otp-md5-words-$ndx "otp-md5 check words encoding" {
+ list [catch {
+ set res [::otp::otp-md5 -words -seed $seed -count $cnt $pass]
+ } msg] $msg
+ } [list 0 $wrds]
+}
+
+
+set tests_md4 {
+ 0 "This is a test." "TeSt" 0 "D1854218EBBB0B51" "ROME MUG FRED SCAN LIVE LACE"
+ 1 "This is a test." "TeSt" 1 "63473EF01CD0B444" "CARD SAD MINI RYE COL KIN"
+ 2 "This is a test." "TeSt" 99 "C5E612776E6C237A" "NOTE OUT IBIS SINK NAVE MODE"
+ 3 "AbCdEfGhIjK" "alpha1" 0 "50076F47EB1ADE4E" "AWAY SEN ROOK SALT LICE MAP"
+ 4 "AbCdEfGhIjK" "alpha1" 1 "65D20D1949B5F7AB" "CHEW GRIM WU HANG BUCK SAID"
+ 5 "AbCdEfGhIjK" "alpha1" 99 "D150C82CCE6F62D1" "ROIL FREE COG HUNK WAIT COCA"
+ 6 "OTP's are good" "correct" 0 "849C79D4F6F55388" "FOOL STEM DONE TOOL BECK NILE"
+ 7 "OTP's are good" "correct" 1 "8C0992FB250847B1" "GIST AMOS MOOT AIDS FOOD SEEM"
+ 8 "OTP's are good" "correct" 99 "3F3BF4B4145FD74B" "TAG SLOW NOV MIN WOOL KENO"
+}
+
+foreach {ndx pass seed cnt hex wrds} $tests_md4 {
+ test otp-md4-hex-$ndx "otp-md4 check hex result" {
+ list [catch {
+ set res [::otp::otp-md4 -hex -seed $seed -count $cnt $pass]
+ string toupper $res
+ } msg] $msg
+ } [list 0 $hex]
+}
+
+foreach {ndx pass seed cnt hex wrds} $tests_md4 {
+ test otp-md4-words-$ndx "otp-md4 check words encoding" {
+ list [catch {
+ set res [::otp::otp-md4 -words -seed $seed -count $cnt $pass]
+ } msg] $msg
+ } [list 0 $wrds]
+}
+
+set tests_sha1 {
+ 0 "This is a test." "TeSt" 0 "BB9E6AE1979D8FF4" "MILT VARY MAST OK SEES WENT"
+ 1 "This is a test." "TeSt" 1 "63D936639734385B" "CART OTTO HIVE ODE VAT NUT"
+ 2 "This is a test." "TeSt" 99 "87FEC7768B73CCF9" "GAFF WAIT SKID GIG SKY EYED"
+ 3 "AbCdEfGhIjK" "alpha1" 0 "AD85F658EBE383C9" "LEST OR HEEL SCOT ROB SUIT"
+ 4 "AbCdEfGhIjK" "alpha1" 1 "D07CE229B5CF119B" "RITE TAKE GELD COST TUNE RECK"
+ 5 "AbCdEfGhIjK" "alpha1" 99 "27BC71035AAF3DC6" "MAY STAR TIN LYON VEDA STAN"
+ 6 "OTP's are good" "correct" 0 "D51F3E99BF8E6F0B" "RUST WELT KICK FELL TAIL FRAU"
+ 7 "OTP's are good" "correct" 1 "82AEB52D943774E4" "FLIT DOSE ALSO MEW DRUM DEFY"
+ 8 "OTP's are good" "correct" 99 "4F296A74FE1567EC" "AURA ALOE HURL WING BERG WAIT"
+}
+
+foreach {ndx pass seed cnt hex wrds} $tests_sha1 {
+ test otp-sha1-hex-$ndx "otp-sha1 check hex result" {
+ list [catch {
+ set res [::otp::otp-sha1 -hex -seed $seed -count $cnt $pass]
+ string toupper $res
+ } msg] $msg
+ } [list 0 $hex]
+}
+
+foreach {ndx pass seed cnt hex wrds} $tests_sha1 {
+ test otp-sha1-words-$ndx "otp-sha1 check words encoding" {
+ list [catch {
+ set res [::otp::otp-sha1 -words -seed $seed -count $cnt $pass]
+ } msg] $msg
+ } [list 0 $wrds]
+}
+
+set tests_rmd160 {
+ 0 "This is a test." "TeSt" 0 "3A1BFB10A64B4CCD" "SAG SLUG NICE AMOS LUSH CHUM"
+ 1 "This is a test." "TeSt" 1 "39D56BF655E65DE7" "SAC LAVA WORD LEAD CHEW VAST"
+ 2 "This is a test." "TeSt" 99 "42F84BA862941033" "UN NAVY THEE NOLL TO HEN"
+ 3 "AbCdEfGhIjK" "alpha1" 0 "726EDD1BB5DB3642" "DENY DREW YEA COVE LOWE JUG"
+ 4 "AbCdEfGhIjK" "alpha1" 1 "46A231C501A1D2CE" "YAW ELY DEBT ARK IDA CLAW"
+ 5 "AbCdEfGhIjK" "alpha1" 99 "848664EF3A300CC9" "FOOL PER SHE DOCK ADD CENT"
+ 6 "OTP's are good" "correct" 0 "F90D03CC969208C8" "WEAN CLUB VALE NOW JOB CASH"
+ 7 "OTP's are good" "correct" 1 "B6F5D25A08A90009" "MANA LIEU HELL ELK GREW AVE"
+ 8 "OTP's are good" "correct" 99 "C890C1F05018BA5F" "ONCE FRAY EROS JADE GINA ONE"
+}
+
+foreach {ndx pass seed cnt hex wrds} $tests_rmd160 {
+ test otp-rmd160-hex-$ndx "otp-rmd160 check hex result" {
+ list [catch {
+ set res [::otp::otp-rmd160 -hex -seed $seed -count $cnt $pass]
+ string toupper $res
+ } msg] $msg
+ } [list 0 $hex]
+}
+
+foreach {ndx pass seed cnt hex wrds} $tests_rmd160 {
+ test otp-rmd160-words-$ndx "otp-rmd160 check words result" {
+ list [catch {
+ set res [::otp::otp-rmd160 -words -seed $seed -count $cnt $pass]
+ } msg] $msg
+ } [list 0 $wrds]
+}
+# -------------------------------------------------------------------------
+
+testsuiteCleanup
diff --git a/tcllib/modules/otp/pkgIndex.tcl b/tcllib/modules/otp/pkgIndex.tcl
new file mode 100644
index 0000000..803854b
--- /dev/null
+++ b/tcllib/modules/otp/pkgIndex.tcl
@@ -0,0 +1,3 @@
+# pkgIndex.tcl -*- tcl -*-
+if {![package vsatisfies [package provide Tcl] 8.2]} { return }
+package ifneeded otp 1.0.0 [list source [file join $dir otp.tcl]]