pyrit-0.4.0/0000775000076400007640000000000011526302053013137 5ustar slaveslave00000000000000pyrit-0.4.0/MANIFEST.in0000664000076400007640000000056711526301766014717 0ustar slaveslave00000000000000include CHANGELOG include COPYING include README include MANIFEST.in include cpyrit/_cpyrit_cpu.h include test/dict.gz include test/wpa2psk-linksys.dump.gz include test/wpapsk-linksys.dump.gz include test/wpa2psk-MOM1.dump.gz include test/wpa2psk-2WIRE972.dump.gz include test/wpapsk-virgin_broadband.dump.gz include test/wpa2psk-Red_Apple.dump.gz include test/test_pyrit.py pyrit-0.4.0/pyrit0000775000076400007640000000044511524611424014242 0ustar slaveslave00000000000000#!/usr/bin/env python if __name__ == "__main__": import sys import pyrit_cli try: pyrit_cli.Pyrit_CLI().initFromArgv() except (KeyboardInterrupt, SystemExit): print >>sys.stderr, "\nInterrupted..." except pyrit_cli.PyritRuntimeError, e: sys.exit(e) pyrit-0.4.0/test/0000775000076400007640000000000011526302053014116 5ustar slaveslave00000000000000pyrit-0.4.0/test/wpapsk-virgin_broadband.dump.gz0000664000076400007640000006475411524611424022244 0ustar slaveslave00000000000000a7Kwpapsk-virgin_broadband.dumppU4mRJPSP"*"RP=V`+-A".eYDF1,z]cXDZȰ,ǰ N"vP3X9A}_ic&N'/?%7%X_ǥk7o!'4\sWt5 .iYgy5lژv9cbhq&Ox:Tg #Ց~]-N#$vq{V^1G4#R1:E~91ohf{aϖ.>`TLk9bQ 6`t1!n>{U# Ռ9]̖%9i-.fkDو|O~S:\ш[wIVM6ϫts-g'7բO[%Ggtl5"cls's϶1o_R0VtݢsGl}BdRȤYdPL 1\drQȭL w3dIL);@^bR)`Ip&u)3i@V2g290dȵLFob2qL@d2 gRwv&=I&5HїOH>2&uLL!L &Ð@BL!w3d2%rO@0Ac+eV&};!0i@d2yY&ÐLF N>!Ac2YdByL&!L.YIdI r^(>ȏ!ۘ4N05&ÐYl}BjLF|x1IL!L& g3\Ĥȗr9) w1ˤ0:I& )3.>!s C0(&cUL!g1drk@naR}&}RNI8d;AN>!ˮ!=LFјBzANf29&RNo)[i5)+;$ iN_9}4A LJ'$G G;$ǤwHOH3IH}Ji?vC4/9IYw!sX5IQHAVJ A*0$@*(V!i@B:ZNkHZA5RtH!i41C>Jk in? Is1ʹfH7Iy @Ҍy @Ҍx!iހOs>ㅬ9^H@.5RVӌ} f4c($Q@h( nBdQHYO;EtP AOVHCQ; H#YJBzAH1I2F{;tbPLB{uI}#y;V+t+$ݡh@AhP CN{!($ = Iw(&!i(Vx ?5 I|RΤU:$0 i@V i@VD0K:Al5^  @z$d x iFk ?5 ^ 9$Cz$B΢5J .eV $!i"%@~@ z)ki2'U@ZOIBRVL)YbJ`dV i@V $ i@V [UHZ%A*/$A*r?* $ 4 I3)uяBҌ~ O i?4'2@B&Z;*]<ʢ^>Ka5GوK&z>ΣjN7#9De͖)C2J;39U~T׶WcLeiݱ٪ T;@ß)N"хw6}j.}ַZ,ɒ#X#(3*Cd}& />wuɺlҧ`}jY^H6YQ*{؇qf#֥~6!9*!} >Ð[YìϨ9g 8z/zsZ7伪VKe1S>GuR5&$]#Oʢ ^d:TuҚe|}l KGkWr|J:띬Юn9KbbgY34syfONϢìOveS IEm="5O~-#˜@?R-B8gzBDRv(Zx2&lCKzǺR9k>QdZrt#2nSLylGgVpĮO|l3CUڗٜF͵J6()4iء, ^?(-^R6܍,n?)<;XhEs7ZS6& Mǡ&χHodXle}>Rc-F\Q4{W~*n_`{WZ=M YܗCz> H2 A Ce}F47-]8;E϶,$7cEGw^Uծ TB?Қ|ӞQmXZA#l#cϔpuvPGߏpSv &R,[Ϣ]$KVRi oϪGݥ/e=WEwr ԺtzCϣMFȀ[GG.E+NfOUlX-g҅)Պy6W̖L-Sȵؿص:<`ɭ#K̇(gy̛a{9u;dZmtd9e^Wv\?Xn.WV|ӧڶ(9Zi7թ?8da>  mwPH-dKH"!`dE/YB}_3)D#,YȒ"[%|gn|?g9sΜyy}?Owk@U$/q`dڒѓd|CumY" u ˺}e9eOԶ%'z۲ʧgy-%p>NQē~F8<;MQijcAl%@guDwL4cmG:ضʖD<vJ1: q2gߖ+TʳjFSʒqI --eyr3*a2[P>-s|roY:Jb<2PuY*b VScEAs~cq:?[fCx~tBuC)FoY?0N)|[꼭}d.ϘmKmK8K~8Uy2`-K"d6CPC,UŽ=NA@IWRL@I_JFP?(yЄ_g3ŭPJ 11ǿ x:ZJ[EY rV,_PX MZݏb-vTl[A,޶ٸeIhP%r `8u@;AH/Eh8!F0D=!T a$]H A4O /A_Ծ'X{yjoG[TWۈ~zg`-"(r+W骿sjq*8-" TVtaat ;X:GIE'E^P Y6d ƽ2'7 ńCwP"8Nȍxp@Ӷ!%~H/;ٍU,LBYrݗMQoOccC;W6wI^ {E^F{:s㻲PGrI;Pa'V'sf: MI+xl,,ac?7cD| | C@m#CPQ6!Z͠9E6@yNC4 f1!a0B+$b /췽wG`5@ Hye0D@!>O!쿫jy8S6U^~"FE>-g8 :='ua$Ұ=X E5fkkǥr3{.2rLFpUuq~C#s: CO[ ]yծ`Tł^uư,M]#㶭p#eOv4K>pڳn@6Ue];>u?E b,_wh*h5.m6=Hŝn4FfVޫӪ/WD}ok7Rqk!rQ!@؆b|"NcK=Ŋ&'Q,w3:ѦLW]^xNW6ز `iRf/_anwp>@ckhq!M E܊9׮OEL+d3D?.y$S.7}t.J溥m_7,8JB1Ǣf}czs|y}."wF18F Ek V͓HL5]o]yAyA1}Ш; bNODϣy#=Mq,EnsZL*TOF^S΄,}a$m&r1}'!la7]]T8&=Bj=_}E&I60>/8IdvMM;O"7C+YɖDZ.XP&@ϊ\J3|BR /tz`TXBTjVSaw,YS>7GvЬhn S9j$/s#s\/C4$0VR$CT$D 'sVk Yokn%sxV;~ʡ)d~%Z~%eG4$8/`Կ+ƉkY^,Pg6l>iTR0w_8l}Thf")h(cUDV2҃lD/U2ll`c枍w]\7.%Piӿ,{ջ,{$| 7RI$..г؈U $jC"MЇ1h aK䏜0Rq13aaTr7B(?=J-=mSf}{ u8O,47>5G؋uZtȢ G_7]]~r>Dٖ H&krqs+QSƺߣb%I` >8i-WLGc<^E:o6 t›gf)Ö{ۮX Ndsf WNji)ܻ]qpH I==.ի MAJTHz7-Up) ;SAvp cڧ 2ʍ?0诔F$+H/TY] ?JMQ1c*r!({8`bZ5 "_IFawEUJ_߫oԛ(4 M^w 129?'UUa{5䥫$ k}I<&R=Pu ΆBeP]ɊA#8FʬTNQfKOڳR>#aW)"jWǁ&%É[L'JѬiAk`hPͶ ͙>z{Ѯҝ*E=tӣu͍|rt|d6>F MktՐ{y7Q41Իَ+#ھct~ٜxPk5Dtq\z.\v&V9_ ZW_fw>lhE_ҪA&0:"zCULi@BG/䏼h6b^7]$%?Tj)cmh6ʫ"LAO%cMiɉڌS M]v:de9P/ykqL坍җreAΌ"55cr_.J_vtu [3UzUϦcSécD16ʢ5LFޑgQwHlLi6fpeNqW}9b~4K3}|=y ]e,s&.@h}- ZϏսۼwHFO4Q}8‘+|/k|4'rm)Jipԁ__1`$⏅? P U$c,^ZCcna]jjtĩ4' L˼%y2fz^#Qb{U'Lf 7 ƪTU沙Jg `E ,M&F `:n[֫3cS'Nէlt>>1H/nQCȮ RfTC'\rd:?Q/@1VxiuPO+(IzJ>D187쭱xZrL߂}skĶZwOީ(r3E I!rA@Awbۗ5iX~+>m`km.E=Ifܢ#~L/u6Vɱw-&D>TG6R=TM|8sx&LE|UN^D29f?Dxj |>Æ 6,noQV %dq3I[!@9a]yӒQFWb<ON_ l-uVj13/h$]_2?22Fj',MGYϘWnnFͽkҷV<ڬ?7R awk<2Yyʛ×69rpΙ)lz+bSOQW|% ty+j7)PTR$O!2u]kѠR!ū /UenxR2TT$ 4>{9ZVq|>{Bq4!h/Ѭ٭Qҩi*qoYҋ6W\x%gs94`g\}4CqU"!j_2u>tށ;K멯Ybz>%&#xI}[lTSS ?%%k{_?K?/2~E`g]J˝Nx..w/H9+D CNEN_W6qm]f+Z&׎9hK\965!RmAvg'mȔ_'<.9UϵŮ >Y"ZwvW6])Vut׌w3S4Swm-eRx]guMIXS ᤱ62浽eȣҩny!aVd=(T4PDԓIR>wpG]v׮Ey sgm'={fیg>AoQe4*Α=㦺{̀[ <<7єwOܕ}hl[qsi!1+﬒+ ,Mϙ"u༸Iis츁d;M. eCZ=YICCɁ;Vjשּׂrg4,Y|ʵV][ꀂuQgf'ݿizƵC'O&>O9W*0Dgk̩^Cj>rWeuWlenUpb [}[eߘS)=#dwME'<cT2۽ؼh9M}]dRCG?:1X^mϏH^?>9vz嬥k&nW 5=jWnIpZ-*P[ rA G"C4Ž7K (V\=3;O[ WJO.O _1>-R~+ҸT}mvQ;[).:;xNnV]Q+[]F5VQF썵Ci|&%Sr#:mbIv/3zL_YU$34Z|9:tϟ\zMiV_}po9r}{!1`rr£R8tM[/U/S3;qnޗS9z雚%c+pgi~{\kV2_j?ѶITvN0tlimgTߟg'Kaǖx)7+b͠т/^i nu董+.*^Tӵr|܃c?lĜ'zBn"C"Ā>4 Ѐl=bpפYuN7j=6k¢BMEMv޻ Jͦ==<ȎLqlZo6n!O徖4ѡǘ5/Mp~)jt9ƫO~t(ݰGtykt#wTHG[/bhoޑu?l/٪5jj]&'tp+kv(UaqW~V.xmbdNvrqtgy_qIUv{X7#'.j I>lq.Yqj?vvk2ut¿t<9#uyDڭ/z"_;٭e-MZ4 WN>S)$@{n[dׄMQ}w+ǐ׏=R6h< I;+fcƩ׺iG\jSmgvA(tBi=[N<:=Ҹ*O }7uӝ۲~z}֫x}R*b޼;EԨZ^?'-&l>i'c<,"l%A#mE9Jk>AH~)Mb=X;!DXt!SD):f' !E?KsRԝJ㨄[B:,Xݩ]>NĖ*uJX]jԽFS6A'ڛC vmF+LG3rGRd f|C,kMoߐ}DsK39vD;񆪸ZmD@UL0Q7BNB -:?sBf< LÙ\Y3gP|1p%%t$~y٦'&Matli 18™ |&#(B%2$Dh!G$ Y( (lǙ>),ؤY=D<0rLY!+|2mp @~~*U:iڷе3efYF0QAn'1-iи3̭MYTeMIǙMDY'z{oGJ3_k @}au)S-ڼr ߨsVW[@$/+o`?,SG 5|7̉pX>s>5D=MYiA&?*t','d>s i+F:׉bSbE} m8 ƇFǙKFxOP8S0>fgA6pfd3 B6pb?޵g,F'y l6%C6qf5dg FlLy),!]8 grM:̅lLېM)4B ̻>q10b.bt-SDMq tG ۣBXݜ9{")Sg"2~=/og2!|&#Y3S 8dg@6pIs31eqdSgFA6uLίM:,"}Quk}=׃ϴQ[e`^Y9^#ʇuBLv\#qɋ!#A;CB\/}|}_@յ]틴L:.XRU@h4]ʮL 7eQ5cpv!!YMDL0y:͋g :,i+>oFb9()CV&FH!VlDMO?:f! @&#՗+qtx=LDRn+&H!E􈶤1: a4GCl$^ c2@)r2P:i hAG"$ȰY!D D'`顑 6D1 b82!l?=!l|WKaifcdlْc\Y pA~N(:K("Uٰ딍;nW#UlP@\X]j,`ѹ?-J<"uD5p@)ie/$PA# LElAtb7RSLMX^Ե.Q-n0 uƘDyngdR 'aPJ}NdB2$'sRƜ1zj zJmZ`?m n6Z,~?fNuz^scl$UH޹cpsG+"$FM4tl䉾=#8J}!X $D3$ !sO^cps//2/J60wgV}gζYx/SmvyδUuκT4{3z /&*g-=~aw;:ϳXDX~}{ ;>{h#+|"n_Wԫ3F1 x̙GڞM9$pl4ƖD_ϹtEsUO]EK"nb8ʭ- }ﵿ{tûR#FSI}w#w{0hޝt1ymY0nǼO* al+AJ.crÓ;+wMO4:coWu`FMuO(]5uqGk;*kU|' |~oshFi81+:[{]gNUR+>s*0:`xpEQ&Ndų[]M.דV;.6`cj^ִɾs.KnS…[+ ]*=\k-wXiY ] WܳwW^jjx)R:HHEH4.@@@QJD(tbiJ {{\/y=3#q$~6.K o/z *h0'lj7|rk+~E@G[wH īL3UgVeEN}@$z$5?"+ #fޠϯ۫H[)S>J~H: 3gCMS2R8KZ!+>OEOaMAow)UΓ{~$0ȣ#+5""tZMTփwT$rگIoC<Ɩǟ˶Of]P qhDOAh֛Z2B'g7HK[I/,] t*ɣ}eߡ7ۥ5-XryVҼriyhRڃ#wDW|7Ʋ2rT@U-?D-1.XBߴ+L2I0 @{qGyܚev. E& $fu<""d5f=>BQ)TiR`Dġw1Kէ˾(aGfYX~o-W'׿$d>J)M0 u36Sj)QS$o_ڍ/ "ҽiw= nX8 A$u~nsgi=&c!LHNt|CY9 g yM^j?8Sߌҧ/~Բ;N+Ynnd]( U_̺py~]ckHwi\I"V)38`U7AYDs.3&[{k*];tʣY^nThk=mB YE! \݃ 0ρoU+ ^?MtXB+wo{:}4g7^>sA f~pkc7rE+V^鍬}U(sb݂㊶EZ ̀]tι܏N{H&\艴FO|cWJm\T`6-}nӪo(ľB@51Yd%rJF*xPט BQ})RZP'Ss3 q썐XR/BH=:nq `Ws4&f_VOw猈}Ёu,}}í˜I~GH+W q+;KsYb,>P'JzX2&#)QmZˣܣLHաJ d!TQLo4p4)hQe˽pЖ@ڏ柡qIsLzH@`Ue(Aʓ;u`/2be˴zu{c47¤*B2]?U&Ҷcs'`R0?5=|X}u7 ]޲q h$NC?4RR?qRx,_8w@M4)\cEᚫ'Q}*y;SQIwpйСpF:L()`m%pyIvV6$1<$ygtJ\9هo|ԕY~taVC#8&t}ZB5h ^z\.j]ntu6KOnTS^qc EJqPL*7Ba>,oY}璆A,.g=bҎ.<ާCeN퓜 2'>4~0l f筗Ӂn> ϥG;m$ tBJwWtpJp:(48]"@j4u Y>,vatጮڄGr\|*"YSǴĉۋ@37J/DY<Ӄ {ǙmhKHwF0Mgoi\4}G^ ;6 5~Đ!lOhH4.@.]GY#z[a?aol#U_uFxKd̸쯒Jgmb7I%,썜'e1@&"?Oag^Hg"jP S4SNyD=1cΰyJJTx—#t`jl3鮋+Tٝ?d6՟JUf\d``+::8#x,gI@bRpEg|HR+-&]?!gtt8CH%`tI8{is*ie@O)۫0XQ K|(_ inPDhd8;s e^(*(AFC^sXya\X ;꼊BҼ~⡱WHmFxF̈ވKS[ICK~쩯}Pf?yґGB餬uczD`S{ߏE=<pڜn,}HF'5LVy2Gq/ /b]_+ְ {XhロwO +4>jM[:>NL^ŰG|G9"jF8++ߘY 㰈/C U}M-~>цR7 K60|z#.c?k[>ǏWf49EÎ-tNmpiy(gL}Fd;kTc {ӅQ iTxm_\= (q%q#^CzB+ vTj}-jm}owbI9eIeI&BH'FS~sF(%(Ħzq{P6Oy՚GtlW k $ژ:M?@VKuYzEȋ@)ҏ\ߟ`wݸ"C9!}õ[>KLc/SN;$:3pVf}3ҥ-t{7щv3܍'7w|( LOq5jIYŪ9 @j+rB )ko\6ǁI^O|mu3NH4ZB[y~:|Iى6,9std;k%mtN~N'K9!뀯l?ڄW5x[u+TK,iKz*pxL^ *#~l4iqfO&zsfI& }:]" Rt$ni/\uXZz0o$pmi2ίoř $(vɳ*[%vb&V5 =1 D)HI-!@0]SY|eܤ64챲d&ѬjnvMil`[ oɦbi9p7 >B4hO$'2Zyc}cTh׉[蔉i V꙽OP k?>IJ$,$&2Mg N'@(`ezf0k&떻+!"i.U$w?i5eɔ ]lu! # S?qh[]SY0vOUYpl>+)3lח#-VL©&mlߧXTG;A_a)A5@u'kC3&%osܪfDb,rq39O3U8]nJ$ը}SdC:*S>gxX%e`1 I '+P:>pnLډ_2 fK<0Qy3b/pEnl(K>8\gttkuW/[,! RED\B)źEdI@YhŸ ŵ@Fܕ( J] 니U_2*p>_'rw̝;3g_Ȟj\#ЎؼhJВlK,i!+4T? zHTIZ;"mr}ؙE2Ȣ5Ue˅XC$3;ǽd_'3 Z(CZ0$kHTR~-TZ"1>P:9ûm%0$Bzr)PirBt*)d9Dvs04hr jy `vqccw9.Fm*lGJ 6pfw!ȞjH͎/cfl."d?赍O!'B:r!SB"E]7lˋytHeYZ68B̷ %R[}/Xz Sh-JB:C7P0H'1DtrIa& <$~Ln})7"MpCh![gS\&}$;.vPݲ|7f'BC`P,덐a;vfG!05Pv,& [^5t7#4.  c472ɣN 9F Ŧ|ɤVos)>]0.b7Vuۣag!.f](̮W86vaZhF fv̜f7+ZPEyy?lGA\v beT!$һӸǐIbX@?LIiY@*SNtf78.vs0l̮[#.vV7' +&~Dw7V>Oxz v[SӥyDt-A1!:;tVC.әB7] tɈt^6 ;A;\a0؍g.o-1[k_yICN#t7WQ;zt~!z>{.7KFW&ؾ5j XIq9;UJcJƩ9; Ų\.TN %g/c3&+3ID$a~Ng12`2jckS{ E1H…"o.]ܖ1H5{)$ Icdr/B^tQ)6f`>,;?<}Rٶ) }*vlٕIq]-MʤȎLll^ -K(;@KI::)$&W! (&,B"R, F>녡<harٰ (Bٜ>y bې$:HDP#.r)dӊD !9G!?֣ɧb  PȍN)B:))'|mmhG!YF$RBMҁ6:=oB^>U?&;0;m.v0; ln#5lB%lGN\v5cr 5xBQ< F.)$(&"06PB!@)cIwCz(6Y0́S&=hԬځ~VJ؉&bc'V?6vavCR:*ZEܤbX{#Axn$f騖f5ZV©%(4V3D+G}fTm:aA-CbHO D˗>,Q okD" KM"!)9`"i;G] 3=X, ؿU_dǜxbEL-L%|_#U:/"ed,w&-ll_ Nh]4bK[ "F3AIMx6ͤhI"#tG~SBҫؙ8[P]ohY/U#`;zjdh8NG;7h^ͅCL!Kr$$RI燫~p5=fBA"lt)SBz\ly|iWK<&o".t绑$I"<XO(b"_P@h+mHBB:T+,22I&eN.Adcm m`PN8f;)nz\D 0C)oٝ  m`A:9> cU.60'vj!(.tVޞDI887m&D .frvhrnџm@]W#ƍL7ntׯ,}T IjV,qyf(^`|XV+8ƾ+tn@Mf?S%s;-F3iф>~/2h)/:.[ Ya9YQ5D>%!(a1I"e%"/~D_"0Ye)O~NȌ?"%F@l?=w-rU/[\Gic5v;+Yk!awH^ hcNw 6ZNٛQ?,}n #,\h^aajN/=G#q N46eVirtL0]s|)i iϺ4Ѹ"Tѹ\vĪ%#if>/|dDXKLqzSp HR&*U߱nnIy{6kǯ3]g˛J^o Xzܦи9*1Wo.Ae!a 2;24clտvUguj+ $r"\ ]<ۉuR]iRJvyqޛie7VX.y gk8^"?L>~)uIDbL/-~&/V> zm豸Oo)Fkjzghz_1w[7ݵ4GS~8a.*6yuZ|Xmy{dFuyٟyWUld݆}]>!Zwȯ9{cp6V;t^>4%<]_xǎ.>n6nHwW,/9óf{2KS+ ObeI5$;l]\#UQ/3ukPa(rcwEN􎙺iYHq3t!J<(QtDc:#DŽ=ѥFuEYͷݯE^`$V:Lܟ=Xjڄ]G;HΖ?Dc,Y2w鎮c\Klq ~:^JGϳ1x;!pػ@}<} NZ y؛#G4)^.y%hmpx}:O9 '_wQ?FKl3'|HU6ViB w?zl%yIg#&5E +}9?de]X3oio-xI2>o ׂuM&ɌDJ8nn1Nܪ}(vȍxbu?5Āy-(8">lxoZ[Z 7^cl^)M\>VqNۣ+wtFۅ=y_i=RQz Ɠdig?GM%@$id7D2ʚg'|sf}}BWg/=ݹ)ecޛ"+~a cP*;*yM/Izf9V{pXNy2w,n[w4鏟Wj.zzzJ$ܞOdR׉yڎI{Sf&;xn{"Q]%#+B9b?VI +dG}wgcfq;:q:wt鈡S˂gu\"{KWS.Q4uJ6]K|a(#084XZ#NvƜ)|%Iu^`6.պF<~ ؟^} Wٷq=u* Qn/ Oʿmlr+]zS_ZUON8w*K_L~BQ3 :*ḱY%'?ݓ^.[yO1V2z~ ,  ?0o 8`}}d繺r» :7R@Sc76OM;ebC%rĢֿdoHlv2e[UnhD%7|M[{.x{7Fl^¢NWCR$XpBBhڬYwV?XNMNp̋3.]FS8nωbko/Z;,[L>*]:AG煬^X<+tƺg*h]PW[k7)nm*}:+0PY;ᖌ]_#f}wq~y;yr:笋3)hֶw}rSSu㟽't}UUN )AFiy0 ] (ׅ!:sqbae(wOY{ >0cBu aenv]7{ˀ*Vt]tl\P]'v\Au8nr]aם{:`"T;*f@d`ۋ)p G \2uL YK!lzAz4uYnc o6VW_~9k OYմ}}Й}mǖ~x[0CUtBϜS/?;w±Wo&qOi0N;dee[&-PdrlS 7غ`}aaÀoF.BF{m&f*tZr8+_;nɂn<&^2ԩ(/>(xfu%;ldk&h]/ v|As%KWTZ2mj3=(:~Q=knZK+K'+'ns:jT"ZBpyrit-0.4.0/test/wpapsk-linksys.dump.gz0000664000076400007640000003143411524611424020433 0ustar slaveslave00000000000000 ~Jwpapsk-linksys.dumpY 8Tm?0KPUhʒ,!BdɈ!QvٗJEl1YI$+|J&7}ߙz8׳< $KKBl΅Vӟ'DH,dUp n4N6*G^Q;$Џ?9 .뢿e9f:lfeY|>,LzYaz0*VpX!ƐWY,@lgzh,AE4qsqw A"p( jvP 7Y_0Aлf-1"(޳PCMxHݟd_"@VKmF@NlF - ˚P`eD `#*2ъ5`e⚎40Z~ⴛ:g(IT٬Ռ @2SNڒwrY4S9 h VXF'"P"'w2~?inq{!^?P%27Ȥ?ay~S)`zć\Rτ͝$v{av2_Vn8N|9X,Ӣ1 ވ0<&7~.]^sf A`XX_DױcRmxWTJs`?%g.̯dԟb7#T!?c֜}e.Y5g Vj1sk~ p66-FjMޫ]מ7n;o~f԰Z[Mt#IJnAK)g|)JRC~)bûD3l P峴Q=&Rg[55Eh{ {Q8ϱWE{׈v bqcs=78 ~ʓkih̢hGLtf mX :$y|:$LpZ昝 $ؙ c'U^]) a(v󬌙pC7PD1A/38ɨ`.Uèg67pRz-sw V`77 J ӹj.ث>aket{(q]"< Jk՛}RňɊXVM^ǹf>>1m}p oop%wBO5y'kzT(j@oppoG=VVs8X r^!&V8!<܅g놇s}}(P a0AՑLb833 b:62Ȣ8qۓ q"L&Lp4>(LV X6)8bpW8eRHM|-!uFg/_!<>6tڳ3>s!ӕAAD#>Hm%z/v.Q6K_֙%sϸ-c}-]Z n.V4ת.v3PJa> *p r223&6!zKn7,NbJl=S|HX'kzUf =$%X=ֻ^PnRሠЉ@C7"SniDlޭT!Ő̷Y^geQKy#^8elz5>aA^ޓogFAe\9aDstJeIOw ?Ye=AT"m.pH!@X[_K4:-~s[67ȇ/xkqC#e%[Wv- L tc=">LA%P,p~B|tӚZU}cq2}sڬV')[$b;E_f^ڠ7Uf:o7ѕ `*2A!`*OAYyXK y$`z| \( RVYc?*ϚZgr/{@R|4~?<\j!O5]o3e#`@Y7݇Bc`[@v.,G1ͭ?[l=%ՂXc_'3 *o 2ZиZvk}Qcfդ AOh }2fCVO1 aGFVL7K!IȮy|dCe>,r{@8Ԫ5PU=}7+N;]8~Cلꅵ^+C M䭝Dy9>5d-2'$1)jKJƱ޶ܠMw3^D-n_4gD]a#Yy h\Wڑ5W"#wwYzt EAߗ^'U_]3rϝl;c?{mZZ:.顇z_yI_Á?9.8Aɡ=wz u(s57 E D׹炲"dh@ a°sf.`0Z妝®;\|ߓDǪ4uDgbƘ,Q\ !`6(]g2$Ɛ ! HF¤qRڏ 2[fBP@dU0$/Wh (?)pgf@3p'Q]!PYzGX(y'!csQs-K53r}T/t\(mgXLuh[ [A,mcR24ZMGa.C(7duj6)EB gkߪ PWpjf^>^,V@c $ev`7+RY'Xz.]Ȇa7*z ̀tE֎CBOZ'i[FfWVasJ=SӋ澭 5q= تM>Y"6g%#OE֧I+pNʬp(\U36 `vQ @09乭'7κõ(m,_R83h6fγ+(]Oi@͂UYNG뫮{8l):yxvuNb.KaU8IׇN׊HMoÄ?N/z,,(6đr)4 (fTpU-&`oRDY car[J#"٫پ%:a,1~q߶iE_cK߳JoŢCeEs]!J'|>3ڱcPU6T46M '' di6S`ˀ|RGߝūK ֚XAPΖM|Y!s3y~[q|QS'Ʃ3ŠdES\ΆsFUR\3_4/9X*R&p\l `*sx"^xSl@/7"t?7'( 攟th|Q`vʳ{\ԏJK~S`{YZON}vN$ 5XWz,\n[Je)ѡFKOQ}U۷ $<}:ʑVĵf-+#0:t3vPx\Fz:,lgA ^ t-U 䅡 Ñ-13ߏ hV $Hx_-#;M -v[  : ؘl0;70< d ϐ.륇Z>l^,10ZLLSUWD!aUK3GJ"k/xo|ATEQ12uyJ3_+35jO0F7L'G5独QunSmu^*d`l؆O|-{v lA?`c)9t^>:lI^Z>kd`%G& Jw/FݠPMCC[I/Viq:{&\1`@5hzjv#dpQ2QI\]4}&=+列yǖY0N6b8.KxoG֭+\j9%lޙur&S-paJ-1($}.G}. BvP}.0~^) J>hBؗ23) F= !0GC<+s#H~+ESzd@_37q]F!D.-}VAXVP|\.k'8/y B8xPG|2у#pNҝt\IqT.eBčb0W@6T;Pp#Ȁ pWv_at(x[@*OJ8/IpkB&FwLpd+{JQmwI ff@~%!_etIGc`vP'#v<3'sk?aѼ9c^;)Q_M0qSF]^Em/X'IWdQn}UiJ,[l/kϕܗrHv4+/ÇA,R!Ͻ8HU+q 7/;rQdW/^Kz"7u%HzN&YCYm}<Sc* M˕Cվ c_ƒL0d %["[ek6TD(d,ɚ Ң.*bs:s}gy}΄| Nec;ύbgupLʡ=;,i6MN|Vsn{ОZwy!S{YAsޡBJӾb|ɹ/e$f~޲2E]!7gwBwd?JO#K"ͽґp.ӱ&k#7ʱH^Y8 -9Hi—ܧ❸ 'YFDH%u}IGM*S)G|3*,ļ0>6Y}KhNmFf9sSft΋zڀC\'GŹ :Dթ5d^9~17|Wfݿ??_>~Wk&fS~L$quى$qqS& g07a2H+uOȿ |_ķ9PRD8" "aZT"vm0^G `fd;- ^yzߴcaHhR)!n̾~{/rKS_\ 9t-ll9{i/p71(GMősxby<,8xkIM9i&ˌQN1JҹK䊛NdOרV9qGeZpuǃy<>!:s) *McP)M?iUR⯇6I,D}_`]NЏ?2a`WX~EEoh]Hp?R2(A…?mAo[wu˖.&+9= _ .7g(,\9W';5S7+ iWA5x`[ n la-gQTfh|Lטͅբ3omEYc֙eQ=My#T=0{LP2J_I!l"WPXPI ہD: m A=bq6~ 2*WFk(l{rXy ?U`~r`w٪бC fTt9ϲEUsяV5fQO˙n%+=VοDM`o辜U[1Eʠ~qzrfO8I6U&p[C,$ܝ)ϓI橭f\L.IQF|yL#=n%f`y9Vd2zc> .R;GZX*c/hw=8pT\X]T%AYTGҎtRC6=ĉA 5gyke- |ԓL =8Ma\<}3*frNYv޻Ok*_-Mz#FLdErO\ Ohԕ emGۙbgbi&~d+e e$l jG먵W@4xci3ȸ>:#^0q ΘXO- J@PL 5dit~#^P$AQDOxM- a$:C$ȸȑHI*wT#(Pw0JJ@gD8CEora@ ,Y*Ũ-{"sIG݁UScGvݸ$6I7"QeusIt/>InnI4Q[b@(״,AC^c>7f'd0v ;>:ws[1O'|na04suIzTt85EXw{݊LRTÚʏ󏕵L| e礯mu5KmlF|{rh}DS Te))$<̱1zsZ&F>^E#2X6QR<ψ2LmN^0Uʂ^9gN:wBw#{_wl4$ƌz yyls",Hl 6KS_WҜ|^utԟƯ.6.jy柱#sSG[}1Iip'H6 5@oIE0>@o!,P$3[E3'jPp='3}@YH"k Z8/|yJA4qx $ D&B{jQ0w a`0$9\Ԁe>A$PĤ5D8F 酺A C @AP&!T v)6&BGjQ 0!%@g8ވTCӟfc;(Zww2%gA]D8v)$fR(h &%S gTCܠyfBh(e0>3(pa? ?,jQp=TP@:B3 0 W~CKL[ƺƇ fVzkѓb^Ŀ3{ rw!]짏 o9mYo2 B3`&3Yba]21ln=H@E 9Rv=\.4a oG]*vpѠ]}?/Sv?:STRY1)bc:"d%jrUgu4 XD@3×QDEZzQq:Hz 0y;AtZlo .Q $pT7^o_@^&3(y8@; " g`r8ğw} HqP ah `h $ pd$]Ewi*˷T<^[}=[!C>6  vyq+Ko~72mc_HM4cNdϾ a\9P*nAkʽ>Gol6$`,2 όMj4x-w'[}m׫ Rs߽mbN-a0wtM't!yqKk<:&ɎQ`v02q{TW p|żVzGk2=) Хq8s*z%C_rS*n9&_R2`A}#ցWwa5mxӜ4(nhPiP1%$C GKIr(C$$"\I%n2$J>ѼKDuιk-ϟo~t]~^ygKAjjMME7-LgcsoeK}bO|ɐZi~vERds\Rms.Oxpрw&X^j=>NZ/ؿgW<{bgnDv2D=ϯȻ;smO7۴qZ5Q ^jf_]#w-hkɖ8>ωv$6+W,;meYK|940;y^S곏;smhv-P!5 cC%,k4.D7PG("Rr*Qpn?8LETh2|, ZJV Tz`h]K $[D%&R (DZ/]ΆJU*#H%Jyb5q%A b[lh2=[ Y{L@(HfP ," ez\Yq륜a;fyУgoOg7ˈ+Cʥ ˲"M;Fz1)g~\zb0_ǹ)"^s*|R"b'7 p>9{OK( =[-d 5({B<6<@A@Y6grՏ F֏yTC c|a Y7(If+t]=QP|IE`}(IN)ꂒ_. z7 @E aGX,wDH(mR" p 0*Pۄ#! rWܑz4!. *B cBL[lM3c`51l87c2_kD7:$@Go~NE`CXqR2_X3 !$X,!&pba#8RBƚL`$kGL@+@kXsqd_W2a7. 塅/&0Ql !9 cm&XfOD@%2և0*E"L"F+ߒP!։%8Cf Q90B<6 0T*Yк@pVl]! 1Θ=tIjbaNT\!Bf14]6AP|ME@jcT*r U"M. wh  #pqш!c&3W1ρ!W> ;0WTա#G"9"sUP. CmQv$H-+ܦアm)⇄aJ7̡vV}ZSߕ'-3i윑,/qF9 ͇;<վh.Sӗ$ynֵVGcK-- ,aqT^ɉ{!B_GIQ~.erRnܯ}}|ެee4g u|4Jk2dg?JK]oTf*׆H*R uofpr~~߯W/EQja>m =s6cTjc }}рzFfaW,|sɻqOT-|,yٯL]>*uZ̊׌֧ForizJ4NEob C߳xE͍zSE=EZ!  hqN^(LV"# Rse{*)n7ueh4.2oP?JN{OKwȌ`uʰ:6m4sr{"x?94/qGRԹm )trhsaK'$3^1ug7]\A~{Y̝1q g+9:Хݹ>VvHpMXWȖ0tma߾7 vvBͧ_3VKyQa%ON ĜnJy{g-.#[98GۥZ&aX{Whze0DKq )(uBH!=y9ڥ u|,aorW]~̪(vc-7? ϸuf0{e^Hf(7Pl}>BB^G'zܺW+ uj9uh%c=G61eo)㑼KuZ2n5>Stt&"> tAS5@6`Imtl8]#D JTVS첮GzѻZJEei`YRU pZ% RKF=ξo}y]I-L w>Pʾ7}e$6G׾NW^><2\QU!$Ρ'! txԷmܿصf9Uu'om?S4jU}d2-bf%۶!q=v<ke2a9_Nr*NUfN,9Xb2˾pחޫ&XV3:4C硿ģu1 ݓ|;=:Yu#xn^=).i}E[N`,QJ#R/S؜YI"e?c͊Xk!Bf]czѷIC .!8_NH:,.{yQ?42mO}?MdSt %/DiaU w?N*CbbX3f2\rgx/2/Cؚ2mãC5˶(|f{fj0t] i#"{|j3EY8jW]E0Su 3_ȶ=C(=yԴ>QV'fsDVw{yCtؒSף,:trnκz֬{UM||C.)uFbھ?ml݇Ovo-Z{'|5`׏bi26(vt5EIqhW ֈ%+Opz((`T3ԖؖZ3Q +_׾+,QeĿ0X|YO0 eZ~r U|nH[`M+իV&Պ3 +5>~yo"5H?9uIϨ矶)%o9r^Ǚ!=\g+}Ȟ=]>iV$.96wLk/Q}]#fn2?)]ٚvSigI2C $V]R[].ϿTǿ "=BBH>J<Æq \=Eq `0j*Fj/ 0ZivOL%T>p E@X 3\ F ffwsAݨ@d shd硞!@/iI4obZK#B;1=]LP" +#E SA058tџ"R-Y0;V)8"ߎJ $mH޷㎡e@$Ƭ JSH1Z)7Y %?L| Y2t艫$/?" Ѭ1T҆·z((Y RŐ! 9?{L4Wz86AdHR<3W.}_@ H (iKzCuv(_ពZ1V6)Qf.lru҂+ו)\6#ԨR2FJ[P_U\U4RX1_lt@T-%0/pj^vpP`=YXHuCTP'Gf'``&IY6M^PY72H8)}Bi9g [?`nYQs Aey?fL f>މ1Nd; >siL^"Q t }^}3DѵEl+R4ҘP^3ׄV/Uu;`/&Iq0 F/5j^{7<S@Y:&fM k lve'K?$;Z)ٍlp*6n@C?  B-.e顃K~= VP2۠!0sڡt#;!0/ 9b='g9 aBEșP{ow`T- On^i9u0~H`{& $I;|&?[c&]ȯLhv񱿍0}"L(տ:DulwG`'@DIf S'6鳂%~Ϡ$vƆ21B>)5(' #;FHP)Nr,/198 u%ѿA$RVX"U46'Q.7-ryzNشYzɂ>0 GYEyNO[8TkM ^fw.X +pp.^ٿyNFB;FKߪJ8ɒrP3feJQͰY~‡e~\kl [9DCAzj5=/"sw/m)WM-S׮<'V%lQ\Lfd݂RpˆS)_ƞE,v>S_Y%Gd~$)J@j opڪ/[Ҙ¨pW,,(xӾIS[gHls{1bٶ5cM ڭQ-rTXG{<*gOEGԶ֡MYnMVY?4ٵyd]R͎GN[Mބfy< vN\{#طC_ܖ:ۑ,OߧQ%'N|fuhpm=kt/%|)Vt=k[F?j.s"Ͳ\V)<ԺbkGkY[{N3)Wuh4` ]i?})PS jH˖էjdE3b]KGj.!F7 OE5g ?+1rJwpJ;tw.çaFSEBӲe&}Tw6Rw%P,í꽏!xiw_$_e,-cOW%͆~]L;UyY?cD!#-W@bx٪GZ^3* 0%dO !s(J}=${AM2,  aD> QK! KAY@|}o'?Y<'0Nh2<;{-zY類VV޴߮nZ-ȯrxŞ稦})Yzy,Z<M;qr#mVg:))p]՚q97i%}~f+Pdbx0נ n!$w3eZEp/^3;DBs8`%f5e$K]2@~^RK!I5P9 F^+d)/5FD a-kzL|Ծ@V׆;# J0Bf5ԒqHRFP`R+%vЄ #`m3Ofb8j;0VPҎbԾ@UP#4@^lXS$\`0$FP! #L-$A!F0܆PL`\5BI+` tTU"<@tw j.$-۶k. 0Q6` n0}\|N6H=H$4(-V&_a@nLߟZ I؁ 0#$hPc3 ɗ6IE70Dj1L? #HGBxAЏ_֜:J*>CxJSu}IᣵE<76O?aTpG_ wHV 1?la-!ghէ^K'Nޚ+W2<HzwKUbxn0 Bت!g_b<|rQ q?5닌(X8'|Մn;unf,~V5CZ[ߴvic7=<&(lG}̵#%_,Jct0Y&JY.gJ~204z0p*Hm-u2d{ޓiӧ+Fόk^ƾ{[vj`Qg0J eYK⤫h3LqLZ l%H0B$|!(똅8+@Yǚ~%Lۆ0޶:$mP׌*'675Z $j l"!tzT 9h;PNJAmrd7k@ڵNHE3`v H;ە %qd_ ],F?)PߘS3(]m9 jQAXNP"`2BSxPޠJ#3XN~1A@u% vE46yH#QLЈ7@噠C_l sTfo6nR4LPt&Hփ#0#T; a:RщJF7faV?V).#w=;<$56h#upEsEm;3nG1OoG?+;uՔw^Зp90 tka}"ZG΁iUH<2ݿ ܒDh2wB􎃎bDG|4///ИԢuecsQ,U'w?]Q X`@sDW &;?܋ "t6q >3nFj"mݗH\$|J%@#mDZт_> }eW<1<>bܰ+[6^*%H͠q۩N[1>i[p1gC$r*";02勚J<Ԑ=sW]ecTFsu0# ?=mLŠcq!_~#mq$a16ϰk9 }H{$9 "cAKu@{Psfփ;qh2EOHq1/ 0Xj_@'}p"m$/DsBF I69ɴq)i`DT5t*e! 0( oRi#-Lb6 F֣`?J6%6uuK#z`>j^H{Nh_$hP{K:L9xvw5eNԚ2' =R q@Y4{?DGP/ y%V(mnS;6 Zb)0}7jݦOK,qVYzHCZIĿqy(G=3ں⓷mVK"|ΏoHLuWm]snimeABգA7 nta0nbA{c1Q]z`sQ:_4ZXtbO:׵=٢̓9p^f #':z"Cɤ" [a 9gڷ{8–oԫYs3r޿=Ʊ߽GԷ(Vu[cFLQnLS&Ns]`FEw֕k6zzbMtm$΃~«_%UMѦ2 _LCy5|?"+)~iXqMt &-n[:+޽ b:8(pTQe`灴(aڪRxPu/2ZM`꓅<[&)9mZDrx[Y6+g\&ѱ%&1 es2 f weŕnhw}b}兀&ֱi Li^xn H&[ &$=kdGkW3b[X%t_ pԕeN^}@T;!XzJv[#Ÿ3R93lBȒ!Н;njד_▜Ī|6dP[pe#*_&6g3Sٖqe\j{\S l)D UzrM{I&"f<ؔB.O|`Ν dyom]0x1Cri r ZuZi!m~GDqBo 9k]? '5YSi%s]5##:DU y9o׭mEʀMn4g]=1wThanu4tRF~]irY9mS\Kr#$UQC;G0rTI;v5S2`t%{J*GQpku/ܼFy%k[R 跻tLa) ē 3~Y^5u=F>R[m{m&,_r.笤|rURDYNd3r7&{ۍ[#yF\4ѴP%"ٲO'^SZ2ջ"{z֝oI)fC,om \3@ pk/\}(RGgTųå] w/9|v~y{Kw`www  a޷<Voi2.X했 _n7@OOolIJ>Y `J7anV@0$eFhNC5&.Gk\CʒqO#J2pb-A[#XIfZέncc bT滾 Ո ^zg>,xttIbѿ@>k;YƅޑѰ2$6&$%QeH>D2$rZbxG4A?cPUb$ Y'l-#MvO7ƫS$6syz!p7.N\žGKu1jJKs*`zWY7JR=ʹ͕z3w)r9! oLS^fͼ"69Z+ K֌O*fUT_Q ="\ۍa3ݗ=@p#&UVS" NȕNB[m`g0a;uvoGq5$w9 9x$2>Gbd6cyDj48]Fz(JZGI,FqRMGyx hnc^/Ƃ/>g,ikMj?5Gb&lC&OaSޚU>ںpD1`I^NV [Df]@/"%V2QZs,= /1KAؒwl wt}0fCkPB#  Um=6xž q5x6zZ7а:89G͌c}v(o5fvyr%*xǿRh7(#DD'䇢ЏNگQ{sY;p*@t/> lR& Ob曡{+^\Ωs`pD0L~$wM؎f?J:%hX@_9 4rf`@VkP$e;yd ߥn *%AX6f_+C~fɋ T?' T}A:1RxOq-$O1(6p+ w ՚F>9S~2|oas0>ZVոlAGj@ezrpLMmjd3Kނ"R] FH y>$F RZ2؊H-@TbKxEݡT%8{TvWe[Q5!2 0]b<;GIhme+goF$v xDܜJfDuLgFxr=6*~-=3\p,a!. Ag" ף|Qݯv(yVehB/ijp?$yӗuG{iWxǁG!sQ͂ W85G΋Xu_!lk&Rd/}ЄOJAjh:W]Sbn>*ΑYP6'dPp,i }ҁBzy ،!f- O 33hq/8k~vmY$V#io<撚 ^^?F+_sܳ$~NFfs.Q .@ NvyߩTpr㵉6iPtY65 51]]y"I"EF Bmi U`9I^@R:8lkI +#1M㫛֦*f3tׂG"/:ّ/4?\3G4{7k[d *L1PҖ4hjASƱ^W sH<~qc~ՠYC(F֟HR(E,9 o[ af:,Z͝f Cۏ~ǀ/:p~#aEr/Ƕ`fgTExZSq,ΪH^W%%QL,/ef-2qQ{VT l?2܆ǓbŢ& o [XtA]똴$$eMJ0PH+$v^(5a2 LW/4QDPifJuO<@$4K+ՐWr{om&wve5o EO(ʯp5V:CÞו eysj2"Ǟ+%Z;bMTI5ejCܽZR(CzɂQ;/{ӗY(,\ԮE׹RXŋ`Z(xSaf70IUI#3TLE#`~V{.hX<`c?{^({m6h0i1[B~^N!읁-⁜[:Af/+2ph "-djsqtЙĕY瑮idÕh#I'.~۩x:_l?ѷިE۵Λ?}YBo%f0ǠTv]z2krvt5.Z>_[sh~!oJs"fTgXkrQbJ-jC( *(C.z'WsJw{r_2;ݏ0w#<|X ;UɳвhJ?;y^Oa>k&w/,]!lo`I&|6դ#<]:2p´Ũ%KL ¡Lm-|1$aӻ.UY\,Dw讳G/mSkKB뎜[C<0˾T͉#5KaJs >ޫlҹ]$bE*91E%T/ JB [A$Z.Dp!1:U ^UF2PU ݹpbimtTgK$&lZ޾ dtᅕF|ʂn=CXU%맴qqG*9Qol4iYpsE&jsESlMX,J}@Z_G(ہ6s zEUd ϑ17FW8+}|g2c!_LeH XW8gc@$wϤ [Y X9 =9g⪋ݐ ̧h &*[c&}"{MuV~_lY1\4Rx(߈ qj >"Xx6 8DZ]oĝ)/O qX2[hICwi/1.miv]R ̖*S`p) d[To6}} >\dwZk 'eS/plZ=YU;ap! [hrT{,u#24.? D;ύ$\(_U ȟUH~D|YC)s"bJ1- @+@3f~q qIC8ͫA2>:a Rj{w5 Rnhl|6e_ v A7Uެc[i&שBdPBef8w'=q ΨQTW\v»;Ix`-}x} =*Vw`(]F"w،.?Y-^yV8&y'pMe/CKocs[[fSx!( 5=tjIdT?;G@3^5傷_QgKMs Oy={KP-[q'-;71rYas#kQd1 BfҖ3s<u%f7r:uN\e(W(Q bcvR&\-bOT/Jѐ>,Ɂ 7R08~+۵0$^:GE*K]R-f=^{I2ntĚKi_lo#TwwΦ(L[ywZD' S]yg~F?~\ءEV- ph:FsTla]NK3Of`z$SżVY\$a6bFXѩGbZ60$xqKܼ&ip`:pgrBNpr$C&(4l#4G^g_ھ l]JeWYr><FH1qBEv  SX˸@ĺ>½zFn a-ËtU˜Z C1ޖpY̏f}%|߬pnT' J@rw"AH!AWeo1g,dg:9~2PݙHo9I[+YCF4&]&ϬVWK~0D9'yL_-ú뜜KˁpM˟M \ 9vd[^/xV@B֭N=M Qfmp~inPF _ =~wR"Q$gnuZ_㞃(p Q\@Ts633LGZA$۴WRmh4Yà*oA͔)c4j.Q@O붹ԢF[s.v4ND7m!/g^~ĻQ{cEphԕ6ѻsP{[MDjθ)R6$í/o'،l7btɯr_d9Z(پ''- KM"`{W9ɍg@n_75]ODK$%2jy3UiL0BS,;-ӿn)/l{!i.euvXn})fQ OWr& +&`I#-=z.BF+ RŹɏLIQ)[+.iM+K|f\^DŽ\`Jr(9'%|:(˭ļܴ$>m#~.7`#EmQn_B~hr%aI1̮`l\ѐl0su~]"8)d2_<{|D6 6LmdŰiU.;IC19ł|2`0?dufAYZohXoO^4~?j:*盠rB=è>]8XiDh=Ax^¨I;_Ϧ%dWy^I~V[aA/o𧗧U;u֓DyfdVZ!gSMnt>O+>=_A~JKߑAO>ղ>.]'5pɗOo8_ʬʛlNPmL\`ZsGIM˛e[DBN[%eU42wȜ t YQkSek狺hJ<@Xp!&Ҽ^O* H"Մ/c*U*Z nܒ0P!]Dj>>x y)/jk9ByfeEȏ>L^H[Xlqܖ$Eɺ=)+C,WrDZ"{%GhYp+O;MG^PrW?I)]Ѯ``4g=FK8>:=J;393ciC}0Ma3Td8作O7d4V 5LtÇS3޶ IiP[Fm=y{-}ݬtDTWDE9g1jdӤ@:"X9ߖh l?H^P@L{tSFca@)h.CG%jl@_lȀ + BO,>Fɩ2ihllQˠ͆0+7oV*` mH?CiXU&  &~nޝWd`qV=f˙?>K`qrǎrypZUO])b |)@,XM2s(I"D%~*aI61pL5 h q%pPq"G%۵<f`y'(ʓ)[93ٝVf{"vkﵷvm=+ץtk0)ɑŇZoPxmT@pe FvMY*X|N#" [=ΗظnͿTIs-;0' `.TYEj[ b`v.3z&cWw1s\@gK2 Ƌ!CVs>1S'[_ y*1$enފ2m)@ɟyOdyrphUt⃍:s҂F ~C),Ӷa S EKۮLxX%~R^˓` @8 78ܔg98bF6dFlؼB' cO͉4^2S2ORNlK3;tFY=Rpo2n3J6=Gx=] j܎UNZ5xMn, YKgZ(R`=l.g,k࣫s{`%ᨆC5zHYGve+;gכx_!RK4n㌌8@3T Ix k+ R΄ϧf} VQ w^̟M=}_A?oH g{tELWp}6?ÂJ=BϒJ"ٱX2׶H~'ros?EW̛{&%ԅA9צ>È]T 9+=x 7g(j)/(26Shʢ$|jllx.ʹ PdBx(|B' ö^;kw-y1a&]/6FR#ď=OUf =Xn{|bUxI~!yS*pԧc)jW7z=0VmrNlQN˫w]jݍPɨ~%reR"좔udO넛2UWw]nWn4.@RTryG-?oݐ2^  1OjG(繡|姅oZ+Z{ѕ7Ŭ$OwIE'NA )fҠ5$FYzV ,rwhMEzq_H Jc߀2%'b-'pbK˥o'+|<01!K2o*xbicp@TDL(oF-Co$BM_-ۯ\O@اn69u%gRA{cFׅd \@?7hڬl$@Sy:! ZE1{YUKGtb9~ViA0ciڪ8Opse"y2 @ _HJ| dNɦ)IuzQ#eV*1""&6b@R P[zqLуdpښ!H7l);*6 @$'~b͆F=6o *!rAHKWSt}egL53 gRš$U+ BΥŸ\ջbKTXt~QCXyȞ?-FH{‡FYDdY$B!dg~G?~ț\lYZ7Wm>CG|5غQ/"6N{37WŻLZq&կ XZ0F3Sا흓A;PgH/v;!8j+Y:BϠw(7D#KENOi=tǃT(=kH_@I44p7ZݨdsÇH*8 U5Q#d j(ڜ hz$M# {O/I2Ny= 2GL+S9}k|iYLFX$l߸͖-[NCP:2s)CʂQ }ǘgCDxg 9*n $zh5o~هc17, 7[q8_p_D` IǺc5$o 7o֭4`o0!⎫]*އX89ZЁCH@ЍZhjlK_ g/H y 5[&m/l,ڤLir\%,;BJ&@_Uk&'K =ӼD̓%լ ~)g> *Bh0e%gW0m v~b壞Ō\*RM¢:](0("Bm;W@2^h6s'î>OhKi_8TV*A)<7r05yw_/rlcgLGFEE{@NX7=#iN`XBe5 9'i#3󅑡K; س׹x!`P,a߼[MeT`d/wͫZ $ۛ1o= QJsK*#k#[7J8mzw 9еV֏%N>DSq-PkBۦH+{y1J3J~1 {>8^ )מx^f" GƝ蒱Z˭=nd'?.{Cg*c\%f[mhxWrXvq"u|Zl1UtۼK܁H*i̲3ƥ@ߵhJ  ڳcf+nܰL].[b+]A$~3&]M0_ 26 k1jĠAXPw͛2}i0W&ڟjxaR7 %"z8MzUF(s-i#J#EbP~fĒ.#zF7p0ĺ#LHL%lGINْGt:1D@R5Bvo&;t  t^S R┵ЉǞ3YxW|zw]W5Bof rޙd.nޚmXLuVZ@i Cp@s`ӳ$ZAZTJðAKLXw92jg7,S!#ne;ژ3[( .ۘ-s!UuM"q&>X,ow9O m N"|7^ІRöV{U]ꭵB(DZ2tBמwٙ䎚D#Y1ih Qc@ǣ#1?NxA7QPҮ eNz^oOo>,L Rl- Kӿ?@?'6;$~:IԤ |>}K9O Rw$峊f1`A-"@~و#]1GIO/GH 'LߎN;L. """A test-suite for Pyrit. Tests are done by creating a sandbox and executing the cli-functions normally executed by the user. Please notice that the tests backed by the storage-relay open a TCP socket bound to localhost. """ from __future__ import with_statement import os import shutil import random import unittest import sys import tempfile import cpyrit.config import cpyrit.storage import cpyrit.util import pyrit_cli cpyrit.config.cfg["rpc_server"] = "false" def requires_pckttools(*params): """Decorate a function to check for cpyrit.cpyrit_pckttools before execution. """ def check_pkttools(f): def new_f(*args, **kwds): import cpyrit.util try: import cpyrit.pckttools except cpyrit.util.ScapyImportError: sys.stderr.write("(Skipped: Scapy not installed) ") else: f(*args, **kwds) new_f.func_name = f.func_name new_f.__doc__ = f.__doc__ return new_f return check_pkttools class FilesystemFunctions(object): def getStorage(self): return cpyrit.storage.getStorage('file://' + self.storage_path) def corrupt(self, storage): # Destroy some passwords keys = list(storage.passwords.iterkeys()) for i in xrange(13): key = random.choice(keys) # This is specific to storage.FSPasswordStore filename = os.path.join(storage.passwords.pwfiles[key], key) filename += '.pw' if i % 3 == 0: # Delete the workunit without deleting the results. # Should cause a reference error del storage.passwords[key] else: with open(filename, 'r+b') as f: # Overwrite either part of the header or part of the file if i % 2 == 0: f.seek(4) f.write('x') keys.remove(key) if len(keys) == 0: break # Destroy some results keys = list(storage.essids.iterkeys('test')) for i in xrange(13): key = random.choice(keys) # This is specific to storage.FSEssidStore filename = os.path.join(storage.essids.essids['test'][0], key) filename += '.pyr' with open(filename, 'r+b') as f: if i % 2 == 0: f.seek(4) f.write('x') keys.remove(key) if len(keys) == 0: break class BaseTestCase(unittest.TestCase): handshakes = (('wpapsk-linksys.dump.gz', 'linksys', '00:0b:86:c2:a4:85', '00:13:ce:55:98:ef', 'dictionary'), ('wpa2psk-linksys.dump.gz', 'linksys', '00:0b:86:c2:a4:85', '00:13:ce:55:98:ef', 'dictionary'), ('wpa2psk-2WIRE972.dump.gz', '2WIRE972', '00:40:10:20:00:03', '00:18:41:9c:a4:a0', 'helium02'), ('wpa2psk-MOM1.dump.gz', 'MOM1', '00:21:29:72:a3:19', '00:21:00:ab:55:a9', 'MOM12345'), ('wpa2psk-Red_Apple.dump.gz', 'Red Apple', '00:1d:7e:2c:b1:af', '00:0e:35:72:1a:98', 'password'), ('wpapsk-virgin_broadband.dump.gz', 'virgin broadband', '00:22:3f:1b:2e:e6', '00:1f:e2:a0:a1:21', 'preinstall')) def setUp(self): self.storage_path = tempfile.mkdtemp() self.tempfile1 = os.path.join(self.storage_path, 'testfile1') self.tempfile2 = os.path.join(self.storage_path, 'testfile2') self.cli = pyrit_cli.Pyrit_CLI() self.cli.verbose = False def tearDown(self): shutil.rmtree(self.storage_path) def _createPasswords(self, filename): test_passwds = ['test123%i' % i for i in xrange(5000 - 5)] test_passwds += ['dictionary', 'helium02', 'MOM12345', \ 'preinstall', 'password'] random.shuffle(test_passwds) with cpyrit.util.AsyncFileWriter(filename) as f: f.write('\n'.join(test_passwds)) return test_passwds def _createDatabase(self, storage): self.cli.create_essid(storage, 'linksys') self._createPasswords(self.tempfile1) self.cli.import_passwords(storage, self.tempfile1) def _computeFakeDatabase(self, storage, essid): self.cli.create_essid(storage, essid) for key, passwords in storage.passwords.iteritems(): storage.essids[essid, key] = [(pw, 'x' * 32) for pw in passwords] def _computeDatabase(self, storage, essid): self.cli.create_essid(storage, essid) l = 0 with cpyrit.cpyrit.StorageIterator(storage, essid) as dbiter: for results in dbiter: l += len(results) self.assertEqual(l, 5000) def _testHandshake(self, filename, essid, ap, sta, passwd): parser = cpyrit.pckttools.PacketParser(filename) with cpyrit.cpyrit.PassthroughIterator(essid, (passwd,)) as cp: solution = cp.next() auths = parser[ap][sta].getAuthentications() for auth in parser[ap][sta].getAuthentications(): with cpyrit.pckttools.EAPOLCracker(auth) as cracker: cracker.enqueue(solution) if cracker.solution == passwd: break else: self.fail('Did not detect passphrase in "%s"' % filename) class TestCase(BaseTestCase): def testListEssids(self): storage = self.getStorage() self._createDatabase(storage) self.cli.list_essids(storage) def testCreateAndDeleteEssid(self): storage = self.getStorage() # EssidStore should be empty self.assertEqual(len(storage.essids), 0) self.assertFalse('testessid' in storage.essids) # Add one ESSID self.cli.create_essid(storage, essid='testessid') self.assertEqual(len(storage.essids), 1) self.assertTrue('testessid' in storage.essids) # Adding it second time should not cause an error self.cli.create_essid(storage, 'testessid') self.cli.delete_essid(storage, 'testessid', confirm=False) # EssidStore should be empty again self.assertEqual(len(storage.essids), 0) self.assertTrue('testessid' not in storage.essids) def testImportPasswords(self): storage = self.getStorage() self.assertEqual(len(storage.passwords), 0) # valid_passwds should get accepted, short_passwds ignored valid_passwds = ['test123%i' % i for i in xrange(100000)] short_passwds = ['x%i' % i for i in xrange(30000)] test_passwds = valid_passwds + short_passwds random.shuffle(test_passwds) with cpyrit.util.AsyncFileWriter(self.tempfile1) as f: f.write('\n'.join(test_passwds)) self.cli.import_passwords(storage, self.tempfile1) new_passwds = set() for key, pwset in storage.passwords.iteritems(): new_passwds.update(pwset) self.assertEqual(new_passwds, set(valid_passwds)) # There should be no duplicates random.shuffle(test_passwds) with cpyrit.util.FileWrapper(self.tempfile1, 'a') as f: f.write('\n') f.write('\n'.join(test_passwds)) self.cli.import_passwords(storage, self.tempfile1) new_passwds = set() i = 0 for key, pwset in storage.passwords.iteritems(): new_passwds.update(pwset) i += len(pwset) self.assertEqual(i, len(valid_passwds)) self.assertEqual(new_passwds, set(valid_passwds)) @requires_pckttools() def testAttackDB(self): storage = self.getStorage() self._createDatabase(storage) self._computeDatabase(storage, 'linksys') self.cli.attack_db(storage, 'wpapsk-linksys.dump.gz') @requires_pckttools() def testAttackCowpatty(self): storage = self.getStorage() self._createDatabase(storage) self._computeDatabase(storage, 'linksys') self.cli.export_cowpatty(storage, 'linksys', self.tempfile1) self.cli.attack_cowpatty('wpapsk-linksys.dump.gz', self.tempfile1) @requires_pckttools() def testAttackBatch(self): storage = self.getStorage() self._createPasswords(self.tempfile1) self.cli.import_passwords(storage, self.tempfile1) self.cli.attack_batch(storage, 'wpapsk-linksys.dump.gz') def testPassthrough(self): storage = self.getStorage() self._createDatabase(storage) self.cli.passthrough('linksys', self.tempfile1, self.tempfile2) fileresults = [] for results in cpyrit.util.CowpattyFile(self.tempfile2): fileresults.extend(results) dbresults = [] with cpyrit.cpyrit.StorageIterator(storage, 'linksys', \ yieldNewResults=True) as dbiter: for results in dbiter: dbresults.extend(results) self.assertEqual(sorted(fileresults), sorted(dbresults)) def testBatch(self): storage = self.getStorage() test_passwds = self._createPasswords(self.tempfile1) self.cli.import_passwords(storage, self.tempfile1) self.cli.create_essid(storage, 'test1234') self.cli.batchprocess(storage) self.assertEqual(len(storage.passwords), \ storage.essids.keycount('test1234')) keys = list(storage.essids.iterkeys('test1234')) for key in keys: self.assertTrue(key in storage.passwords) for key in storage.passwords: self.assertTrue(key in keys) passwds = storage.passwords[key] r = storage.essids['test1234', key] self.assertTrue(sorted((pw for pw, pmk in r)) == sorted(passwds)) def testBatchWithFile(self): storage = self.getStorage() test_passwds = self._createPasswords(self.tempfile1) self.cli.import_passwords(storage, self.tempfile1) self.cli.create_essid(storage, 'test1234') self.cli.batchprocess(storage, 'test1234', self.tempfile1) self.assertEqual(len(storage.passwords), \ storage.essids.keycount('test1234')) fileresults = [] for results in cpyrit.util.CowpattyFile(self.tempfile1): fileresults.extend(results) dbresults = [] with cpyrit.cpyrit.StorageIterator(storage, 'test1234', \ yieldNewResults=False) as dbiter: for results in dbiter: dbresults.extend(results) self.assertEqual(sorted(fileresults), sorted(dbresults)) def testEval(self): storage = self.getStorage() self._createDatabase(storage) self._computeFakeDatabase(storage, 'test1') self._computeFakeDatabase(storage, 'test2') self.cli.eval_results(storage) def testVerify(self): storage = self.getStorage() self._createDatabase(storage) self._computeDatabase(storage, 'test') # Should be OK self.cli.verify(storage) keys = list(storage.essids.iterkeys('test')) for i in xrange(25): key = random.choice(keys) results = storage.essids['test', key] corrupted = tuple((pw, 'x' * 32) for pw, pmk in results) storage.essids['test', key] = corrupted # Should fail self.assertRaises(pyrit_cli.PyritRuntimeError, \ self.cli.verify, storage) def testCheckDB(self): storage = self.getStorage() self._createDatabase(storage) self._computeFakeDatabase(storage, 'test') self.corrupt(storage) # Should fail but repair self.assertRaises(pyrit_cli.PyritRuntimeError, \ self.cli.checkdb, storage, False) # Should now be OK self.cli.checkdb(storage, False) def testExportPasswords(self): storage = self.getStorage() test_passwds = self._createPasswords(self.tempfile1) self.cli.import_passwords(storage, self.tempfile1) self.cli.export_passwords(storage, self.tempfile1) with cpyrit.util.FileWrapper(self.tempfile1) as f: new_passwds = map(str.strip, f.readlines()) self.assertEqual(sorted(test_passwds), sorted(new_passwds)) def testExportCowpatty(self): storage = self.getStorage() self._createDatabase(storage) self._computeDatabase(storage, 'test') self.cli.export_cowpatty(storage, 'test', self.tempfile1) fileresults = [] for results in cpyrit.util.CowpattyFile(self.tempfile1): fileresults.extend(results) dbresults = [] with cpyrit.cpyrit.StorageIterator(storage, 'test', \ yieldNewResults=False) as dbiter: for results in dbiter: dbresults.extend(results) self.assertEqual(sorted(fileresults), sorted(dbresults)) def testExportHashdb(self): storage = self.getStorage() self._createDatabase(storage) self._computeFakeDatabase(storage, 'test') os.unlink(self.tempfile1) self.cli.export_hashdb(storage, self.tempfile1) class RPCTestCase(TestCase, FilesystemFunctions): def getStorage(self): return cpyrit.storage.getStorage('http://127.0.0.1:17934') def setUp(self): TestCase.setUp(self) self.backend = FilesystemFunctions.getStorage(self) self.server = cpyrit.storage.StorageRelay(self.backend, \ iface='127.0.0.1') def tearDown(self): self.server.shutdown() TestCase.tearDown(self) def corrupt(self, storage): # Corrupt backing storage instead... FilesystemFunctions.corrupt(self, self.backend) class DatabaseTestCase(TestCase): def getStorage(self): return cpyrit.storage.getStorage('sqlite:///:memory:') def corrupt(self, storage): conn = storage.engine.connect() # Destroy some passwords keys = list(storage.passwords.iterkeys()) tbl = cpyrit.storage.passwords_table for i in xrange(13): key = random.choice(keys) sql = tbl.update().where(tbl.c._key == key) if i % 2 == 0: buf = 'x' else: buf = 'PAW2x' sql = sql.values(collection_buffer=buf) conn.execute(sql) keys.remove(key) if len(keys) == 0: break # Destroy some results keys = list(storage.essids.iterkeys('test')) tbl = cpyrit.storage.results_table for i in xrange(13): key = random.choice(keys) if i % 3 == 0: # Delete workunit # Should cause a reference-error sql = tbl.delete().where(tbl.c._key == key) else: # Corrupt it sql = tbl.update().where(tbl.c._key == key) if i % 2 == 0: buf = 'x' else: buf = 'PYR2xxxxxyyyyyzzz' sql = sql.values(results_buffer=buf) conn.execute(sql) keys.remove(key) if len(keys) == 0: break class FilesystemTestCase(TestCase, FilesystemFunctions): def testListCores(self): self.cli.list_cores() def testPrintHelp(self): self.cli.print_help() def testSelfTest(self): self.cli.selftest(timeout=3) def testBenchmark(self): self.cli.benchmark(timeout=3) @requires_pckttools() def testHandshakes(self): for filename, essid, ap, sta, passwd in self.handshakes: self._testHandshake(filename, essid, ap, sta, passwd) @requires_pckttools() def testAnalyze(self): self.cli.analyze(capturefile='wpapsk-linksys.dump.gz') self.cli.analyze(capturefile='wpa2psk-linksys.dump.gz') self.cli.analyze(capturefile='wpa2psk-MOM1.dump.gz') self.cli.analyze(capturefile='wpa2psk-2WIRE972.dump.gz') self.cli.analyze(capturefile='wpapsk-virgin_broadband.dump.gz') @requires_pckttools() def testStripCapture(self): self.cli.stripCapture('wpapsk-linksys.dump.gz', self.tempfile1) parser = self.cli._getParser(self.tempfile1) self.assertTrue('00:0b:86:c2:a4:85' in parser) self.assertEqual(parser['00:0b:86:c2:a4:85'].essid, 'linksys') self.assertTrue('00:13:ce:55:98:ef' in parser['00:0b:86:c2:a4:85']) self.assertTrue(parser['00:0b:86:c2:a4:85'].isCompleted()) for filename, essid, ap, sta, passwd in self.handshakes: self.cli.stripCapture(filename, self.tempfile1) self._testHandshake(self.tempfile1, essid, ap, sta, passwd) @requires_pckttools() def testStripLive(self): self.cli.stripLive('wpa2psk-linksys.dump.gz', self.tempfile1) parser = self.cli._getParser(self.tempfile1) self.assertTrue('00:0b:86:c2:a4:85' in parser) self.assertEqual(parser['00:0b:86:c2:a4:85'].essid, 'linksys') self.assertTrue('00:13:ce:55:98:ef' in parser['00:0b:86:c2:a4:85']) self.assertTrue(parser['00:0b:86:c2:a4:85'].isCompleted()) for filename, essid, ap, sta, passwd in self.handshakes: self.cli.stripLive(filename, self.tempfile1) self._testHandshake(self.tempfile1, essid, ap, sta, passwd) @requires_pckttools() def testAttackPassthrough(self): self._createPasswords(self.tempfile1) self.cli.attack_passthrough(self.tempfile1, 'wpapsk-linksys.dump.gz') def _runTests(case): loader = unittest.TestLoader() suite = loader.loadTestsFromTestCase(case) return unittest.TextTestRunner(verbosity=2).run(suite).wasSuccessful() if __name__ == "__main__": print "Testing with filesystem-storage..." if not _runTests(FilesystemTestCase): sys.exit(1) # should have been imported by cpyrit.storage if 'sqlalchemy' not in sys.modules: print "SQLAlchemy seems to be unavailable; skipping all tests..." else: print "Testing with database-storage..." if not _runTests(DatabaseTestCase): sys.exit(1) print "Testing with RPC-storage..." if not _runTests(RPCTestCase): sys.exit(1) pyrit-0.4.0/test/wpa2psk-Red_Apple.dump.gz0000664000076400007640000000115211524611424020646 0ustar slaveslave00000000000000׶KWPA_eapol_filtered.caprxB&!HQ\: |ER3dt6@j3H!C$C~&C*ešW-.O%H G5103@p#!HN+@v#T[};/Lt_ v~J_@j``% p4x(6Ap : ܀_4 p_E/"//>Qw.(VrGqRQz GqVxE}G郊4B (n;}a>=~sʛ'Oo,"7cDX Zr=bń\Snig̐_J0|9/d5 @ȹ8sK9 Őӥ<8rnBe`\n-%ܯ|'rqDBέ`X vjc<O<&Sozf-yeCV 73ݕA8 \Ùx B|hy)y9]8WjYuͺ pyrit-0.4.0/test/wpa2psk-MOM1.dump.gz0000664000076400007640000000134711524611424017532 0ustar slaveslave00000000000000Kwpa2psk-MOM1.dumprxB&!H?00(j-7;j.zKada`5dhj鞦bȢb dab, `#$p! #]N &/@>X>w+LJ`bauJ{U} L\p?s{jt|G{ṏؔ~1[m]{[?Un|p2o`84C7,P+*I}?~?j<%`\3YiQ !zn,6=1[\c.ZÀp(}s(ȡ ] P{E զ _xUͶn s"̞W,7!b" J^Iٱh̟9@>X+Df@]GL\kӄܕzÉv 뭽 C? ;4P\ +|aag˔O20dK 6 ;myMgԒb6Phͳ-ޛWDRLI.SRZ1w;S9 #!P:a~yKTpOqJJ΄*mH%ϡ9 Drn\Hd\^61rgJ|\ 9@pyrit-0.4.0/test/wpa2psk-2WIRE972.dump.gz0000664000076400007640000001410511524611424020107 0ustar slaveslave00000000000000}Kwpa2psk-2WIRE972.dump TTU3\hDh^ȔL'QBSCRS"~2\5,GPpfhRRJFȌTFf~Sy!35s)??.^~+5Kϼ{39>.u\?!YܸbTOH]|d5NX~(<"NxerMvw`g wcd^wȄBeߢ.;Y?uQPŒˤKi~V,x\ے+CK%<]u%G(gU-GKnwm.9X9BW!2>2˛[8"Ihx_ g'8dY:AT!o\i|E׋(aL\K[b?g@V-RF2`yt6TJYV&4=*&,ҽͻv]'~P\̔-sr+98u4,Huv=2HJWE@5,~,OhA t{O Gĥ%Jx^[ȵ|fӔmQGP[fܡU#nHXeEei+ͪ=X~8h]M{H&&UVe_ ]u>CWw骾V*AJL9 3تCU =QXi&aJêmt[r>5[UTU@۩lWY UEX5˶*ZU]OUXyځU]Us<ʐp϶ڲĪ;XteOSv_GYU%>FWyAWc*]'2UEtn.S2k9ᩧkdwD)دJH^OV/@VBb,Jܶ:fuKξU7UHeNl=h`N:p:2'r[TM6^j+[%ȡc/U$t6jHZ(p*vHUUeXƪ!U[[UVqҟƪAYx2S%=s 92`UN)]UUtU VUV+zVuXAzV.^`jjgt9T+ڻ-CTJXjqJjJkS%S@W7*7t.&5eQq[T]^KWumv6]U>Rѹ㬔US%=?e/]e=ЇچUFUPk7lOW]GYd.풔NmQUk*~tFۜrdzd0:gN96-&$U'jz[TuVCgS.>@VU@;*ʌUdV=YV麣3Uy+G~X>jV-;Do#)#zHylUGNDٟUUpUBI*OW Ǫ)tU6AjGY_VUe(+:-V|VkJwN:{ڇU~.`U{YU&u Di {#fvgEU"{ -%G̉ gEuO"CdO&7+߻VtGJilX[-̰~m/j,)m~V,hѬrf&M{.)4tuGŎS<,pm}3LH>IͶ$T*:ZOWUc}*~NaUb4=XISKja8k~́ezϼnl5iUt~[TM\FWU`UJJՈRj7VVmj;=Ye}㟻ZÛeU\̓[̲ۺeOﰯk;fj&7%Id9 dJwU.=Y`[elJVa/=5Y]`z{Jp:]5&acd:T}.jdhskFϿM .k0dݽTN`@̐wuĪ̳l>_g5a"Jہ2u}*i1=(etutiD42b>[7-<52f6lUݢU UaURzV |%TWU^*wSuP}ɾ| ̺nC*= Uu7jVbP*fMd֔aժYL7bVy\f4V]JB5參`յAt(RhUAkhPVAV5Un>{*dx+@`0V NWcթ lqVmJ*=z yQL۪TFEWc:JU5P,;BPV*MTEW܌QZе-< ]TͰU%}.ͲtU*=*lJ7jAj_4K$O Av~AbXvǪ_C#M3PU^tUҌ`LÑVxm[T`td`O^}-zMx%]UUXuf'=D꾇2ա.,wcoE`U@OK KWŪqy:*Y%u{Ykڑ ~bf2^i޼[qfw{R\nٍsI ՝l$ӳZUg_f5X[t~Dg)CYgGW]Ī5*/yǪxZxQxhm۔|U:k\ZWTU'Y !!oƪl7YBUnfqTIWF±Ml RVFcՁ*5V~GW?[!2n};@WOUEHՐ;̇2[(\gUn,UnUX{vĪ1U4ث>eKMj X4{OUjhgJWU3Ud쮖x>zU]}IGV*;]u UGOOmVDV"]FͥF`B*{MǪk4ھeA}0J{%j/*J`"J{ :Uw.bUJWFR!G* cOUKёWvvmP[{ `ծo5XU;~vcU7l"tc*mU [+@լг*ªŇ*Fz]UU9?k0guwz[OǪ-aYUbU͏LMB#ikOEGnY!,Ÿ?v}3)qCh^̻6^qíc;m>< #6+݄bG'ĊEͽ!웚;,P/ޙ"뽲^1fDrmut ߧ۔+dܽe(fgwҳ7Jwٽb/y+#o!ۆ(=&J*:IWEbu-]e7C˗UX5y-] /e?J ^)2*Z*[e *ytp0U?Sha0+i[ՈTstP =U2Ю ^٪ UlʪpzV7Prs5Xk3wYVAWCY k*@b-Su^<$ e6CKKO֧U]ukW*R5eU?۩̪~iI&[ߋU LObV*pP-jt:[m=:<ҷ@Z,=,ʣ H\2N;w\lB|4xPB:2@5&UfUW#* BAH Ye(Y gƱUUݰXXVv݆s.EosD_𦧞`Q%6(4>oVYݚ]Nvk[e}cU\Z̟}<mϽl~bjBf~*-ُno$BPBa{$QBW[9pv!HTy*GQdm+Of쒈*~R.U%[UR[_R~2zFkV M`ո ?ն;pMٟ*iW۬$T`tݠ2;]0UUXOAV]鵴S]%@z]%ǪY`ړV"*z[e*B0#V0^IU3X f*jDJ@VkD6uUlftU>liVFW}U>atQ*LWyYT**Yndq#YJծZ2@w]٪w8a]*]Vu ê#LsEu(թ0kUliTG~p:[et8vvTGѳU;UzO7K_8fRZi4] O#UzS2]OmtjCLiLj{MmuT.&qWjڼqar(/& y/Ū^oUݰMtUVdtC*Nn`Ko VK{sr86cI/ # vS nWmٷ9-X'ݼq _y}L| XMxju_ ۊbO()B[wnKX [l%ًVNu ׵4ijfpa(ɞo“o̾%)'dU}Bkl#I< [n]ol.wo8d;+! ݰ>f_5q/hK =- ]ia-U;5JIKƻ."-flR׈e`?3K"^}fJUimmnq:f-&[X?ZN[XKܛutnaropc8۶`8G[t4̀mcז"ؚa:.-w Ͷ l vx,mfmsa5esbk>X|,MXnl'BPFL3w~q9sa{Ǟ0.vU[;XvPv-$Şi®>vTĚ9Gu -ϧ߰pxG+ əuc}lxa6n{|IG%" W{- 4% 8;NJ{xrIǛ=,CvsYr=rK7ÂmA[͝}N9qzW~Sy6 o۞=i>&;–ݴv+~{ ]]ܱ1n2 {kD\^mKژ9pYpZ S@hg3k nZ29[ !5!c`Ooa!J8o ~od<^c#{L;{qh,mN#-^U1`k\-;xY;{n3crF6ęPb_{[0mD=6IXcqhؐc]{du?m! kZKĢ/DF\{bنv+9$s///ਲ%gjs&&z zϼ>!|@vXܶƜ= u[x2`UmX֖:} pZ޶#&<'[G&>1|04#f^ .f{c&\O If^) aXhNMe)g;qdrNh[;gKmrSCfMnpaWiv&kښ};{dKGhO-3-4 ܕ#Uٙe'{կ1X iX%EA{ũgJ X9ش342; gF$,MdpűbǞxAXRӇWfkޘUӐfu= {6)(w%J俷6=‚-1̪ >_9XSl峅5b9 =>5(ZoiuL؀v縦K6d;2VsYp,=ǔmcLDV`V l'd[0{-cj7K#UT3/[,VZI7xGovu{Ŷli@Ĕ~܊X̖V,m- @ZAgS6S\?R;ٱa'jݘڥ*Sö\EU/߾߾i>3=P̢*0*,@laS`r4!D :رRбaq?p-ǏDZq1]qKٲ-iv݈x%:ym>AkqdamPcZ*S@<=}7.]=ŻEsaFa;+l|dKΈ<dEJ.y! rr3Dc]tp}"gE\!u=,:lklXf=hYSb W9[̷g$ Vq/RY{BU9q_u2;GYۂh=j.W{-( XIvζV VYZ ty\Zo 6Rqx}ERnPi0,B*o\p& *iT[&Tr5`?mbo?~xCz ,EFdRy&(KȈXۑ,VUxbx^vlIتձEODL,!bҳ5}bo[r!xA6?0T?H*f~pղ+! \ APP[Ҿa'̌E~+a})[,vf;+^J~'{LOY6⢜֝ Bғ:gP^\hᤴ/ p͏vXAZ!ja?zvVk#-[8pg;_تŢd|a+P.#T yZTOd@!?O} v1)bvéTyd 00`l>_'oō{% &@&rGhQʣgp ]Lg6NqdWbN Xm82t ڬ^J3D,nwv֞<јv{+*7K=l{iJ&Mlgs= ~VIv".+ZC q@Vd,k5<y=X2ޱpOyqfTJMźk3@y ߭,u/ + fl}-x6s-3 +;jr' n\kP 1٫"ռid92B&UV茸XR Cd^:lNKhVܼ~JS{%<6<> ԀBj%2wd+7le#E!Ďv3?˫Š% ~j|h ~5yn>-K "~ӟ_G~ Om@.xĞ P%qy"C·5ZV B- oQalOr&}X 68wz޺Z#.'<I/ғEq\-:ܵY 7ccƳ}";E2gg`+}!Bd:#`,g,e }O:cl{svZ2dŁPv+*]g(%Lg6柩ܘG0AْN@DctZ⹆=_7/ ;xn ei`wod"QC=س^Eؓ>UْXG aFMm'*/[ P9B.l+iEL챇p`Yln1Ȳ9#D"4'~$;ʢlai`DXBĦGag=nHtH -˪Tg437CZ|R}葳v%xgE p1*JLy\@4b̈'.V@Ռ%M!idnj" dX'|-n4wGP˲h9m>oddǮu72'[e=9 8XR1{PQ爚MDdX ҍ<6j8 9CKX0QŒE8,ˏgG3Di,GC@\lO :.PlbR \MR3 P3[@ 4cZZU0UTuZT[X-oƞxaxߒ/vݨvv&Djds>u=mUn^]]^49kB׉pkD,# ;XIF.͡teK y8c$` ]>P1`S/uA4`o{etV۳\YGU)% '02Ĝ?[C|DgZ6val).>? -$Ͷ5Rsb)y.$ YGw%.l,J.N(Ԏt~Hα.;r–{ {gYm/\\E~| ŞpIb1.%@ʜޞy/DfF2ISN&,7FFqYŵh# -v+Y,Gy%FƸmw7{8ck`b *Gsj(`Z1Ir?1CqG wqƪƲ,2qزw wmH-cW۾ 9GRvVm؂d_֞+b۰=[DJ$hʯyJ$vej>8J@X!JX0u!wAzjW) 0UeQ9,Q"r (*&ԒbAr)$qܒVEZL$(DXfѸKo_]vJ@Dcni2O-)C m:[uҵ[0-QmbM6 (V P(LmېSV!X=qW7 xtmCUAj}jDA5$D#H]Ai ʵg #zuy+%4\ˁ|)mĚn {xja87!AӢoAĿU^)ED`Kysq TR$Zh\ά0Zτ̯- jfmϠ<4X甗v'LJZH3æKԉeKt]` oUz!WX"OȖ>F\@EzB~h lq*2]~Vo+g>~%Y13D)Q wDw/|GF't!ī M#WId?}EE1OVkL팹U Cn@2{ߩXvNM3а, II|[XY-,s p;*EBcAh%Ơ3{o|nP^[2$hț ^22yN$qCw\WtljؓY?}y aͱ.)Zf҇b ZsK.]""BVX>U"A+K"Y(!iն'_S ;l Jmansu|,:c:^ ZA%v\;B .Q/i"SB$qrv.B+/2<{=@\gM6Sϛ8O*Q:* 9$vU6boAI3-YYh&*uHOTJg?r-{kw4ԁۧzCJB5dlvCﵤ%Y^K>t~J>Q+vXf( 0_<6u83"Wy 1zpp<ﱝבWK V^gۊnGd;70|6(뭡? K܇6`A~#tD/wGS= q‰+jp~<֕RdB7\t Qw@HBPKlMP 6 <) |6[P QY(^QyZJqrPlvbER^+#_1fxd($+%zcY^r:l'(d0zSw:r*t!j l8LI-F`Km'$]!{U.M7)NnU9;FcHE"THORc޲V9v}V {;$׌;ґ8Q-'0[e;e\ik pAI#i"%<:u%-P[diz\8*5lY;\#ΙW/iLsIcdCb+Bϥ _+:x.ˆ1&,Xx#6 QDt$^D#E|m? O3lgjwRG4j`b8ZSIL+>鱝b'hj^DHkxT<[Gnpy HWw@џ vݔ,d?q}b/|Y eeH}9(]ym0SFf(YUwݹt$=7`}-g_DW&Wd !.Z]'jPe?/1+UE ' lj.m){z})QXvl P_5Û 7\3 NQe(6ƧccqG& 16{"i {e&bZΔ'%򭀯6{~rI$y) 6 ۮ8ȂARL+*dD"%bhgt5@ -HIt{*ؙxgZ+Z$?^&4*]p#d>pt$[[+A-E2陏ňc–x4DFw!QoȖ)nW#*4J(7ٵ| L 4!ЬoӃoT1alZ,C,ns9:vNo |VT}?MPߋR"zm;D;H-YS!o6vb@pkY $ Z›4$9SlZyW=q&`Mx?АMg"a0@+3ي 8v"Ĭ_ oW,2eO(_r횐|% 8´W'ߏ"R܀*: psʁCovDdG#0Ofc)pE?'-SjED4EtE!w%wA @;zC{ADOS&缄S"ULG `Rk=;s0b5yҷ [kc!UK R0\RR(fޠ}IK3ExɨfaX [#+?EΞܲE$ ci]g0N$JTO´kQ#Ak!Sܲ"~;l_72 !ľ4$y*2(UhՂumܲJc+\djsт,o::JӨ8_U jN;NV+b1*#LJ.|=2Q+;m [϶>:gY)ɂas'|Z f;,҃i'>\$]|fȲɽIɜ>R1m0THdiERGNC9::'lUlQ+ETpgє^߄E#VC%U'( |Vw0< !dKmt>>ѕ*tjϔ|?$$KAq3)Ζ ^Vd4=QnEn р3V-A=*AD4_UڼK֊* 1x}<|28{s蓑;I?$YȝWe5 , $g&hd?%R@VpVR 65a\ AUYMVfrtgN6j *ݫ00ϴZ>(><)˱ȯm\ ҷPaAg@!F] yzzh5%:ANÊFr^G"'_)dc d5.hl| %}pyk*3Vdo=:?lgSI}v&cZI#);\#.⢼NK}\$1<4[,`O{Q`界 㪮 K#ɫA@^A zJ<4& VR2s \-l2W"S`tL"4ʮd꧚ RMnFT+ʷ֌-s*$ǡI`1]' AwZ\dP6 ٸ- &ce}΁,<\}8Q+Ĝ$ ܀ vXԴfST]×rl$E]0Q!"538ﬢ 1\6<-+&hӪ*y*+W:& +>bx1qmBpP ߥe" f@ m`43wejgQ}Y4FCI2*mb?},Zz-f+üZ=j*$-{#'i~98BEDzmB\W/IS|cvMV*` [\7(-6:erX !lj˪O*=A5wl}@0ɢ |nִrY J s-tW%HT-Eꋼ%|s!*^RȌC%z9&G 6tܿ=1@%&C g)' {|U{R,Vr!@[i "ݗU+AvQ36U@EFkX)CRfr^)lA,IM#̨]\Tk+pm` zĂ;u,ŊiP 1I{"IN[)!b ö5QV)*p^{{3nA;kŪZ3Ɏe:&%4fM ibb!X[5yF$m'TI=tǩsdګhb%d+w%sٳ1eYDHG,yUlӂ׆v$5<}gvlGGէ1Ey# "O-e>) zi:HxN9}ב@EU;D3y)U|y',b\ iޓ>`)b+QCXЉx@~BZIiz荜 !dP< K#ڳB\-R<ԟPf~i}ǡ߼`cm''zuDp p'L{RcMnܐ\u}XG{u@E_TI*Mjd%k떶#*,*nLҗ2q/"T(x{ *]f^+|]zl?:۽ >oԕ4%Γ<*$2@;Qbny#iF?gVE"-Tn sFp.(&[{3m(K|wQMx‹Xӻmjv(!Z\惸p(E&З?"r,a\IͿˡI]p!-ArW]zUKcQ#ND:b>MAa!.pI$F!κ'WW[Wja6# p.bx+(:sQ$a4AS` R1 W9k2hTocLr?H"n'7Cк"$ \.l1 ?@X@aspD.wF148L D^b+&roUVcb0t "!lr"Vm\1̇J2 - V.3]mFꏶ@@Z'M3EN;Wa7APv.Pݻ@S_u{"~5YUįVzK[Be0˹;66$NH; Wqg0hOV-ii4 'p+{>*@Lou<g5=U}!yE2OA~JE Ûb?eIjdeݳST!*iuHFx</o%H B;)9~kohs؃i=>9vzjziҝ җ7V6{r]/Ze.y=&rjG oGK8hEl Nt2dW)pzȮ3sTȱ@֫kS.&1 d#"ffD0iq{eRG8ӼbN)<2w,jrC9`_vTz;FƑFhįD(hN=~ҶԷF{` C{6>Aga9PxB7ۅfuH,MgL'2ڀ\ܞ#a9g"t".#ENf5'KIZSC7=Tkq)>HDm1DaGh:I>!ɤں)s0TXPڐeJh@AX`y_ Qī5.I/m]QQ96:Iݡ9:j=(脢T[Dž/R48YL^LNu\5icbdV|AөPW+:f]쩹/[BlJ/ 31C.]9?r*T[ʷﴱLFVYTjNQSҌ? p9loC Rg]*)n4;БV`|=zAө`:G7+e }.O>#zgRBf$ĥ|89vۊ$-ұ28E4[+0%`XlBTHd,V k.vlHl'Qaz Vv#jwv%n&x-8MƮw_{J37@R\IyX8(/M칀hp(]xp8ޡmG[/7#jwv)S$D:C>#Sn~9IkҀXrh !|[YF@O?Vf6uxFva<zk*E3 n)]Fe*O n1~ brcj-t.5=[Ӱj[d R ˃W1m$ ?:%!#M=@,'E@j'C@p"}Jz26Mwe2J"#SCyRxHZ# 80y,dy*h!$$6$o)v>\vd KPĠ60R|lNvۓ^r] uwޒJ1t2mehr B2x&h€փRrSj"5]erD`rA4؎DϞ?ٶ`??|RX`1Mud~$[P6B Vx˚tĦj\ ^L'_ӧo/X״ 4,;b7tw22a* `PXxS3/mCJk3QF8ϻJ+[w)I*St#Mf9IT'KT0@R9D:s/'[OvY wHZ) h鄮ͰqnQ$qC>t*_/Ojk/xZ߆MͽL+*A[J^$ 5Џ*'ayg=auƔPoxj֣$`=7[DrA#jy4<͆B&ܮO@)%e\I_eQeW_g}CF{-qPK _,x7B[dxw*8s.*A$t !ؠ4`+gH*zI< v=T'Ed{Nd'д$:S2J[ + " f106nڨPlj붊c74c_o- 0 OfDJQ[q,gb'"jO|^=YDl z͟oj7mnGUe;}HQG]l%/z/P1V/7)ef?4MC^.-ٟ\ҀL+ Y(dqhUӛKwZ*SG#;44Fp~OWIf&69jiGJɊ4de㨶ߺ.,nf9KN.cȖZ,!@1˰HĜ)&Y$zwsưrg[2m9X|dFW#@954-Yӵ>EHR)Ֆ}ITeڌlK64*|$0˫VL n4"7P!L"rWǣp#ѷG4"nT53w v8Ğ a&ZT 2Zx9Iw!-p:2e /ٞEA i'I ݋FїQ!og0v⍶\o/ |{,i;Hi0 VmAԻP,9*?YػHA>Fjv Fa ):t鲤Sピ4p~T;H yFaQ*%Ҵc \9' XW*d^N(,ޞ`Q$mh" OY>Њzu4aԉ$DGbKcR Ku|"i"T#!~1?E難MN>4OkP› GDwEaOaAx,K;-zN+䑲sI8)e!,@odZ::;Q)uLC E̐yp \ SN!7{u +RTvq~S]1-t<CD+9R}++w :qRJhTCZjv:PN'Za8cՆryu {핯1e,6@ N]Nx7e5'8,N:&GF.tڡku+V׶lb ye YK, GϷA($'yM ܧ4{Hvu'Bš#kߥEJvƷ $rڶE 3wrx9.sb?Iۢ}GrQؒ܅_YyMx%}D%T =F|>$+œd룦@TG'SNK*UR"MS$T\p ]>;'9Ӣ:W$6Pl6CCn +gUcsj84N$/)wv D 皱\?>ʡtع]}?>Fmh5l F <te;L^bUJ?d,t!kq᲍i@ZBס5sKV9|Am.j`P'S +s4:"Uw&,(HHW`tv=}U+{v=ИZKF583 $ȋ'MP ?Ŕc'" pF7A8^RZm.wL-d-P}D%e_ǶŽV"_[\)VG" &|=|8Ix?Q a9BuW0EJ3~{vi-(< cdyo^̒8"Fo태Kp!RcuQ)a"_8TwX^  U!j] 0 ޵+i{4(~nbɒ Ƀ> 2nI;pW)X₣Q]Q;BER4k,l5{Roi}N<hR pTQb; nbG1GA{.KCO:#zCTZ#uʳֹnt8^q7)6> %D5yR=A\ " P+qw+O8Z9>p܈%i1_3 `:Ja; &Ym*:JI cF>[6%l u8L2,Tk3nOёh_IFvwgKRzi_}& >A g~rxHsgw`s%5;mD7FϱzOtR5 8g, |W`?ϓh?غ$:Zc `Ky,.!hÌۇ;nM¦H;GQ6Ri@ Ζ=cA'8 ?ǚH9wqwt'ږeHs5gϋG%$sGCtroۛ@?rśy?#q4wah1F2Xh42P.sb> wALIͅ?9RTri/4,oah *n$Sai4d ,S4嫖"m R^eVҀ #k4[h3Áզ/#Kܞ=zsNc?&mw y<~慜=oQeeUx?27桀|nu4@=xju_U]-O *Nh ^+DzI_6𴪓"/qBq$C@*= "M#ha|[.՞%|-heqV^MiH4>̈́g=nc#=r.^%CtdjqP1:/w&;kXSXg Wdž"P]z+ ݚN8Ri:LnG (ߜB#հ0'|I1)"ށvt;:5I;(+UcmTѴHWZ[oi T(TcکN46ŗ'[P}ZiVMt936ӮGwSOgi؊]CO{/n&g9apgYѝQ:&2%$t .`=nCՊ}7`&i+Fn A`Cν,~\dM&ȡ" GR#)a\vdGNu#Zʴ'Yct \nn"ܫQ{{2߽Z,e `Jr(IqQ79ctTUZ9XQzKe`WIѢt# Rc!賷~qlEOIfIi,63m%9(pAc40V(f ̧kP־i[)zʨ}ql2?m$JU= `ldDSvȶ]9+|Q.khY]%oi 8g2LHo1P=zz z]Ylْ\GV[iakS~!7Grav](#_s{@:Ɠqئ~4%vȆ| Ydz=>DÃ>gM]@2ъObp퉸k zmTm-lxQjf-h$lLY86cH^G0`;g'.LH!!TxYDw)OP۟M5X v$:̠# D }l&|%j, î*uy]g6:4 N7Ҋcmy}| }Y}r)e-\FL3Ģ5,ZʵLV0R,(K3:?ʒ4{Gqq}=d\ꭨk:*v*XzE- 'oUT;La.Y+"e14u9@k⠩CU؉=oU#R4`u%؈CصHfhD 5d@ВWqcA)7r~"5s3Jj*yx]$~TIVfP?;,ɟxg8N0d{!I2םT泿+V5tU3½W}ޛOCJ'` /YcjM{w6\3 >Y tEɽG`rqG)<6B0_ꁩܰ2Dfk 缍 XH ^RM޵p 5H(dS|O,_ y= `v/)L`f[A66n9H716D8,ѰqB*_|˛<mP'鴍eݥQ ~p#5~8@Sj5POӊ84 4 7<-':5G56s)37njh*'lzz1ŎLH)}*kF:9˭a3gQ:@Yzyr`:8QVw0et;J;T2z10$ec>^t$k_rޙ>saS2Qg+&KG\qρ(Zm:)Y}]J&'3A>+B+X Gg0 (u?os9"::v)7ornƨV[${BFw.[1t Qsd]/[34|ҫLgt2]7SU /iC +]!,J^풶I>Rtb_Z22)#ȉN3M5h(hJ.;.i @8rqW[a(!$hn 9"Q8}ű;F}p#}im 2$ ^-QKSxl*F2D{hXO/sYGNgFciN>ُi)9:1>%7?!qxa֦N⡣J$4!doCt=0\2.y( ؙTxl.هY'BUCffR wA-Ko8 ~(@Թl{V6ςd:A X5U^ #;urϱjs=cۣ XnL}yL/C"ŶUH='ѿuD pNGGշE.7Rǿ1]fCRpRƆ'4|ʣ{6F?F\xO"S~u3 Vn(wb _ f9x1[9+`w\l%U3V! [bJr>piC:xR+|,Dpx-hK> |P5[bn OEAXw{]WxLi?YCq^T:zIUp*˛ᡵEq&֘hl"a_9sDžA~Vԫn+N@)s3h*2yټbbfS;|UF5C~ACu(՚+¡Bh0Ep k?':N9T~V J0)Ks8*5Ru&70&&Q=IS@*[fvnrMýL6*;I$}IJH܇%iɬ$SR5 X%ib|/B kt`hZᙔW*;eJsG&7T*MX(I_9$vpw6 Mh*t9hRDvȮNwM\$~aŒV r`)Mb\;<\8ZT 2bp G:a:('t PCԻ62n]. Ƿj>:qg"77Ï,nJ$B/V̢\O+PR-ew; Xu7A$R;n_de@v9zTX:K#ZOҷMSq䷈.v E rkAdU_]ѷ';[7sOI1jrG_恤3vGׅkKŏCѧŭJc29^o8POoD ^5p=5y-,!>ԋyk&J# 4%G-[ogosPh~g:ڸ=kﻊyr2k4RW:Gx;3l-fx{Rmװjw"c !r=u !Ӗ??|cBk#z VR2jNgˍ`ag-SLC.)KJjQS[F9J4Z,j'Yv[ݨxs ;P #<_ 'w:)7{|9tWp}_ ;IrY9>7mW[椐1Bq=\N$TW4~w=L)rVgrMoNB8 a ,y2b G-DjfNW#-AXSǞ&d1aU~Ѿɭ;ӻwm> 6˞7q,~ s8"wGN9`Kr'd(2Zvr]V2v8dt + C97=mb#(L5 mnfz(@=&XB`/|ˇ:9CU6Z4 nvOiTkTYQ>,ST0p%*WSdM kdVKVz90-wv|*2g:hwj$cLڛֲ a雐0eH~oG 3YxۊbZ(r K*AEUGP-0`h4ψͰ<aO\ 9mm'Q6i0UO @% v zm$q :AFfaZ5#=񖵘F&tTE{H&ACaHļ-AW8|3hH!)`և?-p昀hlac(J_:)F˦ʰdW@L-mKg[h??5D:sj>TB$WE~X~4yh"!󛜅 8U- ]v 2D, `XY'rotd^L;`U&fSmaxP#9 Q:KHӲ@I5sD tG[zY U3f K$cH.Q>JXC&5};#hVy̞gZli(-|$FNd21}{РEZ)Z2%kZc }ͱiNؓ{/. ~8eKp l ;xM9?N)^|٧ YVW5@6oI{cCZWʋjX]H{d bJY)DGmYΚFOW}9iuVaw瓻֨4Dt#܇Ϊ-L,7B9: F9,^Id Y'qΊ:Kl ' gz=ST|D/L4B3MI>Knh}C -ntbkDʏ}koEz& _00VyPkҎ`ꂙ EEDsvLhA`g]7&[8\`O!Ƽ핺 aS֑blL0隀R8#K/] IqA9kX^|V@|`!܏c b.neس\[[Nsp%P1ލ,rULfyL`bowK Q-䲊|j:Ҟњq`~0rU3Uh, ,̨${I&(1ܚ^ s>x3.nf @\Uœ1Ѕ,91 ɊzשʴZ9 hInGCǪ%D]q>B\L>vT.6D[A@.0>9cLɋOf|ہ- ]m|u^@E*4F?,4pL0`FNᯏNn[W0D[j>ciAGwQy@`@{~ӭ@uunÌTе]9UUF@z?n&0MZBfr?Fu2=b_D!qHmͿ=C8`N[뤍x }^<nfYI'lIEbUt' "_zQS - 6X!>d4XG6f6h}4B^@"k#b`/PiJ'|jisKLν[،F;!s\Q–0LBu]]Pjb. 8[$ǗpoH:S|pRzB;xl3q;sfnJ~1Cc7B`B>w"D9,WH7 f( s wF /{6j$w0]M~hH3 C%21~z,'rAgQI*[=cDHB1E\ UlX Ql˜?t~eYt++a];1Hm?ϳYO IL.T,9uFѭɦq=hH::D>5fwK7cs1ND eK4΂7I]iZZ_&8mfh8h郢fo#wt .Eh-[V;A>eުqnG)-Ff'6MDߧ=u;D[wJ" Q<$ucFhwhN/xAÊj֌D#T=PXuCpDH*^:x6d+!Sܰw0FJ^x{|Mo~|[Qi}[WEx`#ݍHDen>K=SvkYjuHʽ.b~ގT998r>@i|SjivgBV8pxki,ht>$ބEmD94rXje? MZ=vHW}!?J IJNpf~.ݒ8xg4Sg9!+2Q9x`U@pid>#+8tJXޔ@(pǍ>GLl&Zme m ݴr4e)5T;g*'+K_8GD3NMi֧l7<pBSigWA1AUǽUfeXuA+D'+iZḁOb"@V׿[r.'!3/R+ ,`/qKQ>N99sAwSaLYJ % ȽG}!49 lީ%oU2+nSOWfm jpD^ Aua[F/Y}3~3=6t֛V/cjK+`hej`W]{aWla9E@w.3dRmt)sNviޒ! &^R*fȁn3ye΢4ó2ÐSC=iEjU j?NM; >Y,!'ms}Z|Ğ,yg}ܙ[ZvCt|p.pD7]9U-UrB kvvx=r઼2 "ظv/c84got؉/pJ" , s?;,)^uS>#$!yi!W`&3R1xㄫ9VUӽ6W*xnF2v(7mv]!l`hvj,[<CWh_C[xK,Iu(/ z CDW_Z vj95!dd"l1=XQ=J64}7c|h.4#RQc,6܋-n*)1wiىKW~>aAa SOܦĉ9)=ZA 0^* GlK7%7'rE`xBͫ9'*BP]/SGr5n[n8pTWX@wٱO22I&í{I_$X#8>0U&>sب7̉: & smӖUJ=|>?WaBܸ)۲ř8wu:M8&YcLPZ#D=!`{F\nj^L;ضzM(4-ҜcmyCRA Uxbh^(> 9+sSO9BU^P)2ӘAK}r ?6#+~,Pr]P%H;a^ڭ$.A肮b'th- W+}l[HjYqz92hC>[.yS^aF\@@~ZY#AswP30:;ouw+b]E:^~ǐeيZDwdf]ቶv/=pD=0(pn0c"NaaOR|R]G3@3?sZ$_Wgj۞p:EУ/w{LD  'Z? qT>0m$cKt|߫Y曯FD_+L7Ea}HXcN>D}AF؁w 'd^L*BZ%Q4ɁG+=Hj@Ȉ8MuDE"F[g?{@ݍ7'v{M"nd/OMlT=I;8gS9yI)6 ĝ&NYotܮ V حn]Bgbgzdbd7;}ݯvBvW׎|R~XxkjYqzЃP?Kh-޸g6ѐN.]N:/כ`(C vRupz)ppuu(jǣV ar~1}dc 1'&xՖ#>;a" 1)lӕ-k XwPn'>ů""}fR9fǤ Ueb.') ;8΄a6@ZBAi`W5ȓ'Rƕ\w $-V*1~u"ɸVv& |9+ : =AjD5?}qu8wpD#Sd%iJfk2dqD[%LM* bMr[*^gSֽi@FRyD5qhRA BcE[UIs6NYeiku6k\տ7 hnPU L+N9-g ԓ Vo!">8^H)SÑ{K6a9ɇI$IO؋A b7M9DF洚-fV#~e$H:r X$p >gβ%TQ Zl)\D9~:ׇ=ء(0*T( Z!0Q5庑ٯ>N1}:_v3 3rRV /SAS/me` Vee2LPOawz8Pgmn*"E43g o^sr kEZD:ߝa3YqL_|p 'j[|-iJi50F-,UI>d9RY ( ANuF5h"Bu7U=Z-ky گ̙}hXˀOKdB4y=:saf*d(ɒf2qJPMY_N,]d 0Xɞ5̢. .?9jϖS+:\Hu0JN.ոˎ'5]H {t tX7Ia p`.B([nv鱢CG$cvbu>4d֤c`:F?Y{RDۇvTd)9Iu"׼YjF#_,Ѕo@S|M% ^m[3 S"Ӣ.2h!wO"E,!}6?YA*|wX Q-;V/LqB hó -WmU>a^aT:k:,V9#Kzitp!p'=7*懏B66{]BB^}le8֦x88vk7LЮ JS5cZ[d\^s4V؆٥W(0m$&cNIHZE5ʇhv BB4* >6:}a 04ZWҚuF. r[z&A+ca.Ķ/%n;=Rvb@<(Jװ4vj.]\#J8\ 8H&@x,n0x,b< gq,Upl}hѐt:AsxL+12.TSɩ~3L:̇ `ԃ2j&wq P'RN=?N*Sa4˧VU2#(ۯ\,GyTUkD|"R؅ zS`'4P,N\f!i$>v"_dw RcA;н`2F ꃪ bhVS9.F * t2`zef sЦs╂Yf1`-67<={N}$DT{Ȫ 0//6rYMd40Nm&& +gl}2Jm0$}u!ls&yVr̢9B,DK%s쏊aLvF im8iz!3c}t`GȔ}LD O>+~Y!pptx뤕ӹ(/p`eԴ7,/ЄVJw6|>QSq"eĦOLr#b&;`fHQ|YqrkD`X Bj"o댶0&Z!D!]$4RG t7nUd IFBp#iep*;on_4]_deȻ4'&%%n^y; ;s8!\UVtmػ?ew}CyP†J>l,`>\Z.nE5*`ZPrn*],A2妁>8)߷"ZR|!F."\ᡂ9y18;BAVbcy$aeNbKIU2ݲo\쥓8![D!@K@<7'?TwHyF$ť`h56nBe)dsQ( EA(,B)Bo6T#*3Im Xd9ؼUmMTCzCF xz$,Ljޯr-i8-U{3HI󅀆:c♩̑Mj~˧}I ˙.` W=76ۿ~w|s_P1 |} 6*8]Ht$v0iCz~LJI!+9ȃZ+uPNMŷ߳RbHLGH5fI\p5ٔtIs/se>4W1N[.Rנ'D)-T2\bzǮ6 hC"j0Pvlyʂ%JhkSwZJ_L@"+Mj [rq9ͪ6ÅyB}p7$<\ M\whabY:Gnj/RruNMtS"YiJ8e*g};R;Ȉd\A?{:,V;b 76qkɮfA\bdR FCI&K7nt4_ygdD oTgv*$!lf 'tx/^#O`SZE`dQPz z4 B;ڷV<2#"{`ۈ>oq `(4ǿ hs8xm0cѮ/!Ǝiar޽ ׅ|<^4̞n8}#Q0i퓝I{~FHiݣTb6n wÉ\ԝKBKa[F~k=1=Pl 'h c8= I䶝0hF|s `@)OnHij7l\tᔙ %-B1'FP=( B3L#G7RX@ )t;x\w.JN qH1O-{ayN<lI^{Vٰ~Tkq~&2 k0-d|x&B#4\8~!܌2S5K PqD43HK΢F`'C;91z+T:$:<6-!Z``: XDŽ" ~ CQqM3.}T4݇4mL]T,Մj DXc;!nGTҳN1aT6mifY?\&[.yWLmBo_Q,4Xtx NG/ Gd5xIq̫͝s3i/<x9_Hp@|gvdXa^D6Qez1Gh!X0$t;=^c^Վ"qazwIՊּg5&_x<޽jkrKnuTc#iMPHuh+\ Lu&+x3k{q[K[3kېӜ\D|3-P̹E7BpD=l/tC.$PL!lZq{猽(f_nNܺ )?-GS^|Mnh[C[B^#!5lEPqb_ x4r28TŒԽ3k"`i/K,sg;?}'&\=j 0+$ϚB,$"YܼU+4rww [hKけ^6L#='b:#+Xy5P L49ָLh^s^~JFjf[ń'l^ip4 e2Я W~zzpDŽXzU' q0v9L@G5]d=q9LSKUDsf!F*VQŦ;!GdADs[ua}:Td {; vT}ȢJBZ /R 8jE`x&nU*pe:Q1yFuVgڭ $0rK*g_AQ=S:Õrd5?'KXve-gl_Xط _DVfK=!z͞R >"INDB@ nࠑ;Z)Z4BCd,*bg\ *PSN1E1ydf_\wlkJB _4bsct[S|,WG\vYD:8bj( '6'@f%Dv{GY"E=l8Pk,USLAf#ܭ"J Dz0‡Nc'!D~dMB'xzL&p,9*Jj-]T卣9#J'z. j`@Wz hgOr K o5ޑ'ؗ(뒁B Ӿj@SB82`ŗBggTf]$f+C˲|Z/TO9%;vD5uPِ>&p{ג %'+T0 g=hxzpK¼!`UvW2NGr{e5ɯ=@vҐ}IAu4w?wg!rRAYKQ8-B`r7k:4O L\Easg:q`{ˠ9 #Fsn):tXx#w5/5, b[ lp翻.JBwۚ/ɛ=[e/RM USZJ;餬T͢5D<00qfvtҼ"x.Hm{}AР}-"W [|Ph> ޾>`p6 QQEbha3WG6D\zNvF+{hʓJlK4 $g0GRS#k9͋ !~rH-GH9-,.ZW+3[ rǹ h.lzRfWpwfȿפ6ٓ04˨i[sK'g'6SO>,,UO5psRҩ}BX$1 REa`V]pb`I($ 瑲glBF0`8֡ٺrk4n-rYBqJp[UQ !m=Ml"v|Ohs p:F52" kt 7:]>9 &+B,ЧiH??|xڡxj12)1þ] .{="GBdC2`q;KB`8i)(G~<Ӌ&D$gY$WGq\NX`7nBd5Ҫ= VL!`Z$L% ~)V)j"z٪48_oԻ]QLbc]> {a.܍rq?]NT~d}oճ6Pn 1r>OsVlK{f1JCT_?Q-s6j #5!@\e¨_ &طX3~~(;VvTD@s߷31գ= q]MH&4)?[k.rO=Ѐ8%=ibGpyrit-0.4.0/COPYING0000664000076400007640000010451311524611424014201 0ustar slaveslave00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . pyrit-0.4.0/PKG-INFO0000664000076400007640000000165411526302053014242 0ustar slaveslave00000000000000Metadata-Version: 1.0 Name: pyrit Version: 0.4.0 Summary: GPU-accelerated attack against WPA-PSK authentication Home-page: http://pyrit.googlecode.com Author: Lukas Lueg Author-email: lukas.lueg@gmail.com License: GNU General Public License v3 Description: Pyrit allows to create massive databases, pre-computing part of the WPA/WPA2-PSK authentication phase in a space-time-tradeoff. Exploiting the computational power of Many-Core- and other platforms through ATI-Stream, Nvidia CUDA, OpenCL and VIA Padlock, it is currently by far the most powerful attack against one of the world's most used security-protocols. Platform: any Classifier: Development Status :: 4 - Beta Classifier: Environment :: Console Classifier: License :: OSI Approved :: GNU General Public License (GPL) Classifier: Natural Language :: English Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Topic :: Security pyrit-0.4.0/setup.py0000775000076400007640000000620011526301306014652 0ustar slaveslave00000000000000#!/usr/bin/env python # -*- coding: UTF-8 -*- # # Copyright 2008-2011, Lukas Lueg, lukas.lueg@gmail.com # # This file is part of Pyrit. # # Pyrit is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Pyrit is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Pyrit. If not, see . from distutils.core import setup, Extension from distutils.unixccompiler import UnixCCompiler import subprocess import re VERSION = '0.4.0' UnixCCompiler.src_extensions.append('.S') try: svn_info = subprocess.Popen(('svn', 'info'), \ stdout=subprocess.PIPE).stdout.read() VERSION += ' (svn r%i)' % \ int(re.compile('Revision: ([0-9]*)').findall(svn_info)[0]) except: pass EXTRA_COMPILE_ARGS = ['-Wall', '-fno-strict-aliasing', \ '-DVERSION="%s"' % (VERSION,)] cpu_extension = Extension(name='cpyrit._cpyrit_cpu', sources = ['cpyrit/_cpyrit_cpu.c', 'cpyrit/_cpyrit_cpu_sse2.S'], libraries = ['crypto', 'pcap'], extra_compile_args=EXTRA_COMPILE_ARGS) setup_args = dict( name = 'pyrit', version = VERSION, description = 'GPU-accelerated attack against WPA-PSK authentication', long_description = \ "Pyrit allows to create massive databases, pre-computing part " \ "of the WPA/WPA2-PSK authentication phase in a space-time-" \ "tradeoff. Exploiting the computational power of Many-Core- " \ "and other platforms through ATI-Stream, Nvidia CUDA, OpenCL " \ "and VIA Padlock, it is currently by far the most powerful " \ "attack against one of the world's most used security-protocols.", license = 'GNU General Public License v3', author = 'Lukas Lueg', author_email = 'lukas.lueg@gmail.com', url = 'http://pyrit.googlecode.com', classifiers = \ ['Development Status :: 4 - Beta', 'Environment :: Console', 'License :: OSI Approved :: GNU General Public License (GPL)', 'Natural Language :: English', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Topic :: Security'], platforms = ['any'], packages = ['cpyrit'], py_modules = ['pyrit_cli', 'cpyrit.cpyrit', 'cpyrit.util', 'cpyrit.pckttools', 'cpyrit.config', 'cpyrit.network'], scripts = ['pyrit'], ext_modules = [cpu_extension], options = {'install': {'optimize': 1}}) if __name__ == '__main__': setup(**setup_args) pyrit-0.4.0/pyrit_cli.py0000664000076400007640000020367411526274651015537 0ustar slaveslave00000000000000# -*- coding: UTF-8 -*- # # Copyright 2008-2011, Lukas Lueg, lukas.lueg@gmail.com # # This file is part of Pyrit. # # Pyrit is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Pyrit is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Pyrit. If not, see . from __future__ import with_statement import getopt import glob import gzip import hashlib import itertools import os import random import sys import time import cpyrit.cpyrit import cpyrit.config import cpyrit.util import cpyrit.storage class PyritRuntimeError(RuntimeError): pass class Pyrit_CLI(object): def __init__(self): self.verbose = True def tell(self, text, sep=' ', end='\n', stream=sys.stdout, flush=False): if self.verbose or stream != sys.stdout: stream.write(text) if end is not None: stream.write(end) else: if sep is not None: stream.write(sep) if flush or end is None: stream.flush() def initFromArgv(self): options = {} args, commands = getopt.getopt(sys.argv[1:], \ 'b:e:i:o:r:u:h', \ ['all-handshakes']) args = dict(args) if len(commands) == 1 and commands[0] in self.commands: command = commands[0] else: command = 'help' args = {} if '-h' in args: func = Pyrit_CLI.print_command_help args = {'-c': command} else: func = self.commands[command] req_params, opt_params = func.cli_options for param in req_params: if param not in ('-u',) and param not in args: raise PyritRuntimeError("The command '%s' requires the " \ "option '%s'. See 'help'." % \ (command, param)) for arg, value in args.iteritems(): if arg in req_params or arg in opt_params: if arg == '-e': options['essid'] = value elif arg == '-b': options['bssid'] = str(value).lower() elif arg == '-i': options['infile'] = value elif arg == '-o': options['outfile'] = value # Prevent messages from corrupting stdout if value == '-': self.verbose = False elif arg == '-r': options['capturefile'] = value elif arg == '-c': options['command'] = value elif arg == '--all-handshakes': options['all_handshakes'] = True else: raise PyritRuntimeError("The command '%s' ignores the " \ "option '%s'." % (command, arg)) self.tell("Pyrit %s (C) 2008-2011 Lukas Lueg " \ "http://pyrit.googlecode.com\n" \ "This code is distributed under the GNU General Public " \ "License v3+\n" % cpyrit.util.VERSION, stream=sys.stdout) if '-u' in req_params: if '-u' in args: storage_url = args['-u'] else: storage_url = cpyrit.config.cfg['default_storage'] options['storage'] = self._getStorage(storage_url) func(self, **options) def print_help(self): """Print general help You should have figured this one out by now. """ self.tell('Usage: pyrit [options] command' '\n' '\nRecognized options:' '\n -b : Filters AccessPoint by BSSID' '\n -e : Filters AccessPoint by ESSID' '\n -h : Print help for a certain command' "\n -i : Filename for input ('-' is stdin)" "\n -o : Filename for output ('-' is stdout)" '\n -r : Packet capture source in pcap-format' '\n -u : URL of the storage-system to use' '\n --all-handshakes : Use all handshakes instead of the best one' '\n' '\nRecognized commands:') m = max([len(command) for command in self.commands]) for command, func in sorted(self.commands.items()): self.tell(' %s%s : %s' % (command, \ ' ' * (m - len(command)), \ func.__doc__.split('\n')[0])) print_help.cli_options = ((), ()) def print_command_help(self, command): """Print help about a certain command""" doc = self.commands[command].__doc__ self.tell('\n'.join(l.strip() for l in doc.split('\n'))) print_command_help.cli_options = (('-c',), ()) def requires_pckttools(*params): """Decorate a function to check for cpyrit.cpyrit_pckttools before execution. """ def check_pkttools(f): def new_f(*args, **kwds): try: import cpyrit.pckttools except ImportError: raise PyritRuntimeError("Scapy 2.x is required to use " \ "Pyrit's analyze/attack " \ "functions but seems to be " \ "unavailable.") f(*args, **kwds) new_f.func_name = f.func_name new_f.__doc__ = f.__doc__ return new_f return check_pkttools def _getParser(self, capturefilemask): filelist = glob.glob(capturefilemask) if len(filelist) == 0: raise PyritRuntimeError("No file found that matches '%s'" % \ capturefilemask) parser = cpyrit.pckttools.PacketParser() for idx, capturefile in enumerate(filelist): self.tell("Parsing file '%s' (%i/%i)..." % (capturefile, idx + 1, \ len(filelist))) dev = cpyrit.pckttools.PcapDevice(capturefile, True) parser.parse_pcapdevice(dev) self.tell("Parsed %i packets (%i 802.11-packets), got %i AP(s)\n" % \ (parser.pcktcount, parser.dot11_pcktcount, len(parser))) return parser def _fuzzyGetAP(self, parser, bssid=None, essid=None): if bssid is None and essid is None: for ap in parser: if ap.isCompleted() and ap.essid is not None: self.tell("Picked AccessPoint %s ('%s') automatically." % \ (ap, ap.essid)) return ap raise PyritRuntimeError("Specify an AccessPoint's BSSID or " \ "ESSID using the options -b and -e. " \ "See 'help'") if bssid is not None: if bssid not in parser: raise PyritRuntimeError("No AccessPoint with BSSID '%s' " \ "found in the capture file..." % \ bssid) ap = parser[bssid] else: ap = None if essid is not None: if ap is None: aps = filter(lambda ap: (ap.essid is None or ap.essid == essid) and ap.isCompleted(), parser) if len(aps) > 0: ap = aps[0] self.tell("Picked AccessPoint %s automatically..." % ap) else: raise PyritRuntimeError("No suitable AccessPoint with " \ "that ESSID in the capture file.") else: if ap.essid is not None and ap.essid != essid: self.tell("WARNING: AccessPoint %s has ESSID '%s'. " \ "Using '%s' anyway." % (ap, ap.essid, essid), \ stream=sys.stderr) else: if ap.essid is None: raise PyritRuntimeError("The ESSID for AccessPoint %s is " \ "not known from the capture file. " \ "Specify it using the option -e." % ap) return ap def _getStorage(self, url): safe_url = cpyrit.storage.pruneURL(url) self.tell("Connecting to storage at '%s'... " % (safe_url,), end=None) storage = cpyrit.storage.getStorage(url) self.tell("connected.") return storage def create_essid(self, storage, essid=None, infile=None): """Create a new ESSID Add the ESSID given by -e to the database. Re-creating an existing ESSID does not result in an error. For example: pyrit -e NETGEAR create_essid """ if essid is None and infile is None: raise PyritRuntimeError("Please specify the ESSID to create " \ "with '-e' or a file to read ESSIDs " \ "from with '-i'.") if essid is not None: if essid in storage.essids: self.tell("ESSID already created") else: storage.essids.create_essid(essid) self.tell("Created ESSID '%s'" % essid) elif infile is not None: with cpyrit.util.FileWrapper(infile) as reader: for essid in reader: essid = essid.strip() if essid not in storage.essids: storage.essids.create_essid(essid) self.tell("Created ESSID '%s'" % essid) create_essid.cli_options = (('-u', ), ('-e', '-i')) def delete_essid(self, storage, essid, confirm=True): """Delete a ESSID from the database Delete the ESSID given by -e from the database. This includes all results that may have been stored for that particular ESSID. For example: pyrit -e NETGEAR delete_essid """ if essid not in storage.essids: raise PyritRuntimeError("ESSID not found...") else: if confirm: self.tell("All results for ESSID '%s' will be deleted! " \ "Continue? [y/N]" % essid, end=None) if sys.stdin.readline().strip() != 'y': raise PyritRuntimeError("aborted.") self.tell("deleting...") del storage.essids[essid, None] self.tell("Deleted ESSID '%s'." % essid) delete_essid.cli_options = (('-e', '-u'), ()) def list_cores(self): """List available cores Show a list of all available hardware modules pyrit currently uses. For example: pyrit list_cores """ with cpyrit.cpyrit.CPyrit() as cp: self.tell("The following cores seem available...") for i, core in enumerate(cp.cores): self.tell("#%i: '%s'" % (i + 1, core)) list_cores.cli_options = ((), ()) def list_essids(self, storage): """List all ESSIDs but don't count matching results Show a list of all ESSIDs currently stored in the database. This function is faster than eval in case you don't need to know the number of computed results. For example: pyrit list_essids """ self.tell("Listing ESSIDs...\n") for essid in sorted(storage.essids): self.tell("ESSID '%s'" % essid) self.tell("") list_essids.cli_options = (('-u', ), ()) def eval_results(self, storage): """Count the available passwords and matching results Count all available passwords, all ESSIDs and their respective results in the database. For example: pyrit eval """ self.tell("Querying...", end=None, flush=True) pwcount, essid_results = storage.getStats() self.tell("\rPasswords available: %i\n" % pwcount) if len(essid_results) > 0: m = max(len(essid) for essid in essid_results.iterkeys()) n = max(len(str(c)) for c in essid_results.itervalues()) for essid, rescnt in sorted(essid_results.iteritems()): self.tell("ESSID '%s'%s : %s%i (%.2f%%)" % (essid, \ ' ' * (m - len(essid)), \ ' ' * (n - len(str(rescnt))), rescnt, \ (rescnt * 100.0 / pwcount) if pwcount > 0 else 0)) self.tell('') eval_results.cli_options = (('-u', ), ()) def import_passwords(self, storage, infile, unique_check=True): """Import passwords from a file-like source Read the file given by -i and import one password per line to the database. The passwords may contain all characters (including NULL-bytes) apart from the terminating newline-character \\n. Passwords that are not suitable for being used with WPA-/WPA2-PSK are ignored. Pyrit's storage-implementation guarantees that all passwords remain unique throughout the entire database. For example: pyrit -i dirty_words.txt import_passwords """ i = 0 storage.passwords.unique_check = unique_check perfcounter = cpyrit.util.PerformanceCounter() with cpyrit.util.FileWrapper(infile) as reader: with storage.passwords as pwstore: for i, line in enumerate(reader): pwstore.store_password(line) if i % 100000 == 0: perfcounter += 100000 self.tell("\r%i lines read (%.1f lines/s)..." % \ (i, perfcounter.avg), end=None, flush=True) self.tell("\r%i lines read. Flushing buffers..." % (i + 1)) self.tell('All done.') import_passwords.cli_options = (('-i', '-u'), ()) def import_unique_passwords(self, storage, infile): """Import unique passwords from a file-like source Read the file given by -i and import one password per line to the database. The passwords may contain all characters (including NULL-bytes) apart from the terminating newline-character \\n. Passwords that are not suitable for being used with WPA-/WPA2-PSK are ignored. This command does not check if there are duplicating passwords within the file or between the file and the database; it should be used with caution to prevent the database from getting poisoned with duplicated passwords. This command however can be much faster than import_passwords. For example: pyrit -i dirty_words.txt import_unique_passwords """ self.import_passwords(storage, infile, unique_check=False) import_unique_passwords.cli_options = (('-i', '-u'), ()) def export_passwords(self, storage, outfile): """Export passwords to a file Write all passwords that are currently stored in the database to a new file given by -o. Passwords are terminated by a single newline-character (\\n). Existing files are overwritten without confirmation. For example: pyrit -o myword.txt.gz export_passwords """ perfcounter = cpyrit.util.PerformanceCounter() with cpyrit.util.AsyncFileWriter(outfile) as awriter: for idx, pwset in enumerate(storage.iterpasswords()): awriter.write('\n'.join(pwset)) awriter.write('\n') perfcounter += len(pwset) self.tell("%i lines written (%.1f lines/s, %.1f%%)\r" % \ (perfcounter.total, perfcounter.avg, \ (idx + 1) * 100.0 / len(storage.passwords)), \ end=None, sep=None) self.tell("\nAll done") export_passwords.cli_options = (('-o', '-u'), ()) def export_cowpatty(self, storage, essid, outfile): """Export results to a new cowpatty file Write all results for the ESSID given by -e to the file given by -o in cowpatty's binary format. Existing files are overwritten without confirmation. For example: pyrit -o NETGEAR.cow -e NETGEAR export_cowpatty """ if essid not in storage.essids: raise PyritRuntimeError("The ESSID you specified can't be found.") perfcounter = cpyrit.util.PerformanceCounter() self.tell("Exporting to '%s'..." % outfile) with cpyrit.util.AsyncFileWriter(outfile) as filewriter: with cpyrit.util.CowpattyFile(filewriter, 'w', essid) as cpwriter: try: for results in storage.iterresults(essid): cpwriter.write(results) perfcounter += len(results) self.tell("\r%i entries written (%.1f/s)..." % \ (perfcounter.total, perfcounter.avg), \ end=None, sep=None) except IOError: self.tell("IOError while exporting to " \ "stdout ignored...", stream=sys.stderr) self.tell("\r%i entries written. All done." % perfcounter.total) export_cowpatty.cli_options = (('-u', '-e', '-o'), ()) @requires_pckttools() def analyze(self, capturefile): """Analyze a packet-capture file Parse one or more packet-capture files (in pcap-format, possibly gzip-compressed) and try to detect Access-Points, Stations and EAPOL-handshakes. For example: pyrit -r "test*.pcap" analyze """ parser = self._getParser(capturefile) for i, ap in enumerate(parser): self.tell("#%i: AccessPoint %s ('%s'):" % (i + 1, ap, ap.essid)) for j, sta in enumerate(ap): self.tell(" #%i: Station %s" % (j + 1, sta), \ end=None, sep=None) auths = sta.getAuthentications() if len(auths) > 0: self.tell(", %i handshake(s):" % (len(auths),)) for k, auth in enumerate(auths): self.tell(" #%i: %s" % (k + 1, auth)) else: self.tell("") if not any(ap.isCompleted() and ap.essid is not None for ap in parser): raise PyritRuntimeError("No valid EAOPL-handshake + ESSID " \ "detected.") analyze.cli_options = (('-r', ), ()) @requires_pckttools() def stripCapture(self, capturefile, outfile, bssid=None, essid=None): """Strip packet-capture files to the relevant packets Parse one or more packet-capture files given by the option -r, extract only packets that are necessary for EAPOL-handshake detection and write a new dump to the filename given by the option -o. The options -e and -b can be used to filter certain Access-Points. For example: pyrit -r "dumps_*.pcap" -e MyNetwork -o tiny.dump.gz strip """ parser = self._getParser(capturefile) if essid is not None or bssid is not None: ap_iter = (self._fuzzyGetAP(parser, bssid, essid), ) else: ap_iter = parser with cpyrit.pckttools.Dot11PacketWriter(outfile) as writer: for i, ap in enumerate(ap_iter): self.tell("#%i: AccessPoint %s ('%s')" % (i + 1, ap, ap.essid)) if ap.essidframe: writer.write(ap.essidframe) for j, sta in enumerate(ap): self.tell(" #%i: Station %s" % (j, sta), \ end=None, sep=None) auths = sta.getAuthentications() if len(auths) > 0: self.tell(", %i handshake(s)" % (len(auths),)) for k, auth in enumerate(auths): self.tell(" #%i: %s" % (k + 1, auth)) else: self.tell("") packets = [] for frames in sta.frames.itervalues(): for i in xrange(3): packets.extend(frames[i].itervalues()) for pckt_idx, pckt in sorted(packets): writer.write(pckt) self.tell("\nNew pcap-file '%s' written (%i out of %i packets)" % \ (outfile, writer.pcktcount, parser.pcktcount)) stripCapture.cli_options = (('-r', '-o'), ('-e', '-b')) def _stripLive_newAP(self, parser, writer, ap): writer.write(ap.essidframe) self.tell("%i/%i: New AccessPoint %s ('%s')" % \ (writer.pcktcount, parser.pcktcount, ap, ap.essid)) def _stripLive_newStation(self, parser, writer, station): self.tell("%i/%i: New Station %s (AP %s)" % \ (writer.pcktcount, parser.pcktcount, station, station.ap)) def _stripLive_newKeyPckt(self, parser, writer, station, idx, pckt): writer.write(pckt) self.tell("%i/%i: %s AP %s <-> STA %s" % \ (writer.pcktcount, parser.pcktcount, \ ["Challenge", "Response", "Confirmation"][idx], \ station.ap, station)) def _stripLive_newAuth(self, parser, writer, station, auth): self.tell("%i/%i: New Handshake AP %s: %s" % \ (writer.pcktcount, parser.pcktcount, station.ap, auth)) @requires_pckttools() def stripLive(self, capturefile, outfile): """Capture relevant packets from a live capture-source Parse a packet-capture file given by the option -r, extract only packets that are necessary for EAPOL-handshake detection and write a new dump to the file given by the option -o. This command differs from strip as the capture-file can be any character device including sockets and other pseudo-files that look like files in pcap-format. stripLive writes relevant packets to the new file given by -o as they arrive instead of trying to read the entire capture-file first. For example: pyrit -r /temp/kismet_dump -o small_dump.pcap stripLive """ writer = cpyrit.pckttools.Dot11PacketWriter(outfile) parser = cpyrit.pckttools.PacketParser() parser.new_ap_callback = \ lambda ap: self._stripLive_newAP(parser, writer, ap) parser.new_station_callback = \ lambda sta: self._stripLive_newStation(parser, writer, sta) parser.new_keypckt_callback = \ lambda (sta, idx, pckt): \ self._stripLive_newKeyPckt(parser, writer, sta, idx, pckt) parser.new_auth_callback = \ lambda (sta, auth): self._stripLive_newAuth(parser, writer, sta, \ auth) self.tell("Parsing packets from '%s'..." % capturefile) pckt_rdr = cpyrit.pckttools.PcapDevice(use_bpf=True) try: pckt_rdr.open_offline(capturefile) except IOError, offline_error: try: pckt_rdr.open_live(capturefile) except IOError, live_error: raise PyritRuntimeError("Failed to open '%s' either as a " \ "file ('%s') or as a device " \ "('%s')" % (capturefile, \ str(offline_error), str(live_error))) try: parser.parse_pcapdevice(pckt_rdr) except (KeyboardInterrupt, SystemExit): self.tell("\nInterrupted...\n") else: self.tell("\nCapture-source was closed...\n") finally: writer.close() for i, ap in enumerate(parser): self.tell("#%i: AccessPoint %s ('%s')" % (i + 1, ap, ap.essid)) for j, sta in enumerate(ap): auths = sta.getAuthentications() if len(auths) > 0: self.tell(" #%i: Station %s, %i handshake(s)" % \ (j, sta, len(auths))) for k, auth in enumerate(auths): self.tell(" #%i: %s" % (k + 1, auth)) self.tell("\nNew pcap-file '%s' written (%i out of %i packets)" % \ (outfile, writer.pcktcount, parser.pcktcount)) stripLive.cli_options = (('-r', '-o'), ()) def export_hashdb(self, storage, outfile, essid=None): """Export results to an airolib database Write all results currently stored in the database to the airolib-ng database given by -o. The database is created with a default table layout if the file does not yet exist. The option -e can be used to limit the export to a single ESSID. For example: pyrit -o NETGEAR.db -e NETGEAR export_hashdb """ import sqlite3 if essid is None: essids = storage.essids else: essids = [essid] con = sqlite3.connect(outfile) con.text_factory = str cur = con.cursor() cur.execute('SELECT * FROM sqlite_master') tbls = [x[1] for x in cur.fetchall() if x[0] == u'table'] if u'pmk' not in tbls or u'essid' not in tbls or u'passwd' not in tbls: self.tell("The database '%s' seems to be uninitialized. " % \ outfile) self.tell("Trying to create default table-layout...", end=None) try: cur.execute("CREATE TABLE essid (" \ "essid_id INTEGER PRIMARY KEY AUTOINCREMENT," \ "essid TEXT," \ "prio INTEGER DEFAULT 64)") cur.execute("CREATE TABLE passwd (" \ "passwd_id INTEGER PRIMARY KEY AUTOINCREMENT, " \ "passwd TEXT)") cur.execute("CREATE TABLE pmk (" \ "pmk_id INTEGER PRIMARY KEY AUTOINCREMENT, " \ "passwd_id INTEGER, " \ "essid_id INTEGER, " \ "pmk BLOB)") cur.execute("CREATE TABLE workbench (" \ "wb_id INTEGER PRIMARY KEY AUTOINCREMENT, " \ "essid_id INTEGER, " \ "passwd_id INTEGER, " \ "lockid INTEGER DEFAULT 0)") cur.execute("CREATE INDEX lock_lockid ON workbench (lockid);") cur.execute("CREATE UNIQUE INDEX essid_u ON essid (essid)") cur.execute("CREATE UNIQUE INDEX passwd_u ON passwd (passwd)") cur.execute("CREATE UNIQUE INDEX ep_u ON pmk " \ "(essid_id, passwd_id)") cur.execute("CREATE UNIQUE INDEX wb_u ON workbench " \ "(essid_id, passwd_id)") cur.execute("CREATE TRIGGER delete_essid DELETE ON essid " \ "BEGIN DELETE FROM pmk " \ "WHERE pmk.essid_id = OLD.essid_id;" \ "DELETE FROM workbench " \ "WHERE workbench.essid_id = OLD.essid_id;" \ "END") cur.execute("CREATE TRIGGER delete_passwd DELETE ON passwd " \ "BEGIN DELETE FROM pmk " \ "WHERE pmk.passwd_id = OLD.passwd_id;" \ "DELETE FROM workbench " \ "WHERE workbench.passwd_id = OLD.passwd_id;" \ "END") self.tell("Tables created...") except: con.rollback() cur.close() con.close() self.tell("Failed to initialize the database.", \ stream=sys.stderr) raise try: cur.execute("PRAGMA synchronous = 1") i = 0 self.tell("Writing passwords...") for pwset in storage.iterpasswords(): i += len(pwset) cur.executemany("INSERT OR IGNORE INTO passwd " \ "(passwd) VALUES (?)", [(p, ) for p in pwset]) self.tell("Wrote %i lines...\r" % i, end=None, sep=None) self.tell("\nWriting ESSIDs and results...") for cur_essid in essids: self.tell("Writing '%s'..." % cur_essid) cur.execute("INSERT OR IGNORE INTO essid " \ "(essid) VALUES (?)", (cur_essid, )) essid_id = cur.execute("SELECT essid_id FROM essid " \ "WHERE essid = ?", \ (cur_essid, )).fetchone()[0] i = 0 for results in storage.iterresults(cur_essid): i += len(results) cur.executemany("INSERT OR IGNORE INTO pmk " \ "(essid_id, passwd_id, pmk) " \ "SELECT ?, passwd_id, ? FROM passwd " \ "WHERE passwd = ?", \ ((essid_id, buffer(pmk), pw) \ for pw, pmk in results)) self.tell("Wrote %i lines...\r" % i, end=None, sep=None) self.tell("\nAll done.") except: con.rollback() self.tell("There was an error while exporting. The database has " \ "not been modified...", stream=sys.stderr) raise else: con.commit() finally: cur.close() con.close() export_hashdb.cli_options = (('-u', '-o', ), ('-e', )) def passthrough(self, essid, infile, outfile): """Compute PMKs and write results to a file Read passwords from the file given by -i and compute their PMKs for the ESSID given by -e. The results are written to the file specified by -o in cowpatty's binary format and are not stored in the database for later use. This command therefor circumvents the entire database and should only be used if storage-space is a problem (e.g. when using pyrit on a LiveCD). The batch-command provides exactly the same functionality as passthrough but can give much better performance as results may be read from the database instead of recomputing them. For example: pyrit -i words.txt -e NETGEAR -o NETGEAR.cow.gz passthrough """ perfcounter = cpyrit.util.PerformanceCounter() with cpyrit.util.FileWrapper(infile) as reader: try: with cpyrit.util.AsyncFileWriter(outfile) as writer: with cpyrit.util.CowpattyFile(writer, 'w', essid) as cowpwriter: with cpyrit.cpyrit.PassthroughIterator(essid, reader) as rstiter: for results in rstiter: cowpwriter.write(results) perfcounter += len(results) self.tell("Computed %i PMKs so far; %i PMKs" \ " per second\r" % \ (perfcounter.total, \ perfcounter.avg), \ end=None, sep=None) except IOError: self.tell("IOError while writing to stdout ignored.", \ stream=sys.stderr) finally: self.tell("Computed %i PMKs total; %i PMKs per second" % \ (perfcounter.total, perfcounter.avg)) passthrough.cli_options = (('-i', '-o', '-e'), ()) def batchprocess(self, storage, essid=None, outfile=None): """Batchprocess the database Start to translate all passwords in the database into their respective PMKs and store the results in the database. The option -e may be used to restrict this command to a single ESSID; if it is ommitted, all ESSIDs are processed one after the other in undefined order. For example: pyrit -e NETGEAR batch The option -o can be used to specify a filename the results should additionally be written to in cowpatty's binary format. The option -e becomes mandatory and the ESSID is automatically created in the database if necessary. Pairwise Master Keys that previously have been computed and stored in the database are exported from there without further processing. Pyrit stops and exits if an IOError is raised while writing to the specified file but signals success on exit. This makes it very convenient to pipe results directly to other programs but also keep them for later use. For example: pyrit -e NETGEAR -o - batch | cowpatty -d - -r MyCap.cap -s NETGEAR """ if outfile is not None and essid is None: raise PyritRuntimeError("Results will be written to a file " \ "while batchprocessing. This requires " \ "to specify a single ESSID.") if essid is not None: if essid not in storage.essids: storage.essids.create_essid(essid) essids = [essid] else: essids = [] pwcount, essid_results = storage.getStats() if len(essid_results) == 0: raise PyritRuntimeError("No ESSID in storage. Use 'create_" \ "essid' first.") for e, rescount in essid_results.iteritems(): if rescount < pwcount: essids.append(e) if outfile is not None: outfilewriter = cpyrit.util.AsyncFileWriter(outfile) cowpwriter = cpyrit.util.CowpattyFile(outfilewriter, 'w', essid) else: cowpwriter = None for cur_essid in essids: perfcounter = cpyrit.util.PerformanceCounter() self.tell("Working on ESSID '%s'" % cur_essid) with cpyrit.cpyrit.StorageIterator(storage, cur_essid, \ yieldOldResults=cowpwriter is not None) as dbiterator: totalKeys = len(dbiterator) for results in dbiterator: perfcounter += len(results) if cowpwriter is not None: try: cowpwriter.write(results) except IOError: self.tell("IOError while batchprocessing...") raise SystemExit solvedKeys = dbiterator.keycount() self.tell("Processed %i/%i workunits so far (%.1f%%); " \ "%i PMKs per second.\r" % (solvedKeys, \ totalKeys, \ 100.0 * solvedKeys / totalKeys, \ perfcounter.avg), \ end=None, sep=None) self.tell("Processed all workunits for ESSID '%s'; " \ "%i PMKs per second." % \ (cur_essid, perfcounter.avg)) self.tell('') if cowpwriter is not None: cowpwriter.close() self.tell("Batchprocessing done.") batchprocess.cli_options = (('-u', ), ('-e', '-o')) def relay(self, storage): """Relay a storage-url via RPC Start a server to relay another storage device via XML-RPC; other pyrit-clients can use the server as storage-device. This allows to have network-based access to storage source that don’t provide network-access on their own (like file:// and sqlite://) or hide a SQL-database behind a firewall and let multiple clients access that database only via Pyrit’s RPC-interface. The TCP-port 17934 must be open for this function to work. For example, on the server (where the database is): pyrit -u sqlite:////var/local/pyrit.db relay ... and the client (where the big GPU is): pyrit -u http://192.168.0.100:17934 batch """ with cpyrit.storage.StorageRelay(storage) as rpcd: self.tell("Server started...") try: rpcd.serve_forever() except (KeyboardInterrupt, SystemExit): pass self.tell("Server closed") relay.cli_options = (('-u', ), ()) def serve(self): """Serve local hardware to other Pyrit clients Start a server that provides access to the local computing hardware to help other Pyrit-clients. The server's IP-address should be added to the clients' configuration file (usually '~/.pyrit/config') as a space-separated list under known_clients. These clients' rpc_server-setting must also be set to 'true'. The TCP- and UDP-port 17935 must be accessible. For example, on the server (where the GPU is): pyrit serve ... and the clients (the server's IP-address has been added to 'known_clients' and rpc_server is set to 'true'): pyrit -r tst.pcap -b 00:de:ad:be:ef:00 -i words attack_passthrough """ server = cpyrit.network.NetworkServer() listener = cpyrit.network.NetworkAnnouncementListener() perfcounter = cpyrit.util.PerformanceCounter() try: while server.isAlive(): addr = listener.waitForAnnouncement(block=True, timeout=1.0) if addr is not None and addr not in server: server.addClient(addr) perfcounter.addAbsolutePoint(server.stat_scattered) if perfcounter.avg > 0: y = (server.stat_gathered - server.stat_enqueued) \ / perfcounter.avg else: y = 0 self.tell("\rServing %i active clients; %i PMKs/s; " \ "%.1f TTS" % (len(server), perfcounter.avg, y), \ end=None) except (KeyboardInterrupt, SystemExit): self.tell("\nShutdown with %i active clients..." % len(server)) listener.shutdown() server.shutdown() serve.cli_options = ((), ()) @requires_pckttools() def attack_passthrough(self, infile, capturefile, essid=None, \ bssid=None, outfile=None, all_handshakes=False): """Attack a handshake with passwords from a file Attack an EAPOL-handshake found in the packet-capture file given by the option -r using the passwords read from the file given by the option -i. The options -b and -e can be used to specify the Access-Point to attack; it is picked automatically if both options are omitted. The password is written to the filename given by the option -o if specified. For example: pyrit -r test.pcap -b 00:de:ad:be:ef:00 -i words attack_passthrough This command circumvents Pyrit's database and should only be used if storage-space is a problem (e.g. on LiveCDs). You should consider using attack_batch otherwise. """ ap = self._fuzzyGetAP(self._getParser(capturefile), bssid, essid) if not ap.isCompleted(): raise PyritRuntimeError("No valid handshakes for AccessPoint %s " \ "found in the capture file." % ap) if essid is None: essid = ap.essid perfcounter = cpyrit.util.PerformanceCounter() auths = ap.getCompletedAuthentications() crackers = [] if not all_handshakes: crackers.append(cpyrit.pckttools.EAPOLCracker(auths[0])) else: self.tell("Attacking %i handshake(s)." % (len(auths),)) for auth in auths: crackers.append(cpyrit.pckttools.EAPOLCracker(auth)) with cpyrit.util.FileWrapper(infile) as reader: with cpyrit.cpyrit.PassthroughIterator(essid, reader) as rstiter: for results in rstiter: for cracker in crackers: cracker.enqueue(results) perfcounter += len(results) self.tell("Tried %i PMKs so far; %i PMKs per second.\r" % \ (perfcounter.total, perfcounter.avg), end=None, sep=None) if any(c.solution is not None for c in crackers): break self.tell("Tried %i PMKs so far; %i PMKs per second." % \ (perfcounter.total, perfcounter.avg)) for cracker in crackers: cracker.join() if cracker.solution is not None: self.tell("\nThe password is '%s'.\n" % cracker.solution) if outfile is not None: with cpyrit.util.FileWrapper(outfile, 'w') as writer: writer.write(cracker.solution) break else: errmsg = "\nPassword was not found." if len(auths) > 1 and all_handshakes is False: errmsg += " Retry the attack with '--all-handshakes'.\n" else: errmsg += "\n" raise PyritRuntimeError(errmsg) attack_passthrough.cli_options = (('-i', '-r'), ('-e', '-b', '-o', \ '--all-handshakes')) @requires_pckttools() def attack_batch(self, storage, capturefile, essid=None, bssid=None, \ outfile=None, all_handshakes=False): """Attack a handshake with PMKs/passwords from the db Attack an EAPOL-handshake found in the packet-capture file(s) given by the option -r using the Pairwise Master Keys and passwords stored in the database. The options -b and -e can be used to specify the Access-Point to attack; it is picked automatically if both options are omitted. The password is written to the filename given by the option -o if specified. For example: pyrit -r test.pcap -b 00:de:ad:c0:de:00 -o passwd.txt attack_batch Pairwise Master Keys that have been computed and stored in the database previously are taken from there; all other passwords are translated into their respective Pairwise Master Keys and added to the database for later re-use. ESSIDs are created automatically in the database if necessary. """ ap = self._fuzzyGetAP(self._getParser(capturefile), bssid, essid) if not ap.isCompleted(): raise PyritRuntimeError("No valid handshakes for AccessPoint %s " \ "found in the capture file." % ap) if essid is None: essid = ap.essid if essid not in storage.essids: storage.essids.create_essid(essid) perfcounter = cpyrit.util.PerformanceCounter() auths = ap.getCompletedAuthentications() if all_handshakes: self.tell("Attacking %i handshake(s)." % (len(auths),)) for auth in auths if all_handshakes else auths[:1]: with cpyrit.pckttools.EAPOLCracker(auth) as cracker: with cpyrit.cpyrit.StorageIterator(storage, essid) as dbiter: self.tell("Attacking handshake with " \ "station %s" % (auth.station,)) for idx, results in enumerate(dbiter): cracker.enqueue(results) perfcounter += len(results) self.tell("Tried %i PMKs so far (%.1f%%); " \ "%i PMKs per second.\r" % (perfcounter.total, 100.0 * (idx + 1) / len(storage.passwords), perfcounter.avg), end=None, sep=None) if cracker.solution: break self.tell('') if cracker.solution is not None: self.tell("\nThe password is '%s'.\n" % cracker.solution) if outfile is not None: with cpyrit.util.FileWrapper(outfile, 'w') as writer: writer.write(cracker.solution) break else: errmsg = "\nPassword was not found." if len(auths) > 1 and all_handshakes is False: errmsg += " Retry the attack with '--all-handshakes'.\n" else: errmsg += "\n" raise PyritRuntimeError(errmsg) attack_batch.cli_options = (('-r', '-u'), ('-e', '-b', '-o', \ '--all-handshakes')) @requires_pckttools() def attack_db(self, storage, capturefile, essid=None, bssid=None, \ outfile=None, all_handshakes=False): """Attack a handshake with PMKs from the db Attack an EAPOL-handshake found in the packet-capture file(s) given by the option -r using the Pairwise Master Keys stored in the database. The options -b and -e can be used to specify the Access-Point to attack; it is picked automatically if both options are omitted. The password is written to the filename given by the option -o if specified. For example: pyrit -r test.pcap -e MyOtherNetwork attack_db Only Pairwise Master Keys that have been computed previously and are stored in the database are used by attack_db. """ ap = self._fuzzyGetAP(self._getParser(capturefile), bssid, essid) if not ap.isCompleted(): raise PyritRuntimeError("No valid handshakes for AccessPoint " \ "%s found in the capture file." % ap) if essid is None: essid = ap.essid if essid not in storage.essids: raise PyritRuntimeError("The ESSID '%s' can't be found in the " \ "database." % essid) WUcount = storage.essids.keycount(essid) perfcounter = cpyrit.util.PerformanceCounter() auths = ap.getCompletedAuthentications() if all_handshakes: self.tell("Attacking %i handshake(s)." % (len(auths),)) for auth in auths if all_handshakes else auths[:1]: with cpyrit.pckttools.EAPOLCracker(auth) as cracker: self.tell("Attacking handshake with " \ "Station %s..." % auth.station) for idx, results in enumerate(cpyrit.cpyrit.StorageIterator( storage, essid, yieldNewResults=False)): cracker.enqueue(results) perfcounter += len(results) self.tell("Tried %i PMKs so far (%.1f%%); " \ "%i PMKs per second.\r" % (perfcounter.total, 100.0 * (idx + 1) / WUcount, perfcounter.avg), end=None, sep=None) if cracker.solution is not None: break self.tell('') if cracker.solution is not None: self.tell("\nThe password is '%s'.\n" % cracker.solution) if outfile is not None: with cpyrit.util.FileWrapper(outfile, 'w') as writer: writer.write(cracker.solution) break else: errmsg = "\nPassword was not found." if len(auths) > 1 and all_handshakes is False: errmsg += " Retry the attack with '--all-handshakes'.\n" else: errmsg += "\n" raise PyritRuntimeError(errmsg) attack_db.cli_options = (('-r', '-u'), ('-e', '-b', '-o', \ '--all-handshakes')) @requires_pckttools() def attack_cowpatty(self, capturefile, infile, essid=None, bssid=None,\ outfile=None, all_handshakes=False): """Attack a handshake with PMKs from a cowpatty-file Attack an EAPOL-handshake found in the packet-capture file(s) given by the option -r using Pairwise Master Keys from a cowpatty-like file (e.g. generated by genpmk/export_cowpatty) given by the option -i. The options -b and -e can be used to specify the Access-Point to attack; it is picked automatically if both options are omitted. The password is written to the filename given by the option -o if specified. The cowpatty-file may be gzip-compressed and must match the chosen ESSID. For example: pyrit -r MyESSID.pcap -e MyESSID -i MyESSID.cow.gz attack_cowpatty Pyrit's own database is not touched by attack_cowpatty """ with cpyrit.util.CowpattyFile(infile) as cowreader: if essid is None: essid = cowreader.essid ap = self._fuzzyGetAP(self._getParser(capturefile), bssid, essid) if not ap.isCompleted(): raise PyritRuntimeError("No valid handshakes for " \ "AccessPoint %s found in the " \ "capture file." % ap) if essid is None: essid = ap.essid if essid != cowreader.essid: raise PyritRuntimeError("Chosen ESSID '%s' and file's ESSID " \ "'%s' do not match" % \ (essid, cowreader.essid)) perfcounter = cpyrit.util.PerformanceCounter() auths = ap.getCompletedAuthentications() crackers = [] if not all_handshakes: crackers.append(cpyrit.pckttools.EAPOLCracker(auths[0])) else: self.tell("Attacking %i handshake(s)." % (len(auths),)) for auth in auths: crackers.append(cpyrit.pckttools.EAPOLCracker(auth)) for results in cowreader: for cracker in crackers: cracker.enqueue(results) perfcounter.addAbsolutePoint(len(crackers[0])) self.tell("Tried %i PMKs so far; %i PMKs per second.\r" % \ (perfcounter.total, perfcounter.avg), end=None, sep=None) if any(cracker.solution is not None for cracker in crackers): break crackers[0].join() perfcounter.addAbsolutePoint(len(crackers[0])) self.tell("Tried %i PMKs so far; %i PMKs per second." % \ (perfcounter.total, perfcounter.avg)) for cracker in crackers: cracker.join() if cracker.solution is not None: self.tell("\nThe password is '%s'.\n" % cracker.solution) if outfile is not None: with cpyrit.util.FileWrapper(outfile, 'w') as writer: writer.write(cracker.solution) break else: errmsg = "\nPassword was not found." if len(auths) > 1 and all_handshakes is False: errmsg += " Retry the attack with '--all-handshakes'.\n" else: errmsg += "\n" raise PyritRuntimeError(errmsg) attack_cowpatty.cli_options = (('-r', '-i'), ('-e', '-b', '-o', \ '--all-handshakes')) def benchmark(self, timeout=60, calibrate=10): """Determine performance of available cores Determine the peak-performance of the available hardware by computing dummy-results. For example: pyrit benchmark """ with cpyrit.cpyrit.CPyrit() as cp: # 'Burn-in' so that all modules are forced to load and buffers can # calibrate to optimal size self.tell("Calibrating...", end=None) t = time.time() while time.time() - t < calibrate: cp.enqueue('foo', ['barbarbar'] * 500) cp.dequeue(block=False) for r in cp: pass # Minimize scheduling overhead... bsize = max(min(int(cp.getPeakPerformance()), 50000), 500) cp.resetStatistics() cycler = itertools.cycle(('\\|/-')) t = time.time() perfcounter = cpyrit.util.PerformanceCounter(timeout + 5) while time.time() - t < timeout: pws = ["barbarbar%s" % random.random() for i in xrange(bsize)] cp.enqueue('foo', pws) r = cp.dequeue(block=False) if r is not None: perfcounter += len(r) self.tell("\rRunning benchmark (%.1f PMKs/s)... %s" % \ (perfcounter.avg, cycler.next()), end=None) self.tell('') self.tell("\nComputed %.2f PMKs/s total." % perfcounter.avg) for i, core in enumerate(cp.cores): if core.compTime > 0: perf = core.resCount / core.compTime else: perf = 0 if core.callCount > 0 and perf > 0: rtt = (core.resCount / core.callCount) / perf else: rtt = 0 self.tell("#%i: '%s': %.1f PMKs/s (RTT %.1f)" % \ (i + 1, core.name, perf, rtt)) for r in cp: pass benchmark.cli_options = ((), ()) def benchmark_long(self): """Longer and more accurate version of benchmark (~10 minutes)""" self.benchmark(570, 30) benchmark_long.cli_options = ((), ()) def selftest(self, timeout=60): """Test hardware to ensure it computes correct results Run an extensive selftest for about 60 seconds. This test includes the entire scheduling-mechanism and all cores that are listed by list_cores. You can use this function to detect broken hardware-modules or malicious network-clients. For example: pyrit selftest """ with cpyrit.cpyrit.CPyrit() as cp: self.tell("Cores incorporated in the test:") for i, core in enumerate(cp.cores): self.tell("#%i: '%s'" % (i + 1, core)) self.tell("\nRunning selftest...") workunits = [] t = time.time() err = False while time.time() - t < timeout and not err: essid = random.choice(cpyrit.util.PMK_TESTVECTORS.keys()) pws = [] ref = cpyrit.util.PMK_TESTVECTORS[essid].keys() for i in xrange(random.randrange(10, 1000)): pws.append(random.choice(ref)) workunits.append((essid, pws)) cp.enqueue(essid, pws) while True: solvedPMKs = cp.dequeue(block=False) if solvedPMKs is not None: essid, pws = workunits.pop(0) for i, pw in enumerate(pws): ref = cpyrit.util.PMK_TESTVECTORS[essid][pw] if ref != solvedPMKs[i]: err = True break if err or not solvedPMKs: break if not err: for solvedPMKs in cp: essid, pws = workunits.pop(0) for i, pw in enumerate(pws): ref = cpyrit.util.PMK_TESTVECTORS[essid][pw] if ref != solvedPMKs[i]: err = True break if err or len(workunits) != 0 or len(cp) != 0: raise PyritRuntimeError("\n!!! WARNING !!!\nAt least some " \ "results seem to be invalid. This " \ "may be caused by a bug in Pyrit, " \ "faulty hardware or malicious " \ "network clients. Do not trust " \ "this installation...\n") else: self.tell("\nAll results verified. Your installation seems OK") selftest.cli_options = ((), ()) def verify(self, storage, essid=None): """Verify 10% of the results by recomputation Randomly pick 10% of the results stored in the database and verify their value by recomputation. You need this function if you suspect broken hardware or malicious network-clients. For example: pyrit -e NETGEAR verify """ with cpyrit.cpyrit.CPyrit() as cp: if essid is not None: if essid not in storage.essids: raise PyritRuntimeError("The ESSID '%s' is not found in " \ "the repository" % (essid,)) else: essids = [essid] else: essids = storage.essids err = False perfcounter = cpyrit.util.PerformanceCounter() workunits = [] for essid in essids: self.tell("Verifying ESSID '%s'" % essid) for key, results in storage.essids.iteritems(essid): sample = random.sample(results, int(len(results) * 0.1)) if len(sample) > 0: pws, pmks = zip(*sample) workunits.append((essid, key, tuple(pmks))) cp.enqueue(essid, pws) solvedPMKs = cp.dequeue(block=False) if solvedPMKs is not None: perfcounter += len(solvedPMKs) testedEssid, testedKey, testedPMKs = \ workunits.pop(0) if testedPMKs != solvedPMKs: self.tell("Workunit %s for ESSID '%s' is " \ "corrupt." % (testedKey, \ testedEssid), \ stream=sys.stderr) err = True self.tell("Computed %i PMKs so far; %i PMKs per " \ "second.\r" % (perfcounter.total, \ perfcounter.avg), \ end=None, sep=None) for solvedPMKs in cp: perfcounter += len(solvedPMKs) testedEssid, testedKey, testedPMKs = workunits.pop(0) if testedPMKs != solvedPMKs: self.tell("Workunit %s for ESSID '%s' is corrupt." % \ (testedKey, testedEssid), stream=sys.stderr) err = True self.tell("\nVerified %i PMKs with %i PMKs/s." % \ (perfcounter.total, perfcounter.avg)) if err: raise PyritRuntimeError( "\nAt least one workunit-file contains invalid results." \ " There are two options now:\n" \ "* The results on the disk are corrupted or invalid. " \ "You should mistrust the entire repository but at least " \ "delete and recompute the offending ESSIDs.\n" \ "* The result on the disk are correct but your " \ "installation is broken and currently computes invalid " \ "results.\nRun 'selftest' for an extensive self-test " \ "in order to tell the two options apart.") else: self.tell("Everything seems OK.") verify.cli_options = (('-u', ), ('-e', )) def checkdb(self, storage, confirm=True): """Check the database for errors Unpack the entire database and check for errors. This function does not check the value of computed results (see verify). For example: pyrit check_db """ # Check passwords self.tell("Checking workunits...") wu_errors = [] for key in storage.passwords.iterkeys(): try: # Some errors are catched here wu = storage.passwords[key] # Now check what we can... for pw in wu: if len(pw) < 8 or len(pw) > 64: raise cpyrit.storage.StorageError("Invalid password") except cpyrit.storage.StorageError, e: self.tell("Error in workunit %s: %s" % (key, e), \ stream=sys.stderr) wu_errors.append(key) # Check results res_errors = [] for essid in storage.essids: self.tell("Checking results for ESSID '%s'..." % (essid,)) for key in storage.essids.iterkeys(essid): try: if key not in storage.passwords: # A resultset exists that is not referenced by workunit raise cpyrit.storage.StorageError("Reference error") # Some errors are catched here res = storage.essids[essid, key] # Check entries for pw, pmk in res: if len(pw) < 8 or len(pw) > 64: raise cpyrit.storage.StorageError("Invalid " \ "password") if len(pmk) != 32: raise cpyrit.storage.StorageError("Invalid PMK") if key not in wu_errors: # Check that workunit and results match wu = storage.passwords[key] if any(pw not in wu for pw, pmk in res): raise cpyrit.storage.StorageError("Password not" \ " in workunit") res_passwords = set(pw for pw, pmk in res) if any(pw not in res_passwords for pw in wu): raise cpyrit.storage.StorageError("Password not" \ " in resultset") except cpyrit.storage.StorageError, e: self.tell("Error in results %s for ESSID '%s':" \ " %s" % (key, essid, e), stream=sys.stderr) if key not in wu_errors: res_errors.append((essid, key)) if len(wu_errors) + len(res_errors) > 0: self.tell("\nThere have been %i errors in workunits and %i errors"\ " in resultsets. Your option now is to delete these" \ " entries from the database. Workunits are lost" \ " forever, resultsets can be recomputed." % \ (len(wu_errors), len(res_errors)), end=None) if confirm: self.tell(" Continue? [y/N]", end=None) if sys.stdin.readline().strip() != 'y': raise PyritRuntimeError("aborted.") self.tell("deleting...") # Delete workunits including results for key in wu_errors: del storage[key] # Delete results for essid, key in res_errors: del storage.essids[essid, key] raise PyritRuntimeError("Errors were reported and fixed. You may" \ " run 'checkdb' again to make sure" \ " that everything is working now.") else: self.tell("Everything seems OK.") checkdb.cli_options = (('-u',), ()) commands = {'analyze': analyze, 'attack_batch': attack_batch, 'attack_cowpatty': attack_cowpatty, 'attack_db': attack_db, 'attack_passthrough': attack_passthrough, 'batch': batchprocess, 'benchmark': benchmark, 'benchmark_long': benchmark_long, 'check_db': checkdb, 'create_essid': create_essid, 'delete_essid': delete_essid, 'eval': eval_results, 'export_cowpatty': export_cowpatty, 'export_hashdb': export_hashdb, 'export_passwords': export_passwords, 'help': print_help, 'import_passwords': import_passwords, 'import_unique_passwords': import_unique_passwords, 'list_cores': list_cores, 'list_essids': list_essids, 'passthrough': passthrough, 'relay': relay, 'selftest': selftest, 'serve': serve, 'strip': stripCapture, 'stripLive': stripLive, 'verify': verify} pyrit-0.4.0/cpyrit/0000775000076400007640000000000011526302053014451 5ustar slaveslave00000000000000pyrit-0.4.0/cpyrit/config.py0000664000076400007640000000425211526274651016307 0ustar slaveslave00000000000000# -*- coding: UTF-8 -*- # # Copyright 2008-2011, Lukas Lueg, lukas.lueg@gmail.com # # This file is part of Pyrit. # # Pyrit is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Pyrit is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Pyrit. If not, see . from __future__ import with_statement import os import sys def default_config(): config = {'default_storage': 'file://', \ 'rpc_server': 'false', \ 'rpc_announce': 'true', \ 'rpc_announce_broadcast': 'false', \ 'rpc_knownclients': '', \ 'workunit_size': '75000', \ 'limit_ncpus': 0} return config def read_configfile(filename): config = default_config() with open(filename, 'rb') as f: for line in f: if line.startswith('#') or '=' not in line: continue option, value = map(str.strip, line.split('=', 1)) if option in config: config[option] = value else: print >> sys.stderr, "WARNING: Unknown option '%s' " \ "in configfile '%s'" % (option, filename) return config def write_configfile(config, filename): with open(filename, 'wb') as f: for option, value in sorted(config.items()): f.write("%s = %s\n" % (option, value)) configpath = os.path.expanduser(os.path.join('~', '.pyrit')) default_configfile = os.path.join(configpath, 'config') if os.path.exists(default_configfile): cfg = read_configfile(default_configfile) else: cfg = default_config() if not os.path.exists(configpath): os.makedirs(configpath) write_configfile(cfg, default_configfile) pyrit-0.4.0/cpyrit/storage.py0000664000076400007640000012430711526274651016512 0ustar slaveslave00000000000000# -*- coding: UTF-8 -*- # # Copyright 2008-2011, Lukas Lueg, lukas.lueg@gmail.com # # This file is part of Pyrit. # # Pyrit is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Pyrit is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Pyrit. If not, see . """EssidStore and PasswordStore are the primary storage classes. Details of their implementation are reasonably well hidden behind the concept of key:value interaction. """ from __future__ import with_statement import BaseHTTPServer import hashlib import os import random import re import struct import sys import threading import xmlrpclib import zlib try: import sqlalchemy as sql except ImportError: pass else: from sqlalchemy import orm if hasattr(sql, 'LargeBinary'): UniversalBinary = sql.LargeBinary else: UniversalBinary = sql.Binary import config import util # prevent call to socket.getfqdn def fast_address_string(self): return '%s' % self.client_address[0] BaseHTTPServer.BaseHTTPRequestHandler.address_string = fast_address_string del fast_address_string MAX_WORKUNIT_SIZE = int(config.cfg['workunit_size']) if MAX_WORKUNIT_SIZE < 1 or MAX_WORKUNIT_SIZE > 1000000: raise ValueError("Invalid 'workunit_size' in configuration") URL_GROUPER = re.compile("(?P\w+)://(((?P\w+):?(?P\w+)?@)?(?P.+))?") XMLFAULT = re.compile("\[\w\.]+)'\>:(?P.+)") def handle_xmlfault(*params): """Decorate a function to check for and rebuild storage exceptions from xmlrpclib.Fault """ def check_xmlfault(f): def protected_f(*args, **kwds): try: ret = f(*args, **kwds) except xmlrpclib.Fault, e: # rpc does not know Exceptions so they always come as pure # strings. One way would be to hack into the de-marshalling. # These seems easier and less intrusive. match = XMLFAULT.match(e.faultString) if match is None: raise else: groups = match.groupdict() cls = groups['class'] fault = groups['fault'] if cls == 'cpyrit.storage.DigestError': raise DigestError(fault) elif cls == 'cpyrit.storage.StorageError': raise StorageError(fault) else: raise return ret protected_f.func_name = f.func_name protected_f.__doc__ = f.__doc__ return protected_f return check_xmlfault def pruneURL(url): """Remove user/passwd from a storage-url""" match = URL_GROUPER.match(url) if match is None: return url else: url_parts = match.groupdict() protocol = url_parts['protocol'] if protocol is None: protocol = '' tail = url_parts['tail'] if tail is None: tail = '' return "%s://%s" % (protocol, tail) def getStorage(url): if not '://' in url: raise ValueError("URL must be of form [protocol]://" \ "[connection-string]") protocol, conn = url.split('://', 1) if protocol == 'file': return FSStorage(url) elif protocol == 'http': return RPCStorage(url) elif protocol in ('sqlite', 'mysql', 'postgres', 'postgresql', \ 'oracle', 'mssql', 'firebird'): if 'sqlalchemy' not in sys.modules: raise util.SqlalchemyImportError("SQLAlchemy seems to be " \ "unavailable.") return SQLStorage(url) else: raise RuntimeError("The protocol '%s' is unsupported." % protocol) class StorageError(IOError): pass class DigestError(StorageError): pass class PasswordCollection(object): """An abstract collection of passwords.""" def __init__(self, collection=None): if collection is not None: self.collection = collection def __len__(self): return len(self.collection) def __iter__(self): return self.collection.__iter__() class BasePYR_Buffer(object): """The common parts of the PYRT- and PYR2-binary format.""" pyr_head = '<4sH' pyr_len = struct.calcsize(pyr_head) def __init__(self, essid=None, results=None): self.results = results self.essid = essid self._unpackLock = threading.Lock() def unpack(self, buf): if len(buf) < self.pyr_len: raise StorageError("Buffer too short") self._magic, essidlen = struct.unpack(self.pyr_head, \ buf[:self.pyr_len]) if self._magic == 'PYR2': self._delimiter = '\n' elif self._magic == 'PYRT': self._delimiter = '\00' else: raise StorageError("Not a PYRT- or PYR2-buffer.") headfmt = "<%ssi16s" % (essidlen, ) headsize = struct.calcsize(headfmt) header = buf[self.pyr_len:self.pyr_len + headsize] if len(header) != headsize: raise StorageError("Invalid header size") header = struct.unpack(headfmt, header) self.essid, self._numElems, self._digest = header pmkoffset = self.pyr_len + headsize pwoffset = pmkoffset + self._numElems * 32 self._pwbuffer = buf[pwoffset:] self._pmkbuffer = buf[pmkoffset:pwoffset] if len(self._pmkbuffer) % 32 != 0: raise StorageError("pmkbuffer seems truncated") def _unpack(self): with self._unpackLock: if hasattr(self, '_pwbuffer'): pwbuffer = zlib.decompress(self._pwbuffer) pwbuffer = pwbuffer.split(self._delimiter) assert len(pwbuffer) == self._numElems md = hashlib.md5() md.update(self.essid) if self._magic == 'PYR2': md.update(self._pmkbuffer) md.update(self._pwbuffer) else: md.update(self._pmkbuffer) md.update(''.join(pwbuffer)) if md.digest() != self._digest: raise DigestError("Digest check failed") self.results = zip(pwbuffer, util.grouper(self._pmkbuffer, 32)) assert len(self.results) == self._numElems del self._pwbuffer del self._digest del self._magic del self._delimiter def __getitem__(self, idx): self._unpack() try: return self.results[idx] except IndexError: raise IndexError("Instance has %i results, index %i invalid." % \ (len(self.results), idx)) def __len__(self): if not hasattr(self, '_numElems'): return len(self.results) else: return self._numElems def __iter__(self): self._unpack() return self.results.__iter__() def getpmkbuffer(self): return buffer(self._pmkbuffer) class PYRT_Buffer(BasePYR_Buffer): pass class PYR2_Buffer(BasePYR_Buffer): def pack(self): pws, pmks = zip(*self.results) pwbuffer = zlib.compress('\n'.join(pws), 1) pmkbuffer = ''.join(pmks) md = hashlib.md5() md.update(self.essid) md.update(pmkbuffer) md.update(pwbuffer) essidlen = len(self.essid) b = struct.pack('<4sH%ssi%ss' % (essidlen, md.digest_size), 'PYR2', \ essidlen, self.essid, len(pws), md.digest()) return b + pmkbuffer + pwbuffer class PAWD_Buffer(PasswordCollection): def unpack(self, buf): if buf[:4] != "PAWD": raise StorageError("Not a PAWD-buffer.") md = hashlib.md5() inp = tuple(buf[4 + md.digest_size:].split('\00')) md.update(''.join(inp)) if buf[4:4 + md.digest_size] != md.digest(): raise DigestError("Digest check failed.") self.collection = inp self.key = md.hexdigest() class PAW2_Buffer(PasswordCollection): def pack(self): b = zlib.compress('\n'.join(self.collection), 1) md = hashlib.md5(b) self.key = md.hexdigest() return (md.hexdigest(), 'PAW2' + md.digest() + b) def unpack(self, buf): if buf[:4] != "PAW2": raise StorageError("Not a PAW2-buffer.") md = hashlib.md5() md.update(buf[4 + md.digest_size:]) if md.digest() != buf[4:4 + md.digest_size]: raise DigestError("Digest check failed") inp = tuple(zlib.decompress(buf[4 + md.digest_size:]).split('\n')) self.collection = inp self.key = md.hexdigest() class ESSIDStore(object): """Storage-class responsible for ESSID and PMKs. Callers can use the iterator to cycle over available ESSIDs. Results are indexed by keys and returned as iterables of tuples. The keys may be received from .iterkeys() or from PasswordStore. """ def iterresults(self, essid): """Iterate over all results currently stored for the given ESSID.""" for key in self.iterkeys(essid): yield self[essid, key] def iteritems(self, essid): """Iterate over all keys and results currently stored for the given ESSID. """ for key in self.iterkeys(essid): yield (key, self[essid, key]) class PasswordStore(object): """Storage-class responsible for passwords. Passwords are indexed by keys and are returned as iterables. The iterator cycles over all available keys. """ h1_list = ["%02.2X" % i for i in xrange(256)] del i def __init__(self): self.pwbuffer = {} self.unique_check = True def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): if exc_type is None: self.flush_buffer() return False def flush_buffer(self): """Flush all passwords currently buffered to the storage. For efficiency reasons this function should not be called if the caller wants to add more passwords in the foreseeable future. """ for pw_h1, pw_bucket in self.pwbuffer.iteritems(): self._flush_bucket(pw_h1, pw_bucket) self.pwbuffer[pw_h1] = (set if self.unique_check else list)() def store_password(self, passwd): """Add the given password to storage. Passwords passed to this function are buffered in memory for better performance and efficiency. It is the caller's responsibility to call .flush_buffer() (or use the context-manager) when he is done. """ passwd = passwd.strip('\r\n') if len(passwd) < 8 or len(passwd) > 63: return pw_h1 = PasswordStore.h1_list[hash(passwd) & 0xFF] if self.unique_check: pw_bucket = self.pwbuffer.setdefault(pw_h1, set()) pw_bucket.add(passwd) else: pw_bucket = self.pwbuffer.setdefault(pw_h1, list()) pw_bucket.append(passwd) if len(pw_bucket) >= MAX_WORKUNIT_SIZE: self._flush_bucket(pw_h1, pw_bucket) self.pwbuffer[pw_h1] = (set if self.unique_check else list)() def iterkeys(self): """Equivalent to self.__iter__""" return self.__iter__() def iterpasswords(self): """Iterate over all available passwords-sets.""" for key in self: yield self[key] def iteritems(self): """Iterate over all keys and password-sets.""" for key in self: yield (key, self[key]) class Storage(object): def __delitem__(self, key): for essid in self.essids: if self.essids.containskey(essid, key): del self.essids[essid, key] del self.passwords[key] def iterresults(self, essid): return self.essids.iterresults(essid) def iterpasswords(self): return self.passwords.iterpasswords() class FSStorage(Storage): """Storage-class that uses the filesystem Connection strings must be in the form of file://... The special character '~' is automatically expanded to the user's home directory. """ def __init__(self, url): if not url.startswith('file://'): raise ValueError("Connection-string must be of form 'file://'") path = url.split('file://')[1] if path == '': path = os.path.join('~', '.pyrit', 'blobspace') path = os.path.expanduser(path) self.essids = FSEssidStore(os.path.join(path, 'essid')) self.passwords = FSPasswordStore(os.path.join(path, 'password')) def iterresults(self, essid): return self.essids.iterresults(essid) def iterpasswords(self): return self.passwords.iterpasswords() def getStats(self): essid_results = dict.fromkeys(self.essids, 0) pwcount = 0 for i, key in enumerate(self.passwords): pwsize = self.passwords.size(key) pwcount += pwsize for essid in essid_results: if self.essids.containskey(essid, key): essid_results[essid] += pwsize return (pwcount, essid_results) class FSEssidStore(ESSIDStore): def __init__(self, basepath): ESSIDStore.__init__(self) self.basepath = basepath if not os.path.exists(self.basepath): os.makedirs(self.basepath) self.essids = {} for essid_hash in os.listdir(self.basepath): essidpath = os.path.join(self.basepath, essid_hash) with open(os.path.join(essidpath, 'essid'), 'rb') as f: essid = f.read() if essid_hash == hashlib.md5(essid).hexdigest()[:8]: self.essids[essid] = (essidpath, {}) for pyrfile in os.listdir(essidpath): if pyrfile.endswith('.pyr'): self.essids[essid][1][pyrfile[:len(pyrfile) - 4]] = \ os.path.join(essidpath, pyrfile) else: print >>sys.stderr, "ESSID %s is corrupted." % essid_hash def __getitem__(self, (essid, key)): """Receive a iterable of (password,PMK)-tuples stored under the given ESSID and key. Returns a empty iterable if the key is not stored. Raises KeyError if the ESSID is not stored. """ try: fname = self.essids[essid][1][key] except IndexError: raise IndexError("No result for ESSID:Key (%s:%s)" % (essid, key)) else: with open(fname, 'rb') as f: buf = f.read() if buf.startswith('PYR2'): results = PYR2_Buffer() elif buf.startswith('PYRT'): results = PYRT_Buffer() else: raise StorageError("Header for '%s' unknown." % (fname,)) results.unpack(buf) if results.essid != essid: raise StorageError("Invalid ESSID in result-collection") return results def __setitem__(self, (essid, key), results): """Store a iterable of (password,PMK)-tuples under the given ESSID and key. """ if essid not in self.essids: raise KeyError("ESSID not in store.") filename = os.path.join(self.essids[essid][0], key) + '.pyr' with open(filename, 'wb') as f: f.write(PYR2_Buffer(essid, results).pack()) self.essids[essid][1][key] = filename def __len__(self): """Return the number of ESSIDs currently stored.""" return len(self.essids) def __iter__(self): """Iterate over all essids currently stored.""" return self.essids.__iter__() def __contains__(self, essid): """Return True if the given ESSID is currently stored.""" return essid in self.essids def __delitem__(self, (essid, key)): """Delete the given ESSID:key resultset or the entire ESSID and all results from the storage. """ if essid not in self: raise KeyError("ESSID not in storage") if key is not None: if key not in self.essids[essid][1]: raise KeyError("Key not in storage") essid_root, pyrfiles = self.essids[essid] fname = pyrfiles.pop(key) os.unlink(fname) else: essid_root, pyrfiles = self.essids[essid] del self.essids[essid] for fname in pyrfiles.itervalues(): os.unlink(fname) os.unlink(os.path.join(essid_root, 'essid')) os.rmdir(essid_root) def containskey(self, essid, key): """Return True if the given (ESSID,key) combination is stored.""" if essid not in self.essids: raise KeyError("ESSID not in store.") return key in self.essids[essid][1] def keycount(self, essid): """Returns the number of keys that can currently be used to receive results for the given ESSID. """ if essid not in self.essids: raise KeyError("ESSID not in store.") return len(self.essids[essid][1]) def iterkeys(self, essid): """Iterate over all keys that can be used to receive results.""" if essid not in self.essids: raise KeyError("ESSID not in store.") return tuple(self.essids[essid][1]).__iter__() def create_essid(self, essid): """Create the given ESSID in the storage. Re-creating a ESSID is a no-op. """ if len(essid) < 1 or len(essid) > 32: raise ValueError("ESSID '%s' invalid." % (essid, )) root = os.path.join(self.basepath, hashlib.md5(essid).hexdigest()[:8]) if not os.path.exists(root): os.makedirs(root) with open(os.path.join(root, 'essid'), 'wb') as f: f.write(essid) self.essids[essid] = (root, {}) class FSPasswordStore(PasswordStore): def __init__(self, basepath): PasswordStore.__init__(self) self.basepath = basepath if not os.path.exists(self.basepath): os.makedirs(self.basepath) self.pwfiles = {} for pw_h1 in os.listdir(self.basepath): if pw_h1 not in PasswordStore.h1_list: continue pwpath = os.path.join(self.basepath, pw_h1) for pwfile in os.listdir(pwpath): if pwfile[-3:] != '.pw': continue self.pwfiles[pwfile[:len(pwfile) - 3]] = pwpath def __contains__(self, key): """Return True if the given key is currently in the storage.""" return key in self.pwfiles def __iter__(self): """Iterate over all keys that can be used to receive password-sets.""" return self.pwfiles.keys().__iter__() def __len__(self): """Return the number of keys that can be used to receive password-sets. """ return len(self.pwfiles) def __getitem__(self, key): """Return the collection of passwords indexed by the given key.""" filename = os.path.join(self.pwfiles[key], key) + '.pw' with open(filename, 'rb') as f: buf = f.read() if buf[:4] == "PAW2": inp = PAW2_Buffer() elif buf[:4] == "PAWD": inp = PAWD_Buffer() else: raise StorageError("'%s' is not a PasswordFile." % filename) inp.unpack(buf) if inp.key != key: raise StorageError("File doesn't match the key '%s'." % inp.key) return inp def __delitem__(self, key): """Delete workunit from storage""" path = self.pwfiles.pop(key) filename = os.path.join(path, key) + '.pw' os.unlink(filename) def size(self, key): """Return the number of passwords indexed by the given key.""" return len(self[key]) def _flush_bucket(self, pw_h1, bucket): if len(bucket) == 0: return if self.unique_check: for key, pwpath in self.pwfiles.iteritems(): if pwpath.endswith(pw_h1): bucket.difference_update(self[key]) if len(bucket) == 0: return pwpath = os.path.join(self.basepath, pw_h1) if not os.path.exists(pwpath): os.makedirs(pwpath) key, b = PAW2_Buffer(bucket).pack() with open(os.path.join(pwpath, key) + '.pw', 'wb') as f: f.write(b) self.pwfiles[key] = pwpath class RPCStorage(Storage): def __init__(self, url): self.cli = xmlrpclib.ServerProxy(url) self.essids = RPCESSIDStore(self.cli) self.passwords = RPCPasswordStore(self.cli) @handle_xmlfault() def getStats(self): return self.cli.getStats() class RPCESSIDStore(ESSIDStore): def __init__(self, cli): self.cli = cli @handle_xmlfault() def __getitem__(self, (essid, key)): """Receive a iterable of (password,PMK)-tuples stored under the given ESSID and key. Returns a empty iterable if the key is not stored. Raises KeyError if the ESSID is not stored. """ buf = self.cli.essids.getitem(essid, key) if buf: results = PYR2_Buffer() results.unpack(buf.data) if results.essid != essid: raise StorageError("Invalid ESSID in result-collection") return results else: raise KeyError @handle_xmlfault() def __setitem__(self, (essid, key), results): """Store a iterable of (password,PMK)-tuples under the given ESSID and key. """ b = xmlrpclib.Binary(PYR2_Buffer(essid, results).pack()) self.cli.essids.setitem(essid, key, b) @handle_xmlfault() def __len__(self): """Return the number of ESSIDs currently stored.""" return self.cli.essids.len() @handle_xmlfault() def __iter__(self): """Iterate over all essids currently stored.""" return self.cli.essids.essids().__iter__() @handle_xmlfault() def __contains__(self, essid): """Return True if the given ESSID is currently stored.""" return self.cli.essids.contains(essid) @handle_xmlfault() def __delitem__(self, (essid, key)): """Delete the ESSID:key resultset or the entire ESSID and all results from the storage. """ if key is None: key = '' self.cli.essids.delitem(essid, key) return True @handle_xmlfault() def containskey(self, essid, key): """Return True if the given (ESSID,key) combination is stored.""" return self.cli.essids.containskey(essid, key) @handle_xmlfault() def keycount(self, essid): """Returns the number of keys that can currently be used to receive results for the given ESSID. """ return self.cli.essids.keycount(essid) @handle_xmlfault() def iterkeys(self, essid): """Iterate over all keys that can be used to receive results.""" return self.cli.essids.keys(essid).__iter__() @handle_xmlfault() def create_essid(self, essid): """Create the given ESSID in the storage. Re-creating a ESSID is a no-op. """ self.cli.essids.createessid(essid) class RPCPasswordStore(PasswordStore): def __init__(self, cli): PasswordStore.__init__(self) self.cli = cli @handle_xmlfault() def __iter__(self): """Iterate over all keys that can be used to receive password-sets.""" return self.cli.passwords.keys().__iter__() @handle_xmlfault() def __len__(self): """Return the number of keys that can be used to receive password-sets. """ return self.cli.passwords.len() @handle_xmlfault() def __contains__(self, key): """Return True if the given key is currently in the storage.""" return self.cli.passwords.contains(key) @handle_xmlfault() def __getitem__(self, key): """Return the collection of passwords indexed by the given key.""" bucket = PAW2_Buffer() bucket.unpack(self.cli.passwords.getitem(key).data) if bucket.key != key: raise StorageError("File doesn't match the key '%s'." % bucket.key) return bucket @handle_xmlfault() def __delitem__(self, key): return self.cli.passwords.delitem(key) @handle_xmlfault() def size(self, key): """Return the number of passwords indexed by the given key.""" return self.cli.passwords.size(key) @handle_xmlfault() def _flush_bucket(self, pw_h1, bucket): return self.cli.passwords._flush_bucket(pw_h1, tuple(bucket)) class StorageRelay(util.AsyncXMLRPCServer): def __init__(self, storage, iface='', port=17934): util.AsyncXMLRPCServer.__init__(self, (iface, port)) self.methods['getStats'] = self.getStats self.methods['passwords.keys'] = self.passwords_keys self.methods['passwords.len'] = self.passwords_len self.methods['passwords.contains'] = self.passwords_contains self.methods['passwords.getitem'] = self.passwords_getitem self.methods['passwords.delitem'] = self.passwords_delitem self.methods['passwords.size'] = self.passwords_size self.methods['passwords._flush_bucket'] = self.passwords_flush self.methods['essids.essids'] = self.essids_essids self.methods['essids.keys'] = self.essids_keys self.methods['essids.len'] = self.essids_len self.methods['essids.contains'] = self.essids_contains self.methods['essids.getitem'] = self.essids_getitem self.methods['essids.setitem'] = self.essids_setitem self.methods['essids.delitem'] = self.essids_delitem self.methods['essids.keycount'] = self.essids_keycount self.methods['essids.containskey'] = self.essids_containskey self.methods['essids.createessid'] = self.essids_create_essid self.storage = storage self.start() def getStats(self): return self.storage.getStats() def passwords_keys(self): return list(self.storage.passwords) def passwords_len(self): return len(self.storage.passwords) def passwords_contains(self, key): return key in self.storage.passwords def passwords_getitem(self, key): newkey, buf = PAW2_Buffer(self.storage.passwords[key]).pack() return xmlrpclib.Binary(buf) def passwords_delitem(self, key): del self.storage.passwords[key] return True def passwords_size(self, key): return self.storage.passwords.size(key) def passwords_flush(self, pw_h1, bucket): self.storage.passwords._flush_bucket(pw_h1, set(bucket)) return True def essids_essids(self): return list(self.storage.essids) def essids_keys(self, essid): return list(self.storage.essids.iterkeys(essid)) def essids_len(self): return len(self.storage.essids) def essids_contains(self, essid): return essid in self.storage.essids def essids_getitem(self, essid, key): try: results = self.storage.essids[essid, key] except KeyError: return False else: buf = PYR2_Buffer(essid, results).pack() return xmlrpclib.Binary(buf) def essids_setitem(self, essid, key, pyr2_buffer): results = PYR2_Buffer() results.unpack(pyr2_buffer.data) if results.essid != essid: raise ValueError("Invalid ESSID in result-collection") self.storage.essids[essid, key] = results return True def essids_delitem(self, essid, key=None): if key == '': key = None del self.storage.essids[essid, key] return True def essids_keycount(self, essid): return self.storage.essids.keycount(essid) def essids_containskey(self, essid, key): return self.storage.essids.containskey(essid, key) def essids_create_essid(self, essid): self.storage.essids.create_essid(essid) return True if 'sqlalchemy' in sys.modules: metadata = sql.MetaData() essids_table = sql.Table('essids', metadata, \ sql.Column('essid_id', sql.Integer, primary_key=True), sql.Column('essid', UniversalBinary(32), nullable=False), sql.Column('uid', sql.String(32), unique=True, \ nullable=False), \ mysql_engine='InnoDB') passwords_table = sql.Table('passwords', metadata, \ sql.Column('_key', sql.String(32), primary_key=True), sql.Column('h1', sql.String(2), nullable=False, \ index=True), sql.Column('numElems', sql.Integer, nullable=False), sql.Column('collection_buffer', UniversalBinary(2**24-1), \ nullable=False), \ mysql_engine='InnoDB') results_table = sql.Table('results', metadata, \ sql.Column('essid_id', sql.Integer, \ sql.ForeignKey('essids.essid_id'), \ primary_key=True), sql.Column('_key', sql.String(32), \ sql.ForeignKey('passwords._key'), \ primary_key=True, index=True), sql.Column('numElems', sql.Integer, nullable=False), sql.Column('results_buffer', UniversalBinary(2**24 - 1), \ nullable=False), \ mysql_engine='InnoDB') class ESSID_DBObject(object): def __init__(self, essid): if len(essid) < 1 or len(essid) > 32: raise ValueError("ESSID '%s' invalid" % (essid, )) self.essid = essid self.uid = hashlib.md5(essid).hexdigest() def __str__(self): return str(self.essid) class PAW2_DBObject(object): def __init__(self, h1, collection): self.h1 = h1 self.collection = tuple(collection) self.numElems = len(self.collection) key, collection_buffer = PAW2_Buffer(self.collection).pack() self.key, self.collection_buffer = key, collection_buffer def __len__(self): return self.numElems def __iter__(self): if not hasattr(self, 'collection'): self.collection = PAW2_Buffer() self.collection.unpack(self.collection_buffer) assert len(self.collection) == self.numElems return self.collection.__iter__() class PYR2_DBObject(object): def __init__(self, essid_obj, key, results): self.essid = essid_obj self.results = tuple(results) self.key = key self.pack(results) def _unpack(self): if not hasattr(self, 'results'): self.results = PYR2_Buffer() self.results.unpack(self.results_buffer) assert len(self.results) == self.numElems def pack(self, results): self.numElems = len(results) self.results_buffer = PYR2_Buffer(str(self.essid), results).pack() def __len__(self): return self.numElems def __iter__(self): self._unpack() return self.results.__iter__() def __getitem__(self, idx): self._unpack() return self.results[idx] def getpmkbuffer(self): self._unpack() return self.results.getpmkbuffer() orm.mapper(ESSID_DBObject, \ essids_table, \ properties={'results': orm.relation(PYR2_DBObject, \ backref='essid', \ cascade='all,delete,delete-orphan')}) orm.mapper(PAW2_DBObject, \ passwords_table, \ properties={'results': orm.relation(PYR2_DBObject, \ cascade='all,delete,delete-orphan'), '_key': orm.synonym('key', map_column=True)}) orm.mapper(PYR2_DBObject, \ results_table, \ properties={'_key': orm.synonym('key', map_column=True)}) class SessionContext(object): """A wrapper around classes given by sessionmake to add a context-manager. """ def __init__(self, SessionClass): self.session = SessionClass() def __enter__(self): return self.session def __exit__(self, type, value, traceback): if type is not None: self.session.rollback() self.session.close() class SQLStorage(Storage): def __init__(self, url): self.engine = sql.create_engine(url, echo=False) metadata.create_all(self.engine) self.SessionClass = orm.sessionmaker(bind=self.engine) self.essids = SQLEssidStore(self.SessionClass) self.passwords = SQLPasswordStore(self.SessionClass) def getStats(self): with SessionContext(self.SessionClass) as session: q = session.query(sql.func.sum(PAW2_DBObject.numElems)) pwtotal = q.one()[0] pwtotal = 0 if pwtotal is None else int(pwtotal) q = session.query(ESSID_DBObject.essid, sql.func.sum(PAW2_DBObject.numElems)) q = q.outerjoin(PYR2_DBObject).outerjoin(PAW2_DBObject) q = q.group_by(ESSID_DBObject.essid) essid_results = {} for essid, pwcount in q: if pwcount is not None: essid_results[str(essid)] = int(pwcount) else: essid_results[str(essid)] = 0 return (pwtotal, essid_results) class SQLEssidStore(ESSIDStore): def __init__(self, session_class): ESSIDStore.__init__(self) self.SessionClass = session_class def __contains__(self, essid): """Return True if the given ESSID is currently stored.""" with SessionContext(self.SessionClass) as session: q = session.query(ESSID_DBObject) return q.filter(ESSID_DBObject.essid == essid).count() == 1 def __iter__(self): """Iterate over all essids currently stored.""" with SessionContext(self.SessionClass) as session: essids = session.query(ESSID_DBObject.essid) return (str(c[0]) for c in essids) def __len__(self): """Return the number of ESSIDs currently stored.""" with SessionContext(self.SessionClass) as session: return session.query(ESSID_DBObject).count() def __getitem__(self, (essid, key)): """Receive a iterable of (password,PMK)-tuples stored under the given ESSID and key. Returns a empty iterable if the key is not stored. Raises KeyError if the ESSID is not stored. """ with SessionContext(self.SessionClass) as session: q = session.query(PYR2_DBObject).join(ESSID_DBObject) result = q.filter(sql.and_(ESSID_DBObject.essid == essid, \ PYR2_DBObject.key == key)).first() if result is None: raise KeyError("No result for ESSID:Key-combination " \ "(%s:%s)." % (essid, key)) else: return result def __setitem__(self, (essid, key), results): """Store a iterable of (password,PMK)-tuples under the given ESSID and key. """ with SessionContext(self.SessionClass) as session: q = session.query(ESSID_DBObject) essid_obj = q.filter(ESSID_DBObject.essid == essid).one() session.add(PYR2_DBObject(essid_obj, key, results)) try: session.commit() except sql.exc.IntegrityError: # Assume we hit a concurrent insert that causes # a constraint-error on (essid-key). session.rollback() q = session.query(PYR2_DBObject).join(ESSID_DBObject) q = q.filter(sql.and_( \ ESSID_DBObject.essid == essid_obj.essid, \ PYR2_DBObject.key == key)) result_obj = q.one() result_obj.pack(results) session.commit() def __delitem__(self, (essid, key)): """Delete the given ESSID:key resultset or the entire ESSID and all results from the storage. """ with SessionContext(self.SessionClass) as session: essid_query = session.query(ESSID_DBObject) essid_query = essid_query.filter(ESSID_DBObject.essid == essid) essid_obj = essid_query.one() res_query = session.query(PYR2_DBObject) res_query = res_query.filter(PYR2_DBObject.essid == essid_obj) if key is not None: res_query = res_query.filter(PYR2_DBObject.key == key) assert res_query.delete(synchronize_session=False) == 1 else: res_query.delete(synchronize_session=False) assert essid_query.delete(synchronize_session=False) == 1 session.commit() def containskey(self, essid, key): """Return True if the given (ESSID,key) combination is stored.""" with SessionContext(self.SessionClass) as session: q = session.query(PYR2_DBObject).join(ESSID_DBObject) q = q.filter(sql.and_(ESSID_DBObject.essid == essid, \ PYR2_DBObject.key == key)) return q.count() == 1 def iterkeys(self, essid): """Iterate over all keys that can be used to receive results. """ with SessionContext(self.SessionClass) as session: q = session.query(PAW2_DBObject.key) q = q.join(PYR2_DBObject).join(ESSID_DBObject) q = q.filter(ESSID_DBObject.essid == essid) keys = q.all() return (c[0] for c in keys) def keycount(self, essid): """Returns the number of keys that can currently be used to receive results for the given ESSID. """ with SessionContext(self.SessionClass) as session: q = session.query(PAW2_DBObject.key) q = q.join(PYR2_DBObject).join(ESSID_DBObject) q = q.filter(ESSID_DBObject.essid == essid) return q.count() def create_essid(self, essid): """Create the given ESSID in the storage. Re-creating a ESSID is a no-op. """ with SessionContext(self.SessionClass) as session: essid_obj = ESSID_DBObject(essid) session.add(essid_obj) session.commit() class SQLPasswordStore(PasswordStore): def __init__(self, session_class): PasswordStore.__init__(self) self.SessionClass = session_class def __contains__(self, key): """Return True if the given key is currently in the storage.""" with SessionContext(self.SessionClass) as session: q = session.query(PAW2_DBObject) return q.filter(PAW2_DBObject.key == key).count() == 1 def __iter__(self): """Iterate over all keys that can be used to receive password-sets. The order of the keys is randomized on every call to __iter__ """ with SessionContext(self.SessionClass) as session: keys = session.query(PAW2_DBObject.key) keys = [c[0] for c in keys] random.shuffle(keys) return keys.__iter__() def __len__(self): """Return the number of keys that can be used to receive password-sets. """ with SessionContext(self.SessionClass) as session: return session.query(PAW2_DBObject).count() def __getitem__(self, key): """Return the collection of passwords indexed by the given key.""" with SessionContext(self.SessionClass) as session: q = session.query(PAW2_DBObject) return q.filter(PAW2_DBObject.key == key).one() def __delitem__(self, key): """Delete the collection of passwords indexed by the given key.""" with SessionContext(self.SessionClass) as session: q = session.query(PAW2_DBObject) q = q.filter(PAW2_DBObject.key == key) assert q.delete(synchronize_session=False) == 1 session.commit() def size(self, key): """Return the number of passwords indexed by the given key.""" with SessionContext(self.SessionClass) as session: q = session.query(PAW2_DBObject.numElems) return q.filter(PAW2_DBObject.key == key).one()[0] def _flush_bucket(self, pw_h1, bucket): if len(bucket) == 0: return with SessionContext(self.SessionClass) as session: if self.unique_check: q = session.query(PAW2_DBObject) for db_bucket in q.filter(PAW2_DBObject.h1 == pw_h1): bucket.difference_update(db_bucket) if len(bucket) == 0: return session.add(PAW2_DBObject(pw_h1, bucket)) session.commit() pyrit-0.4.0/cpyrit/cpyrit.py0000664000076400007640000007411211526274651016356 0ustar slaveslave00000000000000# -*- coding: UTF-8 -*- # # Copyright 2008-2011, Lukas Lueg, lukas.lueg@gmail.com # # This file is part of Pyrit. # # Pyrit is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Pyrit is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Pyrit. If not, see . """Abstracted hardware-access for Pyrit. Core is a base-class to glue hardware-modules into python. CPUCore, OpenCLCore and NetworkCore are subclasses of Core and provide access to their respective hardware-platforms. CPyrit enumerates the available cores and schedules workunits among them. """ from __future__ import with_statement from collections import deque import BaseHTTPServer import hashlib import random import socket import sys import threading import time import uuid import util import warnings import xmlrpclib import config import network import storage import util import _cpyrit_cpu # prevent call to socket.getfqdn def fast_address_string(self): return '%s' % self.client_address[0] BaseHTTPServer.BaseHTTPRequestHandler.address_string = fast_address_string del fast_address_string def version_check(mod): ver = getattr(mod, "VERSION", "unknown") if ver != _cpyrit_cpu.VERSION: warnings.warn("WARNING: Version mismatch between %s ('%s') and %s " \ "('%s')\n" % (_cpyrit_cpu, _cpyrit_cpu.VERSION, mod, ver)) class Core(util.Thread): """The class Core provides basic scheduling and testing. It should not be used directly but through sub-classes. Subclasses must mix-in a .solve()-function and should set the .buffersize-, .minBufferSize- and .maxBufferSize-attributes. The default .run() provided here calibrates itself to pull work from the queue worth 3 seconds of execution time in .solve() """ TV_ESSID = 'foo' TV_PW = 'barbarbar' TV_PMK = ''.join(map(chr, (6, 56, 101, 54, 204, 94, 253, 3, 243, 250, 132, 170, 142, 162, 204, 132, 8, 151, 61, 243, 75, 216, 75, 83, 128, 110, 237, 48, 35, 205, 166, 126))) def __init__(self, queue): """Create a new Core that pulls work from the given CPyrit instance.""" util.Thread.__init__(self) self.queue = queue self.compTime = self.resCount = self.callCount = 0 self.isTested = False self.shallStop = False self.buffersize = 4096 """Number of passwords currently pulled by calls to _gather() This number is dynamically adapted in run() but limited by .minBufferSize and .maxBufferSize. """ self.minBufferSize = 128 """Min. number of passwords that get pulled in each call to _gather.""" self.maxBufferSize = 20480 """Max. number of passwords that get pulled in each call to _gather.""" self.setDaemon(True) def _testComputeFunction(self, i): if any((pmk != Core.TV_PMK for pmk in \ self.solve(Core.TV_ESSID, [Core.TV_PW] * i))): raise ValueError("Test-vector does not result in correct PMK.") def resetStatistics(self): self.compTime = self.resCount = self.callCount = 0 def run(self): while not self.shallStop: essid, pwlist = self.queue._gather(self.buffersize, timeout=0.5) if essid is not None: if not self.isTested: self._testComputeFunction(101) self.isTested = True t = time.time() res = self.solve(essid, pwlist) assert len(res) == len(pwlist) self.compTime += time.time() - t self.resCount += len(res) self.callCount += 1 if self.compTime > 0: # carefully move towards three seconds of execution-time avg = (2 * self.buffersize + \ (self.resCount / self.compTime * 3)) / 3 self.buffersize = int(max(self.minBufferSize, min(self.maxBufferSize, avg))) self.queue._scatter(essid, pwlist, res) def __str__(self): return self.name class CPUCore(Core, _cpyrit_cpu.CPUDevice): """Standard-CPU implementation. The underlying C-code may use VIA Padlock, SSE2 or a generic OpenSSL-interface to compute results.""" def __init__(self, queue): Core.__init__(self, queue) _cpyrit_cpu.CPUDevice.__init__(self) self.buffersize = 512 self.name = "CPU-Core (%s)" % _cpyrit_cpu.getPlatform() self.start() class LowLatencyCore(Core): def __init__(self, queue): Core.__init__(self, queue) self.bufferSizeDiv = 0 def _getTestData(self, i): return (Core.TV_ESSID, [Core.TV_PW] * i) def _testData(self, res): if any((pmk != Core.TV_PMK for pmk in res)): raise ValueError("Test-vector does not result in correct PMK.") def _processData(self, essid, pwlist, res, tm): assert(len(res) == len(pwlist)) t = time.time() self.compTime += t - tm self.resCount += len(res) self.callCount += 1 avg = (2 * self.buffersize + (self.resCount / self.compTime)) / 3 if self.bufferSizeDiv > 0: avg = self.bufferSizeDiv * int((avg + self.bufferSizeDiv - 1) \ / self.bufferSizeDiv) self.buffersize = int(max(self.minBufferSize, min(self.maxBufferSize, avg))) self.queue._scatter(essid, pwlist, res) return t def solve(self, essid, pwlist): enq = self.send(essid, pwlist) assert(enq) return self.receive(True) def run(self): work_queue = deque() work_available = False t = time.time() while not self.shallStop: if not work_available: if not self.isTested: essid, pwlist = self._getTestData(101) else: essid, pwlist = self.queue._gather(self.buffersize, \ timeout=0.5) if essid is not None: work_queue.append((essid, pwlist, not self.isTested)) self.isTested = True work_available = True if len(work_queue) == 1: t = time.time() if len(work_queue) <= 0: continue if work_available: essid, pwlist, testing = work_queue[-1] work_available = not self.send(essid, pwlist) res = self.receive(work_available) if res is not None: essid, pwlist, testing = work_queue.popleft() if not testing: t = self._processData(essid, pwlist, res, t) else: self._testData(res) try: import _cpyrit_opencl except ImportError: pass except Exception, e: print >> sys.stderr, "Failed to load Pyrit's OpenCL-core ('%s')." % e else: version_check(_cpyrit_opencl) class OpenCLCore(Core, _cpyrit_opencl.OpenCLDevice): """Computes results on OpenCL-capable devices.""" def __init__(self, queue, platform_idx, dev_idx): Core.__init__(self, queue) _cpyrit_opencl.OpenCLDevice.__init__(self, platform_idx, dev_idx) self.name = "OpenCL-Device '%s'" % self.deviceName self.minBufferSize = 1024 self.buffersize = 4096 maxhwsize = reduce(lambda x, y: x * y, self.maxWorkSizes) self.maxBufferSize = min(180224, maxhwsize) self.start() try: import _cpyrit_cuda except ImportError: pass except Exception, e: print >> sys.stderr, "Failed to load Pyrit's CUDA-core ('%s')." % e else: version_check(_cpyrit_cuda) class CUDACore(Core, _cpyrit_cuda.CUDADevice): """Computes results on Nvidia-CUDA capable devices.""" def __init__(self, queue, dev_idx): Core.__init__(self, queue) _cpyrit_cuda.CUDADevice.__init__(self, dev_idx) self.name = "CUDA-Device #%i '%s'" % (dev_idx + 1, self.deviceName) self.minBufferSize = 1024 self.buffersize = 4096 self.maxBufferSize = 131072 self.start() try: import _cpyrit_calpp except ImportError: pass except Exception, e: print >> sys.stderr, "Failed to load Pyrit's CAL-core ('%s')." % e else: version_check(_cpyrit_calpp) class CALCore(LowLatencyCore, _cpyrit_calpp.CALDevice): """Computes results on ATI CAL capable devices.""" def __init__(self, queue, dev_idx): LowLatencyCore.__init__(self, queue) _cpyrit_calpp.CALDevice.__init__(self, dev_idx) self.name = "CAL++ Device #%i '%s'" % \ (dev_idx + 1, self.deviceName) self.minBufferSize, self.buffersize, self.maxBufferSize, \ self.bufferSizeDiv = self.workSizes() self.start() try: import _cpyrit_null except ImportError: pass else: class NullCore(Core, _cpyrit_null.NullDevice): """Dummy-Device that returns zero'ed results instead of PMKs. For testing and demonstration only... """ def __init__(self, queue): raise RuntimeError("The Null-Core should never get initialized!") Core.__init__(self, queue) _cpyrit_null.NullDevice.__init__(self) self.name = "Null-Core" self.start() class NetworkCore(util.AsyncXMLRPCServer, Core): class NetworkObserver(util.Thread): def __init__(self, core): util.Thread.__init__(self) self.core = core self.setDaemon(True) self.start() def run(self): while True: for uuid, client in self.core.clients.items(): if time.time() - client.lastseen > 15.0: self.core.rpc_unregister(uuid) time.sleep(3) class NetworkClient(object): def __init__(self, known_uuids): self.uuid = str(uuid.uuid4()) self.known_uuids = known_uuids self.lastseen = time.time() self.workunits = [] def ping(self): self.lastseen = time.time() def __init__(self, queue, host='', port=17935): util.AsyncXMLRPCServer.__init__(self, (host, port)) Core.__init__(self, queue) self.name = "Network-Clients" self.uuid = str(uuid.uuid4()) self.methods['register'] = self.rpc_register self.methods['unregister'] = self.rpc_unregister self.methods['gather'] = self.rpc_gather self.methods['scatter'] = self.rpc_scatter self.methods['revoke'] = self.rpc_revoke self.client_lock = threading.Lock() self.clients = {} self.host = host self.port = port self.observer = self.NetworkObserver(self) self.startTime = time.time() self.start() def _get_client(self, uuid): with self.client_lock: if uuid in self.clients: client = self.clients[uuid] client.ping() return client else: raise xmlrpclib.Fault(403, "Client unknown or timed-out") def rpc_register(self, uuids): with self.client_lock: known_uuids = set(uuids.split(';')) if self.uuid in known_uuids: return (self.uuid, '') else: client = self.NetworkClient(known_uuids) self.clients[client.uuid] = client return (self.uuid, client.uuid) def rpc_unregister(self, uuid): with self.client_lock: if uuid in self.clients: client = self.clients[uuid] for essid, pwlist in client.workunits: self.queue._revoke(essid, pwlist) del self.clients[uuid] return True else: return False def rpc_gather(self, client_uuid, buffersize): client = self._get_client(client_uuid) essid, pwlist = self.queue._gather(buffersize, block=False) if essid is None: return ('', '') else: client.workunits.append((essid, pwlist)) key, buf = storage.PAW2_Buffer(pwlist).pack() return (essid, xmlrpclib.Binary(buf)) def rpc_scatter(self, client_uuid, encoded_buf): client = self._get_client(client_uuid) essid, pwlist = client.workunits.pop(0) md = hashlib.sha1() digest = encoded_buf.data[:md.digest_size] buf = encoded_buf.data[md.digest_size:] md.update(buf) if md.digest() != digest: raise IOError("Digest check failed.") if len(buf) != len(pwlist) * 32: raise ValueError("Result has invalid size of %i. Expected %i." % \ (len(buf), len(pwlist) * 32)) results = [buf[i * 32:i * 32 + 32] for i in xrange(len(pwlist))] self.compTime = time.time() - self.startTime self.resCount += len(results) self.callCount += 1 self.queue._scatter(essid, pwlist, results) client.ping() return True def rpc_revoke(self, client_uuid): client = self._get_client(client_uuid) essid, passwords = client.workunits.pop() self.queue._revoke(essid, password) client.ping() return True def __iter__(self): with self.client_lock: return self.clients.values().__iter__() class CPyrit(object): """Enumerates and manages all available hardware resources provided in the module and does most of the scheduling-magic. The class provides FIFO-scheduling of workunits towards the 'host' which can use .enqueue() and corresponding calls to .dequeue(). Scheduling towards the hardware is provided by _gather(), _scatter() and _revoke(). """ def __init__(self): """Create a new instance that blocks calls to .enqueue() when more than the given amount of passwords are currently waiting to be scheduled to the hardware. """ self.inqueue = [] self.outqueue = {} self.workunits = [] self.slices = {} self.in_idx = self.out_idx = 0 self.cores = [] self.cv = threading.Condition() ncpus = util.ncpus # CUDA if 'cpyrit._cpyrit_cuda' in sys.modules: for dev_idx, device in enumerate(_cpyrit_cuda.listDevices()): self.cores.append(CUDACore(queue=self, dev_idx=dev_idx)) ncpus -= 1 # OpenCL if 'cpyrit._cpyrit_opencl' in sys.modules: for platform_idx in range(_cpyrit_opencl.numPlatforms): p = _cpyrit_opencl.OpenCLPlatform(platform_idx) for dev_idx in range(p.numDevices): dev = _cpyrit_opencl.OpenCLDevice(platform_idx, dev_idx) if dev.deviceType in ('GPU', 'ACCELERATOR'): core = OpenCLCore(self, platform_idx, dev_idx) self.cores.append(core) ncpus -= 1 # CAL++ if 'cpyrit._cpyrit_calpp' in sys.modules: for dev_idx, device in enumerate(_cpyrit_calpp.listDevices()): self.cores.append(CALCore(queue=self, dev_idx=dev_idx)) ncpus -= 1 #CPUs for i in xrange(ncpus): self.cores.append(CPUCore(queue=self)) #Network if config.cfg['rpc_server'] == 'true': for port in xrange(17935, 18000): try: ncore = NetworkCore(queue=self, port=port) except socket.error: pass else: self.ncore_uuid = ncore.uuid self.cores.append(ncore) if config.cfg['rpc_announce'] == 'true': cl = config.cfg['rpc_knownclients'].split(' ') cl = filter(lambda x: len(x) > 0, map(str.strip, cl)) bcst = config.cfg['rpc_announce_broadcast'] == 'true' self.announcer = network.NetworkAnnouncer(port=port, \ clients=cl, \ broadcast=bcst) break else: self.ncore_uuid = None else: self.ncore_uuid = None def _check_cores(self): for core in self.cores: if not core.shallStop and not core.isAlive(): raise SystemError("The core '%s' has died unexpectedly" % core) def _len(self): return sum((sum((len(pwlist) for pwlist in pwdict.itervalues())) for essid, pwdict in self.inqueue)) def __len__(self): """Return the number of passwords that currently wait to be transfered to the hardware.""" with self.cv: return self._len() def __iter__(self): """Iterates over all pending results. Blocks until no further workunits or results are currently queued. """ while True: r = self.dequeue(block=True) if r is None: break yield r def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): self.shutdown() def shutdown(self): for core in self.cores: core.shallStop = True for core in self.cores: core.shutdown() def isAlive(self): return all(core.isAlive() for core in self.cores) def waitForSchedule(self, maxBufferSize): """Block until less than the given number of passwords wait for being scheduled to the hardware. """ assert maxBufferSize >= 0 with self.cv: while self._len() > maxBufferSize: self.cv.wait(2) self._check_cores() def resetStatistics(self): """Reset all cores' statistics""" for core in self.cores: core.resetStatistics() def getPeakPerformance(self): """Return the summed peak performance of all cores. The number returned is based on the performance all cores would have with 100% occupancy. The real performance is lower if the caller fails to keep the pipeline filled. """ return sum([c.resCount / c.compTime for c in self.cores if c.compTime]) def enqueue(self, essid, passwords, block=True): """Enqueue the given ESSID and iterable of passwords for processing. The call may block if block is True and the number of passwords currently waiting for being scheduled to the hardware is higher than five times the current peak performance. Calls to .dequeue() correspond in a FIFO-manner. """ with self.cv: if self._len() > 0: while self.getPeakPerformance() == 0 \ or self._len() > self.getPeakPerformance() * 5: self.cv.wait(2) self._check_cores() passwordlist = list(passwords) if len(self.inqueue) > 0 and self.inqueue[-1][0] == essid: self.inqueue[-1][1][self.in_idx] = passwordlist else: self.inqueue.append((essid, {self.in_idx: passwordlist})) self.workunits.append(len(passwordlist)) self.in_idx += len(passwordlist) self.cv.notifyAll() def dequeue(self, block=True, timeout=None): """Receive the results corresponding to a previous call to .enqueue(). The function returns None if block is False and the respective results have not yet been completed. Otherwise the call blocks. The function may return None if block is True and the call waited longer than timeout. Calls to .enqueue() correspond in a FIFO-manner. """ t = time.time() with self.cv: if len(self.workunits) == 0: return None while True: wu_length = self.workunits[0] if self.out_idx not in self.outqueue \ or len(self.outqueue[self.out_idx]) < wu_length: self._check_cores() if block: if timeout: while time.time() - t > timeout: self.cv.wait(0.1) if self.out_idx in self.outqueue and \ len(self.outqueue[self.out_idx]) >= wu_length: break else: return None else: self.cv.wait(3) else: return None else: reslist = self.outqueue[self.out_idx] del self.outqueue[self.out_idx] results = reslist[:wu_length] self.out_idx += wu_length self.outqueue[self.out_idx] = reslist[wu_length:] self.workunits.pop(0) self.cv.notifyAll() return tuple(results) def _gather(self, desired_size, block=True, timeout=None): """Try to accumulate the given number of passwords for a single ESSID in one workunit. Return a tuple containing the ESSID and a tuple of passwords. The call blocks if no work is available and may return less than the desired number of passwords. The caller should compute the corresponding results and call _scatter() or _revoke() with the (ESSID,passwords)-tuple returned by this call as parameters. """ t = time.time() with self.cv: passwords = [] pwslices = [] cur_essid = None restsize = desired_size while True: self._check_cores() for essid, pwdict in self.inqueue: for idx, pwslice in sorted(pwdict.items()): if len(pwslice) > 0: if cur_essid is None: cur_essid = essid elif cur_essid != essid: break newslice = pwslice[:restsize] del pwdict[idx] if len(pwslice[len(newslice):]) > 0: pwdict[idx + len(newslice)] = \ pwslice[len(newslice):] pwslices.append((idx, len(newslice))) passwords.extend(newslice) restsize -= len(newslice) if restsize <= 0: break if len(pwdict) == 0: self.inqueue.remove((essid, pwdict)) if restsize <= 0: break if len(passwords) > 0: wu = (cur_essid, tuple(passwords)) try: self.slices[wu].append(pwslices) except KeyError: self.slices[wu] = [pwslices] self.cv.notifyAll() return wu else: if block: if timeout is not None and time.time() - t > timeout: return None, None else: return None, None self.cv.wait(0.1) def _scatter(self, essid, passwords, results): """Spray the given results back to their corresponding workunits. The caller must use the (ESSID,passwords)-tuple returned by _gather() to indicate which workunit it is returning results for. """ assert len(results) == len(passwords) with self.cv: wu = (essid, passwords) slices = self.slices[wu].pop(0) if len(self.slices[wu]) == 0: del self.slices[wu] ptr = 0 for idx, length in slices: self.outqueue[idx] = list(results[ptr:ptr + length]) ptr += length for idx in sorted(self.outqueue.iterkeys(), reverse=True)[1:]: res = self.outqueue[idx] o_idx = idx + len(res) if o_idx in self.outqueue: res.extend(self.outqueue[o_idx]) del self.outqueue[o_idx] self.cv.notifyAll() def _revoke(self, essid, passwords): """Re-insert the given workunit back into the global queue so it may be processed by other Cores. Should be used if the Core that pulled the workunit is unable to process it. It is the Core's responsibility to ensure that it stops pulling work from the queue in such situations. """ with self.cv: wu = (essid, passwords) slices = self.slices[wu].pop() if len(self.slices[wu]) == 0: del self.slices[wu] passwordlist = list(passwords) if len(self.inqueue) > 0 and self.inqueue[0][0] == essid: d = self.inqueue[0][1] else: d = {} self.inqueue.insert(0, (essid, d)) ptr = 0 for idx, length in slices: d[idx] = passwordlist[ptr:ptr + length] ptr += length self.cv.notifyAll() class StorageIterator(object): """Iterates over the database, computes new Pairwise Master Keys if necessary and requested and yields tuples of (password,PMK)-tuples. """ def __init__(self, storage, essid, \ yieldOldResults=True, yieldNewResults=True): self.cp = CPyrit() if yieldNewResults else None self.workunits = [] self.essid = essid self.storage = storage keys = list(self.storage.passwords) random.shuffle(keys) self.iterkeys = iter(keys) self.yieldOldResults = yieldOldResults self.yieldNewResults = yieldNewResults def keycount(self): return self.storage.essids.keycount(self.essid) def __len__(self): return len(self.storage.passwords) def __iter__(self): return self def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): if self.cp is not None: self.cp.shutdown() def next(self): while True: try: key = self.iterkeys.next() except StopIteration: if self.yieldNewResults: solvedPMKs = self.cp.dequeue(block=True) if solvedPMKs is not None: solvedEssid, solvedKey, solvedPasswords = \ self.workunits.pop(0) solvedResults = zip(solvedPasswords, solvedPMKs) self.storage.essids[solvedEssid, solvedKey] = \ solvedResults return solvedResults assert len(self.workunits) == 0 raise StopIteration else: if self.yieldOldResults: try: results = self.storage.essids[self.essid, key] except KeyError: pass else: return results if self.yieldNewResults: passwords = self.storage.passwords[key] self.workunits.append((self.essid, key, passwords)) self.cp.enqueue(self.essid, passwords) solvedPMKs = self.cp.dequeue(block=False) if solvedPMKs is not None: solvedEssid, solvedKey, solvedPasswords = \ self.workunits.pop(0) solvedResults = zip(solvedPasswords, solvedPMKs) self.storage.essids[solvedEssid, solvedKey] = \ solvedResults return solvedResults class PassthroughIterator(object): """A iterator that takes an ESSID and an iterable of passwords, computes the corresponding Pairwise Master Keys and and yields tuples of (password,PMK)-tuples. """ def __init__(self, essid, iterable, buffersize=20000): self.cp = CPyrit() self.essid = essid self.iterator = iter(iterable) self.workunits = [] self.buffersize = buffersize def __iter__(self): return self def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): self.cp.shutdown() def next(self): pwbuffer = [] for line in self.iterator: pw = line.strip('\r\n')[:63] if len(pw) >= 8: pwbuffer.append(pw) if len(pwbuffer) > self.buffersize: self.workunits.append(pwbuffer) self.cp.enqueue(self.essid, self.workunits[-1]) pwbuffer = [] solvedPMKs = self.cp.dequeue(block=False) if solvedPMKs is not None: return zip(self.workunits.pop(0), solvedPMKs) if len(pwbuffer) > 0: self.workunits.append(pwbuffer) self.cp.enqueue(self.essid, self.workunits[-1]) for solvedPMKs in self.cp: return zip(self.workunits.pop(0), solvedPMKs) raise StopIteration pyrit-0.4.0/cpyrit/network.py0000664000076400007640000002403711526274651016536 0ustar slaveslave00000000000000# -*- coding: UTF-8 -*- # # Copyright 2008-2011, Lukas Lueg, lukas.lueg@gmail.com # # This file is part of Pyrit. # # Pyrit is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Pyrit is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Pyrit. If not, see . from __future__ import with_statement import hashlib import socket import time import threading import xmlrpclib import storage import util class NetworkClient(util.Thread): class NetworkGatherer(threading.Thread): def __init__(self, client): threading.Thread.__init__(self) self.client = client self.server = xmlrpclib.ServerProxy("http://%s:%s" % \ client.srv_addr) self.shallStop = False self.setDaemon(True) self.start() def run(self): while not self.shallStop: #TODO calculate optimal max buffersize try: essid, pwbuffer = \ self.server.gather(self.client.uuid, 5000) except socket.error: break if essid != '' or pwbuffer != '': pwlist = storage.PAW2_Buffer() pwlist.unpack(pwbuffer.data) self.client.enqueue(essid, pwlist) else: time.sleep(1) self.client.ping() def shutdown(self): self.shallStop = True self.join() def __init__(self, srv_addr, enqueue_callback, known_uuids): util.Thread.__init__(self) self.server = xmlrpclib.ServerProxy("http://%s:%s" % srv_addr) self.srv_uuid, self.uuid = self.server.register(";".join(known_uuids)) if not self.uuid: raise KeyError("Loop detected to %s" % self.srv_uuid) self.srv_addr = srv_addr self.enqueue_callback = enqueue_callback self.cv = threading.Condition() self.stat_received = self.stat_enqueued = 0 self.stat_scattered = self.stat_sent = 0 self.results = [] self.lastseen = time.time() self.setDaemon(True) def run(self): self.gatherer = self.NetworkGatherer(self) try: while self.gatherer.isAlive() and self.shallStop is False: with self.cv: while len(self.results) == 0 and self.shallStop is False \ and self.gatherer.isAlive(): self.cv.wait(1) if self.shallStop is not False \ or not self.gatherer.isAlive(): break solvedPMKs = self.results.pop(0) buf = ''.join(solvedPMKs) md = hashlib.sha1() md.update(buf) encoded_buf = xmlrpclib.Binary(md.digest() + buf) self.server.scatter(self.uuid, encoded_buf) self.stat_sent += len(solvedPMKs) self.ping() finally: self.gatherer.shutdown() def enqueue(self, essid, pwlist): self.stat_received += len(pwlist) self.enqueue_callback(self.uuid, (essid, pwlist)) self.stat_enqueued += len(pwlist) def scatter(self, results): with self.cv: self.results.append(results) self.cv.notifyAll() self.stat_scattered += len(results) def ping(self): self.lastseen = time.time() class NetworkServer(util.Thread): def __init__(self): util.Thread.__init__(self) import cpyrit self.cp = cpyrit.CPyrit() self.clients_lock = threading.Lock() self.clients = {} self.pending_clients = [] self.stat_gathered = self.stat_enqueued = 0 self.stat_scattered = 0 self.enqueue_lock = threading.Lock() self.setDaemon(True) self.start() def addClient(self, srv_addr): with self.clients_lock: if any(c.srv_addr == srv_addr for c in self.clients.itervalues()): return known_uuids = set(c.srv_uuid for c in self.clients.itervalues()) if self.cp.ncore_uuid is not None: known_uuids.add(self.cp.ncore_uuid) try: client = NetworkClient(srv_addr, self.enqueue, known_uuids) except KeyError: pass else: client.start() self.clients[client.uuid] = client def enqueue(self, uuid, (essid, pwlist)): with self.clients_lock: if uuid not in self.clients: raise KeyError("Client unknown or timed-out") self.stat_gathered += len(pwlist) with self.enqueue_lock: self.pending_clients.append(uuid) self.cp.enqueue(essid, pwlist) self.stat_enqueued += len(pwlist) def run(self): while not self.shallStop: solvedPMKs = self.cp.dequeue(block=True, timeout=3.0) if solvedPMKs is None: time.sleep(1) else: uuid = self.pending_clients.pop(0) with self.clients_lock: if uuid in self.clients: client = self.clients[uuid] client.scatter(solvedPMKs) self.stat_scattered += len(solvedPMKs) with self.clients_lock: for client in self.clients.values(): if not client.isAlive() or \ time.time() - client.lastseen > 15.0: del self.clients[client.uuid] if not self.cp.isAlive(): self.shallStop == True raise RuntimeError def __contains__(self, srv_addr): with self.clients_lock: i = self.clients.itervalues() return any(c.srv_addr == srv_addr for c in i) def __len__(self): with self.clients_lock: return len(self.clients) def __iter__(self): with self.clients_lock: return self.clients.values().__iter__() def shutdown(self): self.shallStop = True with self.clients_lock: for client in self.clients.itervalues(): client.shutdown() self.join() class NetworkAnnouncer(util.Thread): """Announce the existence of a server via UDP-unicast and -broadcast""" def __init__(self, port=17935, clients=[], broadcast=True): util.Thread.__init__(self) self.port = port self.clients = clients msg = '\x00'.join(["PyritServerAnnouncement", '', str(port)]) md = hashlib.sha1() md.update(msg) self.msg = msg + md.digest() self.ucast_sckt = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) if broadcast: self.bcast_sckt = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.bcast_sckt.bind(('', 0)) self.bcast_sckt.setsockopt(socket.SOL_SOCKET, \ socket.SO_BROADCAST, 1) else: self.bcast_sckt = None self.setDaemon(True) self.start() def run(self): while self.shallStop is False: for client in self.clients: self.ucast_sckt.sendto(self.msg, (client, 17935)) if self.bcast_sckt: self.bcast_sckt.sendto(self.msg, ('', 17935)) time.sleep(1) class NetworkAnnouncementListener(util.Thread): def __init__(self): util.Thread.__init__(self) self.cv = threading.Condition() self.servers = [] self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) self.sock.bind(('', 17935)) self.setDaemon(True) self.start() def run(self): while self.shallStop is False: buf, (host, port) = self.sock.recvfrom(4096) if buf.startswith("PyritServerAnnouncement"): md = hashlib.sha1() msg = buf[:-md.digest_size] md.update(msg) if md.digest() == buf[-md.digest_size:]: msg_ann, msg_host, msg_port = msg.split('\x00') if msg_host == '': addr = (host, msg_port) else: addr = (msg_host, msg_port) with self.cv: if addr not in self.servers: self.servers.append(addr) self.cv.notifyAll() def waitForAnnouncement(self, block=True, timeout=None): t = time.time() with self.cv: while True: if len(self.servers) == 0: if block: if timeout is not None: d = time.time() - t if d < timeout: self.cv.wait(d) else: return None else: self.cv.wait(1) else: return None else: return self.servers.pop(0) def __iter__(self): return self def next(self): return self.waitForAnnouncement(block=True) def shutdown(self): try: self.sock.shutdown(socket.SHUT_RDWR) except socket.error: pass util.Thread.shutdown(self) pyrit-0.4.0/cpyrit/__init__.py0000664000076400007640000000001511524611424016561 0ustar slaveslave00000000000000__all__ = [] pyrit-0.4.0/cpyrit/_cpyrit_cpu.h0000664000076400007640000000334211526274651017160 0ustar slaveslave00000000000000/* # # Copyright 2008-2011, Lukas Lueg, lukas.lueg@gmail.com # # This file is part of Pyrit. # # Pyrit is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Pyrit is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Pyrit. If not, see . */ #ifdef __i386__ #define COMPILE_PADLOCK #if defined(linux) #define MCTX_EIP(context) ((context)->uc_mcontext.gregs[REG_EIP]) #elif defined(__APPLE__) #ifdef __DARWIN_UNIX03 #define MCTX_EIP(context) (*((unsigned long*)&(context)->uc_mcontext->__ss.__eip)) #else #define MCTX_EIP(context) (*((unsigned long*)&(context)->uc_mcontext->ss.eip)) #endif #define MAP_ANONYMOUS MAP_ANON #else #undef COMPILE_PADLOCK #endif #endif #if (defined(__i386__) || defined(__x86_64__)) #define COMPILE_SSE2 #define PUT_BE(n,b,i) \ { \ (b)[(i) ] = (unsigned char) ( (n) >> 24 ); \ (b)[(i) + 1] = (unsigned char) ( (n) >> 16 ); \ (b)[(i) + 2] = (unsigned char) ( (n) >> 8 ); \ (b)[(i) + 3] = (unsigned char) ( (n) ); \ } #endif #define HMAC_MD5_RC4 0 #define HMAC_SHA1_AES 1 pyrit-0.4.0/cpyrit/_cpyrit_cpu.c0000664000076400007640000021537011526274651017161 0ustar slaveslave00000000000000/* # # Copyright 2008-2011, Lukas Lueg, lukas.lueg@gmail.com # # This file is part of Pyrit. # # Pyrit is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Pyrit is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Pyrit. If not, see . # # Additional permission under GNU GPL version 3 section 7 # # If you modify this Program, or any covered work, by linking or # combining it with the OpenSSL project's "OpenSSL" library (or a # modified version of that library), containing parts covered by # the terms of OpenSSL/SSLeay license, the licensors of this # Program grant you additional permission to convey the resulting # work. Corresponding Source for a non-source form of such a # combination shall include the source code for the parts of the # OpenSSL library used as well as that of the covered work. */ #include #include #include #include #include #include #include "_cpyrit_cpu.h" typedef struct { uint32_t h0[4]; uint32_t h1[4]; uint32_t h2[4]; uint32_t h3[4]; uint32_t h4[4]; uint32_t cst[6][4]; } fourwise_sha1_ctx; typedef struct { uint32_t a[4]; uint32_t b[4]; uint32_t c[4]; uint32_t d[4]; } fourwise_md5_ctx; struct pmk_ctr { SHA_CTX ctx_ipad; SHA_CTX ctx_opad; uint32_t e1[5]; uint32_t e2[5]; }; typedef struct { PyObject_HEAD char keyscheme; unsigned char *pke; unsigned char keymic[16]; size_t eapolframe_size; unsigned char *eapolframe; } EAPOLCracker; typedef struct { PyObject_HEAD } CPUDevice; typedef struct { PyObject_HEAD } CowpattyFile; typedef struct { PyObject_HEAD unsigned char *buffer, *current_ptr; Py_ssize_t buffersize; int current_idx, itemcount; } CowpattyResult; typedef struct { PyObject_HEAD PyObject *device_name; PyObject *type; PyObject *datalink_name; pcap_t *p; int datalink; char status; } PcapDevice; static PyObject *PlatformString; static PyTypeObject CowpattyResult_type; /* Function pointers depend on the execution path that got compiled and that we can take (SSE2, Padlock, x86) */ /* CPUDevice */ static void (*prepare_pmk)(const unsigned char *essid_pre, int essidlen, const unsigned char *password, int passwdlen, struct pmk_ctr *ctr) = NULL; static int (*finalize_pmk)(struct pmk_ctr *ctr) = NULL; /* EAPOLCracker */ static unsigned char* (*fourwise_sha1hmac_prepare)(unsigned char* msg, int msg_len) = NULL; static void (*fourwise_sha1hmac)(unsigned char* message, int message_length, unsigned char* keys, int key_length, unsigned char* hmacs) = NULL; static unsigned char* (*fourwise_md5hmac_prepare)(unsigned char* msg, int msg_len) = NULL; static void (*fourwise_md5hmac)(unsigned char* message, int message_length, unsigned char* keys, int key_length, unsigned char* hmacs) = NULL; #ifdef COMPILE_SSE2 uint32_t md5_constants[64][4]; extern int detect_sse2(void); extern int sse2_sha1_update(uint32_t ctx[4*5+4*6], uint32_t data[4*16], uint32_t wrkbuf[4*80]) __attribute__ ((regparm(3))); extern int sse2_sha1_finalize(uint32_t ctx[4*5+4*6], uint32_t digests[4*5]) __attribute__ ((regparm(2))); extern int sse2_md5_update(uint32_t ctx[4*5], uint32_t data[4*16], uint32_t constants[4*64]) __attribute__ ((regparm(3))); #endif /* ########################################################################### CPUDevice ########################################################################### */ #ifdef COMPILE_PADLOCK struct xsha1_ctx { unsigned int state[32]; unsigned char inputbuffer[20+64]; } __attribute__((aligned(16))); #include #include #include #include // Snippet taken from OpenSSL 0.9.8 static int detect_padlock(void) { char vendor_string[16]; unsigned int eax, edx; eax = 0x00000000; vendor_string[12] = 0; asm volatile ( "pushl %%ebx\n" "cpuid\n" "movl %%ebx,(%%edi)\n" "movl %%edx,4(%%edi)\n" "movl %%ecx,8(%%edi)\n" "popl %%ebx" : "+a"(eax) : "D"(vendor_string) : "ecx", "edx"); if (strcmp(vendor_string, "CentaurHauls") != 0) return 0; /* Check for Centaur Extended Feature Flags presence */ eax = 0xC0000000; asm volatile ("pushl %%ebx; cpuid; popl %%ebx" : "+a"(eax) : : "ecx", "edx"); if (eax < 0xC0000001) return 0; /* Read the Centaur Extended Feature Flags */ eax = 0xC0000001; asm volatile ("pushl %%ebx; cpuid; popl %%ebx" : "+a"(eax), "=d"(edx) : : "ecx"); return (edx & 0x300) == 0x300; } // This instruction is not available on all CPUs (do we really care about those?) // Therefor it is only used on padlock-enabled machines static inline int bswap(int x) { asm volatile ("bswap %0": "=r" (x): "0" (x)); return x; } static int padlock_xsha1(unsigned char *input, unsigned int *output, int done, int count) { int volatile d = done; asm volatile ("xsha1" : "+S"(input), "+D"(output), "+a"(d) : "c"(count)); return d; } // This handler will ignore the SIGSEGV deliberately caused by padlock_xsha1_prepare static void segv_action(int sig, siginfo_t *info, void *uctxp) { MCTX_EIP((ucontext_t*)uctxp) += 4; } // REP XSHA1 is crashed into the mprotect'ed page so we can // steal the state at *EDI before finalizing. static int padlock_xsha1_prepare(const unsigned char *input, SHA_CTX *output) { size_t page_size = getpagesize(), buffersize = 2 * page_size, hashed = 0; struct sigaction act, oldact; unsigned char *cnt, *inputbuffer; inputbuffer = mmap(0, buffersize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); mprotect(inputbuffer + page_size, page_size, PROT_NONE); struct xsha1_ctx ctx; ((int*)ctx.state)[0] = 0x67452301; ((int*)ctx.state)[1] = 0xEFCDAB89; ((int*)ctx.state)[2] = 0x98BADCFE; ((int*)ctx.state)[3] = 0x10325476; ((int*)ctx.state)[4] = 0xC3D2E1F0; cnt = inputbuffer + page_size - (64*2); memcpy(cnt, input, 64); memset(&act, 0, sizeof(act)); act.sa_sigaction = segv_action; act.sa_flags = SA_SIGINFO; sigaction(SIGSEGV, &act, &oldact); hashed = padlock_xsha1(cnt, ctx.state, 0, (64*2)); sigaction(SIGSEGV, &oldact, NULL); munmap(inputbuffer, buffersize); output->h0 = ((int*)ctx.state)[0]; output->h1 = ((int*)ctx.state)[1]; output->h2 = ((int*)ctx.state)[2]; output->h3 = ((int*)ctx.state)[3]; output->h4 = ((int*)ctx.state)[4]; return hashed; } // Now lie about the total number of bytes hashed by this call to get the correct hash static inline int padlock_xsha1_finalize(SHA_CTX *prestate, unsigned char *buffer) { struct xsha1_ctx ctx; size_t hashed; ((int*)ctx.state)[0] = prestate->h0; ((int*)ctx.state)[1] = prestate->h1; ((int*)ctx.state)[2] = prestate->h2; ((int*)ctx.state)[3] = prestate->h3; ((int*)ctx.state)[4] = prestate->h4; memcpy(ctx.inputbuffer, buffer, 20); hashed = padlock_xsha1(ctx.inputbuffer, ctx.state, 64, 64+20); ((int*)buffer)[0] = bswap(ctx.state[0]); ((int*)buffer)[1] = bswap(ctx.state[1]); ((int*)buffer)[2] = bswap(ctx.state[2]); ((int*)buffer)[3] = bswap(ctx.state[3]); ((int*)buffer)[4] = bswap(ctx.state[4]); return hashed; } static void prepare_pmk_padlock(const unsigned char *essid_pre, int essidlen, const unsigned char *password, int passwdlen, struct pmk_ctr *ctr) { int i; unsigned char pad[64], essid[32+4]; essidlen = essidlen > 32 ? 32 : essidlen; passwdlen = passwdlen > 64 ? 64 : passwdlen; memcpy(essid, essid_pre, essidlen); memset(essid + essidlen, 0, sizeof(essid) - essidlen); memcpy(pad, password, passwdlen); memset(pad + passwdlen, 0, sizeof(pad) - passwdlen); for (i = 0; i < 16; i++) ((unsigned int*)pad)[i] ^= 0x36363636; padlock_xsha1_prepare(pad, &ctr->ctx_ipad); for (i = 0; i < 16; i++) ((unsigned int*)pad)[i] ^= 0x6A6A6A6A; padlock_xsha1_prepare(pad, &ctr->ctx_opad); essid[essidlen + 4 - 1] = '\1'; HMAC(EVP_sha1(), password, passwdlen, essid, essidlen + 4, (unsigned char*)ctr->e1, NULL); essid[essidlen + 4 - 1] = '\2'; HMAC(EVP_sha1(), password, passwdlen, essid, essidlen + 4, (unsigned char*)ctr->e2, NULL); } static int finalize_pmk_padlock(struct pmk_ctr *ctr) { int i, j; unsigned int e1_buffer[5], e2_buffer[5]; memcpy(e1_buffer, ctr->e1, 20); memcpy(e2_buffer, ctr->e2, 20); for (i = 0; i < 4096-1; i++) { padlock_xsha1_finalize(&ctr->ctx_ipad, (unsigned char*)e1_buffer); padlock_xsha1_finalize(&ctr->ctx_opad, (unsigned char*)e1_buffer); for (j = 0; j < 5; j++) ctr->e1[j] ^= e1_buffer[j]; padlock_xsha1_finalize(&ctr->ctx_ipad, (unsigned char*)e2_buffer); padlock_xsha1_finalize(&ctr->ctx_opad, (unsigned char*)e2_buffer); for (j = 0; j < 3; j++) ctr->e2[j] ^= e2_buffer[j]; } return 1; } #endif // COMPILE_PADLOCK #ifdef COMPILE_SSE2 static int finalize_pmk_sse2(struct pmk_ctr *ctr) { int i, j, k; uint32_t ctx_ipad[4*5] __attribute__ ((aligned (16))); uint32_t ctx_opad[4*5] __attribute__ ((aligned (16))); uint32_t sha1_ctx[4*5+4*6] __attribute__ ((aligned (16))); uint32_t e1_buffer[4*16] __attribute__ ((aligned (16))); uint32_t e2_buffer[4*16] __attribute__ ((aligned (16))); uint32_t wrkbuf[4*80] __attribute__ ((aligned (16))); memset(e1_buffer, 0, sizeof(e1_buffer)); memset(e2_buffer, 0, sizeof(e2_buffer)); for (i = 0; i < 4; i++) { sha1_ctx[4*5 + 0*4 + i] = 0x5A827999; /* const_stage0 */ sha1_ctx[4*5 + 1*4 + i] = 0x6ED9EBA1; /* const_stage1 */ sha1_ctx[4*5 + 2*4 + i] = 0x8F1BBCDC; /* const_stage2 */ sha1_ctx[4*5 + 3*4 + i] = 0xCA62C1D6; /* const_stage3 */ sha1_ctx[4*5 + 4*4 + i] = 0xFF00FF00; /* const_ff00 */ sha1_ctx[4*5 + 5*4 + i] = 0x00FF00FF; /* const_00ff */ } // Interleave four ipads, opads and first-round-PMKs to local buffers for (i = 0; i < 4; i++) { ctx_ipad[i+ 0] = ctr[i].ctx_ipad.h0; ctx_ipad[i+ 4] = ctr[i].ctx_ipad.h1; ctx_ipad[i+ 8] = ctr[i].ctx_ipad.h2; ctx_ipad[i+12] = ctr[i].ctx_ipad.h3; ctx_ipad[i+16] = ctr[i].ctx_ipad.h4; ctx_opad[i+ 0] = ctr[i].ctx_opad.h0; ctx_opad[i+ 4] = ctr[i].ctx_opad.h1; ctx_opad[i+ 8] = ctr[i].ctx_opad.h2; ctx_opad[i+12] = ctr[i].ctx_opad.h3; ctx_opad[i+16] = ctr[i].ctx_opad.h4; e1_buffer[20+i] = e2_buffer[20+i] = 0x80; // Terminator bit e1_buffer[60+i] = e2_buffer[60+i] = 0xA0020000; // size = (64+20)*8 for (j = 0; j < 5; j++) { e1_buffer[j*4 + i] = ctr[i].e1[j]; e2_buffer[j*4 + i] = ctr[i].e2[j]; } } // Process through SSE2 and de-interleave back to ctr for (i = 0; i < 4096-1; i++) { memcpy(sha1_ctx, ctx_ipad, 4 * 5 * sizeof(uint32_t)); sse2_sha1_update(sha1_ctx, e1_buffer, wrkbuf); sse2_sha1_finalize(sha1_ctx, e1_buffer); memcpy(sha1_ctx, ctx_opad, 4 * 5 * sizeof(uint32_t)); sse2_sha1_update(sha1_ctx, e1_buffer, wrkbuf); sse2_sha1_finalize(sha1_ctx, e1_buffer); memcpy(sha1_ctx, ctx_ipad, 4 * 5 * sizeof(uint32_t)); sse2_sha1_update(sha1_ctx, e2_buffer, wrkbuf); sse2_sha1_finalize(sha1_ctx, e2_buffer); memcpy(sha1_ctx, ctx_opad, 4 * 5 * sizeof(uint32_t)); sse2_sha1_update(sha1_ctx, e2_buffer, wrkbuf); sse2_sha1_finalize(sha1_ctx, e2_buffer); for (j = 0; j < 4; j++) { for (k = 0; k < 5; k++) { ctr[j].e1[k] ^= e1_buffer[k*4 + j]; ctr[j].e2[k] ^= e2_buffer[k*4 + j]; } } } return 4; } #endif // COMPILE_SSE2 static void prepare_pmk_openssl(const unsigned char *essid_pre, int essidlen, const unsigned char *password, int passwdlen, struct pmk_ctr *ctr) { int i; unsigned char pad[64], essid[32+4]; essidlen = essidlen > 32 ? 32 : essidlen; passwdlen = passwdlen > 64 ? 64 : passwdlen; memcpy(essid, essid_pre, essidlen); memset(essid + essidlen, 0, sizeof(essid) - essidlen); memcpy(pad, password, passwdlen); memset(pad + passwdlen, 0, sizeof(pad) - passwdlen); for( i = 0; i < 16; i++ ) ((unsigned int*)pad)[i] ^= 0x36363636; SHA1_Init(&ctr->ctx_ipad); SHA1_Update(&ctr->ctx_ipad, pad, 64); for( i = 0; i < 16; i++ ) ((unsigned int*)pad)[i] ^= 0x6A6A6A6A; SHA1_Init(&ctr->ctx_opad); SHA1_Update(&ctr->ctx_opad, pad, 64); essid[essidlen + 4 - 1] = '\1'; HMAC(EVP_sha1(), password, passwdlen, essid, essidlen + 4, (unsigned char*)ctr->e1, NULL); essid[essidlen + 4 - 1] = '\2'; HMAC(EVP_sha1(), password, passwdlen, essid, essidlen + 4, (unsigned char*)ctr->e2, NULL); } static int finalize_pmk_openssl(struct pmk_ctr *ctr) { int i, j; SHA_CTX ctx; unsigned int e1_buffer[5], e2_buffer[5]; memcpy(e1_buffer, ctr->e1, 20); memcpy(e2_buffer, ctr->e2, 20); for(i = 0; i < 4096-1; i++) { memcpy(&ctx, &ctr->ctx_ipad, sizeof(ctx)); SHA1_Update(&ctx, (unsigned char*)e1_buffer, 20); SHA1_Final((unsigned char*)e1_buffer, &ctx); memcpy(&ctx, &ctr->ctx_opad, sizeof(ctx)); SHA1_Update(&ctx, (unsigned char*)e1_buffer, 20); SHA1_Final((unsigned char*)e1_buffer, &ctx); for (j = 0; j < 5; j++) ctr->e1[j] ^= e1_buffer[j]; memcpy(&ctx, &ctr->ctx_ipad, sizeof(ctx)); SHA1_Update(&ctx, (unsigned char*)e2_buffer, 20); SHA1_Final((unsigned char*)e2_buffer, &ctx); memcpy(&ctx, &ctr->ctx_opad, sizeof(ctx)); SHA1_Update(&ctx, (unsigned char*)e2_buffer, 20); SHA1_Final((unsigned char*)e2_buffer, &ctx); for (j = 0; j < 3; j++) ctr->e2[j] ^= e2_buffer[j]; } return 1; } PyDoc_STRVAR(CPUDevice_solve__doc__, "solve(essid, passwords) -> tuple\n\n" "Calculate PMKs from ESSID and iterable of strings."); static PyObject * CPUDevice_solve(PyObject *self, PyObject *args) { unsigned char *essid, *passwd; PyObject *passwd_seq, *passwd_obj, *essid_obj, *result; int i, arraysize, essidlen, passwdlen; struct pmk_ctr *pmk_buffer, *t; if (!PyArg_ParseTuple(args, "OO", &essid_obj, &passwd_seq)) return NULL; passwd_seq = PyObject_GetIter(passwd_seq); if (!passwd_seq) return NULL; essid = (unsigned char*)PyString_AsString(essid_obj); essidlen = PyString_Size(essid_obj); if (essid == NULL || essidlen < 1 || essidlen > 32) { Py_DECREF(passwd_seq); PyErr_SetString(PyExc_ValueError, "ESSID must be a string between 1 and 32 bytes."); return NULL; } arraysize = 0; pmk_buffer = NULL; while ((passwd_obj=PyIter_Next(passwd_seq))) { if (arraysize % 100 == 0) { // Step-size must be aligned to four entries so finalize_pmk_sse2 has air to breath t = PyMem_Realloc(pmk_buffer, sizeof(struct pmk_ctr) * (arraysize+100)); if (!t) { Py_DECREF(passwd_obj); Py_DECREF(passwd_seq); PyMem_Free(pmk_buffer); PyErr_NoMemory(); return NULL; } pmk_buffer = t; } passwd = (unsigned char*)PyString_AsString(passwd_obj); passwdlen = PyString_Size(passwd_obj); if (passwd == NULL || passwdlen < 8 || passwdlen > 63) { Py_DECREF(passwd_obj); Py_DECREF(passwd_seq); PyMem_Free(pmk_buffer); PyErr_SetString(PyExc_ValueError, "All passwords must be strings between 8 and 63 characters"); return NULL; } prepare_pmk(essid, essidlen, passwd, passwdlen, &pmk_buffer[arraysize]); Py_DECREF(passwd_obj); arraysize++; } Py_DECREF(passwd_seq); if (arraysize > 0) { Py_BEGIN_ALLOW_THREADS; i = 0; do i += finalize_pmk(&pmk_buffer[i]); while (i < arraysize); Py_END_ALLOW_THREADS; result = PyTuple_New(arraysize); for (i = 0; i < arraysize; i++) PyTuple_SetItem(result, i, PyString_FromStringAndSize((char*)pmk_buffer[i].e1, 32)); } else { result = PyTuple_New(0); } PyMem_Free(pmk_buffer); return result; } /* ########################################################################### EAPOLCracker ########################################################################### */ #ifdef COMPILE_SSE2 static unsigned char* fourwise_sha1hmac_prepare_sse2(unsigned char* msg, int msg_len) { int buffer_len, i, j, k; unsigned char *retval, *buffer, *prepared_msg; /* Align length to 56 bytes for for message, 1 for terminator, 8 for size */ buffer_len = msg_len + (64 - ((msg_len + 1 + 8) % 64)) + 1 + 8; buffer = PyMem_Malloc(buffer_len); if (!buffer) return NULL; /* Terminate msg, total length = 64 bytes for IPAD + sizeof(msg) in bits */ memset(buffer, 0, buffer_len); memcpy(buffer, msg, msg_len); buffer[msg_len] = 0x80; PUT_BE((64 + msg_len) * 8, buffer, buffer_len - 4); retval = PyMem_Malloc(buffer_len * 4 + 16); if (!retval) { PyMem_Free(buffer); return NULL; } /* Interleave buffer four times for SSE2-processing */ prepared_msg = retval + 16 - ((long)retval % 16); for (i = 0; i < buffer_len / 64; i++) for (j = 0; j < 16; j++) for (k = 0; k < 4; k++) ((uint32_t*)prepared_msg)[(i * 64) + (j * 4) + k] = ((uint32_t*)buffer)[(i * 16) + j]; PyMem_Free(buffer); return retval; } static inline void fourwise_sha1_init(fourwise_sha1_ctx* ctx) { int i; for (i = 0; i < 4; i++) { ctx->h0[i] = 0x67452301; /* magic start value */ ctx->h1[i] = 0xEFCDAB89; /* magic start value */ ctx->h2[i] = 0x98BADCFE; /* magic start value */ ctx->h3[i] = 0x10325476; /* magic start value */ ctx->h4[i] = 0xC3D2E1F0; /* magic start value */ ctx->cst[0][i] = 0x5A827999; /* const_stage0 */ ctx->cst[1][i] = 0x6ED9EBA1; /* const_stage1 */ ctx->cst[2][i] = 0x8F1BBCDC; /* const_stage2 */ ctx->cst[3][i] = 0xCA62C1D6; /* const_stage3 */ ctx->cst[4][i] = 0xFF00FF00; /* const_ff00 */ ctx->cst[5][i] = 0x00FF00FF; /* const_00ff */ } } static void fourwise_sha1hmac_sse2(unsigned char* prepared_msg, int message_length, unsigned char* keys, int key_length, unsigned char* hmacs) { int i, j; uint32_t buffer[16]; uint32_t wrkbuf[4*80] __attribute__ ((aligned (16))); uint32_t blockbuffer[16][4] __attribute__ ((aligned (16))); uint32_t digests[4][5]; fourwise_sha1_ctx ctx; key_length = key_length <= 64 ? key_length : 64; prepared_msg = prepared_msg + 16 - ((long)prepared_msg % 16); message_length = message_length + (64 - ((message_length + 1 + 8) % 64)) + 1 + 8; /* Step 1: Inner hash = IPAD ^ K // message */ fourwise_sha1_init(&ctx); /* Process IPAD ^ K */ for (i = 0; i < 4; i++) { memcpy(&buffer, &keys[key_length * i], key_length); memset(&((unsigned char*)buffer)[key_length], 0, sizeof(buffer) - key_length); for (j = 0; j < 16; j++) blockbuffer[j][i] = buffer[j] ^ 0x36363636; } sse2_sha1_update((uint32_t*)&ctx, (uint32_t*)blockbuffer, wrkbuf); for (i = 0; i < message_length / 64; i++) sse2_sha1_update((uint32_t*)&ctx, (uint32_t*)(prepared_msg + 64 * 4 * i), wrkbuf); /* First hash done */ sse2_sha1_finalize((uint32_t*)&ctx, (uint32_t*)&blockbuffer); for (i = 0; i < 4; i++) for (j = 0; j < 5; j++) digests[i][j] = blockbuffer[j][i]; /* Step 2: Outer hash = OPAD ^ K // inner hash */ fourwise_sha1_init(&ctx); for (i = 0; i < 4; i++) { memcpy(&buffer, &keys[key_length * i], key_length); memset(&((unsigned char*)buffer)[key_length], 0, sizeof(buffer) - key_length); for (j = 0; j < 16; j++) blockbuffer[j][i] = buffer[j] ^ 0x5C5C5C5C; } sse2_sha1_update((uint32_t*)&ctx, (uint32_t*)blockbuffer, wrkbuf); memset(blockbuffer, 0, sizeof(blockbuffer)); for (i = 0; i < 4; i++) { for (j = 0; j < 5; j++) blockbuffer[j][i] = digests[i][j]; blockbuffer[ 5][i] = 0x80; /* Terminator bit */ blockbuffer[15][i] = 0xA0020000; /* size = (64 + 20) * 8 */ } sse2_sha1_update((uint32_t*)&ctx, (uint32_t*)blockbuffer, wrkbuf); /* Second hash == HMAC */ sse2_sha1_finalize((uint32_t*)&ctx, (uint32_t*)&blockbuffer); for (i = 0; i < 4; i++) for (j = 0; j < 5; j++) ((uint32_t*)hmacs)[i * 5 + j] = blockbuffer[j][i]; } static unsigned char* fourwise_md5hmac_prepare_sse2(unsigned char* msg, int msg_len) { int buffer_len, i, j, k; unsigned char *retval, *buffer, *prepared_msg; /* Align length to 56 bytes for for message, 1 for terminator, 8 for size */ buffer_len = msg_len + (64 - ((msg_len + 1 + 8) % 64)) + 1 + 8; buffer = PyMem_Malloc(buffer_len); if (!buffer) return NULL; /* Terminate msg, total length = 64 bytes for IPAD + sizeof(msg) in bits */ memset(buffer, 0, buffer_len); memcpy(buffer, msg, msg_len); buffer[msg_len] = 0x80; ((uint32_t*)buffer)[buffer_len / 4 - 2] = (64 + msg_len) * 8; retval = PyMem_Malloc(buffer_len * 4 + 16); if (!retval) { PyMem_Free(buffer); return NULL; } /* Interleave buffer four times for SSE2-processing */ prepared_msg = retval + 16 - ((long)retval % 16); for (i = 0; i < buffer_len / 64; i++) for (j = 0; j < 16; j++) for (k = 0; k < 4; k++) ((uint32_t*)prepared_msg)[(i * 64) + (j * 4) + k] = ((uint32_t*)buffer)[(i * 16) + j]; PyMem_Free(buffer); return retval; } static inline void fourwise_md5_init(fourwise_md5_ctx* ctx) { int i; for (i = 0; i < 4; i++) { ctx->a[i] = 0x67452301; ctx->b[i] = 0xEFCDAB89; ctx->c[i] = 0x98BADCFE; ctx->d[i] = 0x10325476; } } static void fourwise_md5hmac_sse2(unsigned char* prepared_msg, int message_length, unsigned char* keys, int key_length, unsigned char* hmacs) { int i, j; uint32_t buffer[16]; uint32_t blockbuffer[16][4] __attribute__ ((aligned (16))); uint32_t digests[4][4]; fourwise_md5_ctx ctx; key_length = key_length <= 64 ? key_length : 64; prepared_msg = prepared_msg + 16 - ((long)prepared_msg % 16); message_length = message_length + (64 - ((message_length + 1 + 8) % 64)) + 1 + 8; /* Step 1: Inner hash = IPAD ^ K // message */ fourwise_md5_init(&ctx); /* Process IPAD ^ K */ for (i = 0; i < 4; i++) { memcpy(&buffer, &keys[key_length * i], key_length); memset(&((unsigned char*)buffer)[key_length], 0, sizeof(buffer) - key_length); for (j = 0; j < 16; j++) blockbuffer[j][i] = buffer[j] ^ 0x36363636; } sse2_md5_update((uint32_t*)&ctx, (uint32_t*)blockbuffer, (uint32_t*)&md5_constants); for (i = 0; i < message_length / 64; i++) sse2_md5_update((uint32_t*)&ctx, (uint32_t*)(prepared_msg + 64 * 4 * i), (uint32_t*)&md5_constants); /* First hash done */ for (i = 0; i < 4; i++) { digests[i][0] = ctx.a[i]; digests[i][1] = ctx.b[i]; digests[i][2] = ctx.c[i]; digests[i][3] = ctx.d[i]; } /* Step 2: Outer hash = OPAD ^ K // inner hash */ fourwise_md5_init(&ctx); for (i = 0; i < 4; i++) { memcpy(&buffer, &keys[key_length * i], key_length); memset(&((unsigned char*)buffer)[key_length], 0, sizeof(buffer) - key_length); for (j = 0; j < 16; j++) blockbuffer[j][i] = buffer[j] ^ 0x5C5C5C5C; } sse2_md5_update((uint32_t*)&ctx, (uint32_t*)blockbuffer, (uint32_t*)&md5_constants); memset(blockbuffer, 0, sizeof(blockbuffer)); for (i = 0; i < 4; i++) { for (j = 0; j < 4; j++) blockbuffer[j][i] = digests[i][j]; blockbuffer[ 4][i] = 0x80; /* Terminator bit */ blockbuffer[14][i] = (64+16) * 8; /* Size in bits */ } sse2_md5_update((uint32_t*)&ctx, (uint32_t*)blockbuffer, (uint32_t*)&md5_constants); /* Second hash == HMAC */ for (i = 0; i < 4; i++) { ((uint32_t*)hmacs)[i * 4 + 0] = ctx.a[i]; ((uint32_t*)hmacs)[i * 4 + 1] = ctx.b[i]; ((uint32_t*)hmacs)[i * 4 + 2] = ctx.c[i]; ((uint32_t*)hmacs)[i * 4 + 3] = ctx.d[i]; } } #endif // COMPILE_SSE2 static unsigned char* fourwise_hmac_prepare_openssl(unsigned char* msg, int msg_len) { unsigned char* prep_msg; prep_msg = PyMem_Malloc(msg_len); if (!prep_msg) return NULL; memcpy(prep_msg, msg, msg_len); return prep_msg; } static void fourwise_sha1hmac_openssl(unsigned char* message, int message_length, unsigned char* keys, int key_length, unsigned char* hmacs) { int i; for (i = 0; i < 4; i++) HMAC(EVP_sha1(), &keys[i * key_length], key_length, message, message_length, &hmacs[i * 20], NULL); } static void fourwise_md5hmac_openssl(unsigned char* message, int message_length, unsigned char* keys, int key_length, unsigned char* hmacs) { int i; for (i = 0; i < 4; i++) HMAC(EVP_md5(), &keys[i * key_length], key_length, message, message_length, &hmacs[i * 16], NULL); } static int EAPOLCracker_init(EAPOLCracker *self, PyObject *args, PyObject *kwds) { char *keyscheme; unsigned char *pke, *keymic, *eapolframe; int pke_len, keymic_size, eapolframe_size; self->eapolframe = self->pke = NULL; if (!PyArg_ParseTuple(args, "ss#s#s#", &keyscheme, &pke, &pke_len, &keymic, &keymic_size, &eapolframe, &eapolframe_size)) return -1; if (pke_len != 100) { PyErr_SetString(PyExc_ValueError, "PKE must be a string of exactly 100 bytes."); return -1; } self->pke = fourwise_sha1hmac_prepare(pke, 100); if (!self->pke) { PyErr_NoMemory(); return -1; } if (keymic_size != 16) { PyErr_SetString(PyExc_ValueError, "KeyMIC must a string of 16 bytes."); return -1; } memcpy(self->keymic, keymic, 16); self->eapolframe_size = eapolframe_size; if (strcmp(keyscheme, "HMAC_MD5_RC4") == 0) { self->eapolframe = fourwise_md5hmac_prepare(eapolframe, eapolframe_size); self->keyscheme = HMAC_MD5_RC4; } else if (strcmp(keyscheme, "HMAC_SHA1_AES") == 0) { self->eapolframe = fourwise_sha1hmac_prepare(eapolframe, eapolframe_size); self->keyscheme = HMAC_SHA1_AES; } else { PyErr_SetString(PyExc_ValueError, "Invalid key-scheme."); return -1; } if (!self->eapolframe) { PyErr_NoMemory(); return -1; } return 0; } static void EAPOLCracker_dealloc(EAPOLCracker *self) { if (self->pke) PyMem_Free(self->pke); if (self->eapolframe) PyMem_Free(self->eapolframe); self->ob_type->tp_free((PyObject*)self); } static int EAPOLCracker_unpack(PyObject* result_seq, unsigned char **pmkbuffer_ptr) { unsigned char *pmkbuffer, *t; int buffersize, itemcount; PyObject *result_iter, *result_obj, *pmk_obj; pmkbuffer = pmkbuffer_ptr[0] = NULL; buffersize = itemcount = 0; result_iter = PyObject_GetIter(result_seq); if (!result_iter) { PyErr_SetString(PyExc_ValueError, "Parameter must be a iterable of (password, PMK)-sequences."); return -1; } while ((result_obj = PyIter_Next(result_iter))) { if (buffersize <= itemcount) { /* Step-size must be aligned to four entries (SSE2-path) */ buffersize += 50000; t = PyMem_Realloc(pmkbuffer, buffersize*32); if (!t) { PyErr_NoMemory(); Py_DECREF(result_obj); goto out; } pmkbuffer = t; } pmk_obj = PySequence_GetItem(result_obj, 1); if (!pmk_obj) { PyErr_SetString(PyExc_ValueError, "Expected Pairwise Master Key as second item in a sequence-object."); Py_DECREF(result_obj); PyMem_Free(pmkbuffer); goto out; } t = (unsigned char*)PyString_AsString(pmk_obj); if (t == NULL || PyString_Size(pmk_obj) != 32) { PyErr_SetString(PyExc_ValueError, "All PMKs must be strings of 32 characters"); Py_DECREF(result_obj); Py_DECREF(pmk_obj); PyMem_Free(pmkbuffer); goto out; } memcpy(pmkbuffer + itemcount*32, t, 32); itemcount += 1; Py_DECREF(pmk_obj); Py_DECREF(result_obj); } pmkbuffer_ptr[0] = pmkbuffer; out: Py_DECREF(result_iter); return itemcount * 32; } PyDoc_STRVAR(EAPOLCracker_solve__doc__, "solve(object) -> solution or None\n\n" "Try to find the password that corresponds to this instance's EAPOL-session.\n"); static PyObject* EAPOLCracker_solve(EAPOLCracker *self, PyObject *args) { PyObject *result_seq, *pmkbuffer_obj, *solution_obj; unsigned char *pmkbuffer, *t, kck[4][16], md5mics[4][16], sha1mics[4][20]; Py_ssize_t buffersize; int i, j, solution_idx; PyBufferProcs *pb; pmkbuffer = NULL; if (!PyArg_ParseTuple(args, "O", &result_seq)) return NULL; /* Try to get the PMKs through the object's buffer-protocol (faster) */ if (PyObject_HasAttrString(result_seq, "getpmkbuffer")) { pmkbuffer_obj = PyObject_CallMethod(result_seq, "getpmkbuffer", NULL); if (pmkbuffer_obj) { if (!PyBuffer_Check(pmkbuffer_obj)) { PyErr_SetString(PyExc_ValueError, "The object's .getpmkbuffer() must provide a buffer-object."); Py_DECREF(pmkbuffer_obj); return NULL; } else { pb = pmkbuffer_obj->ob_type->tp_as_buffer; buffersize = (*pb->bf_getreadbuffer)(pmkbuffer_obj, 0, (void**)&t); if (buffersize % 32 != 0) { PyErr_SetString(PyExc_ValueError, "Object's buffer's length is not a multiple of 32."); Py_DECREF(pmkbuffer_obj); return NULL; } /* Align size to 4*32 for SSE2 */ pmkbuffer = PyMem_Malloc(buffersize + 128 - (buffersize % 128)); if (!pmkbuffer) { PyErr_NoMemory(); Py_DECREF(pmkbuffer_obj); return NULL; } memcpy(pmkbuffer, t, buffersize); Py_DECREF(pmkbuffer_obj); } } else { /* Pass the error from getpmkbuffer() */ return NULL; } } else { /* Basic sequence-like objects must be unpacked */ buffersize = EAPOLCracker_unpack(result_seq, &pmkbuffer); if (!pmkbuffer) return NULL; } solution_idx = -1; Py_BEGIN_ALLOW_THREADS; for (i = 0; i < buffersize / 32 && solution_idx == -1; i += 4) { fourwise_sha1hmac(self->pke, 100, &pmkbuffer[i*32], 32, (unsigned char*)&sha1mics); for (j = 0; j < 4; j++) memcpy(kck[j], sha1mics[j], 16); if (self->keyscheme == HMAC_MD5_RC4) { fourwise_md5hmac(self->eapolframe, self->eapolframe_size, (unsigned char*)&kck, 16, (unsigned char*)&md5mics); for (j = 0; j < 4 && i + j < buffersize / 32; j++) if (memcmp(&md5mics[j], self->keymic, 16) == 0) { solution_idx = i + j; break; } } else { fourwise_sha1hmac(self->eapolframe, self->eapolframe_size, (unsigned char*)&kck, 16, (unsigned char*)&sha1mics); for (j = 0; j < 4 && i + j < buffersize / 32; j++) if (memcmp(&sha1mics[j], self->keymic, 16) == 0) { solution_idx = i + j; break; } } } Py_END_ALLOW_THREADS; PyMem_Free(pmkbuffer); if (solution_idx == -1) { solution_obj = Py_None; Py_INCREF(solution_obj); } else { solution_obj = PySequence_GetItem(result_seq, solution_idx); } return solution_obj; } /* ########################################################################### CowpattyResult ########################################################################### */ static void CowpattyResult_dealloc(CowpattyResult* self) { if (self->buffer) PyMem_Free(self->buffer); self->ob_type->tp_free((PyObject*)self); } static Py_ssize_t CowpattyResult_bf_getreadbuffer(CowpattyResult* self, Py_ssize_t segment, void **ptrptr) { if (segment != 0) { PyErr_SetString(PyExc_SystemError, "Invalid segment to CowpattyResult-buffer."); return -1; } ptrptr[0] = self->buffer; return self->itemcount * 32; } static Py_ssize_t CowpattyResult_bf_getsegcount(CowpattyResult* self, Py_ssize_t *lenp) { if (lenp) lenp[0] = self->itemcount * 32; return 1; } static Py_ssize_t CowpattyResult_sq_length(CowpattyResult* self) { return self->itemcount; } static PyObject* CowpattyResult_sq_item(CowpattyResult* self, Py_ssize_t idx) { PyObject *result; int entrylen, i, consumed; if (idx < 0 || idx > self->itemcount - 1) { PyErr_SetString(PyExc_IndexError, "Index out of bounds for CowpattyResult."); return NULL; } consumed = 0; for (i = 0; i < idx; i++) consumed += (int)self->buffer[self->itemcount * 32 + consumed]; result = PyTuple_New(2); if (!result) { PyErr_NoMemory(); return NULL; } entrylen = (int)self->buffer[self->itemcount * 32 + consumed]; PyTuple_SetItem(result, 0, PyString_FromStringAndSize((char*)&self->buffer[self->itemcount * 32 + consumed + 1], entrylen - 1)); PyTuple_SetItem(result, 1, PyString_FromStringAndSize((char*)&self->buffer[idx * 32], 32)); return result; } static PyObject* CowpattyResult_iter(CowpattyResult* self) { Py_INCREF(self); return (PyObject*)self; } static PyObject* CowpattyResult_iternext(CowpattyResult *self) { PyObject *result; int entrylen; if (self->current_idx >= self->itemcount) return NULL; result = PyTuple_New(2); if (!result) { PyErr_NoMemory(); return NULL; } entrylen = (int)self->current_ptr[0]; PyTuple_SetItem(result, 0, PyString_FromStringAndSize((char*)self->current_ptr + 1, entrylen - 1)); PyTuple_SetItem(result, 1, PyString_FromStringAndSize((char*)self->buffer + self->current_idx * 32, 32)); self->current_ptr += entrylen; self->current_idx += 1; return result; } PyDoc_STRVAR(CowpattyResult_getpmkbuffer__doc__, "getpmkbuffer() -> buffer-object\n\n" "Return a buffer-object to directly access the PMKs held by this object."); static PyObject* CowpattyResult_getpmkbuffer(PyObject *self, PyObject *args) { return PyBuffer_FromObject(self, 0, Py_END_OF_BUFFER); } /* ########################################################################### CowpattyFile ########################################################################### */ PyDoc_STRVAR(CowpattyFile_gencowpentries__doc__, "gencowpentries(iterable) -> string\n\n" "Generate a data-string in cowpatty-like format from a iterable of (password-PMK)-tuples."); static PyObject * CowpattyFile_gencowpentries(PyObject *self, PyObject *args) { PyObject *result_seq, *result_obj, *passwd_obj, *pmk_obj, *result; char *passwd, *pmk; unsigned char *cowpbuffer, *t; unsigned int passwd_length, buffer_offset, buffersize; if (!PyArg_ParseTuple(args, "O", &result_seq)) return NULL; result_seq = PyObject_GetIter(result_seq); if (!result_seq) { PyErr_NoMemory(); return NULL; } cowpbuffer = NULL; passwd_obj = pmk_obj = NULL; buffer_offset = buffersize = 0; while ((result_obj = PyIter_Next(result_seq))) { if (buffersize - buffer_offset < 1+63+32) { buffersize += 1024*10; t = PyMem_Realloc(cowpbuffer, buffersize); if (!t) { PyErr_NoMemory(); goto errout; } cowpbuffer = t; } passwd_obj = PySequence_GetItem(result_obj, 0); if (!passwd_obj) { PyErr_NoMemory(); goto errout; } passwd = PyString_AsString(passwd_obj); passwd_length = PyString_Size(passwd_obj); if (passwd == NULL || passwd_length < 8 || passwd_length > 63) { PyErr_SetString(PyExc_ValueError, "All passwords must be strings between 8 and 63 characters"); Py_DECREF(passwd_obj); goto errout; } pmk_obj = PySequence_GetItem(result_obj, 1); if (!pmk_obj) { PyErr_NoMemory(); Py_DECREF(passwd_obj); goto errout; } pmk = PyString_AsString(pmk_obj); if (pmk == NULL || PyString_Size(pmk_obj) != 32) { PyErr_SetString(PyExc_ValueError, "All PMKs must be strings of 32 characters"); Py_DECREF(passwd_obj); Py_DECREF(pmk_obj); goto errout; } cowpbuffer[buffer_offset + 0] = passwd_length + 32 + 1; memcpy(&cowpbuffer[buffer_offset + 1], passwd, passwd_length); memcpy(&cowpbuffer[buffer_offset + 1 + passwd_length], pmk, 32); Py_DECREF(passwd_obj); Py_DECREF(pmk_obj); Py_DECREF(result_obj); buffer_offset += passwd_length + 32 + 1; } Py_DECREF(result_seq); result = PyString_FromStringAndSize((char*)cowpbuffer, buffer_offset); PyMem_Free(cowpbuffer); return result; errout: Py_DECREF(result_obj); Py_DECREF(result_seq); PyMem_Free(cowpbuffer); return NULL; } PyDoc_STRVAR(CowpattyFile_unpackcowpentries__doc__, "unpackcowpentries(string) -> (CowpattyResult, string)\n\n" "Unpack a data-string in cowpatty-like format and return a tuple with results and unfinished tail."); static PyObject * CowpattyFile_unpackcowpentries(PyObject *self, PyObject *args) { CowpattyResult *iter; PyObject *result; int i, stringsize, consumed, entrylen, itemcount; char *string; if (!PyArg_ParseTuple(args, "s#", &string, &stringsize)) return NULL; if (stringsize < 1+8+32 || string[0] > stringsize) { PyErr_SetString(PyExc_ValueError, "Input-string is too short."); return NULL; } itemcount = consumed = 0; do { entrylen = (int)string[consumed]; if (entrylen < 1+8+32 || entrylen > 1+63+32) { PyErr_Format(PyExc_ValueError, "Entry of invalid size: %i", entrylen); return NULL; } if (consumed + entrylen > stringsize) break; consumed += entrylen; itemcount += 1; } while (consumed < stringsize); iter = (CowpattyResult*)PyObject_New(CowpattyResult, &CowpattyResult_type); if (!iter) { PyErr_NoMemory(); return NULL; } iter->buffersize = consumed; iter->current_idx = 0; iter->itemcount = itemcount; iter->buffer = PyMem_Malloc(consumed); if (!iter->buffer) { Py_DECREF(iter); PyErr_NoMemory(); return NULL; } iter->current_ptr = iter->buffer + (itemcount * 32); consumed = 0; for (i = 0; i < itemcount; i++) { entrylen = (int)string[consumed]; memcpy(&iter->buffer[32 * i], &string[consumed + entrylen - 32], 32); iter->buffer[32 * itemcount + consumed - (32 * i)] = entrylen - 32; memcpy(&iter->buffer[32 * itemcount + consumed - (32 * i) + 1], &string[consumed + 1], entrylen - (32 + 1)); consumed += entrylen; } result = PyTuple_New(2); if (!result) { PyErr_NoMemory(); Py_DECREF(iter); return NULL; } PyTuple_SetItem(result, 0, (PyObject*)iter); PyTuple_SetItem(result, 1, PyString_FromStringAndSize(string + consumed, stringsize - consumed)); return result; } /* ########################################################################### PcapDevice ########################################################################### */ static int PcapDevice_init(PcapDevice *self, PyObject *args, PyObject *kwds) { self->device_name = Py_None; Py_INCREF(Py_None); self->type = Py_None; Py_INCREF(Py_None); self->datalink_name = Py_None; Py_INCREF(Py_None); self->p = NULL; self->status = self->datalink = 0; return 0; } static void PcapDevice_dealloc(PcapDevice *self) { Py_XDECREF(self->device_name); Py_XDECREF(self->type); Py_XDECREF(self->datalink_name); if (self->p && self->status == 1) pcap_close(self->p); self->ob_type->tp_free((PyObject*)self); } PyDoc_STRVAR(PcapDevice_close__doc__, "close() -> None\n\n" "Close the instance"); static PyObject* PcapDevice_close(PcapDevice *self, PyObject *args) { if (self->status == 1) pcap_close(self->p); self->status = -1; Py_INCREF(Py_None); return Py_None; } static int PcapDevice_setup(PcapDevice *self, const char* type, const char* dev) { const char *dlink_name; self->datalink = pcap_datalink(self->p); dlink_name = pcap_datalink_val_to_name(self->datalink); if (dlink_name) { Py_DECREF(self->datalink_name); self->datalink_name = PyString_FromString(dlink_name); if (!self->datalink_name) { PyErr_NoMemory(); return 0; } } Py_DECREF(self->type); self->type = PyString_FromString(type); if (!self->type) { PyErr_NoMemory(); return 0; } Py_DECREF(self->device_name); self->device_name = PyString_FromString(dev); if (!self->device_name) { PyErr_NoMemory(); return 0; } self->status = 1; return 1; } PyDoc_STRVAR(PcapDevice_open_live__doc__, "open_live(device_name) -> None\n\n" "Open a device for live-capture"); static PyObject* PcapDevice_open_live(PcapDevice *self, PyObject *args) { char errbuf[PCAP_ERRBUF_SIZE]; char *device_name; if (!PyArg_ParseTuple(args, "s", &device_name)) return NULL; if (self->status != 0) { PyErr_SetString(PyExc_RuntimeError, "Already opened."); return NULL; } self->p = pcap_open_live(device_name, 65535, 1, 200, errbuf); if (!self->p) { PyErr_Format(PyExc_IOError, "Failed to open device '%s' (libpcap: %s)", device_name, errbuf); return NULL; } if (!PcapDevice_setup(self, "live", device_name)) return NULL; Py_INCREF(Py_None); return Py_None; } PyDoc_STRVAR(PcapDevice_open_offline__doc__, "open_offline(fname) ->None\n\n" "Open a file for reading"); static PyObject* PcapDevice_open_offline(PcapDevice *self, PyObject *args) { char errbuf[PCAP_ERRBUF_SIZE]; char *fname; if (!PyArg_ParseTuple(args, "s", &fname)) return NULL; if (self->status != 0) { PyErr_SetString(PyExc_RuntimeError, "Already opened."); return NULL; } self->p = pcap_open_offline(fname, errbuf); if (!self->p) { PyErr_Format(PyExc_IOError, "Failed to open file '%s' (libpcap: %s)", fname, errbuf); return NULL; } if (!PcapDevice_setup(self, "offline", fname)) return NULL; Py_INCREF(Py_None); return Py_None; } PyDoc_STRVAR(PcapDevice_read__doc__, "read() -> tuple\n\n" "Read the next packet"); static PyObject* PcapDevice_read(PcapDevice *self, PyObject *args) { PyObject *result, *ts, *pckt_content; int ret; struct pcap_pkthdr *h; const u_char *bytes; if (self->status != 1) { PyErr_SetString(PyExc_RuntimeError, "Instance not ready for reading."); return NULL; } for (;;) { Py_BEGIN_ALLOW_THREADS; ret = pcap_next_ex(self->p, &h, &bytes); Py_END_ALLOW_THREADS; switch (ret) { case 0: // Timeout from live-capture PyErr_CheckSignals(); if (PyErr_Occurred()) return NULL; continue; case 1: // OK pckt_content = PyString_FromStringAndSize((char*)bytes, h->caplen); if (!pckt_content) return PyErr_NoMemory(); ts = PyTuple_New(2); if (!ts) { Py_DECREF(pckt_content); return PyErr_NoMemory(); } PyTuple_SetItem(ts, 0, PyLong_FromLong(h->ts.tv_sec)); PyTuple_SetItem(ts, 1, PyLong_FromLong(h->ts.tv_usec)); result = PyTuple_New(2); if (!result) { Py_DECREF(pckt_content); Py_DECREF(ts); return PyErr_NoMemory(); } PyTuple_SetItem(result, 0, ts); PyTuple_SetItem(result, 1, pckt_content); return result; case -2: // End of file Py_INCREF(Py_None); return Py_None; case -1: // Error PyErr_Format(PyExc_IOError, "libpcap-error while reading: %s", pcap_geterr(self->p)); return NULL; default: PyErr_SetString(PyExc_SystemError, "Unknown return-value from pcap_next_ex()"); return NULL; } } } PyDoc_STRVAR(PcapDevice_send__doc__, "send(object) -> None\n\n" "Send an object's string-representation as a raw packet via a live device."); static PyObject* PcapDevice_send(PcapDevice *self, PyObject *args) { char *pckt_buffer; Py_ssize_t pckt_size; PyObject *pckt, *pckt_string; if (self->status != 1) { PyErr_SetString(PyExc_RuntimeError, "Instance not ready for writing."); return NULL; } if (!PyArg_ParseTuple(args, "O", &pckt)) return NULL; pckt_string = PyObject_Str(pckt); if (!pckt_string) { PyErr_SetString(PyExc_ValueError, "Failed to get string-representation from object."); return NULL; } if (PyString_AsStringAndSize(pckt_string, &pckt_buffer, &pckt_size)) { Py_DECREF(pckt_string); return NULL; } if (pcap_sendpacket(self->p, (unsigned char*)pckt_buffer, pckt_size)) { PyErr_Format(PyExc_IOError, "Failed to send packet (libpcap: %s).", pcap_geterr(self->p)); Py_DECREF(pckt_string); return NULL; } Py_DECREF(pckt_string); Py_INCREF(Py_None); return Py_None; } PyDoc_STRVAR(PcapDevice_set_filter__doc__, "set_filter(filter_string) -> None\n\n" "Set a BPF-filter"); static PyObject* PcapDevice_set_filter(PcapDevice *self, PyObject *args) { struct bpf_program fp; char *filter_string; if (!PyArg_ParseTuple(args, "s", &filter_string)) return NULL; if (self->status != 1) { PyErr_SetString(PyExc_RuntimeError, "Instance not opened yet"); return NULL; } if (pcap_compile(self->p, &fp, filter_string, 0, 0)) { PyErr_Format(PyExc_ValueError, "Failed to compile BPF-filter (libpcap: %s).", pcap_geterr(self->p)); return NULL; } if (pcap_setfilter(self->p, &fp)) { PyErr_Format(PyExc_RuntimeError, "Failed to set BPF-filter (libpcap: %s)", pcap_geterr(self->p)); pcap_freecode(&fp); return NULL; } pcap_freecode(&fp); Py_INCREF(Py_None); return Py_None; } /* ########################################################################### Module functions ########################################################################### */ PyDoc_STRVAR(cpyrit_getPlatform__doc__, "getPlatform() -> string\n\n" "Determine CPU-type"); static PyObject * cpyrit_getPlatform(PyObject *self, PyObject *args) { Py_INCREF(PlatformString); return PlatformString; } PyDoc_STRVAR(cpyrit_grouper__doc__, "grouper(string, groupsize) -> tuple\n\n" "Group a large string into a tuple of strings of equal size each"); static PyObject * cpyrit_grouper(PyObject *self, PyObject *args) { PyObject *result; int i, stringsize, groupsize; char *string; if (!PyArg_ParseTuple(args, "s#i", &string, &stringsize, &groupsize)) return NULL; if (stringsize % groupsize != 0) { PyErr_SetString(PyExc_ValueError, "Invalid size of input string."); return NULL; } result = PyTuple_New(stringsize / groupsize); if (!result) { PyErr_NoMemory(); return NULL; } for (i = 0; i < stringsize / groupsize; i++) PyTuple_SetItem(result, i, PyString_FromStringAndSize(&string[i * groupsize], groupsize)); return result; } /* ########################################################################### Class definitions ########################################################################### */ static PyMethodDef CPUDevice_methods[] = { {"solve", (PyCFunction)CPUDevice_solve, METH_VARARGS, CPUDevice_solve__doc__}, {NULL, NULL} }; static PyTypeObject CPUDevice_type = { PyObject_HEAD_INIT(NULL) 0, /*ob_size*/ "_cpyrit_cpu.CPUDevice", /*tp_name*/ sizeof(CPUDevice), /*tp_basicsize*/ 0, /*tp_itemsize*/ 0, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash*/ 0, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT /*tp_flags*/ | Py_TPFLAGS_BASETYPE, 0, /*tp_doc*/ 0, /*tp_traverse*/ 0, /*tp_clear*/ 0, /*tp_richcompare*/ 0, /*tp_weaklistoffset*/ 0, /*tp_iter*/ 0, /*tp_iternext*/ CPUDevice_methods, /*tp_methods*/ 0, /*tp_members*/ 0, /*tp_getset*/ 0, /*tp_base*/ 0, /*tp_dict*/ 0, /*tp_descr_get*/ 0, /*tp_descr_set*/ 0, /*tp_dictoffset*/ 0, /*tp_init*/ 0, /*tp_alloc*/ 0, /*tp_new*/ 0, /*tp_free*/ 0, /*tp_is_gc*/ }; static PyMethodDef EAPOLCracker_methods[] = { {"solve", (PyCFunction)EAPOLCracker_solve, METH_VARARGS, EAPOLCracker_solve__doc__}, {NULL, NULL} }; static PyTypeObject EAPOLCracker_type = { PyObject_HEAD_INIT(NULL) 0, /*ob_size*/ "_cpyrit_cpu.EAPOLCracker", /*tp_name*/ sizeof(EAPOLCracker), /*tp_basicsize*/ 0, /*tp_itemsize*/ (destructor)EAPOLCracker_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash*/ 0, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT /*tp_flags*/ | Py_TPFLAGS_BASETYPE, 0, /*tp_doc*/ 0, /*tp_traverse*/ 0, /*tp_clear*/ 0, /*tp_richcompare*/ 0, /*tp_weaklistoffset*/ 0, /*tp_iter*/ 0, /*tp_iternext*/ EAPOLCracker_methods, /*tp_methods*/ 0, /*tp_members*/ 0, /*tp_getset*/ 0, /*tp_base*/ 0, /*tp_dict*/ 0, /*tp_descr_get*/ 0, /*tp_descr_set*/ 0, /*tp_dictoffset*/ (initproc)EAPOLCracker_init,/*tp_init*/ 0, /*tp_alloc*/ 0, /*tp_new*/ 0, /*tp_free*/ 0, /*tp_is_gc*/ }; static PyMethodDef CowpattyResult_methods[] = { {"getpmkbuffer", CowpattyResult_getpmkbuffer, METH_NOARGS, CowpattyResult_getpmkbuffer__doc__}, {NULL, NULL} }; static PyBufferProcs CowpattyResults_buffer_procs = { (readbufferproc)CowpattyResult_bf_getreadbuffer, /* bf_getreadbuffer */ 0, /* bf_getwritebuffer */ (segcountproc)CowpattyResult_bf_getsegcount, /* bf_getsegcount */ 0 /* bf_getcharbuffer */ }; static PySequenceMethods CowpattyResult_seq_methods = { (lenfunc)CowpattyResult_sq_length, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ (ssizeargfunc)CowpattyResult_sq_item, /* sq_item */ 0, /* sq_ass_item */ 0, /* sq_contains */ 0, /* sq_inplace_concat */ 0 /* sq_inplace_repeat */ }; static PyTypeObject CowpattyResult_type = { PyObject_HEAD_INIT(NULL) 0, /*ob_size*/ "_cpyrit_cpu.CowpattyResult", /*tp_name*/ sizeof(CowpattyResult), /*tp_basicsize*/ 0, /*tp_itemsize*/ (destructor)CowpattyResult_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash*/ 0, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT /*tp_flags*/ | Py_TPFLAGS_BASETYPE, 0, /*tp_doc*/ 0, /*tp_traverse*/ 0, /*tp_clear*/ 0, /*tp_richcompare*/ 0, /*tp_weaklistoffset*/ (getiterfunc)CowpattyResult_iter, /*tp_iter*/ (iternextfunc)CowpattyResult_iternext, /*tp_iternext*/ CowpattyResult_methods, /*tp_methods*/ 0, /*tp_members*/ 0, /*tp_getset*/ 0, /*tp_base*/ 0, /*tp_dict*/ 0, /*tp_descr_get*/ 0, /*tp_descr_set*/ 0, /*tp_dictoffset*/ 0, /*tp_init*/ 0, /*tp_alloc*/ 0, /*tp_new*/ 0, /*tp_free*/ 0, /*tp_is_gc*/ }; static PyMethodDef CowpattyFile_methods[] = { {"genCowpEntries", CowpattyFile_gencowpentries, METH_VARARGS, CowpattyFile_gencowpentries__doc__}, {"unpackCowpEntries", CowpattyFile_unpackcowpentries, METH_VARARGS, CowpattyFile_unpackcowpentries__doc__}, {NULL, NULL} }; static PyTypeObject CowpattyFile_type = { PyObject_HEAD_INIT(NULL) 0, /*ob_size*/ "_cpyrit_cpu.CowpattyFile", /*tp_name*/ sizeof(CowpattyFile), /*tp_basicsize*/ 0, /*tp_itemsize*/ 0, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash*/ 0, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT /*tp_flags*/ | Py_TPFLAGS_BASETYPE, 0, /*tp_doc*/ 0, /*tp_traverse*/ 0, /*tp_clear*/ 0, /*tp_richcompare*/ 0, /*tp_weaklistoffset*/ 0, /*tp_iter*/ 0, /*tp_iternext*/ CowpattyFile_methods, /*tp_methods*/ 0, /*tp_members*/ 0, /*tp_getset*/ 0, /*tp_base*/ 0, /*tp_dict*/ 0, /*tp_descr_get*/ 0, /*tp_descr_set*/ 0, /*tp_dictoffset*/ 0, /*tp_init*/ 0, /*tp_alloc*/ 0, /*tp_new*/ 0, /*tp_free*/ 0, /*tp_is_gc*/ }; static PyMemberDef PcapDevice_members[] = { {"deviceName", T_OBJECT, offsetof(PcapDevice, device_name), READONLY}, {"type", T_OBJECT, offsetof(PcapDevice, type), READONLY}, {"datalink", T_INT, offsetof(PcapDevice, datalink), READONLY}, {"datalink_name", T_OBJECT, offsetof(PcapDevice, datalink_name), READONLY}, {NULL} }; static PyMethodDef PcapDevice_methods[] = { {"open_live", (PyCFunction)PcapDevice_open_live, METH_VARARGS, PcapDevice_open_live__doc__}, {"open_offline", (PyCFunction)PcapDevice_open_offline, METH_VARARGS, PcapDevice_open_offline__doc__}, {"close", (PyCFunction)PcapDevice_close, METH_NOARGS, PcapDevice_close__doc__}, {"read", (PyCFunction)PcapDevice_read, METH_NOARGS, PcapDevice_read__doc__}, {"send", (PyCFunction)PcapDevice_send, METH_VARARGS, PcapDevice_send__doc__}, {"set_filter", (PyCFunction)PcapDevice_set_filter, METH_VARARGS, PcapDevice_set_filter__doc__}, {NULL, NULL} }; static PyTypeObject PcapDevice_type = { PyObject_HEAD_INIT(NULL) 0, /*ob_size*/ "_cpyrit_cpu.PcapDevice", /*tp_name*/ sizeof(PcapDevice), /*tp_basicsize*/ 0, /*tp_itemsize*/ (destructor)PcapDevice_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash*/ 0, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT /*tp_flags*/ | Py_TPFLAGS_BASETYPE, 0, /*tp_doc*/ 0, /*tp_traverse*/ 0, /*tp_clear*/ 0, /*tp_richcompare*/ 0, /*tp_weaklistoffset*/ 0, /*tp_iter*/ 0, /*tp_iternext*/ PcapDevice_methods, /*tp_methods*/ PcapDevice_members, /*tp_members*/ 0, /*tp_getset*/ 0, /*tp_base*/ 0, /*tp_dict*/ 0, /*tp_descr_get*/ 0, /*tp_descr_set*/ 0, /*tp_dictoffset*/ (initproc)PcapDevice_init, /*tp_init*/ 0, /*tp_alloc*/ 0, /*tp_new*/ 0, /*tp_free*/ 0, /*tp_is_gc*/ }; static PyMethodDef CPyritCPUMethods[] = { {"getPlatform", cpyrit_getPlatform, METH_NOARGS, cpyrit_getPlatform__doc__}, {"grouper", cpyrit_grouper, METH_VARARGS, cpyrit_grouper__doc__}, {NULL, NULL, 0, NULL} }; static void pathconfig(void) { #ifdef COMPILE_PADLOCK if (detect_padlock()) { PlatformString = PyString_FromString("VIA Padlock"); prepare_pmk = prepare_pmk_padlock; finalize_pmk = finalize_pmk_padlock; fourwise_sha1hmac_prepare = fourwise_sha1hmac_prepare_sse2; fourwise_sha1hmac = fourwise_sha1hmac_sse2; fourwise_md5hmac_prepare = fourwise_md5hmac_prepare_sse2; fourwise_md5hmac = fourwise_md5hmac_sse2; return; } #endif #ifdef COMPILE_SSE2 if (detect_sse2()) { PlatformString = PyString_FromString("SSE2"); prepare_pmk = prepare_pmk_openssl; finalize_pmk = finalize_pmk_sse2; fourwise_sha1hmac_prepare = fourwise_sha1hmac_prepare_sse2; fourwise_sha1hmac = fourwise_sha1hmac_sse2; fourwise_md5hmac_prepare = fourwise_md5hmac_prepare_sse2; fourwise_md5hmac = fourwise_md5hmac_sse2; return; } #endif PlatformString = PyString_FromString("x86"); prepare_pmk = prepare_pmk_openssl; finalize_pmk = finalize_pmk_openssl; fourwise_sha1hmac_prepare = fourwise_hmac_prepare_openssl; fourwise_sha1hmac = fourwise_sha1hmac_openssl; fourwise_md5hmac_prepare = fourwise_hmac_prepare_openssl; fourwise_md5hmac = fourwise_md5hmac_openssl; } /* ########################################################################### Module initialization ########################################################################### */ PyMODINIT_FUNC init_cpyrit_cpu(void) { PyObject *m; #ifdef COMPILE_SSE2 int i; for (i = 0; i < 4; i++) { md5_constants[ 0][i] = 0xD76AA478; md5_constants[ 1][i] = 0xE8C7B756; md5_constants[ 2][i] = 0x242070DB; md5_constants[ 3][i] = 0xC1BDCEEE; md5_constants[ 4][i] = 0xF57C0FAF; md5_constants[ 5][i] = 0x4787C62A; md5_constants[ 6][i] = 0xA8304613; md5_constants[ 7][i] = 0xFD469501; md5_constants[ 8][i] = 0x698098D8; md5_constants[ 9][i] = 0x8B44F7AF; md5_constants[10][i] = 0xFFFF5BB1; md5_constants[11][i] = 0x895CD7BE; md5_constants[12][i] = 0x6B901122; md5_constants[13][i] = 0xFD987193; md5_constants[14][i] = 0xA679438E; md5_constants[15][i] = 0x49B40821; md5_constants[16][i] = 0xF61E2562; md5_constants[17][i] = 0xC040B340; md5_constants[18][i] = 0x265E5A51; md5_constants[19][i] = 0xE9B6C7AA; md5_constants[20][i] = 0xD62F105D; md5_constants[21][i] = 0x02441453; md5_constants[22][i] = 0xD8A1E681; md5_constants[23][i] = 0xE7D3FBC8; md5_constants[24][i] = 0x21E1CDE6; md5_constants[25][i] = 0xC33707D6; md5_constants[26][i] = 0xF4D50D87; md5_constants[27][i] = 0x455A14ED; md5_constants[28][i] = 0xA9E3E905; md5_constants[29][i] = 0xFCEFA3F8; md5_constants[30][i] = 0x676F02D9; md5_constants[31][i] = 0x8D2A4C8A; md5_constants[32][i] = 0xFFFA3942; md5_constants[33][i] = 0x8771F681; md5_constants[34][i] = 0x6D9D6122; md5_constants[35][i] = 0xFDE5380C; md5_constants[36][i] = 0xA4BEEA44; md5_constants[37][i] = 0x4BDECFA9; md5_constants[38][i] = 0xF6BB4B60; md5_constants[39][i] = 0xBEBFBC70; md5_constants[40][i] = 0x289B7EC6; md5_constants[41][i] = 0xEAA127FA; md5_constants[42][i] = 0xD4EF3085; md5_constants[43][i] = 0x04881D05; md5_constants[44][i] = 0xD9D4D039; md5_constants[45][i] = 0xE6DB99E5; md5_constants[46][i] = 0x1FA27CF8; md5_constants[47][i] = 0xC4AC5665; md5_constants[48][i] = 0xF4292244; md5_constants[49][i] = 0x432AFF97; md5_constants[50][i] = 0xAB9423A7; md5_constants[51][i] = 0xFC93A039; md5_constants[52][i] = 0x655B59C3; md5_constants[53][i] = 0x8F0CCC92; md5_constants[54][i] = 0xFFEFF47D; md5_constants[55][i] = 0x85845DD1; md5_constants[56][i] = 0x6FA87E4F; md5_constants[57][i] = 0xFE2CE6E0; md5_constants[58][i] = 0xA3014314; md5_constants[59][i] = 0x4E0811A1; md5_constants[60][i] = 0xF7537E82; md5_constants[61][i] = 0xBD3AF235; md5_constants[62][i] = 0x2AD7D2BB; md5_constants[63][i] = 0xEB86D391; } #endif // COMPILE_SSE2 pathconfig(); CPUDevice_type.tp_getattro = PyObject_GenericGetAttr; CPUDevice_type.tp_setattro = PyObject_GenericSetAttr; CPUDevice_type.tp_alloc = PyType_GenericAlloc; CPUDevice_type.tp_new = PyType_GenericNew; CPUDevice_type.tp_free = _PyObject_Del; if (PyType_Ready(&CPUDevice_type) < 0) return; EAPOLCracker_type.tp_getattro = PyObject_GenericGetAttr; EAPOLCracker_type.tp_setattro = PyObject_GenericSetAttr; EAPOLCracker_type.tp_alloc = PyType_GenericAlloc; EAPOLCracker_type.tp_new = PyType_GenericNew; EAPOLCracker_type.tp_free = _PyObject_Del; if (PyType_Ready(&EAPOLCracker_type) < 0) return; CowpattyFile_type.tp_getattro = PyObject_GenericGetAttr; CowpattyFile_type.tp_setattro = PyObject_GenericSetAttr; CowpattyFile_type.tp_alloc = PyType_GenericAlloc; CowpattyFile_type.tp_new = PyType_GenericNew; CowpattyFile_type.tp_free = _PyObject_Del; if (PyType_Ready(&CowpattyFile_type) < 0) return; CowpattyResult_type.tp_getattro = PyObject_GenericGetAttr; CowpattyResult_type.tp_setattro = PyObject_GenericSetAttr; CowpattyResult_type.tp_alloc = PyType_GenericAlloc; CowpattyResult_type.tp_new = PyType_GenericNew; CowpattyResult_type.tp_free = _PyObject_Del; CowpattyResult_type.tp_as_sequence = &CowpattyResult_seq_methods; CowpattyResult_type.tp_as_buffer = &CowpattyResults_buffer_procs; if (PyType_Ready(&CowpattyResult_type) < 0) return; PcapDevice_type.tp_getattro = PyObject_GenericGetAttr; PcapDevice_type.tp_setattro = PyObject_GenericSetAttr; PcapDevice_type.tp_alloc = PyType_GenericAlloc; PcapDevice_type.tp_new = PyType_GenericNew; PcapDevice_type.tp_free = _PyObject_Del; if (PyType_Ready(&PcapDevice_type) < 0) return; m = Py_InitModule("_cpyrit_cpu", CPyritCPUMethods); Py_INCREF(&CPUDevice_type); PyModule_AddObject(m, "CPUDevice", (PyObject*)&CPUDevice_type); Py_INCREF(&EAPOLCracker_type); PyModule_AddObject(m, "EAPOLCracker", (PyObject*)&EAPOLCracker_type); Py_INCREF(&CowpattyFile_type); PyModule_AddObject(m, "CowpattyFile", (PyObject*)&CowpattyFile_type); Py_INCREF(&CowpattyResult_type); PyModule_AddObject(m, "CowpattyResult", (PyObject*)&CowpattyResult_type); Py_INCREF(&PcapDevice_type); PyModule_AddObject(m, "PcapDevice", (PyObject*)&PcapDevice_type); PyModule_AddStringConstant(m, "VERSION", VERSION); } pyrit-0.4.0/cpyrit/pckttools.py0000664000076400007640000006476611526274651017104 0ustar slaveslave00000000000000# -*- coding: UTF-8 -*- # # Copyright 2008-2011, Lukas Lueg, lukas.lueg@gmail.com # # This file is part of Pyrit. # # Pyrit is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Pyrit is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Pyrit. If not, see . """ This modules deals with parsing of IEEE802.11-packets and attacking EAPOL-authentications. Scapy's Packet-class is extended with some utility-functions as described. The class PacketParser can be used to analyze a (possibly gzip-compressed) packet-capture-file in pcap-format. The representation gained from it is not exact in the strictest sense but a straightforward hierarchy of AccessPoint -> Station -> EAPOLAuthentication. """ from __future__ import with_statement import tempfile import threading import Queue import warnings import util import _cpyrit_cpu try: import scapy.config # Suppress useless warnings from scapy... scapy.config.conf.logLevel = 40 import scapy.fields import scapy.layers.dot11 import scapy.packet import scapy.utils except ImportError, e: raise util.ScapyImportError(e) scapy.config.Conf.l2types.register_num2layer(119, scapy.layers.dot11.PrismHeader) def isFlagSet(self, name, value): """Return True if the given field 'includes' the given value. Exact behaviour of this function is specific to the field-type. """ field, val = self.getfield_and_val(name) if isinstance(field, scapy.fields.EnumField): if val not in field.i2s: return False return field.i2s[val] == value else: return (1 << field.names.index([value])) & self.__getattr__(name) != 0 scapy.packet.Packet.isFlagSet = isFlagSet del isFlagSet def areFlagsSet(self, name, values): """Return True if the given field 'includes' all of the given values.""" return all(self.isFlagSet(name, value) for value in values) scapy.packet.Packet.areFlagsSet = areFlagsSet del areFlagsSet def areFlagsNotSet(self, name, values): """Return True if the given field 'includes' none of the given values.""" return all(not self.isFlagSet(name, value) for value in values) scapy.packet.Packet.areFlagsNotSet = areFlagsNotSet del areFlagsNotSet def iterSubPackets(self, cls): """Iterate over all layers of the given type in packet 'self'.""" try: if cls not in self: return elt = self[cls] while elt: yield elt elt = elt[cls:2] except IndexError: return scapy.packet.Packet.iterSubPackets = iterSubPackets del iterSubPackets class XStrFixedLenField(scapy.fields.StrFixedLenField): """String-Field with nice repr() for hexdecimal strings""" def i2repr(self, pkt, x): return util.str2hex(scapy.fields.StrFixedLenField.i2m(self, pkt, x)) class XStrLenField(scapy.fields.StrLenField): """String-Field of variable size with nice repr() for hexdecimal strings""" def i2repr(self, pkt, x): return util.str2hex(scapy.fields.StrLenField.i2m(self, pkt, x)) class EAPOL_Key(scapy.packet.Packet): """EAPOL Key frame""" name = "EAPOL Key" fields_desc = [scapy.fields.ByteEnumField("DescType", 254, {2: "RSN Key", 254: "WPA Key"})] scapy.packet.bind_layers(scapy.layers.l2.EAPOL, EAPOL_Key, type=3) class EAPOL_AbstractEAPOLKey(scapy.packet.Packet): """Base-class for EAPOL WPA/RSN-Key frames""" fields_desc = [scapy.fields.FlagsField("KeyInfo", 0, 16, ["HMAC_MD5_RC4", "HMAC_SHA1_AES", "undefined",\ "pairwise", "idx1", "idx2", "install",\ "ack", "mic", "secure", "error", "request", \ "encrypted"]), scapy.fields.ShortField("KeyLength", 0), scapy.fields.LongField("ReplayCounter", 0), XStrFixedLenField("Nonce", '\x00' * 32, 32), XStrFixedLenField("KeyIV", '\x00' * 16, 16), XStrFixedLenField("WPAKeyRSC", '\x00' * 8, 8), XStrFixedLenField("WPAKeyID", '\x00' * 8, 8), XStrFixedLenField("WPAKeyMIC", '\x00' * 16, 16), scapy.fields.ShortField("WPAKeyLength", 0), scapy.fields.ConditionalField( XStrLenField("WPAKey", None, length_from=lambda pkt: pkt.WPAKeyLength), \ lambda pkt: pkt.WPAKeyLength > 0)] class EAPOL_WPAKey(EAPOL_AbstractEAPOLKey): name = "EAPOL WPA Key" keyscheme = 'HMAC_MD5_RC4' scapy.packet.bind_layers(EAPOL_Key, EAPOL_WPAKey, DescType=254) class EAPOL_RSNKey(EAPOL_AbstractEAPOLKey): name = "EAPOL RSN Key" keyscheme = 'HMAC_SHA1_AES' scapy.packet.bind_layers(EAPOL_Key, EAPOL_RSNKey, DescType=2) class AccessPoint(object): def __init__(self, mac): self.mac = mac self.essidframe = None self.essid = None self.stations = {} def __iter__(self): return self.stations.values().__iter__() def __str__(self): return self.mac def __contains__(self, mac): return mac in self.stations def __getitem__(self, mac): return self.stations[mac] def __setitem__(self, mac, station): self.stations[mac] = station def __len__(self): return len(self.stations) def getCompletedAuthentications(self): """Return list of completed Authentication.""" auths = [] for station in self.stations.itervalues(): auths.extend(station.getAuthentications()) return auths def isCompleted(self): """Returns True if this instance includes at least one valid authentication. """ return any(station.isCompleted() for station in self) class Station(object): def __init__(self, mac, ap): self.ap = ap self.mac = mac self.frames = {} def __str__(self): return self.mac def __iter__(self): return self.getAuthentications().__iter__() def __len__(self): return len(self.auths) def addAuthenticationFrame(self, idx, pckt_idx, pckt): if idx == 0: return self.addChallengeFrame(pckt_idx, pckt) elif idx == 1: return self.addResponseFrame(pckt_idx, pckt) elif idx == 2: return self.addConfirmationFrame(pckt_idx, pckt) else: raise IndexError("Invalid authentication-phase.") def addChallengeFrame(self, pckt_idx, pckt): """Store a packet that contains the EAPOL-challenge""" frames = self.frames.setdefault(pckt.ReplayCounter, ({}, {}, {})) if pckt.Nonce not in frames[0]: frames[0][pckt.Nonce] = (pckt_idx, pckt) return self._buildAuthentications({pckt.Nonce: (pckt_idx, pckt)}, \ frames[1], frames[2]) def addResponseFrame(self, pckt_idx, pckt): """Store a packet that contains the EAPOL-response""" frames = self.frames.setdefault(pckt.ReplayCounter, ({}, {}, {})) if EAPOL_WPAKey in pckt: keypckt = pckt[EAPOL_WPAKey] elif EAPOL_RSNKey in pckt: keypckt = pckt[EAPOL_RSNKey] else: raise TypeError("No key-frame in packet") # WPAKeys 'should' set HMAC_MD5_RC4, RSNKeys HMAC_SHA1_AES # However we've seen cases where a WPAKey-packet sets # HMAC_SHA1_AES in it's KeyInfo-field (see issue #111) if keypckt.isFlagSet('KeyInfo', EAPOL_WPAKey.keyscheme): version = EAPOL_WPAKey.keyscheme elif keypckt.isFlagSet('KeyInfo', EAPOL_RSNKey.keyscheme): version = EAPOL_RSNKey.keyscheme else: # Fallback to packet-types's own default, in case the # KeyScheme is never set. Should not happen... version = keypckt.keyscheme # We need a revirginized version of the EAPOL-frame which produced # that MIC. keymic_frame = pckt[scapy.layers.dot11.EAPOL].copy() keymic_frame.WPAKeyMIC = '\x00' * len(keymic_frame.WPAKeyMIC) # Strip padding and cruft from frame keymic_frame = str(keymic_frame)[:keymic_frame.len + 4] response = (version, keypckt.Nonce, keymic_frame, keypckt.WPAKeyMIC) if response not in frames[1]: frames[1][response] = (pckt_idx, pckt) return self._buildAuthentications(frames[0], \ {response: (pckt_idx, pckt)}, \ frames[2]) def addConfirmationFrame(self, pckt_idx, pckt): """Store a packet that contains the EAPOL-confirmation""" frames = self.frames.setdefault(pckt.ReplayCounter - 1, ({}, {}, {})) if pckt.Nonce not in frames[2]: frames[2][pckt.Nonce] = (pckt_idx, pckt) return self._buildAuthentications(frames[0], frames[1], \ {pckt.Nonce: (pckt_idx, pckt)}) def _buildAuthentications(self, f1_frames, f2_frames, f3_frames): auths = [] for (version, snonce, keymic_frame, WPAKeyMIC), \ (f2_idx, f2) in f2_frames.iteritems(): # Combinations with Frame3 are of higher value as the AP # acknowledges that the STA used the correct PMK in Frame2 for anonce, (f3_idx, f3) in f3_frames.iteritems(): if anonce in f1_frames: # We have F1+F2+F3. Frame2 is only cornered by the # ReplayCounter. Technically we don't benefit # from this combination any more than just # F2+F3 but this is the best we can get. f1_idx, f1 = f1_frames[anonce] spread = min(abs(f3_idx - f2_idx), \ abs(f1_idx - f2_idx)) auth = EAPOLAuthentication(self, version, snonce, \ anonce, WPAKeyMIC, keymic_frame, \ 0, spread, (f1, f2, f3)) else: # There are no matching first-frames. That's OK. spread = abs(f3_idx - f2_idx) auth = EAPOLAuthentication(self, version, snonce, \ anonce, WPAKeyMIC, keymic_frame, \ 1, spread, (None, f2, f3)) auths.append(auth) for anonce, (f1_idx, f1) in f1_frames.iteritems(): # No third frame. Combinations with Frame1 are possible but # can also be triggered by STAs that use an incorrect PMK. spread = abs(f1_idx - f2_idx) if anonce not in f3_frames: auth = EAPOLAuthentication(self, version, snonce, \ anonce, WPAKeyMIC, keymic_frame, \ 2, spread, (f1, f2, None)) auths.append(auth) return auths def getAuthentications(self): """Reconstruct a list of EAPOLAuthentications from captured handshake-packets. Best matches come first. """ auths = [] for frames in self.frames.itervalues(): auths.extend(self._buildAuthentications(*frames)) return sorted(auths) def isCompleted(self): """Returns True if this instance includes at least one valid authentication. """ return len(self.getAuthentications()) > 0 class EAPOLAuthentication(object): def __init__(self, station, version, snonce, anonce, keymic, \ keymic_frame, quality, spread, frames=None): self.station = station self.version = version self.snonce = snonce self.anonce = anonce self.keymic = keymic self.keymic_frame = keymic_frame self.quality = quality self.spread = spread self.frames = frames def getpke(self): pke = "Pairwise key expansion\x00" \ + ''.join(sorted((scapy.utils.mac2str(self.station.ap.mac), \ scapy.utils.mac2str(self.station.mac)))) \ + ''.join(sorted((self.snonce, self.anonce))) \ + '\x00' return pke pke = property(getpke) def __lt__(self, other): if isinstance(other, EAPOLAuthentication): return (self.quality, self.spread) < (other.quality, other.spread) else: return self < other def __gt__(self, other): return not self < other def __str__(self): quality = ['good', 'workable', 'bad'][self.quality] return "%s, %s, spread %s" % (self.version, quality, self.spread) class Dot11PacketWriter(object): def __init__(self, pcapfile): self.writer = scapy.utils.PcapWriter(pcapfile, linktype=105, gz=pcapfile.endswith('.gz'), sync=True) self.pcktcount = 0 def write(self, pckt): if not scapy.layers.dot11.Dot11 in pckt: raise RuntimeError("No Dot11-frame in packet.") self.writer.write(pckt[scapy.layers.dot11.Dot11]) self.pcktcount += 1 def close(self): self.writer.close() def __enter__(self): return self def __exit__(self, type, value, traceback): self.close() class PcapDevice(_cpyrit_cpu.PcapDevice): """Read packets from a 'savefile' or a device using libpcap.""" # Standard filter to always exclude type control, general undirected \ # and broadcast BASE_BPF = "not type ctl " \ " and not (dir fromds and wlan[4] & 0x01 = 1)" \ " and not (dir nods and not " \ " (subtype beacon or subtype probe-resp or subtype assoc-req))" def __init__(self, fname=None, use_bpf=False): _cpyrit_cpu.PcapDevice.__init__(self) self.use_bpf = use_bpf self.filtered_aps = set() self.filtered_stations = set() if fname: self.open_offline(fname) def _setup(self): try: self.datalink_handler = scapy.config.conf.l2types[self.datalink] except KeyError: raise ValueError("Datalink-type %i not supported by Scapy" % \ self.datalink) if self.use_bpf: self.set_filter(PcapDevice.BASE_BPF) def set_filter(self, filter_string): try: _cpyrit_cpu.PcapDevice.set_filter(self, filter_string) except ValueError: self.use_bpf = False warnings.warn("Failed to compile BPF-filter. This may be due to " \ "a bug in Pyrit or because your version of " \ "libpcap is too old. Falling back to unfiltered " \ "processing...") def _update_bpf_filter(self): """ Update the BPF-filter to exclude certain traffic from stations and AccessPoints once they are known. """ if self.use_bpf is False: return bpf = PcapDevice.BASE_BPF if len(self.filtered_aps) > 0: # Prune list randomly to prevent filter from getting too large while len(self.filtered_aps) > 10: self.filtered_aps.pop() # Exclude beacons, prope-responses and association-requests # once a AP's ESSID is known bpf += " and not ((wlan host %s) " \ "and (subtype beacon " \ "or subtype probe-resp " \ "or subtype assoc-req))" % \ (" or ".join(self.filtered_aps), ) if len(self.filtered_stations) > 0: while len(self.filtered_stations) > 10: self.filtered_stations.pop() # Exclude encrypted or null-typed data traffic once a station # is known bpf += " and not (wlan host %s) " \ "or (wlan[1] & 0x40 = 0 " \ "and type data " \ "and not subtype null)" % \ (" or ".join(self.filtered_stations), ) self.set_filter(bpf) def filter_ap(self, ap): self.filtered_aps.add(ap.mac) self._update_bpf_filter() def filter_station(self, station): self.filtered_stations.add(station.mac) self._update_bpf_filter() def open_live(self, device_name): """Open a device for capturing packets""" _cpyrit_cpu.PcapDevice.open_live(self, device_name) self._setup() def open_offline(self, fname): """Open a pcap-savefile""" if fname.endswith('.gz'): tfile = tempfile.NamedTemporaryFile() try: with util.FileWrapper(fname) as infile: while True: buf = infile.read(1024 ** 2) if not buf: break tfile.write(buf) tfile.flush() _cpyrit_cpu.PcapDevice.open_offline(self, tfile.name) finally: tfile.close() else: _cpyrit_cpu.PcapDevice.open_offline(self, fname) self._setup() def read(self): """Read one packet from the capture-source.""" r = _cpyrit_cpu.PcapDevice.read(self) if r is not None: ts, pckt_string = r pckt = self.datalink_handler(pckt_string) return pckt else: return None def __iter__(self): return self def next(self): pckt = self.read() if pckt is not None: return pckt else: raise StopIteration def __enter__(self): if self.type is None: raise RuntimeError("No device/file opened yet") return self def __exit__(self, type, value, traceback): self.close() class PacketParser(object): """Parse packets from a capture-source and reconstruct AccessPoints, Stations and EAPOLAuthentications from the data. """ def __init__(self, pcapfile=None, new_ap_callback=None, new_station_callback=None, new_keypckt_callback=None, new_auth_callback=None, use_bpf=True): self.air = {} self.pcktcount = 0 self.dot11_pcktcount = 0 self.new_ap_callback = new_ap_callback self.new_station_callback = new_station_callback self.new_keypckt_callback = new_keypckt_callback self.new_auth_callback = new_auth_callback self.use_bpf = use_bpf if pcapfile is not None: self.parse_file(pcapfile) def _find_ssid(self, pckt): for elt_pckt in pckt.iterSubPackets(scapy.layers.dot11.Dot11Elt): if elt_pckt.isFlagSet('ID', 'SSID') \ and len(elt_pckt.info) == elt_pckt.len \ and not all(c == '\x00' for c in elt_pckt.info): return elt_pckt.info def _add_ap(self, ap_mac, pckt): ap = self.air.setdefault(ap_mac, AccessPoint(ap_mac)) if ap.essid is None: essid = self._find_ssid(pckt) if essid is not None: ap.essid = essid ap.essidframe = pckt.copy() if self.new_ap_callback is not None: self.new_ap_callback(ap) def _add_station(self, ap, sta_mac): if sta_mac not in ap: sta = Station(sta_mac, ap) ap[sta_mac] = sta if self.new_station_callback is not None: self.new_station_callback(sta) def _add_keypckt(self, station, idx, pckt): new_auths = station.addAuthenticationFrame(idx, self.pcktcount, pckt) if self.new_keypckt_callback is not None: self.new_keypckt_callback((station, idx, pckt)) if new_auths is not None and self.new_auth_callback is not None: for auth in new_auths: self.new_auth_callback((station, auth)) def parse_file(self, pcapfile): with PcapDevice(pcapfile, self.use_bpf) as rdr: self.parse_pcapdevice(rdr) def _filter_sta(self, reader, sta_callback, sta): reader.filter_station(sta) if sta_callback is not None: sta_callback(sta) def _filter_ap(self, reader, ap_callback, ap): reader.filter_ap(ap) if ap_callback is not None: ap_callback(ap) def parse_pcapdevice(self, reader): """Parse all packets from a instance of PcapDevice. This method can be very fast as it updates PcapDevice's BPF-filter to exclude unwanted packets from Stations once we are aware of their presence. """ if not isinstance(reader, PcapDevice): raise TypeError("Argument must be of type PcapDevice") sta_callback = self.new_station_callback ap_callback = self.new_ap_callback # Update the filter only when parsing offline dumps. The kernel can't # take complex filters and libpcap starts throwing unmanageable # warnings.... if reader.type == 'offline': self.new_station_callback = lambda sta: \ self._filter_sta(reader, sta_callback, sta) self.new_ap_callback = lambda ap: \ self._filter_ap(reader, ap_callback, ap) for pckt in reader: self.parse_packet(pckt) self.new_station_callback = sta_callback self.new_ap_callback = ap_callback def parse_packet(self, pckt): """Parse one packet""" self.pcktcount += 1 if not scapy.layers.dot11.Dot11 in pckt: return dot11_pckt = pckt[scapy.layers.dot11.Dot11] self.dot11_pcktcount += 1 if dot11_pckt.isFlagSet('type', 'Control'): return # Get a AP and a ESSID from a Beacon if scapy.layers.dot11.Dot11Beacon in dot11_pckt: self._add_ap(dot11_pckt.addr2, dot11_pckt) return # Get a AP and it's ESSID from a AssociationRequest if scapy.layers.dot11.Dot11AssoReq in dot11_pckt: self._add_ap(dot11_pckt.addr1, dot11_pckt) # Get a AP and it's ESSID from a ProbeResponse if scapy.layers.dot11.Dot11ProbeResp in dot11_pckt: self._add_ap(dot11_pckt.addr2, dot11_pckt) # From now on we are only interested in unicast packets if dot11_pckt.isFlagSet('FCfield', 'to-DS') \ and not int(dot11_pckt.addr2[1], 16) & 1: ap_mac = dot11_pckt.addr1 sta_mac = dot11_pckt.addr2 elif dot11_pckt.isFlagSet('FCfield', 'from-DS') \ and not int(dot11_pckt.addr1[1], 16) & 1: ap_mac = dot11_pckt.addr2 sta_mac = dot11_pckt.addr1 else: return # May result in 'anonymous' AP self._add_ap(ap_mac, dot11_pckt) ap = self.air[ap_mac] self._add_station(ap, sta_mac) sta = ap[sta_mac] if EAPOL_WPAKey in dot11_pckt: wpakey_pckt = dot11_pckt[EAPOL_WPAKey] elif EAPOL_RSNKey in dot11_pckt: wpakey_pckt = dot11_pckt[EAPOL_RSNKey] else: return # Frame 1: pairwise set, install unset, ack set, mic unset # results in ANonce if wpakey_pckt.areFlagsSet('KeyInfo', ('pairwise', 'ack')) \ and wpakey_pckt.areFlagsNotSet('KeyInfo', ('install', 'mic')): self._add_keypckt(sta, 0, pckt) # Frame 2: pairwise set, install unset, ack unset, mic set, # SNonce != 0. Results in SNonce, MIC and keymic_frame elif wpakey_pckt.areFlagsSet('KeyInfo', ('pairwise', 'mic')) \ and wpakey_pckt.areFlagsNotSet('KeyInfo', ('install', 'ack')) \ and not all(c == '\x00' for c in wpakey_pckt.Nonce): self._add_keypckt(sta, 1, pckt) # Frame 3: pairwise set, install set, ack set, mic set # Results in ANonce elif wpakey_pckt.areFlagsSet('KeyInfo', ('pairwise', 'install', \ 'ack', 'mic')): self._add_keypckt(sta, 2, pckt) def __iter__(self): return [ap for essid, ap in sorted([(ap.essid, ap) \ for ap in self.air.itervalues()])].__iter__() def __getitem__(self, bssid): return self.air[bssid] def __contains__(self, bssid): return bssid in self.air def __len__(self): return len(self.air) class EAPOLCrackerThread(threading.Thread, _cpyrit_cpu.EAPOLCracker): def __init__(self, workqueue, auth): threading.Thread.__init__(self) _cpyrit_cpu.EAPOLCracker.__init__(self, auth.version, auth.pke, auth.keymic, auth.keymic_frame) self.workqueue = workqueue self.shallStop = False self.solution = None self.numSolved = 0 self.setDaemon(True) self.start() def run(self): while not self.shallStop: try: results = self.workqueue.get(block=True, timeout=0.5) except Queue.Empty: pass else: solution = self.solve(results) self.numSolved += len(results) if solution: self.solution = solution[0] self.workqueue.task_done() class EAPOLCracker(object): def __init__(self, authentication): self.queue = Queue.Queue(10) self.workers = [] self.solution = None for i in xrange(util.ncpus): self.workers.append(EAPOLCrackerThread(self.queue, authentication)) def _getSolution(self): if self.solution is None: for worker in self.workers: if worker.solution is not None: self.solution = worker.solution break def enqueue(self, results): self.queue.put(results) self._getSolution() def join(self): self.queue.join() for worker in self.workers: worker.shallStop = True self._getSolution() def __len__(self): return sum(worker.numSolved for worker in self.workers) def __enter__(self): return self def __exit__(self, type, value, traceback): self.join() pyrit-0.4.0/cpyrit/util.py0000664000076400007640000004404611526274651016024 0ustar slaveslave00000000000000# -*- coding: UTF-8 -*- # # Copyright 2008-2011, Lukas Lueg, lukas.lueg@gmail.com # # This file is part of Pyrit. # # Pyrit is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Pyrit is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Pyrit. If not, see . """Various utility- and backend-related classes and data for Pyrit. AsyncFileWriter is used for threaded, buffered output. CowpattyFile eases reading/writing files in cowpatty's binary format. ncpus equals the number of available CPUs in the system. Thread is a subclass of threading.Thread that adds a context-manager to make it 'stoppable'. AsyncXMLRPCServer is a stoppable (incl. 'serve_forever') subclass of SimpleXMLRPCServer. PMK_TESTVECTORS has two ESSIDs and ten password:PMK pairs each to verify local installations. """ from __future__ import with_statement import cStringIO import gzip import os import Queue import random import socket import SimpleXMLRPCServer import sys import struct import time import threading import _cpyrit_cpu from _cpyrit_cpu import VERSION, grouper import config __version__ = VERSION def _detect_ncpus(): """Detect the number of effective CPUs in the system""" # Snippet taken from ParallelPython # For Linux, Unix and MacOS if hasattr(os, "sysconf"): if "SC_NPROCESSORS_ONLN" in os.sysconf_names: #Linux and Unix ncpus = os.sysconf("SC_NPROCESSORS_ONLN") if isinstance(ncpus, int) and ncpus > 0: return ncpus else: #MacOS X return int(os.popen2("sysctl -n hw.ncpu")[1].read()) #for Windows if "NUMBER_OF_PROCESSORS" in os.environ: ncpus = int(os.environ["NUMBER_OF_PROCESSORS"]) if ncpus > 0: return ncpus #return the default value return 1 def _limit_ncpus(): """Limit the number of reported CPUs if so requested""" detected_ncpus = _detect_ncpus() try: limited_ncpus = int(config.cfg['limit_ncpus']) except ValueError: raise ValueError("Invalid 'limit_ncpus' in configuration") if limited_ncpus < 0: raise ValueError("Invalid 'limit_ncpus' in configuration") if limited_ncpus > 0 and limited_ncpus < detected_ncpus: return limited_ncpus return detected_ncpus ncpus = _limit_ncpus() """ Number of effective CPUs (in the moment the module was loaded).""" def str2hex(string): """Convert a string to it's hex-decimal representation.""" return ''.join('%02x' % c for c in map(ord, string)) class ScapyImportError(ImportError): """ ScapyImportError is used to indicate failure to import scapy's modules. Used to o separate other ImportErrors so code that tries to import pckttools can continue in case Scapy is simply not installed. """ pass class SqlalchemyImportError(ImportError): """" Indicates that sqlalchemy is not available """ pass class FileWrapper(object): """A wrapper for easy stdin/stdout/gzip-handling""" def __init__(self, filename, mode='rb'): if isinstance(filename, str): if filename == '-': if 'r' in mode: self.f = sys.stdin elif 'w' in mode or 'a' in mode: self.f = sys.stdout else: raise ValueError("Unknown filemode '%s'" % mode) elif filename.endswith('.gz'): self.f = gzip.open(filename, mode, 6) else: self.f = open(filename, mode) else: self.f = filename self.isclosed = False def read(self, size=None): return self.f.read(size) def write(self, buf): self.f.write(buf) def seek(self, offset, whence=None): self.f.seek(offset, whence) def flush(self): self.f.flush() def close(self): if not self.isclosed: try: self.f.close() finally: self.isclosed = True def readlines(self): return self.f.readlines() def __enter__(self): return self def __exit__(self, type, value, traceback): self.close() def __iter__(self): return self.f.__iter__() class CowpattyFile(_cpyrit_cpu.CowpattyFile): """A file-like object to read and write cowpatty-like files.""" def __init__(self, filename, mode='r', essid=None): _cpyrit_cpu.CowpattyFile.__init__(self) if mode == 'r': self.f = FileWrapper(filename, 'r') magic, essidlen, essid = struct.unpack(">4si32s", self.f.read(40)) if magic != 'APWC': raise RuntimeError("Not a cowpatty-file.") if essidlen < 1 or essidlen > 32: raise ValueError("Invalid ESSID") self.essid = essid[:essidlen] elif mode == 'w': if essid is None: raise TypeError("ESSID must be specified when writing.") if len(essid) < 1 or len(essid) > 32: raise ValueError("Invalid ESSID.") self.essid = essid self.f = FileWrapper(filename, 'wb') self.f.write("APWC\00\00\00" + \ chr(len(essid)) + essid + \ '\00' * (32 - len(essid))) else: raise RuntimeError("Invalid mode.") self.tail = '' self.eof = False self.mode = mode def __iter__(self): if self.mode != 'r': raise TypeError("Can't read from write-only file.") self.f.seek(40, os.SEEK_SET) self.tail = '' return self def __enter__(self): return self def __exit__(self, type, value, traceback): self.close() def write(self, results): if self.mode != 'w': raise TypeError("Can't write to read-only file.") self.f.write(self.genCowpEntries(results)) def close(self): self.f.close() def next(self): if self.mode != 'r': raise TypeError("Can't read from write-only file.") self.tail = self.tail + self.f.read(512 * 1024) if len(self.tail) == 0: self.eof = True raise StopIteration results, self.tail = self.unpackCowpEntries(self.tail) return results class AsyncFileWriter(threading.Thread): """A buffered, asynchronous file-like object. Writing to this object will only block if the internal buffer exceeded it's maximum size. The call to .write() is done in a seperate thread. """ def __init__(self, f, maxsize=10 * 1024 ** 2): """Create a instance writing to the given file-like-object and buffering maxsize before blocking. """ threading.Thread.__init__(self) self.filehndl = FileWrapper(f, 'wb') self.shallstop = False self.hasstopped = False self.maxsize = maxsize self.excp = None self.buf = cStringIO.StringIO() self.cv = threading.Condition() self.start() def __enter__(self): with self.cv: if self.shallstop: raise RuntimeError("Writer has already been closed") return self def __exit__(self, type, value, traceback): self.close() def close(self): """Stop the writer and wait for it to finish. The file handle that was used for initialization is closed. Exceptions in the writer-thread are re-raised after the writer is closed. """ with self.cv: self.shallstop = True self.cv.notifyAll() while not self.hasstopped: self.cv.wait() self.filehndl.close() self._raise() def write(self, data): """Write data to the buffer, block if necessary. Exceptions in the writer-thread are re-raised in the caller's thread before the data is written. """ with self.cv: self._raise() while self.buf.tell() > self.maxsize: self.cv.wait() if self.shallstop: raise RuntimeError("Writer has already been closed.") self.buf.write(data) self.cv.notifyAll() def closeAsync(self): """Signal the writer to stop and return to caller immediately. The file handle that was used for initialization is not closed by a call to closeAsync(). The caller must call join() before trying to close the file handle to prevent this instance from writing to a closed file handle. Exceptions are not re-raised. """ with self.cv: self.shallstop = True self.cv.notifyAll() def join(self): """Wait for the writer to stop. Exceptions in the writer-thread are re-raised in the caller's thread after writer has stopped. """ with self.cv: while not self.hasstopped: self.cv.wait() self._raise() def _raise(self): # Assumes we hold self.cv if self.excp: e = self.excp self.excp = None self.shallstop = True self.cv.notifyAll() raise e def run(self): try: while True: with self.cv: data = None if self.buf.tell() == 0: if self.shallstop: break else: self.cv.wait() else: data = self.buf.getvalue() self.buf = cStringIO.StringIO() self.cv.notifyAll() if data: self.filehndl.write(data) self.filehndl.flush() except Exception, e: # Re-create a 'trans-thread-safe' instance self.excp = type(e)(str(e)) finally: with self.cv: self.shallstop = self.hasstopped = True self.cv.notifyAll() class PerformanceCounter(object): def __init__(self, window=60.0): self.window = window self.datapoints = [[time.time(), 0.0]] self.total = 0 def addRelativePoint(self, p): self.total += p t = time.time() if len(self.datapoints) < 1 \ or t - self.datapoints[-1][0] > 0.5 \ or self.datapoints[-1][1] == 0.0: self.datapoints.append([time.time(), p]) else: self.datapoints[-1][1] += p self.__purge() def addAbsolutePoint(self, p): self.addRelativePoint(p - self.total) def __iadd__(self, p): self.addRelativePoint(p) return self def __purge(self): t = time.time() if t - self.datapoints[0][0] > self.window: self.datapoints = filter(lambda x: (t - x[0]) < self.window, \ self.datapoints) def getAvg(self): self.__purge() if len(self.datapoints) < 2: return 0.0 t = self.datapoints[-1][0] - self.datapoints[0][0] if t > 0.0: return sum(x[1] for x in self.datapoints) / t else: return 0.0 def __str__(self): return str(self.value()) avg = property(fget=getAvg) class Thread(threading.Thread): """A stoppable subclass of threading.Thread""" def __init__(self): threading.Thread.__init__(self) self.shallStop = False def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): self.shutdown() def shutdown(self): self.shallStop = True self.join() class AsyncXMLRPCServer(SimpleXMLRPCServer.SimpleXMLRPCServer, Thread): """A stoppable XMLRPCServer The main socket is made non-blocking so we can check on self.shallStop from time to time. Sub-classes should add (name:function)-entries to self.methods """ def __init__(self, (iface, port)=('', 17934)): SimpleXMLRPCServer.SimpleXMLRPCServer.__init__(self, (iface, port), \ logRequests=False) Thread.__init__(self) self.setDaemon(True) # Make the main socket non-blocking (for accept()) self.socket.settimeout(1) self.methods = {} self.register_instance(self) def run(self): while not self.shallStop: self.handle_request() def get_request(self): while not self.shallStop: try: sock, addr = self.socket.accept() except socket.timeout: pass else: # Accepted connections are made blocking again sock.settimeout(None) return sock, addr raise socket.timeout("Server has stopped.") def serve_forever(self): while not self.shallStop: time.sleep(1) def shutdown(self): Thread.shutdown(self) self.socket.close() def _dispatch(self, method, params): if method not in self.methods: raise AttributeError else: return self.methods[method](*params) PMK_TESTVECTORS = { 'foo': { 'soZcEvntHVrGRDIxNaBCyUL': (247, 210, 173, 42, 68, 187, 144, 253, 145, 93, 126, 250, 16, 188, 100, 55, 89, 153, 135, 155, 198, 86, 124, 33, 45, 16, 9, 54, 113, 194, 159, 211), 'EVuYtpQCAZzBXyWNRGTI': (5, 48, 168, 39, 10, 98, 151, 201, 8, 80, 23, 138, 19, 24, 24, 50, 66, 214, 189, 180, 159, 97, 194, 27, 212, 124, 114, 100, 253, 62, 50, 170), 'XNuwoiGMnjlkxBHfhyRgZrJItFDqQVESm': (248, 208, 207, 115, 247, 35, 170, 203, 214, 228, 228, 21, 40, 214, 165, 0, 98, 194, 136, 62, 110, 253, 69, 205, 67, 215, 119, 109, 72, 226, 255, 199), 'bdzPWNTaIol': (228, 236, 73, 0, 189, 244, 21, 141, 84, 247, 3, 144, 2, 164, 99, 205, 37, 72, 218, 202, 182, 246, 227, 84, 24, 58, 147, 114, 206, 221, 40, 127), 'nwUaVYhRbvsH': (137, 21, 14, 210, 213, 68, 210, 123, 35, 143, 108, 57, 196, 47, 62, 161, 150, 35, 165, 197, 154, 61, 76, 14, 212, 88, 125, 234, 51, 38, 159, 208), 'gfeuvPBbaDrQHldZzRtXykjFWwAhS': (88, 127, 99, 35, 137, 177, 147, 161, 244, 32, 197, 233, 178, 1, 96, 247, 5, 109, 163, 250, 35, 222, 188, 143, 155, 70, 106, 1, 253, 79, 109, 135), 'QcbpRkAJerVqHz': (158, 124, 37, 190, 197, 150, 225, 165, 3, 34, 104, 147, 107, 253, 233, 127, 33, 239, 75, 11, 169, 187, 127, 171, 187, 165, 166, 187, 95, 107, 137, 212), 'EbYJsCNiwXDmHtgkFVacuOv': (136, 5, 34, 189, 145, 60, 145, 54, 179, 198, 195, 223, 34, 180, 144, 3, 116, 102, 39, 134, 68, 82, 210, 185, 190, 199, 36, 25, 136, 152, 0, 111), 'GpIMrFZwLcqyt': (28, 144, 175, 10, 200, 46, 253, 227, 219, 35, 98, 208, 220, 11, 101, 95, 62, 244, 80, 221, 111, 49, 206, 255, 174, 100, 240, 240, 33, 229, 172, 207), 'tKxgswlaOMLeZVScGDW': (237, 62, 117, 60, 38, 107, 65, 166, 113, 174, 196, 221, 128, 227, 69, 89, 23, 77, 119, 234, 41, 176, 145, 105, 92, 40, 157, 151, 229, 50, 81, 65)}, 'bar': { 'zLwSfveNskZoR': (38, 93, 196, 77, 112, 65, 163, 197, 249, 158, 180, 107, 231, 140, 188, 60, 254, 77, 12, 210, 77, 185, 233, 59, 79, 212, 222, 181, 44, 19, 127, 220), 'lxsvOCeZXop': (91, 39, 98, 36, 82, 2, 162, 106, 12, 244, 4, 113, 155, 120, 131, 133, 11, 209, 12, 12, 240, 213, 203, 156, 129, 148, 28, 64, 31, 61, 162, 13), 'tfHrgLLOA': (110, 72, 123, 80, 222, 233, 150, 54, 40, 99, 205, 155, 177, 157, 174, 172, 87, 11, 247, 164, 87, 85, 136, 165, 21, 107, 93, 212, 71, 133, 145, 211), 'vBgsaSJrlqajUlQJM': (113, 110, 180, 150, 204, 221, 61, 202, 238, 142, 147, 118, 177, 196, 65, 79, 102, 47, 179, 80, 175, 95, 251, 35, 227, 220, 47, 121, 50, 125, 55, 16), 'daDIHwIMKSUaKWXS': (33, 87, 211, 99, 26, 70, 123, 19, 254, 229, 148, 97, 252, 182, 3, 44, 228, 125, 85, 141, 247, 223, 166, 133, 246, 37, 204, 145, 100, 218, 66, 70), 'agHOeAjOpK': (226, 163, 62, 215, 250, 63, 6, 32, 130, 34, 117, 116, 189, 178, 245, 172, 74, 26, 138, 10, 106, 119, 15, 214, 210, 114, 51, 94, 254, 57, 81, 200), 'vRfEagJIzSohxsakj': (61, 71, 159, 35, 233, 27, 138, 30, 228, 121, 38, 201, 57, 83, 192, 211, 248, 207, 149, 12, 147, 70, 190, 216, 52, 14, 165, 190, 226, 180, 62, 210), 'PuDomzkiwsejblaXs': (227, 164, 137, 231, 16, 31, 222, 169, 134, 1, 238, 190, 55, 126, 255, 88, 178, 118, 148, 119, 244, 130, 183, 219, 124, 249, 194, 96, 94, 159, 163, 185), 'RErvpNrOsW': (24, 145, 197, 137, 14, 154, 1, 36, 73, 148, 9, 192, 138, 157, 164, 81, 47, 184, 41, 75, 225, 34, 71, 153, 59, 253, 127, 179, 242, 193, 246, 177), 'ipptbpKkCCep': (81, 34, 253, 39, 124, 19, 234, 163, 32, 10, 104, 88, 249, 29, 40, 142, 24, 173, 1, 68, 187, 212, 21, 189, 74, 88, 83, 228, 7, 100, 23, 244)}} for essid in PMK_TESTVECTORS: for pw in PMK_TESTVECTORS[essid]: PMK_TESTVECTORS[essid][pw] = \ ''.join(map(chr, PMK_TESTVECTORS[essid][pw])) del essid del pw pyrit-0.4.0/cpyrit/_cpyrit_cpu_sse2.S0000664000076400007640000005206411526274651020074 0ustar slaveslave00000000000000/* # # Copyright 2009-2011, Lukas Lueg, lukas.lueg@gmail.com # # SHA-1 SSE2 Copyright 2008, 2009, Alvaro Salmador, naplam33@msn.com, # included here with permission, under the licensing terms specified below. # ported from SHA-1 MMX by Simon Marechal, simon@banquise.net, # included here with permission, under the licensing terms specified below. # # This file is part of Pyrit. # # Pyrit is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Pyrit is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Pyrit. If not, see . # # Additional permission under GNU GPL version 3 section 7 # # If you modify this Program, or any covered work, by linking or # combining it with the OpenSSL project's "OpenSSL" library (or a # modified version of that library), containing parts covered by # the terms of OpenSSL/SSLeay license, the licensors of this # Program grant you additional permission to convey the resulting # work. Corresponding Source for a non-source form of such a # combination shall include the source code for the parts of the # OpenSSL library used as well as that of the covered work. */ #include "_cpyrit_cpu.h" #ifdef COMPILE_SSE2 #define ctxa %xmm0 #define ctxb %xmm1 #define ctxc %xmm2 #define ctxd %xmm3 #define ctxe %xmm4 #define tmp1 %xmm5 #define tmp2 %xmm6 #define tmp3 %xmm7 #define tmp4 ctxa #define tmp5 ctxb #ifdef __x86_64__ #define eax_rdi %rdi #define ebx_rbx %rbx #define ecx_rcx %rcx #define ecx_rdx %rdx #define edx_rdx %rdx #define edx_rsi %rsi #else #define eax_rdi %eax #define ebx_rbx %ebx #define ecx_rcx %ecx #define ecx_rdx %ecx #define edx_rdx %edx #define edx_rsi %edx #endif #define sha1_cst_stage0 4*5*4 + 4*0*4 #define sha1_cst_stage1 4*5*4 + 4*1*4 #define sha1_cst_stage2 4*5*4 + 4*2*4 #define sha1_cst_stage3 4*5*4 + 4*3*4 #define sha1_cst_ff00 4*5*4 + 4*4*4 #define sha1_cst_00ff 4*5*4 + 4*5*4 #define SHA1_F0(x,y,z) \ movdqa x, tmp2; \ movdqa x, tmp1; \ pand y, tmp2; \ pandn z, tmp1; \ por tmp2, tmp1; #define SHA1_F1(x,y,z) \ movdqa z, tmp1; \ pxor y, tmp1; \ pxor x, tmp1; #define SHA1_F2(x,y,z) \ movdqa x, tmp1; \ movdqa x, tmp2; \ pand y, tmp1; \ por y, tmp2; \ pand z, tmp2; \ por tmp2, tmp1; #define SHA1_subRoundX(a, b, c, d, e, f, k, data) \ f(b,c,d); \ movdqa a, tmp2; \ movdqa a, tmp3; \ paddd tmp1, e; \ pslld $5, tmp2; \ psrld $27, tmp3; \ por tmp3, tmp2; \ paddd tmp2, e; \ movdqa b, tmp2; \ pslld $30, b; \ paddd k, e; \ psrld $2, tmp2; \ por tmp2, b; \ movdqa (data*16)(edx_rsi), tmp1; \ movdqa tmp1, tmp2; \ pand sha1_cst_ff00(eax_rdi), tmp1; \ pand sha1_cst_00ff(eax_rdi), tmp2; \ psrld $8, tmp1; \ pslld $8, tmp2; \ por tmp2, tmp1; \ movdqa tmp1, tmp2; \ psrld $16, tmp1; \ pslld $16, tmp2; \ por tmp2, tmp1; \ movdqa tmp1, (data*16)(ecx_rdx); \ paddd tmp1, e; #define SHA1_subRoundY(a, b, c, d, e, f, k, data) \ movdqa ((data- 3)*16)(ecx_rdx), tmp1; \ pxor ((data- 8)*16)(ecx_rdx), tmp1; \ pxor ((data-14)*16)(ecx_rdx), tmp1; \ pxor ((data-16)*16)(ecx_rdx), tmp1; \ movdqa tmp1, tmp2; \ pslld $1, tmp1; \ psrld $31, tmp2; \ por tmp2, tmp1; \ movdqa tmp1, (data*16)(ecx_rdx); \ paddd tmp1, e; \ f(b,c,d); \ movdqa a, tmp2; \ movdqa a, tmp3; \ paddd tmp1, e; \ pslld $5, tmp2; \ psrld $27, tmp3; \ por tmp3, tmp2; \ paddd tmp2, e; \ movdqa b, tmp2; \ pslld $30, b; \ paddd k, e; \ psrld $2, tmp2; \ por tmp2, b; #define MD5_F(b, c, d) \ movapd c, tmp1; \ pxor d, tmp1; \ pand b, tmp1; \ pxor d, tmp1 #define MD5_G(b, c, d) \ movapd c, tmp1; \ pxor b, tmp1; \ pand d, tmp1; \ pxor c, tmp1 #define MD5_H(b, c, d) \ movapd b, tmp1; \ pxor c, tmp1; \ pxor d, tmp1 #define MD5_I(b, c, d) \ movapd d, tmp1; \ pandn tmp2, tmp1; \ por b, tmp1; \ pxor c, tmp1; #define MD5_subRound(f, a, b, c, d, x, t, s) \ f(b, c, d); \ paddd (x * 4*4)(edx_rsi), tmp1; \ paddd (t * 4*4)(ecx_rdx), a; \ paddd tmp1, a; \ movapd a, tmp3; \ psrld $(32 - s), tmp3; \ pslld $s, a; \ por tmp3, a; \ paddd b, a .globl detect_sse2, _detect_sse2;; .globl sse2_sha1_update, _sse2_sha1_update; .globl sse2_sha1_finalize, _sse2_sha1_finalize; .globl sse2_md5_update, _sse2_md5_update .text // arg 1 (eax) (64bit: rdi): context + constants (4*5*4 + 4*6*4 bytes) // arg 2 (edx) (64bit: rsi): digests (4*5*4 bytes) _sse2_sha1_finalize: sse2_sha1_finalize: movdqa 0(eax_rdi), ctxa movdqa 16(eax_rdi), ctxb movdqa 32(eax_rdi), ctxc movdqa 48(eax_rdi), ctxd movdqa 64(eax_rdi), ctxe movdqa sha1_cst_ff00(eax_rdi), tmp3 movdqa ctxa, tmp1 movdqa ctxb, tmp2 pand tmp3, ctxa pand tmp3, ctxb movdqa sha1_cst_00ff(eax_rdi), tmp3 pand tmp3, tmp1 pand tmp3, tmp2 psrld $8, ctxa psrld $8, ctxb pslld $8, tmp1 pslld $8, tmp2 por tmp1, ctxa por tmp2, ctxb movdqa ctxa, tmp1 movdqa ctxb, tmp2 psrld $16, ctxa psrld $16, ctxb pslld $16, tmp1 pslld $16, tmp2 por tmp1, ctxa por tmp2, ctxb movdqa ctxa, 0(edx_rsi) movdqa ctxb, 16(edx_rsi) movdqa sha1_cst_ff00(eax_rdi), tmp5 movdqa ctxc, tmp1 movdqa ctxd, tmp2 movdqa ctxe, tmp3 pand tmp5, ctxc pand tmp5, ctxd pand tmp5, ctxe movdqa sha1_cst_00ff(eax_rdi), tmp5 pand tmp5, tmp1 pand tmp5, tmp2 pand tmp5, tmp3 psrld $8, ctxc psrld $8, ctxd psrld $8, ctxe pslld $8, tmp1 pslld $8, tmp2 pslld $8, tmp3 por tmp1, ctxc por tmp2, ctxd por tmp3, ctxe movdqa ctxc, tmp1 movdqa ctxd, tmp2 movdqa ctxe, tmp3 psrld $16, ctxc psrld $16, ctxd psrld $16, ctxe pslld $16, tmp1 pslld $16, tmp2 pslld $16, tmp3 por tmp1, ctxc por tmp2, ctxd por tmp3, ctxe movdqa ctxc, 32(edx_rsi) movdqa ctxd, 48(edx_rsi) movdqa ctxe, 64(edx_rsi) ret // arg 1 (eax) (64bit: rdi): context + constants (4*5*4 + 4*6*4 bytes) // arg 2 (edx) (64bit: rsi): input data (4*64 bytes) // arg 3 (ecx) (64bit: rdx): workspace (4*80*4 bytes) _sse2_sha1_update: sse2_sha1_update: movdqa 0(eax_rdi), ctxa movdqa 16(eax_rdi), ctxb movdqa 32(eax_rdi), ctxc movdqa 48(eax_rdi), ctxd movdqa 64(eax_rdi), ctxe prefetchnta (edx_rsi) /* round0 */ SHA1_subRoundX( ctxa, ctxb, ctxc, ctxd, ctxe, SHA1_F0, sha1_cst_stage0(eax_rdi), 0 ); SHA1_subRoundX( ctxe, ctxa, ctxb, ctxc, ctxd, SHA1_F0, sha1_cst_stage0(eax_rdi), 1 ); SHA1_subRoundX( ctxd, ctxe, ctxa, ctxb, ctxc, SHA1_F0, sha1_cst_stage0(eax_rdi), 2 ); SHA1_subRoundX( ctxc, ctxd, ctxe, ctxa, ctxb, SHA1_F0, sha1_cst_stage0(eax_rdi), 3 ); SHA1_subRoundX( ctxb, ctxc, ctxd, ctxe, ctxa, SHA1_F0, sha1_cst_stage0(eax_rdi), 4 ); SHA1_subRoundX( ctxa, ctxb, ctxc, ctxd, ctxe, SHA1_F0, sha1_cst_stage0(eax_rdi), 5 ); SHA1_subRoundX( ctxe, ctxa, ctxb, ctxc, ctxd, SHA1_F0, sha1_cst_stage0(eax_rdi), 6 ); SHA1_subRoundX( ctxd, ctxe, ctxa, ctxb, ctxc, SHA1_F0, sha1_cst_stage0(eax_rdi), 7 ); SHA1_subRoundX( ctxc, ctxd, ctxe, ctxa, ctxb, SHA1_F0, sha1_cst_stage0(eax_rdi), 8 ); SHA1_subRoundX( ctxb, ctxc, ctxd, ctxe, ctxa, SHA1_F0, sha1_cst_stage0(eax_rdi), 9 ); SHA1_subRoundX( ctxa, ctxb, ctxc, ctxd, ctxe, SHA1_F0, sha1_cst_stage0(eax_rdi), 10 ); SHA1_subRoundX( ctxe, ctxa, ctxb, ctxc, ctxd, SHA1_F0, sha1_cst_stage0(eax_rdi), 11 ); SHA1_subRoundX( ctxd, ctxe, ctxa, ctxb, ctxc, SHA1_F0, sha1_cst_stage0(eax_rdi), 12 ); SHA1_subRoundX( ctxc, ctxd, ctxe, ctxa, ctxb, SHA1_F0, sha1_cst_stage0(eax_rdi), 13 ); SHA1_subRoundX( ctxb, ctxc, ctxd, ctxe, ctxa, SHA1_F0, sha1_cst_stage0(eax_rdi), 14 ); SHA1_subRoundX( ctxa, ctxb, ctxc, ctxd, ctxe, SHA1_F0, sha1_cst_stage0(eax_rdi), 15 ); SHA1_subRoundY( ctxe, ctxa, ctxb, ctxc, ctxd, SHA1_F0, sha1_cst_stage0(eax_rdi), 16 ); SHA1_subRoundY( ctxd, ctxe, ctxa, ctxb, ctxc, SHA1_F0, sha1_cst_stage0(eax_rdi), 17 ); SHA1_subRoundY( ctxc, ctxd, ctxe, ctxa, ctxb, SHA1_F0, sha1_cst_stage0(eax_rdi), 18 ); SHA1_subRoundY( ctxb, ctxc, ctxd, ctxe, ctxa, SHA1_F0, sha1_cst_stage0(eax_rdi), 19 ); /* round1 */ SHA1_subRoundY( ctxa, ctxb, ctxc, ctxd, ctxe, SHA1_F1, sha1_cst_stage1(eax_rdi), 20 ); SHA1_subRoundY( ctxe, ctxa, ctxb, ctxc, ctxd, SHA1_F1, sha1_cst_stage1(eax_rdi), 21 ); SHA1_subRoundY( ctxd, ctxe, ctxa, ctxb, ctxc, SHA1_F1, sha1_cst_stage1(eax_rdi), 22 ); SHA1_subRoundY( ctxc, ctxd, ctxe, ctxa, ctxb, SHA1_F1, sha1_cst_stage1(eax_rdi), 23 ); SHA1_subRoundY( ctxb, ctxc, ctxd, ctxe, ctxa, SHA1_F1, sha1_cst_stage1(eax_rdi), 24 ); SHA1_subRoundY( ctxa, ctxb, ctxc, ctxd, ctxe, SHA1_F1, sha1_cst_stage1(eax_rdi), 25 ); SHA1_subRoundY( ctxe, ctxa, ctxb, ctxc, ctxd, SHA1_F1, sha1_cst_stage1(eax_rdi), 26 ); SHA1_subRoundY( ctxd, ctxe, ctxa, ctxb, ctxc, SHA1_F1, sha1_cst_stage1(eax_rdi), 27 ); SHA1_subRoundY( ctxc, ctxd, ctxe, ctxa, ctxb, SHA1_F1, sha1_cst_stage1(eax_rdi), 28 ); SHA1_subRoundY( ctxb, ctxc, ctxd, ctxe, ctxa, SHA1_F1, sha1_cst_stage1(eax_rdi), 29 ); SHA1_subRoundY( ctxa, ctxb, ctxc, ctxd, ctxe, SHA1_F1, sha1_cst_stage1(eax_rdi), 30 ); SHA1_subRoundY( ctxe, ctxa, ctxb, ctxc, ctxd, SHA1_F1, sha1_cst_stage1(eax_rdi), 31 ); SHA1_subRoundY( ctxd, ctxe, ctxa, ctxb, ctxc, SHA1_F1, sha1_cst_stage1(eax_rdi), 32 ); SHA1_subRoundY( ctxc, ctxd, ctxe, ctxa, ctxb, SHA1_F1, sha1_cst_stage1(eax_rdi), 33 ); SHA1_subRoundY( ctxb, ctxc, ctxd, ctxe, ctxa, SHA1_F1, sha1_cst_stage1(eax_rdi), 34 ); SHA1_subRoundY( ctxa, ctxb, ctxc, ctxd, ctxe, SHA1_F1, sha1_cst_stage1(eax_rdi), 35 ); SHA1_subRoundY( ctxe, ctxa, ctxb, ctxc, ctxd, SHA1_F1, sha1_cst_stage1(eax_rdi), 36 ); SHA1_subRoundY( ctxd, ctxe, ctxa, ctxb, ctxc, SHA1_F1, sha1_cst_stage1(eax_rdi), 37 ); SHA1_subRoundY( ctxc, ctxd, ctxe, ctxa, ctxb, SHA1_F1, sha1_cst_stage1(eax_rdi), 38 ); SHA1_subRoundY( ctxb, ctxc, ctxd, ctxe, ctxa, SHA1_F1, sha1_cst_stage1(eax_rdi), 39 ); /* round2 */ SHA1_subRoundY( ctxa, ctxb, ctxc, ctxd, ctxe, SHA1_F2, sha1_cst_stage2(eax_rdi), 40 ); SHA1_subRoundY( ctxe, ctxa, ctxb, ctxc, ctxd, SHA1_F2, sha1_cst_stage2(eax_rdi), 41 ); SHA1_subRoundY( ctxd, ctxe, ctxa, ctxb, ctxc, SHA1_F2, sha1_cst_stage2(eax_rdi), 42 ); SHA1_subRoundY( ctxc, ctxd, ctxe, ctxa, ctxb, SHA1_F2, sha1_cst_stage2(eax_rdi), 43 ); SHA1_subRoundY( ctxb, ctxc, ctxd, ctxe, ctxa, SHA1_F2, sha1_cst_stage2(eax_rdi), 44 ); SHA1_subRoundY( ctxa, ctxb, ctxc, ctxd, ctxe, SHA1_F2, sha1_cst_stage2(eax_rdi), 45 ); SHA1_subRoundY( ctxe, ctxa, ctxb, ctxc, ctxd, SHA1_F2, sha1_cst_stage2(eax_rdi), 46 ); SHA1_subRoundY( ctxd, ctxe, ctxa, ctxb, ctxc, SHA1_F2, sha1_cst_stage2(eax_rdi), 47 ); SHA1_subRoundY( ctxc, ctxd, ctxe, ctxa, ctxb, SHA1_F2, sha1_cst_stage2(eax_rdi), 48 ); SHA1_subRoundY( ctxb, ctxc, ctxd, ctxe, ctxa, SHA1_F2, sha1_cst_stage2(eax_rdi), 49 ); SHA1_subRoundY( ctxa, ctxb, ctxc, ctxd, ctxe, SHA1_F2, sha1_cst_stage2(eax_rdi), 50 ); SHA1_subRoundY( ctxe, ctxa, ctxb, ctxc, ctxd, SHA1_F2, sha1_cst_stage2(eax_rdi), 51 ); SHA1_subRoundY( ctxd, ctxe, ctxa, ctxb, ctxc, SHA1_F2, sha1_cst_stage2(eax_rdi), 52 ); SHA1_subRoundY( ctxc, ctxd, ctxe, ctxa, ctxb, SHA1_F2, sha1_cst_stage2(eax_rdi), 53 ); SHA1_subRoundY( ctxb, ctxc, ctxd, ctxe, ctxa, SHA1_F2, sha1_cst_stage2(eax_rdi), 54 ); SHA1_subRoundY( ctxa, ctxb, ctxc, ctxd, ctxe, SHA1_F2, sha1_cst_stage2(eax_rdi), 55 ); SHA1_subRoundY( ctxe, ctxa, ctxb, ctxc, ctxd, SHA1_F2, sha1_cst_stage2(eax_rdi), 56 ); SHA1_subRoundY( ctxd, ctxe, ctxa, ctxb, ctxc, SHA1_F2, sha1_cst_stage2(eax_rdi), 57 ); SHA1_subRoundY( ctxc, ctxd, ctxe, ctxa, ctxb, SHA1_F2, sha1_cst_stage2(eax_rdi), 58 ); SHA1_subRoundY( ctxb, ctxc, ctxd, ctxe, ctxa, SHA1_F2, sha1_cst_stage2(eax_rdi), 59 ); /* round3 */ SHA1_subRoundY( ctxa, ctxb, ctxc, ctxd, ctxe, SHA1_F1, sha1_cst_stage3(eax_rdi), 60 ); SHA1_subRoundY( ctxe, ctxa, ctxb, ctxc, ctxd, SHA1_F1, sha1_cst_stage3(eax_rdi), 61 ); SHA1_subRoundY( ctxd, ctxe, ctxa, ctxb, ctxc, SHA1_F1, sha1_cst_stage3(eax_rdi), 62 ); SHA1_subRoundY( ctxc, ctxd, ctxe, ctxa, ctxb, SHA1_F1, sha1_cst_stage3(eax_rdi), 63 ); SHA1_subRoundY( ctxb, ctxc, ctxd, ctxe, ctxa, SHA1_F1, sha1_cst_stage3(eax_rdi), 64 ); SHA1_subRoundY( ctxa, ctxb, ctxc, ctxd, ctxe, SHA1_F1, sha1_cst_stage3(eax_rdi), 65 ); SHA1_subRoundY( ctxe, ctxa, ctxb, ctxc, ctxd, SHA1_F1, sha1_cst_stage3(eax_rdi), 66 ); SHA1_subRoundY( ctxd, ctxe, ctxa, ctxb, ctxc, SHA1_F1, sha1_cst_stage3(eax_rdi), 67 ); SHA1_subRoundY( ctxc, ctxd, ctxe, ctxa, ctxb, SHA1_F1, sha1_cst_stage3(eax_rdi), 68 ); SHA1_subRoundY( ctxb, ctxc, ctxd, ctxe, ctxa, SHA1_F1, sha1_cst_stage3(eax_rdi), 69 ); SHA1_subRoundY( ctxa, ctxb, ctxc, ctxd, ctxe, SHA1_F1, sha1_cst_stage3(eax_rdi), 70 ); SHA1_subRoundY( ctxe, ctxa, ctxb, ctxc, ctxd, SHA1_F1, sha1_cst_stage3(eax_rdi), 71 ); SHA1_subRoundY( ctxd, ctxe, ctxa, ctxb, ctxc, SHA1_F1, sha1_cst_stage3(eax_rdi), 72 ); SHA1_subRoundY( ctxc, ctxd, ctxe, ctxa, ctxb, SHA1_F1, sha1_cst_stage3(eax_rdi), 73 ); SHA1_subRoundY( ctxb, ctxc, ctxd, ctxe, ctxa, SHA1_F1, sha1_cst_stage3(eax_rdi), 74 ); SHA1_subRoundY( ctxa, ctxb, ctxc, ctxd, ctxe, SHA1_F1, sha1_cst_stage3(eax_rdi), 75 ); SHA1_subRoundY( ctxe, ctxa, ctxb, ctxc, ctxd, SHA1_F1, sha1_cst_stage3(eax_rdi), 76 ); SHA1_subRoundY( ctxd, ctxe, ctxa, ctxb, ctxc, SHA1_F1, sha1_cst_stage3(eax_rdi), 77 ); SHA1_subRoundY( ctxc, ctxd, ctxe, ctxa, ctxb, SHA1_F1, sha1_cst_stage3(eax_rdi), 78 ); SHA1_subRoundY( ctxb, ctxc, ctxd, ctxe, ctxa, SHA1_F1, sha1_cst_stage3(eax_rdi), 79 ); paddd 0(eax_rdi), ctxa paddd 16(eax_rdi), ctxb paddd 32(eax_rdi), ctxc paddd 48(eax_rdi), ctxd paddd 64(eax_rdi), ctxe movdqa ctxa, 0(eax_rdi) movdqa ctxb, 16(eax_rdi) movdqa ctxc, 32(eax_rdi) movdqa ctxd, 48(eax_rdi) movdqa ctxe, 64(eax_rdi) ret // arg 1 (eax) (64bit: rdi): context (4*4*4 bytes) // arg 2 (edx) (64bit: rsi): input data (4*64 bytes) // arg 3 (ecx) (64bit: rdx): constants (4*64*4 bytes) _sse2_md5_update: sse2_md5_update: movdqa 0(eax_rdi), ctxa movdqa 16(eax_rdi), ctxb movdqa 32(eax_rdi), ctxc movdqa 48(eax_rdi), ctxd prefetchnta (edx_rsi) MD5_subRound( MD5_F, ctxa, ctxb, ctxc, ctxd, 0, 0, 7 ) MD5_subRound( MD5_F, ctxd, ctxa, ctxb, ctxc, 1, 1, 12 ) MD5_subRound( MD5_F, ctxc, ctxd, ctxa, ctxb, 2, 2, 17 ) MD5_subRound( MD5_F, ctxb, ctxc, ctxd, ctxa, 3, 3, 22 ) MD5_subRound( MD5_F, ctxa, ctxb, ctxc, ctxd, 4, 4, 7 ) MD5_subRound( MD5_F, ctxd, ctxa, ctxb, ctxc, 5, 5, 12 ) MD5_subRound( MD5_F, ctxc, ctxd, ctxa, ctxb, 6, 6, 17 ) MD5_subRound( MD5_F, ctxb, ctxc, ctxd, ctxa, 7, 7, 22 ) MD5_subRound( MD5_F, ctxa, ctxb, ctxc, ctxd, 8, 8, 7 ) MD5_subRound( MD5_F, ctxd, ctxa, ctxb, ctxc, 9, 9, 12 ) MD5_subRound( MD5_F, ctxc, ctxd, ctxa, ctxb, 10, 10, 17 ) MD5_subRound( MD5_F, ctxb, ctxc, ctxd, ctxa, 11, 11, 22 ) MD5_subRound( MD5_F, ctxa, ctxb, ctxc, ctxd, 12, 12, 7 ) MD5_subRound( MD5_F, ctxd, ctxa, ctxb, ctxc, 13, 13, 12 ) MD5_subRound( MD5_F, ctxc, ctxd, ctxa, ctxb, 14, 14, 17 ) MD5_subRound( MD5_F, ctxb, ctxc, ctxd, ctxa, 15, 15, 22 ) MD5_subRound( MD5_G, ctxa, ctxb, ctxc, ctxd, 1, 16, 5 ) MD5_subRound( MD5_G, ctxd, ctxa, ctxb, ctxc, 6, 17, 9 ) MD5_subRound( MD5_G, ctxc, ctxd, ctxa, ctxb, 11, 18, 14 ) MD5_subRound( MD5_G, ctxb, ctxc, ctxd, ctxa, 0, 19, 20 ) MD5_subRound( MD5_G, ctxa, ctxb, ctxc, ctxd, 5, 20, 5 ) MD5_subRound( MD5_G, ctxd, ctxa, ctxb, ctxc, 10, 21, 9 ) MD5_subRound( MD5_G, ctxc, ctxd, ctxa, ctxb, 15, 22, 14 ) MD5_subRound( MD5_G, ctxb, ctxc, ctxd, ctxa, 4, 23, 20 ) MD5_subRound( MD5_G, ctxa, ctxb, ctxc, ctxd, 9, 24, 5 ) MD5_subRound( MD5_G, ctxd, ctxa, ctxb, ctxc, 14, 25, 9 ) MD5_subRound( MD5_G, ctxc, ctxd, ctxa, ctxb, 3, 26, 14 ) MD5_subRound( MD5_G, ctxb, ctxc, ctxd, ctxa, 8, 27, 20 ) MD5_subRound( MD5_G, ctxa, ctxb, ctxc, ctxd, 13, 28, 5 ) MD5_subRound( MD5_G, ctxd, ctxa, ctxb, ctxc, 2, 29, 9 ) MD5_subRound( MD5_G, ctxc, ctxd, ctxa, ctxb, 7, 30, 14 ) MD5_subRound( MD5_G, ctxb, ctxc, ctxd, ctxa, 12, 31, 20 ) MD5_subRound( MD5_H, ctxa, ctxb, ctxc, ctxd, 5, 32, 4 ) MD5_subRound( MD5_H, ctxd, ctxa, ctxb, ctxc, 8, 33, 11 ) MD5_subRound( MD5_H, ctxc, ctxd, ctxa, ctxb, 11, 34, 16 ) MD5_subRound( MD5_H, ctxb, ctxc, ctxd, ctxa, 14, 35, 23 ) MD5_subRound( MD5_H, ctxa, ctxb, ctxc, ctxd, 1, 36, 4 ) MD5_subRound( MD5_H, ctxd, ctxa, ctxb, ctxc, 4, 37, 11 ) MD5_subRound( MD5_H, ctxc, ctxd, ctxa, ctxb, 7, 38, 16 ) MD5_subRound( MD5_H, ctxb, ctxc, ctxd, ctxa, 10, 39, 23 ) MD5_subRound( MD5_H, ctxa, ctxb, ctxc, ctxd, 13, 40, 4 ) MD5_subRound( MD5_H, ctxd, ctxa, ctxb, ctxc, 0, 41, 11 ) MD5_subRound( MD5_H, ctxc, ctxd, ctxa, ctxb, 3, 42, 16 ) MD5_subRound( MD5_H, ctxb, ctxc, ctxd, ctxa, 6, 43, 23 ) MD5_subRound( MD5_H, ctxa, ctxb, ctxc, ctxd, 9, 44, 4 ) MD5_subRound( MD5_H, ctxd, ctxa, ctxb, ctxc, 12, 45, 11 ) MD5_subRound( MD5_H, ctxc, ctxd, ctxa, ctxb, 15, 46, 16 ) MD5_subRound( MD5_H, ctxb, ctxc, ctxd, ctxa, 2, 47, 23 ) pcmpeqd tmp2, tmp2; // Bitmask for logical NOT in MD5_I MD5_subRound( MD5_I, ctxa, ctxb, ctxc, ctxd, 0, 48, 6 ) MD5_subRound( MD5_I, ctxd, ctxa, ctxb, ctxc, 7, 49, 10 ) MD5_subRound( MD5_I, ctxc, ctxd, ctxa, ctxb, 14, 50, 15 ) MD5_subRound( MD5_I, ctxb, ctxc, ctxd, ctxa, 5, 51, 21 ) MD5_subRound( MD5_I, ctxa, ctxb, ctxc, ctxd, 12, 52, 6 ) MD5_subRound( MD5_I, ctxd, ctxa, ctxb, ctxc, 3, 53, 10 ) MD5_subRound( MD5_I, ctxc, ctxd, ctxa, ctxb, 10, 54, 15 ) MD5_subRound( MD5_I, ctxb, ctxc, ctxd, ctxa, 1, 55, 21 ) MD5_subRound( MD5_I, ctxa, ctxb, ctxc, ctxd, 8, 56, 6 ) MD5_subRound( MD5_I, ctxd, ctxa, ctxb, ctxc, 15, 57, 10 ) MD5_subRound( MD5_I, ctxc, ctxd, ctxa, ctxb, 6, 58, 15 ) MD5_subRound( MD5_I, ctxb, ctxc, ctxd, ctxa, 13, 59, 21 ) MD5_subRound( MD5_I, ctxa, ctxb, ctxc, ctxd, 4, 60, 6 ) MD5_subRound( MD5_I, ctxd, ctxa, ctxb, ctxc, 11, 61, 10 ) MD5_subRound( MD5_I, ctxc, ctxd, ctxa, ctxb, 2, 62, 15 ) MD5_subRound( MD5_I, ctxb, ctxc, ctxd, ctxa, 9, 63, 21 ) paddd 0(eax_rdi), ctxa paddd 16(eax_rdi), ctxb paddd 32(eax_rdi), ctxc paddd 48(eax_rdi), ctxd movdqa ctxa, 0(eax_rdi) movdqa ctxb, 16(eax_rdi) movdqa ctxc, 32(eax_rdi) movdqa ctxd, 48(eax_rdi) ret /* returns 1 if SSE2 is supported, 0 otherwise */ _detect_sse2: detect_sse2: #ifndef __x86_64__ pushfl pushfl popl %eax movl %eax, %ecx xorl $0x200000, %eax push %eax popfl pushfl popl %eax popfl xorl %ecx, %eax jnz do_cpuid ret do_cpuid: #endif push ebx_rbx push ecx_rcx push edx_rdx movl $1, %eax cpuid testl $0x04000000, %edx // bit 26 (SSE2) jz no_sse2 movl $1, %eax jmp cpuid_exit no_sse2: xorl %eax, %eax cpuid_exit: pop edx_rdx pop ecx_rcx pop ebx_rbx ret #endif // COMPILE_SSE2 #if defined(__linux__) && defined(__ELF__) .section .note.GNU-stack,"",%progbits #endif pyrit-0.4.0/CHANGELOG0000664000076400007640000000431311526274651014366 0ustar slaveslave00000000000000Changes since 0.3.0: * Added CPyrit-CAL++ * Added CLI-function 'check_db' * Added CLI-option '-h' * Added option to batch-create ESSIDs by reading them for a file * Complete rework of packet-parsing and handshake detection * Make default workunit-size configureable (workunit_size) * Make maximum number of used CPUs configurable (limit_ncpus) * Use GPU-native bitwise rotation with OpenCL if possible * Use libpcap to access capture-devices/files * CUDA-plugin now compatible with Fermi-GPUs * OpenCL-plugin now builds on MacOS 10.6 * Link with libcrypto instead of libssl * Fixed CUDA-plugin on MacOS 10.6 * Fixed SSE2-detection on old CPUs * Fixed database-indices * Fixed rare IndexError in EAPOLCracker * Numerous fixes in storage-relay code * Fix deprecation-warning with sqlalchemy.Binary * Various API-changes Changes since 0.2.4: * Removed CPyrit-Stream in favor of OpenCL * Added Network-Core * Added SQL-Storage * Added Remote-Storage * Added CLI-function 'stripLive' * Added CLI-function 'attack_cowpatty' * Added CLI-function 'import_unique_passwords' * Added CLI-function 'relay' * Added CLI-function 'serve' * Added SSE2-support for EAPOLCracker * Added output-option to all attack-modes * Fixed EAPOLCracker picking the wrong KeyScheme * Improved lazy-loading of files * Sourcecode now almost completely PEP8-compliant Changes since 0.2.3: * Added module 'pckttools' * Added CLI-function 'analyze' * Added CLI-function 'attack_batch' * Added CLI-function 'attack_db' * Added CLI-function 'attack_passthrough' * Added CLI-function 'strip' * Fixed SSE2 on MacOS * Fixed SSE2 with SELinux * Fixed handling of passwords containing NULLs * Improved 'benchmark' * Most functions can now handle gzip-compressed files (-f) Changes since 0.2.2: * Added docstrings * Added CLI-function 'delete_essid' * Added CLI-function 'verify' * Added CLI-function 'selftest' * Added Core for OpenCL * Added SSE2-path to CPU-Core * Fixed 'CUDA_ERROR_INVALID_IMAGE' when using CUDA 2.2 * Fixed process exit codes * Improved scheduling between client and hardware * Improved storage-code * Improved performance for almost all CLI-functions * Builds from SVN-directories now carry their revision-numberpyrit-0.4.0/README0000664000076400007640000000633011524611424014024 0ustar slaveslave00000000000000Pyrit +++++ A GPGPU-driven WPA/WPA2-PSK key cracker Pyrit exploits the computational power of many-core- and GPGPU-platforms to create massive databases, pre-computing part of the WPA/WPA2-PSK authentication phase in a space-time tradeoff. It is a powerful attack against one of the world's most used security-protocols http://code.google.com/p/pyrit/ Requirements ++++++++++++ Pyrit compiles and runs on Linux, FreeBSD and MacOS. Windows is not (and probably never will be) supported; there are however some reports of successful installations on Windows with the help of MinGW. A couple of libraries and headers are required to build Pyrit: * Python >=2.5 and it's headers http://www.python.org * The OpenSSL library and headers http://www.openssl.org * The Pcap library and it's headers http://www.tcpdump.org * Scapy >=2.x (optional/runtime) http://www.secdev.org/projects/scapy/ * SQLAlchemy >=0.5 (optional/runtime) http://www.sqlalchemy.org Linux users running a binary distribution may need to install the development packages for Python (e.g. python-devel), OpenSSL (e.g. openssl-devel or libssl-dev) and libpcap (e.g. libpcap-devel). You also need a C-compiler like gcc. Users of MacOS probably only need to have Xcode installed. Installing ++++++++++ Unpack the source-code into a new directory like this: tar xvzf pyrit-0.4.0.tar.gz Switch to the main module's directory. We use Python's distutils to compile and install the code: cd pyrit-0.4.0 python setup.py build If everything went well and no errors are thrown at you, use distutils again to install Pyrit: sudo python setup.py install You can now execute 'pyrit' from your commandline; leave the source-code's directory before doing so to prevent Python from getting confused with module-lookups. Reporting bugs / Getting help +++++++++++++++++++++++++++++ Please take a look at the Troubleshooting-page in Pyrit's Wiki if you have problems compiling or running Pyrit: http://code.google.com/p/pyrit/wiki/Troubleshooting Please report bugs, glitches and enhancement proposals using Pyrit's issue- tracker: http://code.google.com/p/pyrit/issues/list License +++++++ Pyrit is free software - free as in freedom. Everyone can inspect, copy or modify it and share derived work under the GNU General Public License v3. You should have received a copy of the GNU General Public License along with Pyrit. If not, see . Pyrit comes with a set of test-files: * "wpapsk-linksys.dump.gz" and "wpa2psk-linksys.dump.gz" from Cowpatty with permission from Joshua Wright (http://www.willhackforsushi.com). * "wpa2psk-MOM1.dump.gz" from Pyrit issue #120 with permission from the original owner (http://code.google.com/p/pyrit/issues/detail?id=120). * "wpa2psk-2WIRE972.dump.gz" from Pyrit issue #111 with permission from the original owner (http://code.google.com/p/pyrit/issues/detail?id=111) * "wpapsk-virgin_broadband.dump.gz" from Aircrack-ng ticket #721 with permission from the original owner (http://trac.aircrack-ng.org/ticket/721). * "wpa2psk-Red_Apple.dump.gz" from Aircrack-ng ticket #491 with permission from the original owner (http://trac.aircrack-ng.org/ticket/491).