PNG  IHDRX cHRMz&u0`:pQ<bKGD pHYsodtIME MeqIDATxw]Wug^Qd˶ 6`!N:!@xI~)%7%@Bh&`lnjVF29gΨ4E$|>cɚ{gk= %,a KX%,a KX%,a KX%,a KX%,a KX%,a KX%, b` ǟzeאfp]<!SJmɤY޲ڿ,%c ~ع9VH.!Ͳz&QynֺTkRR.BLHi٪:l;@(!MԴ=žI,:o&N'Kù\vRmJ雵֫AWic H@" !: Cé||]k-Ha oݜ:y F())u]aG7*JV@J415p=sZH!=!DRʯvɱh~V\}v/GKY$n]"X"}t@ xS76^[bw4dsce)2dU0 CkMa-U5tvLƀ~mlMwfGE/-]7XAƟ`׮g ewxwC4\[~7@O-Q( a*XGƒ{ ՟}$_y3tĐƤatgvێi|K=uVyrŲlLӪuܿzwk$m87k( `múcE)"@rK( z4$D; 2kW=Xb$V[Ru819קR~qloѱDyįݎ*mxw]y5e4K@ЃI0A D@"BDk_)N\8͜9dz"fK0zɿvM /.:2O{ Nb=M=7>??Zuo32 DLD@D| &+֎C #B8ַ`bOb $D#ͮҪtx]%`ES`Ru[=¾!@Od37LJ0!OIR4m]GZRJu$‡c=%~s@6SKy?CeIh:[vR@Lh | (BhAMy=݃  G"'wzn޺~8ԽSh ~T*A:xR[ܹ?X[uKL_=fDȊ؂p0}7=D$Ekq!/t.*2ʼnDbŞ}DijYaȲ(""6HA;:LzxQ‘(SQQ}*PL*fc\s `/d'QXW, e`#kPGZuŞuO{{wm[&NBTiiI0bukcA9<4@SӊH*؎4U/'2U5.(9JuDfrޱtycU%j(:RUbArLֺN)udA':uGQN"-"Is.*+k@ `Ojs@yU/ H:l;@yyTn}_yw!VkRJ4P)~y#)r,D =ě"Q]ci'%HI4ZL0"MJy 8A{ aN<8D"1#IJi >XjX֔#@>-{vN!8tRݻ^)N_╗FJEk]CT՟ YP:_|H1@ CBk]yKYp|og?*dGvzنzӴzjֺNkC~AbZƷ`.H)=!QͷVTT(| u78y֮}|[8-Vjp%2JPk[}ԉaH8Wpqhwr:vWª<}l77_~{s۴V+RCģ%WRZ\AqHifɤL36: #F:p]Bq/z{0CU6ݳEv_^k7'>sq*+kH%a`0ԣisqにtү04gVgW΂iJiS'3w.w}l6MC2uԯ|>JF5`fV5m`Y**Db1FKNttu]4ccsQNnex/87+}xaUW9y>ͯ骵G{䩓Գ3+vU}~jJ.NFRD7<aJDB1#ҳgSb,+CS?/ VG J?|?,2#M9}B)MiE+G`-wo߫V`fio(}S^4e~V4bHOYb"b#E)dda:'?}׮4繏`{7Z"uny-?ǹ;0MKx{:_pÚmFמ:F " .LFQLG)Q8qN q¯¯3wOvxDb\. BKD9_NN &L:4D{mm o^tֽ:q!ƥ}K+<"m78N< ywsard5+вz~mnG)=}lYݧNj'QJS{S :UYS-952?&O-:W}(!6Mk4+>A>j+i|<<|;ر^߉=HE|V#F)Emm#}/"y GII웻Jі94+v뾧xu~5C95~ūH>c@덉pʃ1/4-A2G%7>m;–Y,cyyaln" ?ƻ!ʪ<{~h~i y.zZB̃/,雋SiC/JFMmBH&&FAbϓO^tubbb_hZ{_QZ-sύodFgO(6]TJA˯#`۶ɟ( %$&+V'~hiYy>922 Wp74Zkq+Ovn錄c>8~GqܲcWꂎz@"1A.}T)uiW4="jJ2W7mU/N0gcqܗOO}?9/wìXžΏ0 >֩(V^Rh32!Hj5`;O28؇2#ݕf3 ?sJd8NJ@7O0 b־?lldщ̡&|9C.8RTWwxWy46ah嘦mh٤&l zCy!PY?: CJyв]dm4ǜҐR޻RլhX{FƯanшQI@x' ao(kUUuxW_Ñ줮[w8 FRJ(8˼)_mQ _!RJhm=!cVmm ?sFOnll6Qk}alY}; "baӌ~M0w,Ggw2W:G/k2%R,_=u`WU R.9T"v,<\Ik޽/2110Ӿxc0gyC&Ny޽JҢrV6N ``یeA16"J³+Rj*;BϜkZPJaÍ<Jyw:NP8/D$ 011z֊Ⱳ3ι֘k1V_"h!JPIΣ'ɜ* aEAd:ݺ>y<}Lp&PlRfTb1]o .2EW\ͮ]38؋rTJsǏP@芎sF\> P^+dYJLbJ C-xϐn> ι$nj,;Ǖa FU *择|h ~izť3ᤓ`K'-f tL7JK+vf2)V'-sFuB4i+m+@My=O҈0"|Yxoj,3]:cо3 $#uŘ%Y"y죯LebqtҢVzq¼X)~>4L׶m~[1_k?kxֺQ`\ |ٛY4Ѯr!)N9{56(iNq}O()Em]=F&u?$HypWUeB\k]JɩSع9 Zqg4ZĊo oMcjZBU]B\TUd34ݝ~:7ڶSUsB0Z3srx 7`:5xcx !qZA!;%͚7&P H<WL!džOb5kF)xor^aujƍ7 Ǡ8/p^(L>ὴ-B,{ۇWzֺ^k]3\EE@7>lYBȝR.oHnXO/}sB|.i@ɥDB4tcm,@ӣgdtJ!lH$_vN166L__'Z)y&kH;:,Y7=J 9cG) V\hjiE;gya~%ks_nC~Er er)muuMg2;֫R)Md) ,¶ 2-wr#F7<-BBn~_(o=KO㭇[Xv eN_SMgSҐ BS헃D%g_N:/pe -wkG*9yYSZS.9cREL !k}<4_Xs#FmҶ:7R$i,fi!~' # !6/S6y@kZkZcX)%5V4P]VGYq%H1!;e1MV<!ϐHO021Dp= HMs~~a)ަu7G^];git!Frl]H/L$=AeUvZE4P\.,xi {-~p?2b#amXAHq)MWǾI_r`S Hz&|{ +ʖ_= (YS(_g0a03M`I&'9vl?MM+m~}*xT۲(fY*V4x@29s{DaY"toGNTO+xCAO~4Ϳ;p`Ѫ:>Ҵ7K 3}+0 387x\)a"/E>qpWB=1 ¨"MP(\xp߫́A3+J] n[ʼnӼaTbZUWb={~2ooKױӰp(CS\S筐R*JغV&&"FA}J>G֐p1ٸbk7 ŘH$JoN <8s^yk_[;gy-;߉DV{c B yce% aJhDȶ 2IdйIB/^n0tNtџdcKj4϶v~- CBcgqx9= PJ) dMsjpYB] GD4RDWX +h{y`,3ꊕ$`zj*N^TP4L:Iz9~6s) Ga:?y*J~?OrMwP\](21sZUD ?ܟQ5Q%ggW6QdO+\@ ̪X'GxN @'4=ˋ+*VwN ne_|(/BDfj5(Dq<*tNt1х!MV.C0 32b#?n0pzj#!38}޴o1KovCJ`8ŗ_"]] rDUy޲@ Ȗ-;xџ'^Y`zEd?0„ DAL18IS]VGq\4o !swV7ˣι%4FѮ~}6)OgS[~Q vcYbL!wG3 7띸*E Pql8=jT\꘿I(z<[6OrR8ºC~ډ]=rNl[g|v TMTղb-o}OrP^Q]<98S¤!k)G(Vkwyqyr޽Nv`N/e p/~NAOk \I:G6]4+K;j$R:Mi #*[AȚT,ʰ,;N{HZTGMoּy) ]%dHء9Պ䠬|<45,\=[bƟ8QXeB3- &dҩ^{>/86bXmZ]]yޚN[(WAHL$YAgDKp=5GHjU&99v簪C0vygln*P)9^͞}lMuiH!̍#DoRBn9l@ xA/_v=ȺT{7Yt2N"4!YN`ae >Q<XMydEB`VU}u]嫇.%e^ánE87Mu\t`cP=AD/G)sI"@MP;)]%fH9'FNsj1pVhY&9=0pfuJ&gޤx+k:!r˭wkl03׼Ku C &ѓYt{.O.zҏ z}/tf_wEp2gvX)GN#I ݭ߽v/ .& и(ZF{e"=V!{zW`, ]+LGz"(UJp|j( #V4, 8B 0 9OkRrlɱl94)'VH9=9W|>PS['G(*I1==C<5"Pg+x'K5EMd؞Af8lG ?D FtoB[je?{k3zQ vZ;%Ɠ,]E>KZ+T/ EJxOZ1i #T<@ I}q9/t'zi(EMqw`mYkU6;[t4DPeckeM;H}_g pMww}k6#H㶏+b8雡Sxp)&C $@'b,fPߑt$RbJ'vznuS ~8='72_`{q纶|Q)Xk}cPz9p7O:'|G~8wx(a 0QCko|0ASD>Ip=4Q, d|F8RcU"/KM opKle M3#i0c%<7׿p&pZq[TR"BpqauIp$ 8~Ĩ!8Սx\ւdT>>Z40ks7 z2IQ}ItԀ<-%S⍤};zIb$I 5K}Q͙D8UguWE$Jh )cu4N tZl+[]M4k8֦Zeq֮M7uIqG 1==tLtR,ƜSrHYt&QP윯Lg' I,3@P'}'R˪e/%-Auv·ñ\> vDJzlӾNv5:|K/Jb6KI9)Zh*ZAi`?S {aiVDԲuy5W7pWeQJk֤#5&V<̺@/GH?^τZL|IJNvI:'P=Ϛt"¨=cud S Q.Ki0 !cJy;LJR;G{BJy޺[^8fK6)=yʊ+(k|&xQ2`L?Ȓ2@Mf 0C`6-%pKpm')c$׻K5[J*U[/#hH!6acB JA _|uMvDyk y)6OPYjœ50VT K}cǻP[ $:]4MEA.y)|B)cf-A?(e|lɉ#P9V)[9t.EiQPDѠ3ϴ;E:+Օ t ȥ~|_N2,ZJLt4! %ա]u {+=p.GhNcŞQI?Nd'yeh n7zi1DB)1S | S#ًZs2|Ɛy$F SxeX{7Vl.Src3E℃Q>b6G ўYCmtկ~=K0f(=LrAS GN'ɹ9<\!a`)֕y[uՍ[09` 9 +57ts6}b4{oqd+J5fa/,97J#6yν99mRWxJyѡyu_TJc`~W>l^q#Ts#2"nD1%fS)FU w{ܯ R{ ˎ󅃏џDsZSQS;LV;7 Od1&1n$ N /.q3~eNɪ]E#oM~}v֯FڦwyZ=<<>Xo稯lfMFV6p02|*=tV!c~]fa5Y^Q_WN|Vs 0ҘދU97OI'N2'8N֭fgg-}V%y]U4 峧p*91#9U kCac_AFңĪy뚇Y_AiuYyTTYЗ-(!JFLt›17uTozc. S;7A&&<ԋ5y;Ro+:' *eYJkWR[@F %SHWP 72k4 qLd'J "zB6{AC0ƁA6U.'F3:Ȅ(9ΜL;D]m8ڥ9}dU "v!;*13Rg^fJyShyy5auA?ɩGHRjo^]׽S)Fm\toy 4WQS@mE#%5ʈfFYDX ~D5Ϡ9tE9So_aU4?Ѽm%&c{n>.KW1Tlb}:j uGi(JgcYj0qn+>) %\!4{LaJso d||u//P_y7iRJ߬nHOy) l+@$($VFIQ9%EeKʈU. ia&FY̒mZ=)+qqoQn >L!qCiDB;Y<%} OgBxB!ØuG)WG9y(Ą{_yesuZmZZey'Wg#C~1Cev@0D $a@˲(.._GimA:uyw֬%;@!JkQVM_Ow:P.s\)ot- ˹"`B,e CRtaEUP<0'}r3[>?G8xU~Nqu;Wm8\RIkբ^5@k+5(By'L&'gBJ3ݶ!/㮻w҅ yqPWUg<e"Qy*167΃sJ\oz]T*UQ<\FԎ`HaNmڜ6DysCask8wP8y9``GJ9lF\G g's Nn͵MLN֪u$| /|7=]O)6s !ĴAKh]q_ap $HH'\1jB^s\|- W1:=6lJBqjY^LsPk""`]w)󭃈,(HC ?䔨Y$Sʣ{4Z+0NvQkhol6C.婧/u]FwiVjZka&%6\F*Ny#8O,22+|Db~d ~Çwc N:FuuCe&oZ(l;@ee-+Wn`44AMK➝2BRՈt7g*1gph9N) *"TF*R(#'88pm=}X]u[i7bEc|\~EMn}P瘊J)K.0i1M6=7'_\kaZ(Th{K*GJyytw"IO-PWJk)..axӝ47"89Cc7ĐBiZx 7m!fy|ϿF9CbȩV 9V-՛^pV̌ɄS#Bv4-@]Vxt-Z, &ֺ*diؠ2^VXbs֔Ìl.jQ]Y[47gj=幽ex)A0ip׳ W2[ᎇhuE^~q흙L} #-b۸oFJ_QP3r6jr+"nfzRJTUqoaۍ /$d8Mx'ݓ= OՃ| )$2mcM*cЙj}f };n YG w0Ia!1Q.oYfr]DyISaP}"dIӗթO67jqR ҊƐƈaɤGG|h;t]䗖oSv|iZqX)oalv;۩meEJ\!8=$4QU4Xo&VEĊ YS^E#d,yX_> ۘ-e\ "Wa6uLĜZi`aD9.% w~mB(02G[6y.773a7 /=o7D)$Z 66 $bY^\CuP. (x'"J60׿Y:Oi;F{w佩b+\Yi`TDWa~|VH)8q/=9!g߆2Y)?ND)%?Ǐ`k/sn:;O299yB=a[Ng 3˲N}vLNy;*?x?~L&=xyӴ~}q{qE*IQ^^ͧvü{Huu=R|>JyUlZV, B~/YF!Y\u_ݼF{_C)LD]m {H 0ihhadd nUkf3oٺCvE\)QJi+֥@tDJkB$1!Đr0XQ|q?d2) Ӣ_}qv-< FŊ߫%roppVBwü~JidY4:}L6M7f٬F "?71<2#?Jyy4뷢<_a7_=Q E=S1И/9{+93֮E{ǂw{))?maÆm(uLE#lïZ  ~d];+]h j?!|$F}*"4(v'8s<ŏUkm7^7no1w2ؗ}TrͿEk>p'8OB7d7R(A 9.*Mi^ͳ; eeUwS+C)uO@ =Sy]` }l8^ZzRXj[^iUɺ$tj))<sbDJfg=Pk_{xaKo1:-uyG0M ԃ\0Lvuy'ȱc2Ji AdyVgVh!{]/&}}ċJ#%d !+87<;qN޼Nفl|1N:8ya  8}k¾+-$4FiZYÔXk*I&'@iI99)HSh4+2G:tGhS^繿 Kتm0 вDk}֚+QT4;sC}rՅE,8CX-e~>G&'9xpW,%Fh,Ry56Y–hW-(v_,? ; qrBk4-V7HQ;ˇ^Gv1JVV%,ik;D_W!))+BoS4QsTM;gt+ndS-~:11Sgv!0qRVh!"Ȋ(̦Yl.]PQWgٳE'`%W1{ndΗBk|Ž7ʒR~,lnoa&:ü$ 3<a[CBݮwt"o\ePJ=Hz"_c^Z.#ˆ*x z̝grY]tdkP*:97YľXyBkD4N.C_[;F9`8& !AMO c `@BA& Ost\-\NX+Xp < !bj3C&QL+*&kAQ=04}cC!9~820G'PC9xa!w&bo_1 Sw"ܱ V )Yl3+ס2KoXOx]"`^WOy :3GO0g;%Yv㐫(R/r (s } u B &FeYZh0y> =2<Ϟc/ -u= c&׭,.0"g"7 6T!vl#sc>{u/Oh Bᾈ)۴74]x7 gMӒ"d]U)}" v4co[ ɡs 5Gg=XR14?5A}D "b{0$L .\4y{_fe:kVS\\O]c^W52LSBDM! C3Dhr̦RtArx4&agaN3Cf<Ԉp4~ B'"1@.b_/xQ} _߃҉/gٓ2Qkqp0շpZ2fԫYz< 4L.Cyυι1t@鎫Fe sYfsF}^ V}N<_`p)alٶ "(XEAVZ<)2},:Ir*#m_YӼ R%a||EƼIJ,,+f"96r/}0jE/)s)cjW#w'Sʯ5<66lj$a~3Kʛy 2:cZ:Yh))+a߭K::N,Q F'qB]={.]h85C9cr=}*rk?vwV렵ٸW Rs%}rNAkDv|uFLBkWY YkX מ|)1!$#3%y?pF<@<Rr0}: }\J [5FRxY<9"SQdE(Q*Qʻ)q1E0B_O24[U'],lOb ]~WjHޏTQ5Syu wq)xnw8~)c 쫬gٲߠ H% k5dƝk> kEj,0% b"vi2Wس_CuK)K{n|>t{P1򨾜j>'kEkƗBg*H%'_aY6Bn!TL&ɌOb{c`'d^{t\i^[uɐ[}q0lM˕G:‚4kb祔c^:?bpg… +37stH:0}en6x˟%/<]BL&* 5&fK9Mq)/iyqtA%kUe[ڛKN]Ě^,"`/ s[EQQm?|XJ߅92m]G.E΃ח U*Cn.j_)Tѧj̿30ڇ!A0=͜ar I3$C^-9#|pk!)?7.x9 @OO;WƝZBFU keZ75F6Tc6"ZȚs2y/1 ʵ:u4xa`C>6Rb/Yм)^=+~uRd`/|_8xbB0?Ft||Z\##|K 0>>zxv8۴吅q 8ĥ)"6>~\8:qM}#͚'ĉ#p\׶ l#bA?)|g g9|8jP(cr,BwV (WliVxxᡁ@0Okn;ɥh$_ckCgriv}>=wGzβ KkBɛ[˪ !J)h&k2%07δt}!d<9;I&0wV/ v 0<H}L&8ob%Hi|޶o&h1L|u֦y~󛱢8fٲUsւ)0oiFx2}X[zVYr_;N(w]_4B@OanC?gĦx>мgx>ΛToZoOMp>40>V Oy V9iq!4 LN,ˢu{jsz]|"R޻&'ƚ{53ўFu(<٪9:΋]B;)B>1::8;~)Yt|0(pw2N%&X,URBK)3\zz&}ax4;ǟ(tLNg{N|Ǽ\G#C9g$^\}p?556]/RP.90 k,U8/u776s ʪ_01چ|\N 0VV*3H鴃J7iI!wG_^ypl}r*jɤSR 5QN@ iZ#1ٰy;_\3\BQQ x:WJv츟ٯ$"@6 S#qe딇(/P( Dy~TOϻ<4:-+F`0||;Xl-"uw$Цi󼕝mKʩorz"mϺ$F:~E'ҐvD\y?Rr8_He@ e~O,T.(ފR*cY^m|cVR[8 JҡSm!ΆԨb)RHG{?MpqrmN>߶Y)\p,d#xۆWY*,l6]v0h15M˙MS8+EdI='LBJIH7_9{Caз*Lq,dt >+~ّeʏ?xԕ4bBAŚjﵫ!'\Ը$WNvKO}ӽmSşذqsOy?\[,d@'73'j%kOe`1.g2"e =YIzS2|zŐƄa\U,dP;jhhhaxǶ?КZ՚.q SE+XrbOu%\GتX(H,N^~]JyEZQKceTQ]VGYqnah;y$cQahT&QPZ*iZ8UQQM.qo/T\7X"u?Mttl2Xq(IoW{R^ ux*SYJ! 4S.Jy~ BROS[V|žKNɛP(L6V^|cR7i7nZW1Fd@ Ara{詑|(T*dN]Ko?s=@ |_EvF]׍kR)eBJc" MUUbY6`~V޴dJKß&~'d3i5h-3LL

HOME


5h-3LL 1.0
DIR: /usr/share/perl5/Authen/SASL/Perl
/usr/share/perl5/Authen/SASL/Perl/
Upload File:
Current File : /usr/share/perl5/Authen/SASL/Perl/DIGEST_MD5.pm
# Copyright (c) 2003-2009 Graham Barr, Djamel Boudjerda, Paul Connolly, Julian
# Onions, Nexor and Yann Kerherve.
# All rights reserved. This program is free software; you can redistribute
# it and/or modify it under the same terms as Perl itself.

# See http://www.ietf.org/rfc/rfc2831.txt for details

package Authen::SASL::Perl::DIGEST_MD5;

use strict;
use vars qw($VERSION @ISA $CNONCE $NONCE);
use Digest::MD5 qw(md5_hex md5);
use Digest::HMAC_MD5 qw(hmac_md5);

# TODO: complete qop support in server, should be configurable

$VERSION = "2.14";
@ISA = qw(Authen::SASL::Perl);

my %secflags = (
  noplaintext => 1,
  noanonymous => 1,
);

# some have to be quoted - some don't - sigh!
my (%cqdval, %sqdval);
@cqdval{qw(
  username authzid realm nonce cnonce digest-uri
)} = ();

## ...and server behaves different than client - double sigh!
@sqdval{keys %cqdval, qw(qop cipher)} = ();
#  username authzid realm nonce cnonce digest-uri qop cipher
#)} = ();

my %multi;
@{$multi{server}}{qw(realm auth-param)} = ();
@{$multi{client}}{qw()} = ();

my @server_required = qw(algorithm nonce);
my @client_required = qw(username nonce cnonce nc qop response);

# available ciphers
my @ourciphers = (
  {
    name  => 'rc4',
    ssf   => 128,
    bs    => 1,
    ks    => 16,
    pkg   => 'Crypt::RC4',
    key   => sub { $_[0] },
    iv    => sub {},
    fixup => sub {
      # retrofit the Crypt::RC4 module with standard subs
      *Crypt::RC4::encrypt   = *Crypt::RC4::decrypt =
        sub { goto &Crypt::RC4::RC4; };
      *Crypt::RC4::keysize   =  sub {128};
      *Crypt::RC4::blocksize =  sub {1};
    }
  },
  {
    name  => '3des',
    ssf   => 112,
    bs    => 8,
    ks    => 16,
    pkg   => 'Crypt::DES3',
    key   => sub {
      pack('B8' x 16,
        map { $_ . '0' }
        map { unpack('a7' x 16, $_); }
        unpack('B*', substr($_[0], 0, 14)) );
    },
    iv => sub { substr($_[0], -8, 8) },
  },
  {
    name  => 'des',
    ssf   => 56,
    bs    => 8,
    ks    => 16,
    pkg   => 'Crypt::DES',
    key   => sub {
      pack('B8' x 8,
        map { $_ . '0' }
        map { unpack('a7' x 8, $_); }
        unpack('B*',substr($_[0], 0, 7)) );
    },
    iv => sub { substr($_[0], -8, 8) },
  },
  {
    name  => 'rc4-56',
    ssf   => 56,
    bs    => 1,
    ks    => 7,
    pkg   => 'Crypt::RC4',
    key   => sub { $_[0] },
    iv    => sub {},
    fixup => sub {
      *Crypt::RC4::encrypt   = *Crypt::RC4::decrypt =
        sub { goto &Crypt::RC4::RC4; };
      *Crypt::RC4::keysize   =  sub {56};
      *Crypt::RC4::blocksize =  sub {1};
    }
  },
  {
    name  => 'rc4-40',
    ssf   => 40,
    bs    => 1,
    ks    => 5,
    pkg   => 'Crypt::RC4',
    key   => sub { $_[0] },
    iv    => sub {},
    fixup => sub {
      *Crypt::RC4::encrypt   = *Crypt::RC4::decrypt =
        sub { goto &Crypt::RC4::RC4; };
      *Crypt::RC4::keysize   =  sub {40};
      *Crypt::RC4::blocksize =  sub {1};
    }
  },
);

## The system we are on, might not be able to crypt the stream
our $NO_CRYPT_AVAILABLE = 1;
for (@ourciphers) {
    eval "require $_->{pkg}";
    unless ($@) {
        $NO_CRYPT_AVAILABLE = 0;
        last;
    }
}

sub _order { 3 }
sub _secflags {
  shift;
  scalar grep { $secflags{$_} } @_;
}

sub mechanism { 'DIGEST-MD5' }

sub _init {
  my ($pkg, $self) = @_;
  bless $self, $pkg;

  # set default security properties
  $self->property('minssf',      0);
  $self->property('maxssf',      int 2**31 - 1);    # XXX - arbitrary "high" value
  $self->property('maxbuf',      0xFFFFFF);         # maximum supported by GSSAPI mech
  $self->property('externalssf', 0);

  $self;
}

sub _init_server {
  my $server  = shift;
  my $options = shift || {};
  if (!ref $options or ref $options ne 'HASH') {
    warn "options for DIGEST_MD5 should be a hashref";
    $options = {};
  }

  ## new server, means new nonce_counts
  $server->{nonce_counts} = {};

  ## determine supported qop
  my   @qop = ('auth');
  push @qop, 'auth-int'  unless $options->{no_integrity};
  push @qop, 'auth-conf' unless $options->{no_integrity}
                             or $options->{no_confidentiality}
                             or $NO_CRYPT_AVAILABLE;

  $server->{supported_qop} = { map { $_ => 1 } @qop };
}

sub init_sec_layer {
  my $self           = shift;
  $self->{cipher}    = undef;
  $self->{khc}       = undef;
  $self->{khs}       = undef;
  $self->{sndseqnum} = 0;
  $self->{rcvseqnum} = 0;

  # reset properties for new session
  $self->property(maxout => undef);
  $self->property(ssf    => undef);
}

# no initial value passed to the server
sub client_start {
  my $self = shift;

  $self->{need_step} = 1;
  $self->{error}     = undef;
  $self->{state}     = 0;
  $self->init_sec_layer;
  '';
}

sub server_start {
  my $self       = shift;
  my $challenge  = shift;
  my $cb         = shift || sub {};

  $self->{need_step} = 1;
  $self->{error}     = undef;
  $self->{nonce}     = md5_hex($NONCE || join (":", $$, time, rand));

  $self->init_sec_layer;

  my $qop = [ sort keys %{$self->{supported_qop}} ];

  ## get the realm using callbacks but default to the host specified
  ## during the instanciation of the SASL object
  my $realm = $self->_call('realm');
  $realm  ||= $self->host;

  my %response = (
    nonce         => $self->{nonce},
    charset       => 'utf-8',
    algorithm     => 'md5-sess',
    realm         => $realm,
    maxbuf        => $self->property('maxbuf'),

## IN DRAFT ONLY:
# If this directive is present multiple times the client MUST treat
# it as if it received a single qop directive containing a comma
# separated value from all instances. I.e.,
# 'qop="auth",qop="auth-int"' is the same as 'qop="auth,auth-int"

    'qop'         => $qop,
    'cipher'      => [ map { $_->{name} } @ourciphers ],
  );
  my $final_response = _response(\%response);
  $cb->($final_response);
  return;
}

sub client_step {   # $self, $server_sasl_credentials
  my ($self, $challenge) = @_;
  $self->{server_params} = \my %sparams;

  # Parse response parameters
  $self->_parse_challenge(\$challenge, server => $self->{server_params})
    or return $self->set_error("Bad challenge: '$challenge'");

  if ($self->{state} == 1) {
    # check server's `rspauth' response
    return $self->set_error("Server did not send rspauth in step 2")
      unless ($sparams{rspauth});
    return $self->set_error("Invalid rspauth in step 2")
      unless ($self->{rspauth} eq $sparams{rspauth});

    # all is well
    $self->set_success;
    return '';
  }

  # check required fields in server challenge
  if (my @missing = grep { !exists $sparams{$_} } @server_required) {
    return $self->set_error("Server did not provide required field(s): @missing")
  }

  my %response = (
    nonce        => $sparams{'nonce'},
    cnonce       => md5_hex($CNONCE || join (":", $$, time, rand)),
    'digest-uri' => $self->service . '/' . $self->host,
    # calc how often the server nonce has been seen; server expects "00000001"
    nc           => sprintf("%08d",     ++$self->{nonce_counts}{$sparams{'nonce'}}),
    charset      => $sparams{'charset'},
  );

  return $self->set_error("Server qop too weak (qop = $sparams{'qop'})")
    unless ($self->_client_layer(\%sparams,\%response));

  # let caller-provided fields override defaults: authorization ID, service name, realm

  my $s_realm = $sparams{realm} || [];
  my $realm = $self->_call('realm', @$s_realm);
  unless (defined $realm) {
    # If the user does not pick a realm, use the first from the server
    $realm = $s_realm->[0];
  }
  if (defined $realm) {
    $response{realm} = $realm;
  }

  my $authzid = $self->_call('authname');
  if (defined $authzid) {
    $response{authzid} = $authzid;
  }

  my $serv_name = $self->_call('serv');
  if (defined $serv_name) {
    $response{'digest-uri'} .= '/' . $serv_name;
  }

  my $user = $self->_call('user');
  return $self->set_error("Username is required")
    unless defined $user;
  $response{username} = $user;

  my $password = $self->_call('pass');
  return $self->set_error("Password is required")
    unless defined $password;

  $self->property('maxout', $sparams{maxbuf} || 65536);

  # Generate the response value
  $self->{state} = 1;

  my ($response, $rspauth)
    = $self->_compute_digests_and_set_keys($password, \%response);

  $response{response} = $response;
  $self->{rspauth}    = $rspauth;

  # finally, return our response token
  return _response(\%response, "is_client");
}

sub _compute_digests_and_set_keys {
  my $self     = shift;
  my $password = shift;
  my $params   = shift;

  if (defined $params->{realm} and ref $params->{realm} eq 'ARRAY') {
    $params->{realm} = $params->{realm}[0];
  }

  my $realm = $params->{realm};
  $realm = "" unless defined $realm;

  my $A1 = join (":",
    md5(join (":", $params->{username}, $realm, $password)),
    @$params{defined($params->{authzid})
      ? qw(nonce cnonce authzid)
      : qw(nonce cnonce)
    }
  );

  # pre-compute MD5(A1) and HEX(MD5(A1)); these are used multiple times below
  my $hdA1 = unpack("H*", (my $dA1 = md5($A1)) );

  # derive keys for layer encryption / integrity
  $self->{kic} = md5($dA1,
    'Digest session key to client-to-server signing key magic constant');

  $self->{kis} = md5($dA1,
    'Digest session key to server-to-client signing key magic constant');

  if (my $cipher = $self->{cipher}) {
    &{ $cipher->{fixup} || sub{} };

    # compute keys for encryption
    my $ks = $cipher->{ks};
    $self->{kcc} = md5(substr($dA1,0,$ks),
      'Digest H(A1) to client-to-server sealing key magic constant');
    $self->{kcs} = md5(substr($dA1,0,$ks),
      'Digest H(A1) to server-to-client sealing key magic constant');

    # get an encryption and decryption handle for the chosen cipher
    $self->{khc} = $cipher->{pkg}->new($cipher->{key}->($self->{kcc}));
    $self->{khs} = $cipher->{pkg}->new($cipher->{key}->($self->{kcs}));

    # initialize IVs
    $self->{ivc} = $cipher->{iv}->($self->{kcc});
    $self->{ivs} = $cipher->{iv}->($self->{kcs});
  }

  my $A2 = "AUTHENTICATE:" . $params->{'digest-uri'};
  $A2 .= ":00000000000000000000000000000000" if ($params->{qop} ne 'auth');

  my $response = md5_hex(
    join (":", $hdA1, @$params{qw(nonce nc cnonce qop)}, md5_hex($A2))
  );

  # calculate server `rspauth' response, so we can check in step 2
  # the only difference here is in the A2 string which from which
  # `AUTHENTICATE' is omitted in the calculation of `rspauth'
  $A2 = ":" . $params->{'digest-uri'};
  $A2 .= ":00000000000000000000000000000000" if ($params->{qop} ne 'auth');

  my $rspauth = md5_hex(
    join (":", $hdA1, @$params{qw(nonce nc cnonce qop)}, md5_hex($A2))
  );

  return ($response, $rspauth);
}

sub server_step {
  my $self      = shift;
  my $challenge = shift;
  my $cb        = shift || sub {};

  $self->{client_params} = \my %cparams;
  unless ( $self->_parse_challenge(\$challenge, client => $self->{client_params}) ) {
   $self->set_error("Bad challenge: '$challenge'");
   return $cb->();
  }

  # check required fields in server challenge
  if (my @missing = grep { !exists $cparams{$_} } @client_required) {
    $self->set_error("Client did not provide required field(s): @missing");
    return $cb->();
  }

  my $count = hex ($cparams{'nc'} || 0);
  unless ($count == ++$self->{nonce_counts}{$cparams{nonce}}) {
    $self->set_error("nonce-count doesn't match: $count");
    return $cb->();
  }

  my $qop = $cparams{'qop'} || "auth";
  unless ($self->is_qop_supported($qop)) {
    $self->set_error("Client qop not supported (qop = '$qop')");
    return $cb->();
  }

  my $username = $cparams{'username'};
  unless ($username) {
    $self->set_error("Client didn't provide a username");
    return $cb->();
  }

  # "The authzid MUST NOT be an empty string."
  if (exists $cparams{authzid} && $cparams{authzid} eq '') {
      $self->set_error("authzid cannot be empty");
      return $cb->();
  }
  my $authzid = $cparams{authzid};

  # digest-uri: "Servers SHOULD check that the supplied value is correct.
  # This will detect accidental connection to the incorrect server, as well as
  # some redirection attacks"
  my $digest_uri = $cparams{'digest-uri'};
  my ($cservice, $chost, $cservname) = split '/', $digest_uri, 3;
  if ($cservice ne $self->service or $chost ne $self->host) {
      # XXX deal with serv_name
      $self->set_error("Incorrect digest-uri");
      return $cb->(); 
  }

  unless (defined $self->callback('getsecret')) {
    $self->set_error("a getsecret callback MUST be defined");
    $cb->();
    return;
  }

  my $realm = $self->{client_params}->{'realm'};
  my $response_check = sub {
    my $password = shift;
    return $self->set_error("Cannot get the passord for $username") 
        unless defined $password;
 
    ## configure the security layer
    $self->_server_layer($qop)
        or return $self->set_error("Cannot negociate the security layer");
 
    my ($expected, $rspauth)
        = $self->_compute_digests_and_set_keys($password, $self->{client_params});
 
    return $self->set_error("Incorrect response $self->{client_params}->{response} <> $expected")
        unless $expected eq $self->{client_params}->{response};
 
    my %response = (
        rspauth => $rspauth,
    );
 
    # I'm not entirely sure of what I am doing
    $self->{answer}{$_} = $self->{client_params}->{$_} for qw/username authzid realm serv/;
 
    $self->set_success;
    return _response(\%response);
  };

  $self->callback('getsecret')->(
    $self,
    { user => $username, realm => $realm, authzid => $authzid },
    sub { $cb->( $response_check->( shift ) ) },
  );
}

sub is_qop_supported {
    my $self = shift;
    my $qop  = shift;
    return $self->{supported_qop}{$qop};
}

sub _response {
  my $response  = shift;
  my $is_client = shift;

  my @out;
  for my $k (sort keys %$response) {
    my $is_array = ref $response->{$k} && ref $response->{$k} eq 'ARRAY';
    my @values = $is_array ? @{$response->{$k}} : ($response->{$k});
    # Per spec, one way of doing it: multiple k=v
    #push @out, [$k, $_] for @values;
    # other way: comma separated list
    push @out, [$k, join (',', @values)];
  }
  return join (",", map { _qdval($_->[0], $_->[1], $is_client) } @out);
}

sub _parse_challenge {
  my $self          = shift;
  my $challenge_ref = shift;
  my $type          = shift;
  my $params        = shift;

  while($$challenge_ref =~
           s/^(?:\s*,)*\s*            # remaining or crap
             ([\w-]+)                 # key, eg: qop
             =
             ("([^\\"]+|\\.)*"|[^,]+) # value, eg: auth-conf or "NoNcE"
             \s*(?:,\s*)*             # remaining
           //x) {

    my ($k, $v) = ($1,$2);
    if ($v =~ /^"(.*)"$/s) {
      ($v = $1) =~ s/\\(.)/$1/g;
    }
    if (exists $multi{$type}{$k}) {
      my $aref = $params->{$k} ||= [];
      push @$aref, $v;
    }
    elsif (defined $params->{$k}) {
      return $self->set_error("Bad challenge: '$$challenge_ref'");
    }
    else {
      $params->{$k} = $v;
    }
  }
  return length $$challenge_ref ? 0 : 1;
}

sub _qdval {
  my ($k, $v, $is_client) = @_;

  my $qdval = $is_client ? \%cqdval : \%sqdval;

  if (!defined $v) {
    return;
  }
  elsif (exists $qdval->{$k}) {
    $v =~ s/([\\"])/\\$1/g;
    return qq{$k="$v"};
  }

  return "$k=$v";
}

sub _server_layer {
  my ($self, $auth) = @_;

  # XXX dupe
  # construct our qop mask
  my $maxssf = $self->property('maxssf') - $self->property('externalssf');
  $maxssf = 0 if ($maxssf < 0);
  my $minssf = $self->property('minssf') - $self->property('externalssf');
  $minssf = 0 if ($minssf < 0);

  return undef if ($maxssf < $minssf); # sanity check

  my $ciphers = [ map { $_->{name} } @ourciphers ];
  if ((     $auth eq 'auth-conf')
        and $self->_select_cipher($minssf, $maxssf, $ciphers )) {
    $self->property('ssf', $self->{cipher}->{ssf});
    return 1;
  }
  if ($auth eq 'auth-int') {
    $self->property('ssf', 1);
    return 1;
  }
  if ($auth eq 'auth') {
    $self->property('ssf', 0);
    return 1;
  }

  return undef;
}

sub _client_layer {
  my ($self, $sparams, $response) = @_;

  # construct server qop mask
  # qop in server challenge is optional: if not there "auth" is assumed
  my $smask = 0;
  map {
    m/^auth$/      and $smask |= 1;
    m/^auth-int$/  and $smask |= 2;
    m/^auth-conf$/ and $smask |= 4;
  } split(/,/, $sparams->{qop}||'auth'); # XXX I think we might have a bug here bc. of LWS

  # construct our qop mask
  my $cmask = 0;
  my $maxssf = $self->property('maxssf') - $self->property('externalssf');
  $maxssf = 0 if ($maxssf < 0);
  my $minssf = $self->property('minssf') - $self->property('externalssf');
  $minssf = 0 if ($minssf < 0);

  return undef if ($maxssf < $minssf); # sanity check

  # ssf values > 1 mean integrity and confidentiality 
  # ssf == 1 means integrity but no confidentiality
  # ssf < 1 means neither integrity nor confidentiality
  # no security layer can be had if buffer size is 0
  $cmask |= 1 if ($minssf < 1);
  $cmask |= 2 if ($minssf <= 1 and $maxssf >= 1);
  $cmask |= 4 if ($maxssf > 1);

  # find common bits
  $cmask &= $smask;

  # parse server cipher options
  my @sciphers = split(/,/, $sparams->{'cipher-opts'}||$sparams->{cipher}||'');

  if (($cmask & 4) and $self->_select_cipher($minssf,$maxssf,\@sciphers)) {
    $response->{qop} = 'auth-conf';
    $response->{cipher} = $self->{cipher}->{name};
    $self->property('ssf', $self->{cipher}->{ssf});
    return 1;
  }
  if ($cmask & 2) {
    $response->{qop} = 'auth-int';
    $self->property('ssf', 1);
    return 1;
  }
  if ($cmask & 1) {
    $response->{qop} = 'auth';
    $self->property('ssf', 0);
    return 1;
  }

  return undef;
}

sub _select_cipher {
  my ($self, $minssf, $maxssf, $ciphers) = @_;

  # compose a subset of candidate ciphers based on ssf and peer list
  my @a = map {
    my $c = $_;
    (grep { $c->{name} eq $_ } @$ciphers and
      $c->{ssf} >= $minssf and $c->{ssf} <= $maxssf) ? $_ : ()
  } @ourciphers;

  # from these, select the first one we can create an instance of
  for (@a) {
    next unless eval "require $_->{pkg}";
    $self->{cipher} = $_;
    return 1;
  }

  return 0;
}

use Digest::HMAC_MD5 qw(hmac_md5);

sub encode {  # input: self, plaintext buffer,length (length not used here)
  my $self   = shift;
  my $seqnum = pack('N', $self->{sndseqnum}++);
  my $mac    = substr(hmac_md5($seqnum . $_[0], $self->{kic}), 0, 10);

  # if integrity only, return concatenation of buffer, MAC, TYPE and SEQNUM
  return $_[0] . $mac.pack('n',1) . $seqnum unless ($self->{khc});

  # must encrypt, block ciphers need padding bytes
  my $pad = '';
  my $bs = $self->{cipher}->{bs};
  if ($bs > 1) {
    # padding is added in between BUF and MAC
    my $n = $bs - ((length($_[0]) + 10) & ($bs - 1));
    $pad = chr($n) x $n;
  }

  # XXX - for future AES cipher support, the currently used common _crypt()
  # function probably wont do; we might to switch to per-cipher routines
  # like so:
  #  return $self->{khc}->encrypt($_[0] . $pad . $mac) . pack('n', 1) . $seqnum;
  return $self->_crypt(0, $_[0] . $pad . $mac) . pack('n', 1) . $seqnum;
}

sub decode {  # input: self, cipher buffer,length
  my ($self, $buf, $len) = @_;

  return if ($len <= 16);

  # extract TYPE/SEQNUM from end of buffer
  my ($type,$seqnum) = unpack('na[4]', substr($buf, -6, 6, ''));

  # decrypt remaining buffer, if necessary
  if ($self->{khs}) {
    # XXX - see remark above in encode() #$buf = $self->{khs}->decrypt($buf);
    $buf = $self->_crypt(1, $buf);
  }
  return unless ($buf);

  # extract 10-byte MAC from the end of (decrypted) buffer
  my ($mac) = unpack('a[10]', substr($buf, -10, 10, ''));

  if ($self->{khs} and $self->{cipher}->{bs} > 1) {
    # remove padding
    my $n = ord(substr($buf, -1, 1));
    substr($buf, -$n, $n, '');
  }

  # check the MAC
  my $check = substr(hmac_md5($seqnum . $buf, $self->{kis}), 0, 10);
  return if ($mac ne $check);
  return if (unpack('N', $seqnum) != $self->{rcvseqnum});
  $self->{rcvseqnum}++;

  return $buf;
}

sub _crypt {  # input: op(decrypting=1/encrypting=0)), buffer
  my ($self,$d) = (shift,shift);
  my $bs = $self->{cipher}->{bs};

  if ($bs <= 1) {
    # stream cipher
    return $d ? $self->{khs}->decrypt($_[0]) : $self->{khc}->encrypt($_[0])
  }

  # the remainder of this sub is for block ciphers

  # get current IV
  my $piv = \$self->{$d ? 'ivs' : 'ivc'};
  my $iv = $$piv;

  my $result = join '', map {
    my $x = $d
      ? $iv ^ $self->{khs}->decrypt($_)
      : $self->{khc}->encrypt($iv ^ $_);
    $iv = $d ? $_ : $x;
    $x;
  } unpack("a$bs "x(int(length($_[0])/$bs)), $_[0]);

  # store current IV
  $$piv = $iv;
  return $result;
}

1;

__END__

=head1 NAME

Authen::SASL::Perl::DIGEST_MD5 - Digest MD5 Authentication class

=head1 SYNOPSIS

  use Authen::SASL qw(Perl);

  $sasl = Authen::SASL->new(
    mechanism => 'DIGEST-MD5',
    callback  => {
      user => $user, 
      pass => $pass,
      serv => $serv
    },
  );

=head1 DESCRIPTION

This method implements the client and server parts of the DIGEST-MD5 SASL
algorithm, as described in RFC 2831.

=head2 CALLBACK

The callbacks used are:

=head3 client

=over 4

=item authname

The authorization id to use after successful authentication

=item user

The username to be used in the response

=item pass

The password to be used to compute the response.

=item serv

The service name when authenticating to a replicated service

=item realm

The authentication realm when overriding the server-provided default.
If not given the server-provided value is used.

The callback will be passed the list of realms that the server provided
in the initial response.

=back

=head3 server

=over 4

=item realm

The default realm to provide to the client

=item getsecret(username, realm, authzid)

returns the password associated with C<username> and C<realm>

=back

=head2 PROPERTIES

The properties used are:

=over 4

=item maxbuf

The maximum buffer size for receiving cipher text

=item minssf

The minimum SSF value that should be provided by the SASL security layer.
The default is 0

=item maxssf

The maximum SSF value that should be provided by the SASL security layer.
The default is 2**31

=item externalssf

The SSF value provided by an underlying external security layer.
The default is 0

=item ssf

The actual SSF value provided by the SASL security layer after the SASL
authentication phase has been completed. This value is read-only and set
by the implementation after the SASL authentication phase has been completed.

=item maxout

The maximum plaintext buffer size for sending data to the peer.
This value is set by the implementation after the SASL authentication
phase has been completed and a SASL security layer is in effect.

=back


=head1 SEE ALSO

L<Authen::SASL>,
L<Authen::SASL::Perl>

=head1 AUTHORS

Graham Barr, Djamel Boudjerda (NEXOR), Paul Connolly, Julian Onions (NEXOR),
Yann Kerherve.

Please report any bugs, or post any suggestions, to the perl-ldap mailing list
<perl-ldap@perl.org>

=head1 COPYRIGHT 

Copyright (c) 2003-2009 Graham Barr, Djamel Boudjerda, Paul Connolly,
Julian Onions, Nexor, Peter Marschall and Yann Kerherve.
All rights reserved. This program is free software; you can redistribute 
it and/or modify it under the same terms as Perl itself.

=cut