1#![no_std]
141use cow_utils::CowUtils;
142use md4::{Md4, Digest};
143use hex::FromHex;
144
145extern crate alloc;
146use alloc::{borrow::ToOwned, boxed::Box};
147
148#[cfg(feature = "words")]
156pub const STANDARD_DICTIONARY: [&'static str; 2048] = [
157 "A", "ABE", "ACE", "ACT", "AD", "ADA", "ADD",
158 "AGO", "AID", "AIM", "AIR", "ALL", "ALP", "AM", "AMY",
159 "AN", "ANA", "AND", "ANN", "ANT", "ANY", "APE", "APS",
160 "APT", "ARC", "ARE", "ARK", "ARM", "ART", "AS", "ASH",
161 "ASK", "AT", "ATE", "AUG", "AUK", "AVE", "AWE", "AWK",
162 "AWL", "AWN", "AX", "AYE", "BAD", "BAG", "BAH", "BAM",
163 "BAN", "BAR", "BAT", "BAY", "BE", "BED", "BEE", "BEG",
164 "BEN", "BET", "BEY", "BIB", "BID", "BIG", "BIN", "BIT",
165 "BOB", "BOG", "BON", "BOO", "BOP", "BOW", "BOY", "BUB",
166 "BUD", "BUG", "BUM", "BUN", "BUS", "BUT", "BUY", "BY",
167 "BYE", "CAB", "CAL", "CAM", "CAN", "CAP", "CAR", "CAT",
168 "CAW", "COD", "COG", "COL", "CON", "COO", "COP", "COT",
169 "COW", "COY", "CRY", "CUB", "CUE", "CUP", "CUR", "CUT",
170 "DAB", "DAD", "DAM", "DAN", "DAR", "DAY", "DEE", "DEL",
171 "DEN", "DES", "DEW", "DID", "DIE", "DIG", "DIN", "DIP",
172 "DO", "DOE", "DOG", "DON", "DOT", "DOW", "DRY", "DUB",
173 "DUD", "DUE", "DUG", "DUN", "EAR", "EAT", "ED", "EEL",
174 "EGG", "EGO", "ELI", "ELK", "ELM", "ELY", "EM", "END",
175 "EST", "ETC", "EVA", "EVE", "EWE", "EYE", "FAD", "FAN",
176 "FAR", "FAT", "FAY", "FED", "FEE", "FEW", "FIB", "FIG",
177 "FIN", "FIR", "FIT", "FLO", "FLY", "FOE", "FOG", "FOR",
178 "FRY", "FUM", "FUN", "FUR", "GAB", "GAD", "GAG", "GAL",
179 "GAM", "GAP", "GAS", "GAY", "GEE", "GEL", "GEM", "GET",
180 "GIG", "GIL", "GIN", "GO", "GOT", "GUM", "GUN", "GUS",
181 "GUT", "GUY", "GYM", "GYP", "HA", "HAD", "HAL", "HAM",
182 "HAN", "HAP", "HAS", "HAT", "HAW", "HAY", "HE", "HEM",
183 "HEN", "HER", "HEW", "HEY", "HI", "HID", "HIM", "HIP",
184 "HIS", "HIT", "HO", "HOB", "HOC", "HOE", "HOG", "HOP",
185 "HOT", "HOW", "HUB", "HUE", "HUG", "HUH", "HUM", "HUT",
186 "I", "ICY", "IDA", "IF", "IKE", "ILL", "INK", "INN",
187 "IO", "ION", "IQ", "IRA", "IRE", "IRK", "IS", "IT",
188 "ITS", "IVY", "JAB", "JAG", "JAM", "JAN", "JAR", "JAW",
189 "JAY", "JET", "JIG", "JIM", "JO", "JOB", "JOE", "JOG",
190 "JOT", "JOY", "JUG", "JUT", "KAY", "KEG", "KEN", "KEY",
191 "KID", "KIM", "KIN", "KIT", "LA", "LAB", "LAC", "LAD",
192 "LAG", "LAM", "LAP", "LAW", "LAY", "LEA", "LED", "LEE",
193 "LEG", "LEN", "LEO", "LET", "LEW", "LID", "LIE", "LIN",
194 "LIP", "LIT", "LO", "LOB", "LOG", "LOP", "LOS", "LOT",
195 "LOU", "LOW", "LOY", "LUG", "LYE", "MA", "MAC", "MAD",
196 "MAE", "MAN", "MAO", "MAP", "MAT", "MAW", "MAY", "ME",
197 "MEG", "MEL", "MEN", "MET", "MEW", "MID", "MIN", "MIT",
198 "MOB", "MOD", "MOE", "MOO", "MOP", "MOS", "MOT", "MOW",
199 "MUD", "MUG", "MUM", "MY", "NAB", "NAG", "NAN", "NAP",
200 "NAT", "NAY", "NE", "NED", "NEE", "NET", "NEW", "NIB",
201 "NIL", "NIP", "NIT", "NO", "NOB", "NOD", "NON", "NOR",
202 "NOT", "NOV", "NOW", "NU", "NUN", "NUT", "O", "OAF",
203 "OAK", "OAR", "OAT", "ODD", "ODE", "OF", "OFF", "OFT",
204 "OH", "OIL", "OK", "OLD", "ON", "ONE", "OR", "ORB",
205 "ORE", "ORR", "OS", "OTT", "OUR", "OUT", "OVA", "OW",
206 "OWE", "OWL", "OWN", "OX", "PA", "PAD", "PAL", "PAM",
207 "PAN", "PAP", "PAR", "PAT", "PAW", "PAY", "PEA", "PEG",
208 "PEN", "PEP", "PER", "PET", "PEW", "PHI", "PI", "PIE",
209 "PIN", "PIT", "PLY", "PO", "POD", "POE", "POP", "POT",
210 "POW", "PRO", "PRY", "PUB", "PUG", "PUN", "PUP", "PUT",
211 "QUO", "RAG", "RAM", "RAN", "RAP", "RAT", "RAW", "RAY",
212 "REB", "RED", "REP", "RET", "RIB", "RID", "RIG", "RIM",
213 "RIO", "RIP", "ROB", "ROD", "ROE", "RON", "ROT", "ROW",
214 "ROY", "RUB", "RUE", "RUG", "RUM", "RUN", "RYE", "SAC",
215 "SAD", "SAG", "SAL", "SAM", "SAN", "SAP", "SAT", "SAW",
216 "SAY", "SEA", "SEC", "SEE", "SEN", "SET", "SEW", "SHE",
217 "SHY", "SIN", "SIP", "SIR", "SIS", "SIT", "SKI", "SKY",
218 "SLY", "SO", "SOB", "SOD", "SON", "SOP", "SOW", "SOY",
219 "SPA", "SPY", "SUB", "SUD", "SUE", "SUM", "SUN", "SUP",
220 "TAB", "TAD", "TAG", "TAN", "TAP", "TAR", "TEA", "TED",
221 "TEE", "TEN", "THE", "THY", "TIC", "TIE", "TIM", "TIN",
222 "TIP", "TO", "TOE", "TOG", "TOM", "TON", "TOO", "TOP",
223 "TOW", "TOY", "TRY", "TUB", "TUG", "TUM", "TUN", "TWO",
224 "UN", "UP", "US", "USE", "VAN", "VAT", "VET", "VIE",
225 "WAD", "WAG", "WAR", "WAS", "WAY", "WE", "WEB", "WED",
226 "WEE", "WET", "WHO", "WHY", "WIN", "WIT", "WOK", "WON",
227 "WOO", "WOW", "WRY", "WU", "YAM", "YAP", "YAW", "YE",
228 "YEA", "YES", "YET", "YOU", "ABED", "ABEL", "ABET", "ABLE",
229 "ABUT", "ACHE", "ACID", "ACME", "ACRE", "ACTA", "ACTS", "ADAM",
230 "ADDS", "ADEN", "AFAR", "AFRO", "AGEE", "AHEM", "AHOY", "AIDA",
231 "AIDE", "AIDS", "AIRY", "AJAR", "AKIN", "ALAN", "ALEC", "ALGA",
232 "ALIA", "ALLY", "ALMA", "ALOE", "ALSO", "ALTO", "ALUM", "ALVA",
233 "AMEN", "AMES", "AMID", "AMMO", "AMOK", "AMOS", "AMRA", "ANDY",
234 "ANEW", "ANNA", "ANNE", "ANTE", "ANTI", "AQUA", "ARAB", "ARCH",
235 "AREA", "ARGO", "ARID", "ARMY", "ARTS", "ARTY", "ASIA", "ASKS",
236 "ATOM", "AUNT", "AURA", "AUTO", "AVER", "AVID", "AVIS", "AVON",
237 "AVOW", "AWAY", "AWRY", "BABE", "BABY", "BACH", "BACK", "BADE",
238 "BAIL", "BAIT", "BAKE", "BALD", "BALE", "BALI", "BALK", "BALL",
239 "BALM", "BAND", "BANE", "BANG", "BANK", "BARB", "BARD", "BARE",
240 "BARK", "BARN", "BARR", "BASE", "BASH", "BASK", "BASS", "BATE",
241 "BATH", "BAWD", "BAWL", "BEAD", "BEAK", "BEAM", "BEAN", "BEAR",
242 "BEAT", "BEAU", "BECK", "BEEF", "BEEN", "BEER", "BEET", "BELA",
243 "BELL", "BELT", "BEND", "BENT", "BERG", "BERN", "BERT", "BESS",
244 "BEST", "BETA", "BETH", "BHOY", "BIAS", "BIDE", "BIEN", "BILE",
245 "BILK", "BILL", "BIND", "BING", "BIRD", "BITE", "BITS", "BLAB",
246 "BLAT", "BLED", "BLEW", "BLOB", "BLOC", "BLOT", "BLOW", "BLUE",
247 "BLUM", "BLUR", "BOAR", "BOAT", "BOCA", "BOCK", "BODE", "BODY",
248 "BOGY", "BOHR", "BOIL", "BOLD", "BOLO", "BOLT", "BOMB", "BONA",
249 "BOND", "BONE", "BONG", "BONN", "BONY", "BOOK", "BOOM", "BOON",
250 "BOOT", "BORE", "BORG", "BORN", "BOSE", "BOSS", "BOTH", "BOUT",
251 "BOWL", "BOYD", "BRAD", "BRAE", "BRAG", "BRAN", "BRAY", "BRED",
252 "BREW", "BRIG", "BRIM", "BROW", "BUCK", "BUDD", "BUFF", "BULB",
253 "BULK", "BULL", "BUNK", "BUNT", "BUOY", "BURG", "BURL", "BURN",
254 "BURR", "BURT", "BURY", "BUSH", "BUSS", "BUST", "BUSY", "BYTE",
255 "CADY", "CAFE", "CAGE", "CAIN", "CAKE", "CALF", "CALL", "CALM",
256 "CAME", "CANE", "CANT", "CARD", "CARE", "CARL", "CARR", "CART",
257 "CASE", "CASH", "CASK", "CAST", "CAVE", "CEIL", "CELL", "CENT",
258 "CERN", "CHAD", "CHAR", "CHAT", "CHAW", "CHEF", "CHEN", "CHEW",
259 "CHIC", "CHIN", "CHOU", "CHOW", "CHUB", "CHUG", "CHUM", "CITE",
260 "CITY", "CLAD", "CLAM", "CLAN", "CLAW", "CLAY", "CLOD", "CLOG",
261 "CLOT", "CLUB", "CLUE", "COAL", "COAT", "COCA", "COCK", "COCO",
262 "CODA", "CODE", "CODY", "COED", "COIL", "COIN", "COKE", "COLA",
263 "COLD", "COLT", "COMA", "COMB", "COME", "COOK", "COOL", "COON",
264 "COOT", "CORD", "CORE", "CORK", "CORN", "COST", "COVE", "COWL",
265 "CRAB", "CRAG", "CRAM", "CRAY", "CREW", "CRIB", "CROW", "CRUD",
266 "CUBA", "CUBE", "CUFF", "CULL", "CULT", "CUNY", "CURB", "CURD",
267 "CURE", "CURL", "CURT", "CUTS", "DADE", "DALE", "DAME", "DANA",
268 "DANE", "DANG", "DANK", "DARE", "DARK", "DARN", "DART", "DASH",
269 "DATA", "DATE", "DAVE", "DAVY", "DAWN", "DAYS", "DEAD", "DEAF",
270 "DEAL", "DEAN", "DEAR", "DEBT", "DECK", "DEED", "DEEM", "DEER",
271 "DEFT", "DEFY", "DELL", "DENT", "DENY", "DESK", "DIAL", "DICE",
272 "DIED", "DIET", "DIME", "DINE", "DING", "DINT", "DIRE", "DIRT",
273 "DISC", "DISH", "DISK", "DIVE", "DOCK", "DOES", "DOLE", "DOLL",
274 "DOLT", "DOME", "DONE", "DOOM", "DOOR", "DORA", "DOSE", "DOTE",
275 "DOUG", "DOUR", "DOVE", "DOWN", "DRAB", "DRAG", "DRAM", "DRAW",
276 "DREW", "DRUB", "DRUG", "DRUM", "DUAL", "DUCK", "DUCT", "DUEL",
277 "DUET", "DUKE", "DULL", "DUMB", "DUNE", "DUNK", "DUSK", "DUST",
278 "DUTY", "EACH", "EARL", "EARN", "EASE", "EAST", "EASY", "EBEN",
279 "ECHO", "EDDY", "EDEN", "EDGE", "EDGY", "EDIT", "EDNA", "EGAN",
280 "ELAN", "ELBA", "ELLA", "ELSE", "EMIL", "EMIT", "EMMA", "ENDS",
281 "ERIC", "EROS", "EVEN", "EVER", "EVIL", "EYED", "FACE", "FACT",
282 "FADE", "FAIL", "FAIN", "FAIR", "FAKE", "FALL", "FAME", "FANG",
283 "FARM", "FAST", "FATE", "FAWN", "FEAR", "FEAT", "FEED", "FEEL",
284 "FEET", "FELL", "FELT", "FEND", "FERN", "FEST", "FEUD", "FIEF",
285 "FIGS", "FILE", "FILL", "FILM", "FIND", "FINE", "FINK", "FIRE",
286 "FIRM", "FISH", "FISK", "FIST", "FITS", "FIVE", "FLAG", "FLAK",
287 "FLAM", "FLAT", "FLAW", "FLEA", "FLED", "FLEW", "FLIT", "FLOC",
288 "FLOG", "FLOW", "FLUB", "FLUE", "FOAL", "FOAM", "FOGY", "FOIL",
289 "FOLD", "FOLK", "FOND", "FONT", "FOOD", "FOOL", "FOOT", "FORD",
290 "FORE", "FORK", "FORM", "FORT", "FOSS", "FOUL", "FOUR", "FOWL",
291 "FRAU", "FRAY", "FRED", "FREE", "FRET", "FREY", "FROG", "FROM",
292 "FUEL", "FULL", "FUME", "FUND", "FUNK", "FURY", "FUSE", "FUSS",
293 "GAFF", "GAGE", "GAIL", "GAIN", "GAIT", "GALA", "GALE", "GALL",
294 "GALT", "GAME", "GANG", "GARB", "GARY", "GASH", "GATE", "GAUL",
295 "GAUR", "GAVE", "GAWK", "GEAR", "GELD", "GENE", "GENT", "GERM",
296 "GETS", "GIBE", "GIFT", "GILD", "GILL", "GILT", "GINA", "GIRD",
297 "GIRL", "GIST", "GIVE", "GLAD", "GLEE", "GLEN", "GLIB", "GLOB",
298 "GLOM", "GLOW", "GLUE", "GLUM", "GLUT", "GOAD", "GOAL", "GOAT",
299 "GOER", "GOES", "GOLD", "GOLF", "GONE", "GONG", "GOOD", "GOOF",
300 "GORE", "GORY", "GOSH", "GOUT", "GOWN", "GRAB", "GRAD", "GRAY",
301 "GREG", "GREW", "GREY", "GRID", "GRIM", "GRIN", "GRIT", "GROW",
302 "GRUB", "GULF", "GULL", "GUNK", "GURU", "GUSH", "GUST", "GWEN",
303 "GWYN", "HAAG", "HAAS", "HACK", "HAIL", "HAIR", "HALE", "HALF",
304 "HALL", "HALO", "HALT", "HAND", "HANG", "HANK", "HANS", "HARD",
305 "HARK", "HARM", "HART", "HASH", "HAST", "HATE", "HATH", "HAUL",
306 "HAVE", "HAWK", "HAYS", "HEAD", "HEAL", "HEAR", "HEAT", "HEBE",
307 "HECK", "HEED", "HEEL", "HEFT", "HELD", "HELL", "HELM", "HERB",
308 "HERD", "HERE", "HERO", "HERS", "HESS", "HEWN", "HICK", "HIDE",
309 "HIGH", "HIKE", "HILL", "HILT", "HIND", "HINT", "HIRE", "HISS",
310 "HIVE", "HOBO", "HOCK", "HOFF", "HOLD", "HOLE", "HOLM", "HOLT",
311 "HOME", "HONE", "HONK", "HOOD", "HOOF", "HOOK", "HOOT", "HORN",
312 "HOSE", "HOST", "HOUR", "HOVE", "HOWE", "HOWL", "HOYT", "HUCK",
313 "HUED", "HUFF", "HUGE", "HUGH", "HUGO", "HULK", "HULL", "HUNK",
314 "HUNT", "HURD", "HURL", "HURT", "HUSH", "HYDE", "HYMN", "IBIS",
315 "ICON", "IDEA", "IDLE", "IFFY", "INCA", "INCH", "INTO", "IONS",
316 "IOTA", "IOWA", "IRIS", "IRMA", "IRON", "ISLE", "ITCH", "ITEM",
317 "IVAN", "JACK", "JADE", "JAIL", "JAKE", "JANE", "JAVA", "JEAN",
318 "JEFF", "JERK", "JESS", "JEST", "JIBE", "JILL", "JILT", "JIVE",
319 "JOAN", "JOBS", "JOCK", "JOEL", "JOEY", "JOHN", "JOIN", "JOKE",
320 "JOLT", "JOVE", "JUDD", "JUDE", "JUDO", "JUDY", "JUJU", "JUKE",
321 "JULY", "JUNE", "JUNK", "JUNO", "JURY", "JUST", "JUTE", "KAHN",
322 "KALE", "KANE", "KANT", "KARL", "KATE", "KEEL", "KEEN", "KENO",
323 "KENT", "KERN", "KERR", "KEYS", "KICK", "KILL", "KIND", "KING",
324 "KIRK", "KISS", "KITE", "KLAN", "KNEE", "KNEW", "KNIT", "KNOB",
325 "KNOT", "KNOW", "KOCH", "KONG", "KUDO", "KURD", "KURT", "KYLE",
326 "LACE", "LACK", "LACY", "LADY", "LAID", "LAIN", "LAIR", "LAKE",
327 "LAMB", "LAME", "LAND", "LANE", "LANG", "LARD", "LARK", "LASS",
328 "LAST", "LATE", "LAUD", "LAVA", "LAWN", "LAWS", "LAYS", "LEAD",
329 "LEAF", "LEAK", "LEAN", "LEAR", "LEEK", "LEER", "LEFT", "LEND",
330 "LENS", "LENT", "LEON", "LESK", "LESS", "LEST", "LETS", "LIAR",
331 "LICE", "LICK", "LIED", "LIEN", "LIES", "LIEU", "LIFE", "LIFT",
332 "LIKE", "LILA", "LILT", "LILY", "LIMA", "LIMB", "LIME", "LIND",
333 "LINE", "LINK", "LINT", "LION", "LISA", "LIST", "LIVE", "LOAD",
334 "LOAF", "LOAM", "LOAN", "LOCK", "LOFT", "LOGE", "LOIS", "LOLA",
335 "LONE", "LONG", "LOOK", "LOON", "LOOT", "LORD", "LORE", "LOSE",
336 "LOSS", "LOST", "LOUD", "LOVE", "LOWE", "LUCK", "LUCY", "LUGE",
337 "LUKE", "LULU", "LUND", "LUNG", "LURA", "LURE", "LURK", "LUSH",
338 "LUST", "LYLE", "LYNN", "LYON", "LYRA", "MACE", "MADE", "MAGI",
339 "MAID", "MAIL", "MAIN", "MAKE", "MALE", "MALI", "MALL", "MALT",
340 "MANA", "MANN", "MANY", "MARC", "MARE", "MARK", "MARS", "MART",
341 "MARY", "MASH", "MASK", "MASS", "MAST", "MATE", "MATH", "MAUL",
342 "MAYO", "MEAD", "MEAL", "MEAN", "MEAT", "MEEK", "MEET", "MELD",
343 "MELT", "MEMO", "MEND", "MENU", "MERT", "MESH", "MESS", "MICE",
344 "MIKE", "MILD", "MILE", "MILK", "MILL", "MILT", "MIMI", "MIND",
345 "MINE", "MINI", "MINK", "MINT", "MIRE", "MISS", "MIST", "MITE",
346 "MITT", "MOAN", "MOAT", "MOCK", "MODE", "MOLD", "MOLE", "MOLL",
347 "MOLT", "MONA", "MONK", "MONT", "MOOD", "MOON", "MOOR", "MOOT",
348 "MORE", "MORN", "MORT", "MOSS", "MOST", "MOTH", "MOVE", "MUCH",
349 "MUCK", "MUDD", "MUFF", "MULE", "MULL", "MURK", "MUSH", "MUST",
350 "MUTE", "MUTT", "MYRA", "MYTH", "NAGY", "NAIL", "NAIR", "NAME",
351 "NARY", "NASH", "NAVE", "NAVY", "NEAL", "NEAR", "NEAT", "NECK",
352 "NEED", "NEIL", "NELL", "NEON", "NERO", "NESS", "NEST", "NEWS",
353 "NEWT", "NIBS", "NICE", "NICK", "NILE", "NINA", "NINE", "NOAH",
354 "NODE", "NOEL", "NOLL", "NONE", "NOOK", "NOON", "NORM", "NOSE",
355 "NOTE", "NOUN", "NOVA", "NUDE", "NULL", "NUMB", "OATH", "OBEY",
356 "OBOE", "ODIN", "OHIO", "OILY", "OINT", "OKAY", "OLAF", "OLDY",
357 "OLGA", "OLIN", "OMAN", "OMEN", "OMIT", "ONCE", "ONES", "ONLY",
358 "ONTO", "ONUS", "ORAL", "ORGY", "OSLO", "OTIS", "OTTO", "OUCH",
359 "OUST", "OUTS", "OVAL", "OVEN", "OVER", "OWLY", "OWNS", "QUAD",
360 "QUIT", "QUOD", "RACE", "RACK", "RACY", "RAFT", "RAGE", "RAID",
361 "RAIL", "RAIN", "RAKE", "RANK", "RANT", "RARE", "RASH", "RATE",
362 "RAVE", "RAYS", "READ", "REAL", "REAM", "REAR", "RECK", "REED",
363 "REEF", "REEK", "REEL", "REID", "REIN", "RENA", "REND", "RENT",
364 "REST", "RICE", "RICH", "RICK", "RIDE", "RIFT", "RILL", "RIME",
365 "RING", "RINK", "RISE", "RISK", "RITE", "ROAD", "ROAM", "ROAR",
366 "ROBE", "ROCK", "RODE", "ROIL", "ROLL", "ROME", "ROOD", "ROOF",
367 "ROOK", "ROOM", "ROOT", "ROSA", "ROSE", "ROSS", "ROSY", "ROTH",
368 "ROUT", "ROVE", "ROWE", "ROWS", "RUBE", "RUBY", "RUDE", "RUDY",
369 "RUIN", "RULE", "RUNG", "RUNS", "RUNT", "RUSE", "RUSH", "RUSK",
370 "RUSS", "RUST", "RUTH", "SACK", "SAFE", "SAGE", "SAID", "SAIL",
371 "SALE", "SALK", "SALT", "SAME", "SAND", "SANE", "SANG", "SANK",
372 "SARA", "SAUL", "SAVE", "SAYS", "SCAN", "SCAR", "SCAT", "SCOT",
373 "SEAL", "SEAM", "SEAR", "SEAT", "SEED", "SEEK", "SEEM", "SEEN",
374 "SEES", "SELF", "SELL", "SEND", "SENT", "SETS", "SEWN", "SHAG",
375 "SHAM", "SHAW", "SHAY", "SHED", "SHIM", "SHIN", "SHOD", "SHOE",
376 "SHOT", "SHOW", "SHUN", "SHUT", "SICK", "SIDE", "SIFT", "SIGH",
377 "SIGN", "SILK", "SILL", "SILO", "SILT", "SINE", "SING", "SINK",
378 "SIRE", "SITE", "SITS", "SITU", "SKAT", "SKEW", "SKID", "SKIM",
379 "SKIN", "SKIT", "SLAB", "SLAM", "SLAT", "SLAY", "SLED", "SLEW",
380 "SLID", "SLIM", "SLIT", "SLOB", "SLOG", "SLOT", "SLOW", "SLUG",
381 "SLUM", "SLUR", "SMOG", "SMUG", "SNAG", "SNOB", "SNOW", "SNUB",
382 "SNUG", "SOAK", "SOAR", "SOCK", "SODA", "SOFA", "SOFT", "SOIL",
383 "SOLD", "SOME", "SONG", "SOON", "SOOT", "SORE", "SORT", "SOUL",
384 "SOUR", "SOWN", "STAB", "STAG", "STAN", "STAR", "STAY", "STEM",
385 "STEW", "STIR", "STOW", "STUB", "STUN", "SUCH", "SUDS", "SUIT",
386 "SULK", "SUMS", "SUNG", "SUNK", "SURE", "SURF", "SWAB", "SWAG",
387 "SWAM", "SWAN", "SWAT", "SWAY", "SWIM", "SWUM", "TACK", "TACT",
388 "TAIL", "TAKE", "TALE", "TALK", "TALL", "TANK", "TASK", "TATE",
389 "TAUT", "TEAL", "TEAM", "TEAR", "TECH", "TEEM", "TEEN", "TEET",
390 "TELL", "TEND", "TENT", "TERM", "TERN", "TESS", "TEST", "THAN",
391 "THAT", "THEE", "THEM", "THEN", "THEY", "THIN", "THIS", "THUD",
392 "THUG", "TICK", "TIDE", "TIDY", "TIED", "TIER", "TILE", "TILL",
393 "TILT", "TIME", "TINA", "TINE", "TINT", "TINY", "TIRE", "TOAD",
394 "TOGO", "TOIL", "TOLD", "TOLL", "TONE", "TONG", "TONY", "TOOK",
395 "TOOL", "TOOT", "TORE", "TORN", "TOTE", "TOUR", "TOUT", "TOWN",
396 "TRAG", "TRAM", "TRAY", "TREE", "TREK", "TRIG", "TRIM", "TRIO",
397 "TROD", "TROT", "TROY", "TRUE", "TUBA", "TUBE", "TUCK", "TUFT",
398 "TUNA", "TUNE", "TUNG", "TURF", "TURN", "TUSK", "TWIG", "TWIN",
399 "TWIT", "ULAN", "UNIT", "URGE", "USED", "USER", "USES", "UTAH",
400 "VAIL", "VAIN", "VALE", "VARY", "VASE", "VAST", "VEAL", "VEDA",
401 "VEIL", "VEIN", "VEND", "VENT", "VERB", "VERY", "VETO", "VICE",
402 "VIEW", "VINE", "VISE", "VOID", "VOLT", "VOTE", "WACK", "WADE",
403 "WAGE", "WAIL", "WAIT", "WAKE", "WALE", "WALK", "WALL", "WALT",
404 "WAND", "WANE", "WANG", "WANT", "WARD", "WARM", "WARN", "WART",
405 "WASH", "WAST", "WATS", "WATT", "WAVE", "WAVY", "WAYS", "WEAK",
406 "WEAL", "WEAN", "WEAR", "WEED", "WEEK", "WEIR", "WELD", "WELL",
407 "WELT", "WENT", "WERE", "WERT", "WEST", "WHAM", "WHAT", "WHEE",
408 "WHEN", "WHET", "WHOA", "WHOM", "WICK", "WIFE", "WILD", "WILL",
409 "WIND", "WINE", "WING", "WINK", "WINO", "WIRE", "WISE", "WISH",
410 "WITH", "WOLF", "WONT", "WOOD", "WOOL", "WORD", "WORE", "WORK",
411 "WORM", "WORN", "WOVE", "WRIT", "WYNN", "YALE", "YANG", "YANK",
412 "YARD", "YARN", "YAWL", "YAWN", "YEAH", "YEAR", "YELL", "YOGA",
413 "YOKE",
414];
415
416
417#[cfg(any(feature = "md4", feature = "md5"))]
421pub fn fold_md (input: &mut [u8]) {
422 let mut j = 0;
423 for i in 8..input.len() {
424 input[j] ^= input[i];
425 j = (j + 1) % 8;
426 }
427}
428
429#[cfg(feature = "sha1")]
437pub fn fold_sha1 (digest: &mut [u8; 20]) {
438 fold_md(&mut digest[..]);
439 digest.swap(0, 3);
440 digest.swap(1, 2);
441 digest.swap(4, 7);
442 digest.swap(5, 6);
443}
444
445const INIT_SIX_WORDS: [&'static str; 6] = [ "A", "A", "A", "A", "A", "A" ];
446
447pub fn calculate_checksum (input: &[u8; 8]) -> u64 {
450 input
451 .iter()
452 .map(|num| -> u64 {
453 let n = *num;
454 ((n & 0b0000_0011)
455 + ((n & 0b0000_1100) >> 2)
456 + ((n & 0b0011_0000) >> 4)
457 + ((n & 0b1100_0000) >> 6)).into()
458 })
459 .reduce(|acc, curr| acc + curr)
460 .unwrap()
461 & 0b11
462}
463
464#[cfg(feature = "words")]
471pub fn convert_to_word_format (result: &[u8; 8]) -> [&'static str; 6] {
472 let checksum: u64 = calculate_checksum(result);
473 let mut result = u64::from_be_bytes(*result);
474 let mut output: [&'static str; 6] = INIT_SIX_WORDS;
475 for i in 0..5 {
476 let bits = (result & (0b11111111111 << (64 - 11))) >> (64 - 11); output[i] = STANDARD_DICTIONARY[bits as usize];
478 result = result.wrapping_shl(11);
479 }
480 let bits: u64 = ((result & (0b11111111111 << (64 - 11))) >> (64 - 11)) + checksum; output[5] = STANDARD_DICTIONARY[bits as usize];
482 output
483}
484
485#[cfg(feature = "words")]
496pub fn decode_word_format_with_std_dict (words: [&str; 6]) -> Option<([u8; 8], bool)> {
497 let mut output: u64 = 0;
498 for word in words.iter().take(5) {
499 let bits = STANDARD_DICTIONARY.iter().position(|w| *w == *word)?;
500 output <<= 11;
501 output |= bits as u64;
502 }
503 let bits = STANDARD_DICTIONARY.iter().position(|w| *w == words[5])?;
505 output <<= 9;
506 output |= bits as u64 / 4; let checksum_bits = bits as u64 % 4;
508 let output = output.to_be_bytes();
509 let checksum: u64 = calculate_checksum(&output);
510 Some((output, checksum_bits == checksum))
511}
512
513#[cfg(feature = "parsing")]
519#[derive(Debug)]
520pub struct OTPChallenge <'a> {
521 pub hash_alg: &'a str,
522 pub hash_count: usize,
523 pub seed: &'a str,
524}
525
526#[cfg(feature = "parsing")]
529pub fn parse_otp_challenge <'a> (s: &'a str) -> Option<OTPChallenge<'a>> {
530 if s.len() < 9 { return None;
532 }
533 if s.len() > 128 {
534 return None; }
536 if !s.starts_with("otp-") {
537 return None;
538 }
539 let x = &s[4..];
540 let mut hash_alg: Option<&'a str> = None;
541 let mut seed: Option<&'a str> = None;
542 let mut count: Option<usize> = None;
543 for token in x.split_ascii_whitespace() {
544 if hash_alg.is_none() {
545 hash_alg = Some(token);
546 }
547 else if count.is_none() {
548 count = Some(usize::from_str_radix(token, 10).ok()?);
549 }
550 else if seed.is_none() {
551 seed = Some(token);
552 break;
553 }
554 }
555 Some(OTPChallenge{
556 hash_alg: hash_alg?,
557 seed: seed?,
558 hash_count: count?,
559 })
560}
561
562pub type Hex64Bit = [u8; 8];
563
564#[cfg(feature = "parsing")]
566#[derive(Debug, PartialEq, Eq)]
567pub enum HexOrWords <'a> {
568 Hex(Hex64Bit),
569 Words(&'a str),
570}
571
572impl HexOrWords<'_> {
573
574 pub fn try_into_bytes (&self) -> Option<[u8; 8]> {
575 match self {
576 HexOrWords::Hex(h) => Some(h.to_owned()),
577 HexOrWords::Words(w) => {
578 let mut w = w.split_ascii_whitespace();
579 let six_words = [ w.next(), w.next(), w.next(), w.next(), w.next(), w.next() ];
580 if six_words[5].is_none() {
581 return None;
582 }
583 if w.next().is_some() {
584 return None;
585 }
586 let six_words = [
587 six_words[0].unwrap(),
588 six_words[1].unwrap(),
589 six_words[2].unwrap(),
590 six_words[3].unwrap(),
591 six_words[4].unwrap(),
592 six_words[5].unwrap(),
593 ];
594 let maybe_64bits = decode_word_format_with_std_dict(six_words);
595 if maybe_64bits.is_none() {
596 return None;
597 }
598 let (v, valid_checksum) = maybe_64bits.unwrap();
599 if !valid_checksum {
600 return None;
601 }
602 Some(v)
603 },
604 }
605 }
606
607}
608
609#[cfg(feature = "parsing")]
612#[derive(Debug)]
613pub struct OTPInit <'a> {
614 pub current_otp: HexOrWords<'a>,
615 pub new_otp: HexOrWords<'a>,
616 pub new_alg: &'a str,
617 pub new_seq_num: usize,
618 pub new_seed: &'a str,
619}
620
621#[cfg(feature = "parsing")]
624#[derive(Debug)]
625pub enum OTPResponse <'a> {
626 Init(OTPInit <'a>),
627 Current(HexOrWords<'a>)
628}
629
630#[cfg(feature = "parsing")]
633fn parse_otp_init_hex <'a> (s: &'a str) -> Option<OTPInit<'a>> {
634 let mut sections = s.split(":");
635 let current_otp = sections.next()?.cow_replace(" ", "");
636 let new_params = sections.next()?;
637 let new_otp = sections.next()?.cow_replace(" ", "");
638 if sections.next().is_some() {
639 return None;
640 }
641 let current_otp = <Hex64Bit>::from_hex(current_otp.cow_replace("\t", "").as_ref()).ok()?;
642 let new_otp = <Hex64Bit>::from_hex(new_otp.cow_replace("\t", "").as_ref()).ok()?;
643 let mut params = new_params.split(" ");
644 let algorithm = params.next()?;
645 let sequence_number = params.next()?;
646 let seed = params.next()?;
647 let sequence_number = <usize>::from_str_radix(sequence_number, 10).ok()?;
648 Some(OTPInit {
649 current_otp: HexOrWords::Hex(current_otp),
650 new_otp: HexOrWords::Hex(new_otp),
651 new_alg: algorithm,
652 new_seq_num: sequence_number,
653 new_seed: seed,
654 })
655}
656
657#[cfg(feature = "parsing")]
660fn parse_otp_init_word <'a> (s: &'a str) -> Option<OTPInit<'a>> {
661 let mut sections = s.split(":");
662 let current_otp = sections.next()?;
663 let new_params = sections.next()?;
664 let new_otp = sections.next()?;
665 if sections.next().is_some() {
666 return None;
667 }
668 let mut params = new_params.split(" ");
669 let algorithm = params.next()?;
670 let sequence_number = params.next()?;
671 let seed = params.next()?;
672 let sequence_number = <usize>::from_str_radix(sequence_number, 10).ok()?;
673 Some(OTPInit {
674 current_otp: HexOrWords::Words(current_otp),
675 new_otp: HexOrWords::Words(new_otp),
676 new_alg: algorithm,
677 new_seq_num: sequence_number,
678 new_seed: seed,
679 })
680}
681
682#[cfg(feature = "parsing")]
685pub fn parse_otp_init <'a> (s: &'a str) -> Option<OTPInit<'a>> {
686 if s.len() <= 50 || s.len() > 100 { return None;
688 }
689 if s.starts_with("init-hex:") {
690 parse_otp_init_hex(&s[9..])
691 }
692 else if s.starts_with("init-word:") {
693 parse_otp_init_word(&s[10..])
694 }
695 else {
696 None
697 }
698}
699
700#[cfg(feature = "parsing")]
703pub fn parse_otp_response <'a> (s: &'a str) -> Option<OTPResponse<'a>> {
704 if s.len() < 20 || s.len() > 100 { return None;
706 }
707 if s.starts_with("hex:") {
708 let h = <Hex64Bit>::from_hex(&s[4..].cow_replace(" ", "").cow_replace("\t", "").as_ref()).ok()?;
709 Some(OTPResponse::Current(HexOrWords::Hex(h)))
710 }
711 else if s.starts_with("word:") {
712 Some(OTPResponse::Current(HexOrWords::Words(&s[5..])))
713 }
714 else if s.starts_with("init-hex:") {
715 parse_otp_init_hex(&s[9..]).map(|i| OTPResponse::Init(i))
716 }
717 else if s.starts_with("init-word:") {
718 parse_otp_init_word(&s[10..]).map(|i| OTPResponse::Init(i))
719 }
720 else {
721 None
722 }
723}
724
725#[cfg(feature = "dyndig")]
727fn calculate_otp_via_digest (
728 hasher: &mut dyn digest::DynDigest,
729 passphrase: &str,
730 seed: &str,
731 count: usize,
732) -> [u8; 8] {
733 hasher.update(seed.as_bytes());
734 hasher.update(passphrase.as_bytes());
735 let mut digest_bytes: [u8; 64] = [0; 64]; hasher.finalize_into_reset(digest_bytes.as_mut_slice()).expect("hash size too large");
737 fold_md(&mut digest_bytes);
738 let mut prev_hash = digest_bytes;
739 for _ in 0..count {
740 hasher.update(&prev_hash[0..hasher.output_size()]);
741 hasher.finalize_into_reset(digest_bytes.as_mut_slice()).expect("hash size too large");
742 fold_md(&mut digest_bytes[0..hasher.output_size()]);
743 prev_hash = digest_bytes;
744 }
745 [
746 prev_hash[0],
747 prev_hash[1],
748 prev_hash[2],
749 prev_hash[3],
750 prev_hash[4],
751 prev_hash[5],
752 prev_hash[6],
753 prev_hash[7],
754 ]
755}
756
757#[cfg(feature = "md4")]
759pub fn calculate_md4_otp (
760 passphrase: &str,
761 lowercased_seed: &str,
762 count: usize,
763) -> Option<[u8; 8]> {
764 let mut m = Md4::new();
765 m.update(lowercased_seed.as_bytes());
766 m.update(passphrase.as_bytes());
767 let mut digest_bytes = m.finalize();
768 fold_md(&mut digest_bytes);
769 let mut prev_hash = digest_bytes;
770 for _ in 0..count {
771 let mut m = Md4::new();
772 m.update(&prev_hash[0..8]);
773 let mut digest_bytes = m.finalize();
774 fold_md(&mut digest_bytes);
775 prev_hash = digest_bytes;
776 }
777 Some([
778 prev_hash[0],
779 prev_hash[1],
780 prev_hash[2],
781 prev_hash[3],
782 prev_hash[4],
783 prev_hash[5],
784 prev_hash[6],
785 prev_hash[7],
786 ])
787}
788
789#[cfg(feature = "md5")]
791pub fn calculate_md5_otp (
792 passphrase: &str,
793 lowercased_seed: &str,
794 count: usize,
795) -> Option<[u8; 8]> {
796 let digest = md5::compute([
797 lowercased_seed.as_ref(),
798 passphrase,
799 ].concat());
800 let mut digest_bytes = digest.0;
801 fold_md(&mut digest_bytes);
802 let mut prev_hash = digest_bytes;
803 for _ in 0..count {
804 let mut digest_bytes = md5::compute(&prev_hash[0..8]).0;
805 fold_md(&mut digest_bytes);
806 prev_hash = digest_bytes;
807 }
808 Some([
809 prev_hash[0],
810 prev_hash[1],
811 prev_hash[2],
812 prev_hash[3],
813 prev_hash[4],
814 prev_hash[5],
815 prev_hash[6],
816 prev_hash[7],
817 ])
818}
819
820#[cfg(feature = "sha1")]
822pub fn calculate_sha1_otp (
823 passphrase: &str,
824 lowercased_seed: &str,
825 count: usize,
826) -> Option<[u8; 8]> {
827 let mut m = sha1_smol::Sha1::new();
828 m.update(lowercased_seed.as_bytes());
829 m.update(passphrase.as_bytes());
830 let mut digest_bytes = m.digest().bytes();
831 fold_sha1(&mut digest_bytes);
832 let mut prev_hash = digest_bytes;
833 for _ in 0..count {
834 let mut m = sha1_smol::Sha1::new();
835 m.update(&prev_hash[0..8]);
836 let mut digest_bytes = m.digest().bytes();
837 fold_sha1(&mut digest_bytes);
838 prev_hash = digest_bytes;
839 }
840 Some([
841 prev_hash[0],
842 prev_hash[1],
843 prev_hash[2],
844 prev_hash[3],
845 prev_hash[4],
846 prev_hash[5],
847 prev_hash[6],
848 prev_hash[7],
849 ])
850}
851
852pub fn calculate_otp (
862 hash_alg: &str,
863 passphrase: &str,
864 seed: &str,
865 count: usize,
866 #[cfg(feature = "dyndig")]
867 maybe_get_digest: Option<fn(&str) -> Option<Box<dyn digest::DynDigest>>>,
868) -> Option<[u8; 8]> {
869 let lowercased_seed = seed.cow_to_ascii_lowercase();
870 match hash_alg {
871 #[cfg(feature = "md4")]
872 "md4" => calculate_md4_otp(passphrase, lowercased_seed.as_ref(), count),
873 #[cfg(feature = "md5")]
874 "md5" => calculate_md5_otp(passphrase, lowercased_seed.as_ref(), count),
875 #[cfg(feature = "sha1")]
876 "sha1" => calculate_sha1_otp(passphrase, lowercased_seed.as_ref(), count),
877 #[cfg(feature = "dyndig")]
878 _ => {
879 let get_digest = maybe_get_digest?;
880 let mut digest = get_digest(hash_alg)?;
881 Some(calculate_otp_via_digest(digest.as_mut(), passphrase, lowercased_seed.as_ref(), count))
882 },
883 #[cfg(not(feature = "dyndig"))]
884 _ => {
885 None
886 }
887 }
888}
889
890#[cfg(test)]
891mod tests {
892 use super::*;
893
894 type TestCase = (&'static str, &'static str, usize, [u8; 8], &'static str);
897
898 const OFFICIAL_MD4_TEST_CASES: [TestCase; 9] = [
899 ("This is a test.", "TeSt", 0, [ 0xD1, 0x85, 0x42, 0x18, 0xEB, 0xBB, 0x0B, 0x51 ], "ROME MUG FRED SCAN LIVE LACE"),
900 ("This is a test.", "TeSt", 1, [ 0x63, 0x47, 0x3E, 0xF0, 0x1C, 0xD0, 0xB4, 0x44 ], "CARD SAD MINI RYE COL KIN"),
901 ("This is a test.", "TeSt", 99, [ 0xC5, 0xE6, 0x12, 0x77, 0x6E, 0x6C, 0x23, 0x7A ], "NOTE OUT IBIS SINK NAVE MODE"),
902 ("AbCdEfGhIjK", "alpha1", 0, [ 0x50, 0x07, 0x6F, 0x47, 0xEB, 0x1A, 0xDE, 0x4E ], "AWAY SEN ROOK SALT LICE MAP"),
903 ("AbCdEfGhIjK", "alpha1", 1, [ 0x65, 0xD2, 0x0D, 0x19, 0x49, 0xB5, 0xF7, 0xAB ], "CHEW GRIM WU HANG BUCK SAID"),
904 ("AbCdEfGhIjK", "alpha1", 99, [ 0xD1, 0x50, 0xC8, 0x2C, 0xCE, 0x6F, 0x62, 0xD1 ], "ROIL FREE COG HUNK WAIT COCA"),
905 ("OTP's are good", "correct", 0, [ 0x84, 0x9C, 0x79, 0xD4, 0xF6, 0xF5, 0x53, 0x88 ], "FOOL STEM DONE TOOL BECK NILE"),
906 ("OTP's are good", "correct", 1, [ 0x8C, 0x09, 0x92, 0xFB, 0x25, 0x08, 0x47, 0xB1 ], "GIST AMOS MOOT AIDS FOOD SEEM"),
907 ("OTP's are good", "correct", 99, [ 0x3F, 0x3B, 0xF4, 0xB4, 0x14, 0x5F, 0xD7, 0x4B ], "TAG SLOW NOV MIN WOOL KENO"),
908 ];
909
910 const OFFICIAL_MD5_TEST_CASES: [TestCase; 9] = [
911 ("This is a test.", "TeSt", 0, [ 0x9E, 0x87, 0x61, 0x34, 0xD9, 0x04, 0x99, 0xDD ], "INCH SEA ANNE LONG AHEM TOUR"),
912 ("This is a test.", "TeSt", 1, [ 0x79, 0x65, 0xE0, 0x54, 0x36, 0xF5, 0x02, 0x9F ], "EASE OIL FUM CURE AWRY AVIS"),
913 ("This is a test.", "TeSt", 99, [ 0x50, 0xFE, 0x19, 0x62, 0xC4, 0x96, 0x58, 0x80 ], "BAIL TUFT BITS GANG CHEF THY"),
914 ("AbCdEfGhIjK", "alpha1", 0, [ 0x87, 0x06, 0x6D, 0xD9, 0x64, 0x4B, 0xF2, 0x06 ], "FULL PEW DOWN ONCE MORT ARC"),
915 ("AbCdEfGhIjK", "alpha1", 1, [ 0x7C, 0xD3, 0x4C, 0x10, 0x40, 0xAD, 0xD1, 0x4B ], "FACT HOOF AT FIST SITE KENT"),
916 ("AbCdEfGhIjK", "alpha1", 99, [ 0x5A, 0xA3, 0x7A, 0x81, 0xF2, 0x12, 0x14, 0x6C ], "BODE HOP JAKE STOW JUT RAP"),
917 ("OTP's are good", "correct", 0, [ 0xF2, 0x05, 0x75, 0x39, 0x43, 0xDE, 0x4C, 0xF9 ], "ULAN NEW ARMY FUSE SUIT EYED"),
918 ("OTP's are good", "correct", 1, [ 0xDD, 0xCD, 0xAC, 0x95, 0x6F, 0x23, 0x49, 0x37 ], "SKIM CULT LOB SLAM POE HOWL"),
919 ("OTP's are good", "correct", 99, [ 0xB2, 0x03, 0xE2, 0x8F, 0xA5, 0x25, 0xBE, 0x47 ], "LONG IVY JULY AJAR BOND LEE"),
920 ];
921
922 const OFFICIAL_SHA1_TEST_CASES: [TestCase; 9] = [
923 ("This is a test.", "TeSt", 0, [ 0xBB, 0x9E, 0x6A, 0xE1, 0x97, 0x9D, 0x8F, 0xF4 ], "MILT VARY MAST OK SEES WENT"),
924 ("This is a test.", "TeSt", 1, [ 0x63, 0xD9, 0x36, 0x63, 0x97, 0x34, 0x38, 0x5B ], "CART OTTO HIVE ODE VAT NUT"),
925 ("This is a test.", "TeSt", 99, [ 0x87, 0xFE, 0xC7, 0x76, 0x8B, 0x73, 0xCC, 0xF9 ], "GAFF WAIT SKID GIG SKY EYED"),
926 ("AbCdEfGhIjK", "alpha1", 0, [ 0xAD, 0x85, 0xF6, 0x58, 0xEB, 0xE3, 0x83, 0xC9 ], "LEST OR HEEL SCOT ROB SUIT"),
927 ("AbCdEfGhIjK", "alpha1", 1, [ 0xD0, 0x7C, 0xE2, 0x29, 0xB5, 0xCF, 0x11, 0x9B ], "RITE TAKE GELD COST TUNE RECK"),
928 ("AbCdEfGhIjK", "alpha1", 99, [ 0x27, 0xBC, 0x71, 0x03, 0x5A, 0xAF, 0x3D, 0xC6 ], "MAY STAR TIN LYON VEDA STAN"),
929 ("OTP's are good", "correct", 0, [ 0xD5, 0x1F, 0x3E, 0x99, 0xBF, 0x8E, 0x6F, 0x0B ], "RUST WELT KICK FELL TAIL FRAU"),
930 ("OTP's are good", "correct", 1, [ 0x82, 0xAE, 0xB5, 0x2D, 0x94, 0x37, 0x74, 0xE4 ], "FLIT DOSE ALSO MEW DRUM DEFY"),
931 ("OTP's are good", "correct", 99, [ 0x4F, 0x29, 0x6A, 0x74, 0xFE, 0x15, 0x67, 0xEC ], "AURA ALOE HURL WING BERG WAIT"),
932 ];
933
934 #[test]
935 #[cfg(all(feature = "md4", feature = "words"))]
936 fn passes_official_md4_test_cases() {
937 for test_case in OFFICIAL_MD4_TEST_CASES {
938 let otp = calculate_otp("md4", test_case.0, test_case.1, test_case.2, None).unwrap();
939 assert_eq!(otp, test_case.3);
940 let words = convert_to_word_format(&otp);
941 assert_eq!(words.join(" "), test_case.4);
942 }
943 }
944
945 #[test]
946 #[cfg(all(feature = "md5", feature = "words"))]
947 fn passes_official_md5_test_cases() {
948 for test_case in OFFICIAL_MD5_TEST_CASES {
949 let otp = calculate_otp("md5", test_case.0, test_case.1, test_case.2, None).unwrap();
950 assert_eq!(otp, test_case.3);
951 let words = convert_to_word_format(&otp);
952 assert_eq!(words.join(" "), test_case.4);
953 }
954 }
955
956 #[test]
957 #[cfg(all(feature = "sha1", feature = "words"))]
958 fn passes_official_sha1_test_cases() {
959 for test_case in OFFICIAL_SHA1_TEST_CASES {
960 let otp = calculate_otp("sha1", test_case.0, test_case.1, test_case.2, None).unwrap();
961 assert_eq!(otp, test_case.3);
962 let words = convert_to_word_format(&otp);
963 assert_eq!(words.join(" "), test_case.4);
964 let decoded = decode_word_format_with_std_dict(words).unwrap();
965 assert_eq!(decoded, (test_case.3, true));
966 }
967 }
968
969 #[test]
970 #[cfg(feature = "parsing")]
971 fn parses_otp_challenge() {
972 let challenge = "otp-md5 487 dog2";
973 let challenge = parse_otp_challenge(challenge).unwrap();
974 assert_eq!(challenge.hash_alg, "md5");
975 assert_eq!(challenge.hash_count, 487);
976 assert_eq!(challenge.seed, "dog2");
977 }
978
979 #[test]
980 #[cfg(feature = "parsing")]
981 fn parses_otp_response_hex () {
982 let otp_response = "hex:5Bf0 75d9 959d 036f";
983 let r = parse_otp_response(&otp_response).unwrap();
984 if let OTPResponse::Current(x) = r {
985 if let HexOrWords::Hex(h) = x {
986 assert_eq!(h, [ 0x5B, 0xf0, 0x75, 0xd9, 0x95, 0x9d, 0x03, 0x6f ]);
987 } else {
988 panic!()
989 }
990 } else {
991 panic!()
992 }
993 }
994
995 #[test]
996 #[cfg(feature = "parsing")]
997 fn parses_otp_response_word () {
998 let otp_response = "word:BOND FOGY DRAB NE RISE MART";
999 let r = parse_otp_response(&otp_response).unwrap();
1000 if let OTPResponse::Current(x) = r {
1001 if let HexOrWords::Words(w) = x {
1002 assert_eq!(w, "BOND FOGY DRAB NE RISE MART");
1003 } else {
1004 panic!()
1005 }
1006 } else {
1007 panic!()
1008 }
1009 }
1010
1011 #[test]
1012 #[cfg(feature = "parsing")]
1013 fn parses_otp_response_init_hex () {
1014 let otp_response = "init-hex:5bf0 75d9 959d 036f:md5 499 ke1235:3712 dcb4 aa53 16c1";
1015 let r = parse_otp_response(&otp_response).unwrap();
1016 if let OTPResponse::Init(x) = r {
1017 assert_eq!(x.current_otp, HexOrWords::Hex([ 0x5B, 0xf0, 0x75, 0xd9, 0x95, 0x9d, 0x03, 0x6f ]));
1018 assert_eq!(x.new_otp, HexOrWords::Hex([ 0x37, 0x12, 0xdc, 0xb4, 0xaa, 0x53, 0x16, 0xc1 ]));
1019 assert_eq!(x.new_alg, "md5");
1020 assert_eq!(x.new_seq_num, 499);
1021 assert_eq!(x.new_seed, "ke1235");
1022 } else {
1023 panic!()
1024 }
1025 }
1026
1027 #[test]
1028 #[cfg(feature = "parsing")]
1029 fn parses_otp_response_init_word () {
1030 let otp_response = "init-word:BOND FOGY DRAB NE RISE MART:md5 499 ke1235:RED HERD NOW BEAN PA BURG";
1031 let r = parse_otp_response(&otp_response).unwrap();
1032 if let OTPResponse::Init(x) = r {
1033 assert_eq!(x.current_otp, HexOrWords::Words("BOND FOGY DRAB NE RISE MART"));
1034 assert_eq!(x.new_otp, HexOrWords::Words("RED HERD NOW BEAN PA BURG"));
1035 assert_eq!(x.new_alg, "md5");
1036 assert_eq!(x.new_seq_num, 499);
1037 assert_eq!(x.new_seed, "ke1235");
1038 } else {
1039 panic!()
1040 }
1041 }
1042}