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