PNG  IHDRQgAMA a cHRMz&u0`:pQ<bKGDgmIDATxwUﹻ& ^CX(J I@ "% (** BX +*i"]j(IH{~R)[~>h{}gy)I$Ij .I$I$ʊy@}x.: $I$Ii}VZPC)I$IF ^0ʐJ$I$Q^}{"r=OzI$gRZeC.IOvH eKX $IMpxsk.쒷/&r[޳<v| .I~)@$updYRa$I |M.e JaֶpSYR6j>h%IRز if&uJ)M$I vLi=H;7UJ,],X$I1AҒJ$ XY XzI@GNҥRT)E@;]K*Mw;#5_wOn~\ DC&$(A5 RRFkvIR}l!RytRl;~^ǷJj اy뷦BZJr&ӥ8Pjw~vnv X^(I;4R=P[3]J,]ȏ~:3?[ a&e)`e*P[4]T=Cq6R[ ~ޤrXR Հg(t_HZ-Hg M$ãmL5R uk*`%C-E6/%[t X.{8P9Z.vkXŐKjgKZHg(aK9ڦmKjѺm_ \#$5,)-  61eJ,5m| r'= &ڡd%-]J on Xm|{ RҞe $eڧY XYrԮ-a7RK6h>n$5AVڴi*ֆK)mѦtmr1p| q:흺,)Oi*ֺK)ܬ֦K-5r3>0ԔHjJئEZj,%re~/z%jVMڸmrt)3]J,T K֦OvԒgii*bKiNO~%PW0=dii2tJ9Jݕ{7"I P9JKTbu,%r"6RKU}Ij2HKZXJ,妝 XYrP ެ24c%i^IK|.H,%rb:XRl1X4Pe/`x&P8Pj28Mzsx2r\zRPz4J}yP[g=L) .Q[6RjWgp FIH*-`IMRaK9TXcq*I y[jE>cw%gLRԕiFCj-ďa`#e~I j,%r,)?[gp FI˨mnWX#>mʔ XA DZf9,nKҲzIZXJ,L#kiPz4JZF,I,`61%2s $,VOϚ2/UFJfy7K> X+6 STXIeJILzMfKm LRaK9%|4p9LwJI!`NsiazĔ)%- XMq>pk$-$Q2x#N ؎-QR}ᶦHZډ)J,l#i@yn3LN`;nڔ XuX5pF)m|^0(>BHF9(cզEerJI rg7 4I@z0\JIi䵙RR0s;$s6eJ,`n 䂦0a)S)A 1eJ,堌#635RIgpNHuTH_SԕqVe ` &S)>p;S$魁eKIuX`I4춒o}`m$1":PI<[v9^\pTJjriRŭ P{#{R2,`)e-`mgj~1ϣLKam7&U\j/3mJ,`F;M'䱀 .KR#)yhTq;pcK9(q!w?uRR,n.yw*UXj#\]ɱ(qv2=RqfB#iJmmL<]Y͙#$5 uTU7ӦXR+q,`I}qL'`6Kͷ6r,]0S$- [RKR3oiRE|nӦXR.(i:LDLTJjY%o:)6rxzҒqTJjh㞦I.$YR.ʼnGZ\ֿf:%55 I˼!6dKxm4E"mG_ s? .e*?LRfK9%q#uh$)i3ULRfK9yxm܌bj84$i1U^@Wbm4uJ,ҪA>_Ij?1v32[gLRD96oTaR׿N7%L2 NT,`)7&ƝL*꽙yp_$M2#AS,`)7$rkTA29_Iye"|/0t)$n XT2`YJ;6Jx".e<`$) PI$5V4]29SRI>~=@j]lp2`K9Jaai^" Ԋ29ORI%:XV5]JmN9]H;1UC39NI%Xe78t)a;Oi Ҙ>Xt"~G>_mn:%|~ޅ_+]$o)@ǀ{hgN;IK6G&rp)T2i୦KJuv*T=TOSV>(~D>dm,I*Ɛ:R#ۙNI%D>G.n$o;+#RR!.eU˽TRI28t)1LWϚ>IJa3oFbu&:tJ*(F7y0ZR ^p'Ii L24x| XRI%ۄ>S1]Jy[zL$adB7.eh4%%누>WETf+3IR:I3Xה)3אOۦSRO'ٺ)S}"qOr[B7ϙ.edG)^ETR"RtRݜh0}LFVӦDB^k_JDj\=LS(Iv─aTeZ%eUAM-0;~˃@i|l @S4y72>sX-vA}ϛBI!ݎߨWl*)3{'Y|iSlEڻ(5KtSI$Uv02,~ԩ~x;P4ցCrO%tyn425:KMlD ^4JRxSهF_}شJTS6uj+ﷸk$eZO%G*^V2u3EMj3k%)okI]dT)URKDS 7~m@TJR~荪fT"֛L \sM -0T KfJz+nإKr L&j()[E&I ߴ>e FW_kJR|!O:5/2跌3T-'|zX ryp0JS ~^F>-2< `*%ZFP)bSn"L :)+pʷf(pO3TMW$~>@~ū:TAIsV1}S2<%ޟM?@iT ,Eūoz%i~g|`wS(]oȤ8)$ ntu`өe`6yPl IzMI{ʣzʨ )IZ2= ld:5+請M$-ї;U>_gsY$ÁN5WzWfIZ)-yuXIfp~S*IZdt;t>KūKR|$#LcԀ+2\;kJ`]YǔM1B)UbG"IRߊ<xܾӔJ0Z='Y嵤 Leveg)$znV-º^3Ւof#0Tfk^Zs[*I꯳3{)ˬW4Ւ4 OdpbZRS|*I 55#"&-IvT&/윚Ye:i$ 9{LkuRe[I~_\ؠ%>GL$iY8 9ܕ"S`kS.IlC;Ҏ4x&>u_0JLr<J2(^$5L s=MgV ~,Iju> 7r2)^=G$1:3G< `J3~&IR% 6Tx/rIj3O< ʔ&#f_yXJiގNSz; Tx(i8%#4 ~AS+IjerIUrIj362v885+IjAhK__5X%nV%Iͳ-y|7XV2v4fzo_68"S/I-qbf; LkF)KSM$ Ms>K WNV}^`-큧32ŒVؙGdu,^^m%6~Nn&͓3ŒVZMsRpfEW%IwdǀLm[7W&bIRL@Q|)* i ImsIMmKmyV`i$G+R 0tV'!V)֏28vU7͒vHꦼtxꗞT ;S}7Mf+fIRHNZUkUx5SAJㄌ9MqμAIRi|j5)o*^'<$TwI1hEU^c_j?Е$%d`z cyf,XO IJnTgA UXRD }{H}^S,P5V2\Xx`pZ|Yk:$e ~ @nWL.j+ϝYb퇪bZ BVu)u/IJ_ 1[p.p60bC >|X91P:N\!5qUB}5a5ja `ubcVxYt1N0Zzl4]7­gKj]?4ϻ *[bg$)+À*x쳀ogO$~,5 زUS9 lq3+5mgw@np1sso Ӻ=|N6 /g(Wv7U;zωM=wk,0uTg_`_P`uz?2yI!b`kĸSo+Qx%!\οe|އԁKS-s6pu_(ֿ$i++T8=eY; צP+phxWQv*|p1. ά. XRkIQYP,drZ | B%wP|S5`~́@i޾ E;Չaw{o'Q?%iL{u D?N1BD!owPHReFZ* k_-~{E9b-~P`fE{AܶBJAFO wx6Rox5 K5=WwehS8 (JClJ~ p+Fi;ŗo+:bD#g(C"wA^ r.F8L;dzdIHUX݆ϞXg )IFqem%I4dj&ppT{'{HOx( Rk6^C٫O.)3:s(۳(Z?~ٻ89zmT"PLtw䥈5&b<8GZ-Y&K?e8,`I6e(֍xb83 `rzXj)F=l($Ij 2*(F?h(/9ik:I`m#p3MgLaKjc/U#n5S# m(^)=y=đx8ŬI[U]~SцA4p$-F i(R,7Cx;X=cI>{Km\ o(Tv2vx2qiiDJN,Ҏ!1f 5quBj1!8 rDFd(!WQl,gSkL1Bxg''՞^ǘ;pQ P(c_ IRujg(Wz bs#P­rz> k c&nB=q+ؔXn#r5)co*Ũ+G?7< |PQӣ'G`uOd>%Mctz# Ԫڞ&7CaQ~N'-P.W`Oedp03C!IZcIAMPUۀ5J<\u~+{9(FbbyAeBhOSܳ1 bÈT#ŠyDžs,`5}DC-`̞%r&ڙa87QWWp6e7 Rϫ/oY ꇅ Nܶըtc!LA T7V4Jsū I-0Pxz7QNF_iZgúWkG83 0eWr9 X]㾮݁#Jˢ C}0=3ݱtBi]_ &{{[/o[~ \q鯜00٩|cD3=4B_b RYb$óBRsf&lLX#M*C_L܄:gx)WΘsGSbuL rF$9';\4Ɍq'n[%p.Q`u hNb`eCQyQ|l_C>Lb꟟3hSb #xNxSs^ 88|Mz)}:](vbۢamŖ࿥ 0)Q7@0=?^k(*J}3ibkFn HjB׻NO z x}7p 0tfDX.lwgȔhԾŲ }6g E |LkLZteu+=q\Iv0쮑)QٵpH8/2?Σo>Jvppho~f>%bMM}\//":PTc(v9v!gոQ )UfVG+! 35{=x\2+ki,y$~A1iC6#)vC5^>+gǵ@1Hy٪7u;p psϰu/S <aʸGu'tD1ԝI<pg|6j'p:tպhX{o(7v],*}6a_ wXRk,O]Lܳ~Vo45rp"N5k;m{rZbΦ${#)`(Ŵg,;j%6j.pyYT?}-kBDc3qA`NWQū20/^AZW%NQ MI.X#P#,^Ebc&?XR tAV|Y.1!؅⨉ccww>ivl(JT~ u`ٵDm q)+Ri x/x8cyFO!/*!/&,7<.N,YDŽ&ܑQF1Bz)FPʛ?5d 6`kQձ λc؎%582Y&nD_$Je4>a?! ͨ|ȎWZSsv8 j(I&yj Jb5m?HWp=g}G3#|I,5v珿] H~R3@B[☉9Ox~oMy=J;xUVoj bUsl_35t-(ՃɼRB7U!qc+x4H_Qo֮$[GO<4`&č\GOc[.[*Af%mG/ ňM/r W/Nw~B1U3J?P&Y )`ѓZ1p]^l“W#)lWZilUQu`-m|xĐ,_ƪ|9i:_{*(3Gѧ}UoD+>m_?VPۅ15&}2|/pIOʵ> GZ9cmíتmnz)yߐbD >e}:) r|@R5qVSA10C%E_'^8cR7O;6[eKePGϦX7jb}OTGO^jn*媓7nGMC t,k31Rb (vyܴʭ!iTh8~ZYZp(qsRL ?b}cŨʊGO^!rPJO15MJ[c&~Z`"ѓޔH1C&^|Ш|rʼ,AwĴ?b5)tLU)F| &g٣O]oqSUjy(x<Ϳ3 .FSkoYg2 \_#wj{u'rQ>o;%n|F*O_L"e9umDds?.fuuQbIWz |4\0 sb;OvxOSs; G%T4gFRurj(֍ڑb uԖKDu1MK{1^ q; C=6\8FR艇!%\YÔU| 88m)֓NcLve C6z;o&X x59:q61Z(T7>C?gcļxѐ Z oo-08jہ x,`' ҔOcRlf~`jj".Nv+sM_]Zk g( UOPyεx%pUh2(@il0ݽQXxppx-NS( WO+轾 nFߢ3M<;z)FBZjciu/QoF 7R¥ ZFLF~#ȣߨ^<쩡ݛкvџ))ME>ώx4m#!-m!L;vv#~Y[đKmx9.[,UFS CVkZ +ߟrY٧IZd/ioi$%͝ب_ֶX3ܫhNU ZZgk=]=bbJS[wjU()*I =ώ:}-蹞lUj:1}MWm=̛ _ ¾,8{__m{_PVK^n3esw5ӫh#$-q=A̟> ,^I}P^J$qY~Q[ Xq9{#&T.^GVj__RKpn,b=`żY@^՝;z{paVKkQXj/)y TIc&F;FBG7wg ZZDG!x r_tƢ!}i/V=M/#nB8 XxЫ ^@CR<{䤭YCN)eKOSƟa $&g[i3.C6xrOc8TI;o hH6P&L{@q6[ Gzp^71j(l`J}]e6X☉#͕ ׈$AB1Vjh㭦IRsqFBjwQ_7Xk>y"N=MB0 ,C #o6MRc0|$)ف"1!ixY<B9mx `,tA>)5ػQ?jQ?cn>YZe Tisvh# GMމȇp:ԴVuږ8ɼH]C.5C!UV;F`mbBk LTMvPʍϤj?ԯ/Qr1NB`9s"s TYsz &9S%U԰> {<ؿSMxB|H\3@!U| k']$U+> |HHMLޢ?V9iD!-@x TIî%6Z*9X@HMW#?nN ,oe6?tQwڱ.]-y':mW0#!J82qFjH -`ѓ&M0u Uγmxϵ^-_\])@0Rt.8/?ٰCY]x}=sD3ojަЫNuS%U}ԤwHH>ڗjܷ_3gN q7[q2la*ArǓԖ+p8/RGM ]jacd(JhWko6ڎbj]i5Bj3+3!\j1UZLsLTv8HHmup<>gKMJj0@H%,W΃7R) ">c, xixј^ aܖ>H[i.UIHc U1=yW\=S*GR~)AF=`&2h`DzT󑓶J+?W+}C%P:|0H܆}-<;OC[~o.$~i}~HQ TvXΈr=b}$vizL4:ȰT|4~*!oXQR6Lk+#t/g lԁߖ[Jڶ_N$k*". xsxX7jRVbAAʯKҎU3)zSNN _'s?f)6X!%ssAkʱ>qƷb hg %n ~p1REGMHH=BJiy[<5 ǁJҖgKR*倳e~HUy)Ag,K)`Vw6bRR:qL#\rclK/$sh*$ 6덤 KԖc 3Z9=Ɣ=o>X Ώ"1 )a`SJJ6k(<c e{%kϊP+SL'TcMJWRm ŏ"w)qc ef꒵i?b7b('"2r%~HUS1\<(`1Wx9=8HY9m:X18bgD1u ~|H;K-Uep,, C1 RV.MR5άh,tWO8WC$ XRVsQS]3GJ|12 [vM :k#~tH30Rf-HYݺ-`I9%lIDTm\ S{]9gOڒMNCV\G*2JRŨ;Rҏ^ڽ̱mq1Eu?To3I)y^#jJw^Ńj^vvlB_⋌P4x>0$c>K†Aļ9s_VjTt0l#m>E-,,x,-W)سo&96RE XR.6bXw+)GAEvL)͞K4$p=Ũi_ѱOjb HY/+@θH9޼]Nԥ%n{ &zjT? Ty) s^ULlb,PiTf^<À] 62R^V7)S!nllS6~͝V}-=%* ʻ>G DnK<y&>LPy7'r=Hj 9V`[c"*^8HpcO8bnU`4JȪAƋ#1_\ XϘHPRgik(~G~0DAA_2p|J묭a2\NCr]M_0 ^T%e#vD^%xy-n}-E\3aS%yN!r_{ )sAw ڼp1pEAk~v<:`'ӭ^5 ArXOI驻T (dk)_\ PuA*BY]yB"l\ey hH*tbK)3 IKZ򹞋XjN n *n>k]X_d!ryBH ]*R 0(#'7 %es9??ښFC,ՁQPjARJ\Ρw K#jahgw;2$l*) %Xq5!U᢯6Re] |0[__64ch&_}iL8KEgҎ7 M/\`|.p,~`a=BR?xܐrQ8K XR2M8f ?`sgWS%" Ԉ 7R%$ N}?QL1|-эټwIZ%pvL3Hk>,ImgW7{E xPHx73RA @RS CC !\ȟ5IXR^ZxHл$Q[ŝ40 (>+ _C >BRt<,TrT {O/H+˟Pl6 I B)/VC<6a2~(XwV4gnXR ϱ5ǀHٻ?tw똤Eyxp{#WK qG%5],(0ӈH HZ])ג=K1j&G(FbM@)%I` XRg ʔ KZG(vP,<`[ Kn^ SJRsAʠ5xՅF`0&RbV tx:EaUE/{fi2;.IAwW8/tTxAGOoN?G}l L(n`Zv?pB8K_gI+ܗ #i?ޙ.) p$utc ~DžfՈEo3l/)I-U?aԅ^jxArA ΧX}DmZ@QLےbTXGd.^|xKHR{|ΕW_h] IJ`[G9{).y) 0X YA1]qp?p_k+J*Y@HI>^?gt.06Rn ,` ?);p pSF9ZXLBJPWjgQ|&)7! HjQt<| ؅W5 x W HIzYoVMGP Hjn`+\(dNW)F+IrS[|/a`K|ͻ0Hj{R,Q=\ (F}\WR)AgSG`IsnAR=|8$}G(vC$)s FBJ?]_u XRvύ6z ŨG[36-T9HzpW̞ú Xg큽=7CufzI$)ki^qk-) 0H*N` QZkk]/tnnsI^Gu't=7$ Z;{8^jB% IItRQS7[ϭ3 $_OQJ`7!]W"W,)Iy W AJA;KWG`IY{8k$I$^%9.^(`N|LJ%@$I}ֽp=FB*xN=gI?Q{٥4B)mw $Igc~dZ@G9K X?7)aK%݅K$IZ-`IpC U6$I\0>!9k} Xa IIS0H$I H ?1R.Чj:4~Rw@p$IrA*u}WjWFPJ$I➓/6#! LӾ+ X36x8J |+L;v$Io4301R20M I$-E}@,pS^ޟR[/s¹'0H$IKyfŸfVOπFT*a$I>He~VY/3R/)>d$I>28`Cjw,n@FU*9ttf$I~<;=/4RD~@ X-ѕzἱI$: ԍR a@b X{+Qxuq$IЛzo /~3\8ڒ4BN7$IҀj V]n18H$IYFBj3̵̚ja pp $Is/3R Ӻ-Yj+L;.0ŔI$Av? #!5"aʄj}UKmɽH$IjCYs?h$IDl843.v}m7UiI=&=0Lg0$I4: embe` eQbm0u? $IT!Sƍ'-sv)s#C0:XB2a w I$zbww{."pPzO =Ɔ\[ o($Iaw]`E).Kvi:L*#gР7[$IyGPI=@R 4yR~̮´cg I$I/<tPͽ hDgo 94Z^k盇΄8I56^W$I^0̜N?4*H`237}g+hxoq)SJ@p|` $I%>-hO0eO>\ԣNߌZD6R=K ~n($I$y3D>o4b#px2$yڪtzW~a $I~?x'BwwpH$IZݑnC㧄Pc_9sO gwJ=l1:mKB>Ab<4Lp$Ib o1ZQ@85b̍ S'F,Fe,^I$IjEdù{l4 8Ys_s Z8.x m"+{~?q,Z D!I$ϻ'|XhB)=…']M>5 rgotԎ 獽PH$IjIPhh)n#cÔqA'ug5qwU&rF|1E%I$%]!'3AFD/;Ck_`9 v!ٴtPV;x`'*bQa w I$Ix5 FC3D_~A_#O݆DvV?<qw+I$I{=Z8".#RIYyjǪ=fDl9%M,a8$I$Ywi[7ݍFe$s1ՋBVA?`]#!oz4zjLJo8$I$%@3jAa4(o ;p,,dya=F9ً[LSPH$IJYЉ+3> 5"39aZ<ñh!{TpBGkj}Sp $IlvF.F$I z< '\K*qq.f<2Y!S"-\I$IYwčjF$ w9 \ߪB.1v!Ʊ?+r:^!I$BϹB H"B;L'G[ 4U#5>੐)|#o0aڱ$I>}k&1`U#V?YsV x>{t1[I~D&(I$I/{H0fw"q"y%4 IXyE~M3 8XψL}qE$I[> nD?~sf ]o΁ cT6"?'_Ἣ $I>~.f|'!N?⟩0G KkXZE]ޡ;/&?k OۘH$IRۀwXӨ<7@PnS04aӶp.:@\IWQJ6sS%I$e5ڑv`3:x';wq_vpgHyXZ 3gЂ7{{EuԹn±}$I$8t;b|591nءQ"P6O5i }iR̈́%Q̄p!I䮢]O{H$IRϻ9s֧ a=`- aB\X0"+5"C1Hb?߮3x3&gşggl_hZ^,`5?ߎvĸ%̀M!OZC2#0x LJ0 Gw$I$I}<{Eb+y;iI,`ܚF:5ܛA8-O-|8K7s|#Z8a&><a&/VtbtLʌI$I$I$I$I$I$IRjDD%tEXtdate:create2022-05-31T04:40:26+00:00!Î%tEXtdate:modify2022-05-31T04:40:26+00:00|{2IENDB`Mini Shell

HOME


Mini Shell 1.0
DIR:/usr/share/perl5/vendor_perl/Net/SNMP/Security/
Upload File :
Current File : //usr/share/perl5/vendor_perl/Net/SNMP/Security/USM.pm
# -*- mode: perl -*-
# ============================================================================

package Net::SNMP::Security::USM;

# $Id: USM.pm,v 4.1 2010/09/10 00:01:22 dtown Rel $

# Object that implements the SNMPv3 User-based Security Model.

# Copyright (c) 2001-2010 David M. Town <dtown@cpan.org>
# All rights reserved.

# This program is free software; you may redistribute it and/or modify it
# under the same terms as the Perl 5 programming language system itself.

# ============================================================================

use strict;

use Net::SNMP::Security qw( :ALL );

use Net::SNMP::Message qw(
   :msgFlags asn1_itoa OCTET_STRING SEQUENCE INTEGER SNMP_VERSION_3 TRUE FALSE
);

use Crypt::DES();
use Digest::MD5();
use Digest::SHA1();
use Digest::HMAC();

## Version of the Net::SNMP::Security::USM module

our $VERSION = v4.0.1;

## Handle importing/exporting of symbols

use base qw( Net::SNMP::Security );

our @EXPORT_OK;

our %EXPORT_TAGS = (
   authprotos => [
      qw( AUTH_PROTOCOL_NONE AUTH_PROTOCOL_HMACMD5 AUTH_PROTOCOL_HMACSHA )
   ],
   levels     => [
      qw( SECURITY_LEVEL_NOAUTHNOPRIV SECURITY_LEVEL_AUTHNOPRIV
          SECURITY_LEVEL_AUTHPRIV )
   ],
   models     => [
      qw( SECURITY_MODEL_ANY SECURITY_MODEL_SNMPV1 SECURITY_MODEL_SNMPV2C
          SECURITY_MODEL_USM )
   ],
   privprotos => [
      qw( PRIV_PROTOCOL_NONE PRIV_PROTOCOL_DES PRIV_PROTOCOL_AESCFB128
          PRIV_PROTOCOL_DRAFT_3DESEDE PRIV_PROTOCOL_DRAFT_AESCFB128
          PRIV_PROTOCOL_DRAFT_AESCFB192 PRIV_PROTOCOL_DRAFT_AESCFB256 )
   ],
);

Exporter::export_ok_tags( qw( authprotos levels models privprotos ) );

$EXPORT_TAGS{ALL} = [ @EXPORT_OK ];

## RCC 3414 - Authentication protocols

sub AUTH_PROTOCOL_NONE    { '1.3.6.1.6.3.10.1.1.1' } # usmNoAuthProtocol
sub AUTH_PROTOCOL_HMACMD5 { '1.3.6.1.6.3.10.1.1.2' } # usmHMACMD5AuthProtocol
sub AUTH_PROTOCOL_HMACSHA { '1.3.6.1.6.3.10.1.1.3' } # usmHMACSHAAuthProtocol

## RFC 3414 - Privacy protocols

sub PRIV_PROTOCOL_NONE    { '1.3.6.1.6.3.10.1.2.1' } # usmNoPrivProtocol
sub PRIV_PROTOCOL_DES     { '1.3.6.1.6.3.10.1.2.2' } # usmDESPrivProtocol

## RFC 3826 - The AES Cipher Algorithm in the SNMP USM 

# usmAesCfb128Protocol
sub PRIV_PROTOCOL_AESCFB128        {  '1.3.6.1.6.3.10.1.2.4' }

# The privacy protocols below have been implemented using the draft 
# specifications intended to extend the User-based Security Model 
# defined in RFC 3414.  Since the object definitions have not been 
# standardized, they have been based on the Extended Security Options 
# Consortium MIB found at http://www.snmp.com/eso/esoConsortiumMIB.txt.

# Extension to Support Triple-DES EDE <draft-reeder-snmpv3-usm-3desede-00.txt> 
# Reeder and Gudmunsson; October 1999, expired April 2000 

# usm3DESPrivProtocol 
sub PRIV_PROTOCOL_DRAFT_3DESEDE    { '1.3.6.1.4.1.14832.1.1' }

# AES Cipher Algorithm in the USM <draft-blumenthal-aes-usm-04.txt>
# Blumenthal, Maino, and McCloghrie; October 2002, expired April 2003 

# usmAESCfb128PrivProtocol 
sub PRIV_PROTOCOL_DRAFT_AESCFB128  { '1.3.6.1.4.1.14832.1.2' }

# usmAESCfb192PrivProtocol 
sub PRIV_PROTOCOL_DRAFT_AESCFB192  { '1.3.6.1.4.1.14832.1.3' }

# usmAESCfb256PrivProtocol
sub PRIV_PROTOCOL_DRAFT_AESCFB256  { '1.3.6.1.4.1.14832.1.4' }

## Package variables

our $ENGINE_ID;  # Our authoritative snmpEngineID                                                         
# [public methods] -----------------------------------------------------------

sub new
{
   my ($class, %argv) = @_;

   # Create a new data structure for the object
   my $this = bless {
      '_error'              => undef,                 # Error message
      '_version'            => SNMP_VERSION_3,        # version 
      '_authoritative'      => FALSE,                 # Authoritative flag
      '_discovered'         => FALSE,                 # Engine discovery flag
      '_synchronized'       => FALSE,                 # Synchronization flag
      '_engine_id'          => q{},                   # snmpEngineID
      '_engine_boots'       => 0,                     # snmpEngineBoots
      '_engine_time'        => 0,                     # snmpEngineTime
      '_latest_engine_time' => 0,                     # latestReceivedEngineTime
      '_time_epoc'          => time(),                # snmpEngineBoots epoc
      '_user_name'          => q{},                   # securityName 
      '_auth_data'          => undef,                 # Authentication data
      '_auth_key'           => undef,                 # authKey 
      '_auth_password'      => undef,                 # Authentication password 
      '_auth_protocol'      => AUTH_PROTOCOL_HMACMD5, # authProtocol
      '_priv_data'          => undef,                 # Privacy data
      '_priv_key'           => undef,                 # privKey 
      '_priv_password'      => undef,                 # Privacy password
      '_priv_protocol'      => PRIV_PROTOCOL_DES,     # privProtocol
      '_security_level'     => SECURITY_LEVEL_NOAUTHNOPRIV
   }, $class;

   # We first need to find out if we are an authoritative SNMP
   # engine and set the authProtocol and privProtocol if they 
   # have been provided.

   foreach (keys %argv) {

      if (/^-?authoritative$/i) {
         $this->{_authoritative} = (delete $argv{$_}) ? TRUE : FALSE;
      } elsif (/^-?authprotocol$/i) {
         $this->_auth_protocol(delete $argv{$_});
      } elsif (/^-?privprotocol$/i) {
         $this->_priv_protocol(delete $argv{$_});
      }

      if (defined $this->{_error}) {
         return wantarray ? (undef, $this->{_error}) : undef;
      }
   }

   # Now validate the rest of the passed arguments

   for (keys %argv) {

      if (/^-?version$/i) {
         $this->_version($argv{$_});
      } elsif (/^-?debug$/i) {
         $this->debug($argv{$_});
      } elsif ((/^-?engineid$/i) && ($this->{_authoritative})) {
         $this->_engine_id($argv{$_});
      } elsif (/^-?username$/i) {
         $this->_user_name($argv{$_});
      } elsif (/^-?authkey$/i) {
         $this->_auth_key($argv{$_});
      } elsif (/^-?authpassword$/i) {
         $this->_auth_password($argv{$_});
      } elsif (/^-?privkey$/i) {
         $this->_priv_key($argv{$_});
      } elsif (/^-?privpassword$/i) {
         $this->_priv_password($argv{$_});
      } else {
         $this->_error('The argument "%s" is unknown', $_);
      }

      if (defined $this->{_error}) {
         return wantarray ? (undef, $this->{_error}) : undef;
      }

   }

   # Generate a snmpEngineID and populate the object accordingly
   # if we are an authoritative snmpEngine.

   if ($this->{_authoritative}) {
      $this->_snmp_engine_init();
   }

   # Define the securityParameters
   if (!defined $this->_security_params()) {
      return wantarray ? (undef, $this->{_error}) : undef;
   }

   # Return the object and an empty error message (in list context)
   return wantarray ? ($this, q{}) : $this;
}

sub generate_request_msg
{
   my ($this, $pdu, $msg) = @_;

   # Clear any previous errors
   $this->_error_clear();

   if (@_ < 3) {
      return $this->_error('The required PDU and/or Message object is missing');
   }

   # Validate the SNMP version of the PDU
   if ($pdu->version() != $this->{_version}) {
      return $this->_error(
         'The SNMP version %d was expected, but %d was found',
         $this->{_version}, $pdu->version()
      );
   }

   # Validate the securityLevel of the PDU
   if ($pdu->security_level() > $this->{_security_level}) {
      return $this->_error(
         'The PDU securityLevel %d is greater than the configured value %d',
         $pdu->security_level(), $this->{_security_level}
      );
   }

   # Validate PDU type with snmpEngine type
   if ($pdu->expect_response()) {
      if ($this->{_authoritative}) {
         return $this->_error(
            'Must be a non-authoritative SNMP engine to generate a %s',
            asn1_itoa($pdu->pdu_type())
         );
      }
   } else {
      if (!$this->{_authoritative}) {
         return $this->_error(
            'Must be an authoritative SNMP engine to generate a %s',
            asn1_itoa($pdu->pdu_type())
         );
      }
   }

   # Extract the msgGlobalData out of the message
   my $msg_global_data = $msg->clear();

   # AES in the USM Section 3.1.2.1 - "The 128-bit IV is obtained as
   # the concatenation of the... ...snmpEngineBoots, ...snmpEngineTime,
   # and a local 64-bit integer.  We store the current snmpEngineBoots
   # and snmpEngineTime before encrypting the PDU so that the computed
   # IV matches the transmitted msgAuthoritativeEngineBoots and
   # msgAuthoritativeEngineTime.

   my $msg_engine_time  = $this->_engine_time();
   my $msg_engine_boots = $this->_engine_boots();

   # Copy the PDU into a "plain text" buffer
   my $pdu_buffer  = $pdu->copy();
   my $priv_params = q{};

   # encryptedPDU::=OCTET STRING
   if ($pdu->security_level() > SECURITY_LEVEL_AUTHNOPRIV) {
      if (!defined $this->_encrypt_data($msg, $priv_params, $pdu_buffer)) {
         return $this->_error();
      }
   }

   # msgPrivacyParameters::=OCTET STRING
   if (!defined $msg->prepare(OCTET_STRING, $priv_params)) {
      return $this->_error($msg->error());
   }

   # msgAuthenticationParameters::=OCTET STRING

   my $auth_params = q{};
   my $auth_location = 0;

   if ($pdu->security_level() > SECURITY_LEVEL_NOAUTHNOPRIV) {

      # Save the location to fill in msgAuthenticationParameters later
      $auth_location = $msg->length() + 12 + length $pdu_buffer;

      # Set the msgAuthenticationParameters to all zeros
      $auth_params = pack 'x12';
   }

   if (!defined $msg->prepare(OCTET_STRING, $auth_params)) {
      return $this->_error($msg->error());
   }

   # msgUserName::=OCTET STRING 
   if (!defined $msg->prepare(OCTET_STRING, $pdu->security_name())) {
      return $this->_error($msg->error());
   }

   # msgAuthoritativeEngineTime::=INTEGER  
   if (!defined $msg->prepare(INTEGER, $msg_engine_time)) {
      return $this->_error($msg->error());
   }

   # msgAuthoritativeEngineBoots::=INTEGER
   if (!defined $msg->prepare(INTEGER, $msg_engine_boots)) {
      return $this->_error($msg->error());
   }

   # msgAuthoritativeEngineID
   if (!defined $msg->prepare(OCTET_STRING, $this->_engine_id())) {
      return $this->_error($msg->error());
   }

   # UsmSecurityParameters::= SEQUENCE
   if (!defined $msg->prepare(SEQUENCE)) {
      return $this->_error($msg->error());
   }

   # msgSecurityParameters::=OCTET STRING
   if (!defined $msg->prepare(OCTET_STRING, $msg->clear())) {
      return $this->_error($msg->error());
   }

   # Append the PDU
   if (!defined $msg->append($pdu_buffer)) {
      return $this->_error($msg->error());
   }

   # Prepend the msgGlobalData
   if (!defined $msg->prepend($msg_global_data)) {
      return $this->_error($msg->error());
   }

   # version::=INTEGER
   if (!defined $msg->prepare(INTEGER, $this->{_version})) {
      return $this->_error($msg->error());
   }

   # message::=SEQUENCE
   if (!defined $msg->prepare(SEQUENCE)) {
      return $this->_error($msg->error());
   }

   # Apply authentication
   if ($pdu->security_level() > SECURITY_LEVEL_NOAUTHNOPRIV) {
      if (!defined $this->_authenticate_outgoing_msg($msg, $auth_location)) {
         return $this->_error($msg->error());
      }
   }

   # Return the Message
   return $msg;
}

sub process_incoming_msg
{
   my ($this, $msg) = @_;

   # Clear any previous errors
   $this->_error_clear();

   return $this->_error('The required Message object is missing') if (@_ < 2);

   # msgSecurityParameters::=OCTET STRING

   my $msg_params = $msg->process(OCTET_STRING);
   return $this->_error($msg->error()) if !defined $msg_params;

   # Need to move the buffer index back to the begining of the data
   # portion of the OCTET STRING that contains the msgSecurityParameters.

   $msg->index($msg->index() - length $msg_params);

   # UsmSecurityParameters::=SEQUENCE
   return $this->_error($msg->error()) if !defined $msg->process(SEQUENCE);

   # msgAuthoritativeEngineID::=OCTET STRING
   my $msg_engine_id;
   if (!defined($msg_engine_id = $msg->process(OCTET_STRING))) {
      return $this->_error($msg->error());
   }

   # msgAuthoritativeEngineBoots::=INTEGER (0..2147483647)
   my $msg_engine_boots;
   if (!defined ($msg_engine_boots = $msg->process(INTEGER))) {
      return $this->_error($msg->error());
   }
   if (($msg_engine_boots < 0) || ($msg_engine_boots > 2147483647)) {
      return $this->_error(
         'The msgAuthoritativeEngineBoots value %d is out of range ' .
         '(0..2147483647)', $msg_engine_boots
      );
   }

   # msgAuthoritativeEngineTime::=INTEGER (0..2147483647)
   my $msg_engine_time;
   if (!defined ($msg_engine_time = $msg->process(INTEGER))) {
      return $this->_error($msg->error());
   }
   if (($msg_engine_time < 0) || ($msg_engine_time > 2147483647)) {
      return $this->_error(
         'The msgAuthoritativeEngineTime value %d is out of range ' .
         '(0..2147483647)', $msg_engine_time
      );
   }

   # msgUserName::=OCTET STRING (SIZE(0..32))
   if (!defined $msg->security_name($msg->process(OCTET_STRING))) {
      return $this->_error($msg->error());
   }

   # msgAuthenticationParameters::=OCTET STRING
   my $auth_params;
   if (!defined ($auth_params = $msg->process(OCTET_STRING))) {
      return $this->_error($msg->error());
   }

   # We need to zero out the msgAuthenticationParameters in order 
   # to compute the HMAC properly.

   if (my $len = length $auth_params) {
      if ($len != 12) {
         return $this->_error(
            'The msgAuthenticationParameters length of %d is invalid', $len
         );
      }
      substr ${$msg->reference}, ($msg->index() - 12), 12, pack 'x12';
   }

   # msgPrivacyParameters::=OCTET STRING
   my $priv_params;
   if (!defined ($priv_params = $msg->process(OCTET_STRING))) {
      return $this->_error($msg->error());
   }

   # Validate the msgAuthoritativeEngineID and msgUserName

   if ($this->{_discovered}) {

      if ($msg_engine_id ne $this->_engine_id()) {
         return $this->_error(
            'The msgAuthoritativeEngineID "%s" was expected, but "%s" was ' .
            'found', unpack('H*', $this->_engine_id()),
            unpack 'H*', $msg_engine_id
         );
      }

      if ($msg->security_name() ne $this->_user_name()) {
         return $this->_error(
            'The msgUserName "%s" was expected, but "%s" was found',
            $this->_user_name(), $msg->security_name()
         );
      }

   } else {

      # Handle authoritativeEngineID discovery
      if (!defined $this->_engine_id_discovery($msg_engine_id)) {
         return $this->_error();
      }

   }

   # Validate the incoming securityLevel

   my $security_level = $msg->security_level();

   if ($security_level > $this->{_security_level}) {
      return $this->_error(
          'The message securityLevel %d is greater than the configured ' .
          'value %d', $security_level, $this->{_security_level}
      );
   }

   if ($security_level > SECURITY_LEVEL_NOAUTHNOPRIV) {

      # Authenticate the message
      if (!defined $this->_authenticate_incoming_msg($msg, $auth_params)) {
         return $this->_error();
      }

      # Synchronize the time
      if (!$this->_synchronize($msg_engine_boots, $msg_engine_time)) {
         return $this->_error();
      }

      # Check for timeliness
      if (!defined $this->_timeliness($msg_engine_boots, $msg_engine_time)) {
         return $this->_error();
      }

      if ($security_level > SECURITY_LEVEL_AUTHNOPRIV) {

         # Validate the msgPrivacyParameters length.

         if (length($priv_params) != 8) {
            return $this->_error(
               'The msgPrivacyParameters length of %d is invalid',
               length $priv_params
            );
         }

         # AES in the USM Section 3.1.2.1 - "The 128-bit IV is
         # obtained as the concatenation of the... ...snmpEngineBoots,
         # ...snmpEngineTime, and a local 64-bit integer.  ...The
         # 64-bit integer must be placed in the msgPrivacyParameters
         # field..."  We must prepend the snmpEngineBoots and
         # snmpEngineTime as received in order to compute the IV.

         if (($this->{_priv_protocol} eq PRIV_PROTOCOL_AESCFB128)       ||
             ($this->{_priv_protocol} eq PRIV_PROTOCOL_DRAFT_AESCFB192) ||
             ($this->{_priv_protocol} eq PRIV_PROTOCOL_DRAFT_AESCFB256))
         {
            substr $priv_params, 0, 0, pack 'NN', $msg_engine_boots,
                                                  $msg_engine_time;
         }

         # encryptedPDU::=OCTET STRING

         return $this->_decrypt_data($msg,
                                     $priv_params,
                                     $msg->process(OCTET_STRING));

      }

   }

   return TRUE;
}

sub user_name
{
   return $_[0]->{_user_name};
}

sub auth_protocol
{
   my ($this) = @_;

   if ($this->{_security_level} > SECURITY_LEVEL_NOAUTHNOPRIV) {
      return $this->{_auth_protocol};
   }

   return AUTH_PROTOCOL_NONE;
}

sub auth_key
{
   return $_[0]->{_auth_key};
}

sub priv_protocol
{
   my ($this) = @_;

   if ($this->{_security_level} > SECURITY_LEVEL_AUTHNOPRIV) {
      return $this->{_priv_protocol};
   }

   return PRIV_PROTOCOL_NONE;
}

sub priv_key
{
   return $_[0]->{_priv_key};
}

sub engine_id
{
   return $_[0]->{_engine_id};
}

sub engine_boots
{
   goto _engine_boots;
}

sub engine_time
{
   goto &_engine_time;
}

sub security_level
{
   return $_[0]->{_security_level};
}

sub security_model
{
   # RFC 3411 - SnmpSecurityModel::=TEXTUAL-CONVENTION

   return SECURITY_MODEL_USM;
}

sub security_name
{
   goto &_user_name;
}

sub discovered
{
   my ($this) = @_;

   if ($this->{_security_level} > SECURITY_LEVEL_NOAUTHNOPRIV) {
      return ($this->{_discovered} && $this->{_synchronized});
   }

   return $this->{_discovered};
}

# [private methods] ----------------------------------------------------------

sub _version
{
   my ($this, $version) = @_;

   if ($version != SNMP_VERSION_3) {
      return $this->_error('The SNMP version %s is not supported', $version);
   }

   return $this->{_version} = $version;
}

sub _engine_id
{
   my ($this, $engine_id) = @_;

   if (@_ < 2) {
      return $this->{_engine_id};
   }

   if ($engine_id =~  m/^(?:0x)?([A-F0-9]+)$/i) {
       my $eid = pack 'H*', length($1) %  2 ? '0'.$1 : $1;
       my $len = length $eid;
       if ($len < 5 || $len > 32) {
          return $this->_error(
             'The authoritativeEngineID length of %d is out of range (5..32)',
             $len
          );
       }
       $this->{_engine_id} = $eid;
   } else {
      return $this->_error(
          'The authoritativeEngineID "%s" is expected in hexadecimal format',
          $engine_id
      );
   }

   return $this->{_engine_id};
}

sub _user_name
{
   my ($this, $user_name) = @_;

   if (@_ == 2) {
      if ($user_name eq q{}) {
         return $this->_error('An empty userName was specified');
      } elsif (length($user_name) > 32) {
         return $this->_error(
            'The userName length of %d is out of range (1..32)',
            length $user_name
         );
      }
      $this->{_user_name} = $user_name;
   }

   # RFC 3414 Section 4 - "Discovery... ...msgUserName of zero-length..."

   return ($this->{_discovered}) ? $this->{_user_name} : q{};
}

sub _snmp_engine_init
{
   my ($this) = @_;

   if ($this->{_engine_id} eq q{}) {

      # Initialize our snmpEngineID using the algorithm described 
      # in RFC 3411 - SnmpEngineID::=TEXTUAL-CONVENTION.

      # The first bit is set to one to indicate that the RFC 3411
      # algorithm is being used.  The first fours bytes are to be
      # the agent's SNMP management private enterprise number, but
      # they are set to all zeros. The fifth byte is set to one to
      # indicate that the final four bytes are an IPv4 address.

      if (!defined $ENGINE_ID) {
         $ENGINE_ID = eval {
            require Sys::Hostname;
            pack('H10', '8000000001') . gethostbyname Sys::Hostname::hostname();
         };

         # Fallback in case gethostbyname() or hostname() fail
         if ($@) {
            $ENGINE_ID = pack 'x11H2', '01';
         }
      }

      $this->{_engine_id} = $ENGINE_ID;
   }

   $this->{_engine_boots} = 1;
   $this->{_time_epoc}    = $^T;
   $this->{_synchronized} = TRUE;
   $this->{_discovered}   = TRUE;

   return TRUE;
}

sub _auth_key
{
   my ($this, $auth_key) = @_;

   if (@_ == 2) {
      if ($auth_key =~ m/^(?:0x)?([A-F0-9]+)$/i) {
         $this->{_auth_key} = pack 'H*', length($1) % 2 ? '0'.$1 : $1;
         if (!defined $this->_auth_key_validate()) {
            return $this->_error();
         }
      } else {
         return $this->_error(
            'The authKey "%s" is expected in hexadecimal format', $auth_key
         );
      }
   }

   return $this->{_auth_key};
}

sub _auth_password
{
   my ($this, $auth_password) = @_;

   if (@_ == 2) {
      if ($auth_password eq q{}) {
         return $this->_error('An empty authentication password was specified');
      }
      $this->{_auth_password} = $auth_password;
   }

   return $this->{_auth_password};
}

{
   my $protocols = {
      '(?:hmac-)?md5(?:-96)?',           AUTH_PROTOCOL_HMACMD5,
      quotemeta AUTH_PROTOCOL_HMACMD5,   AUTH_PROTOCOL_HMACMD5,
      '(?:hmac-)?sha(?:-?1|-96)?',       AUTH_PROTOCOL_HMACSHA,
      quotemeta AUTH_PROTOCOL_HMACSHA,   AUTH_PROTOCOL_HMACSHA,
   };

   sub _auth_protocol
   {
      my ($this, $proto) = @_;

      if (@_ < 2) {
         return $this->{_auth_protocol};
      }

      if ($proto eq q{}) {
         return $this->_error('An empty authProtocol was specified');
      }

      for (keys %{$protocols}) {
         if ($proto =~ /^$_$/i) {
            return $this->{_auth_protocol} = $protocols->{$_};
         }
      }

      return $this->_error('The authProtocol "%s" is unknown', $proto);
   }

}

sub _priv_key
{
   my ($this, $priv_key) = @_;

   if (@_ == 2) {
      if ($priv_key =~ m/^(?:0x)?([A-F0-9]+)$/i) {
         $this->{_priv_key} = pack 'H*', length($1) % 2 ? '0'.$1 : $1;
         if (!defined $this->_priv_key_validate()) {
            return $this->_error();
         }
      } else {
         return $this->_error(
            'The privKey "%s" is expected in hexadecimal format', $priv_key
         );
      }
   }

   return $this->{_priv_key};
}

sub _priv_password
{
   my ($this, $priv_password) = @_;

   if (@_ == 2) {
      if ($priv_password eq q{}) {
         return $this->_error('An empty privacy password was specified');
      }
      $this->{_priv_password} = $priv_password;
   }

   return $this->{_priv_password};
}

{
   my $protocols = {
      '(?:cbc-)?des',                           PRIV_PROTOCOL_DES,
      quotemeta PRIV_PROTOCOL_DES,              PRIV_PROTOCOL_DES,
      '(?:cbc-)?(?:3|triple-)des(?:-?ede)?',    PRIV_PROTOCOL_DRAFT_3DESEDE,
      quotemeta PRIV_PROTOCOL_DRAFT_3DESEDE,    PRIV_PROTOCOL_DRAFT_3DESEDE,
      '(?:(?:cfb)?128-?)?aes(?:-?128)?',        PRIV_PROTOCOL_AESCFB128,
      quotemeta PRIV_PROTOCOL_AESCFB128,        PRIV_PROTOCOL_AESCFB128,
      quotemeta PRIV_PROTOCOL_DRAFT_AESCFB128,  PRIV_PROTOCOL_AESCFB128,
      '(?:(?:cfb)?192-?)aes(?:-?128)?',         PRIV_PROTOCOL_DRAFT_AESCFB192,
      quotemeta PRIV_PROTOCOL_DRAFT_AESCFB192,  PRIV_PROTOCOL_DRAFT_AESCFB192,
      '(?:(?:cfb)?256-?)aes(?:-?128)?',         PRIV_PROTOCOL_DRAFT_AESCFB256,
      quotemeta PRIV_PROTOCOL_DRAFT_AESCFB256,  PRIV_PROTOCOL_DRAFT_AESCFB256,
   };

   sub _priv_protocol
   {
      my ($this, $proto) = @_;

      if (@_ < 2) {
         return $this->{_priv_protocol};
      }

      if ($proto eq q{}) {
         return $this->_error('An empty privProtocol was specified');
      }

      my $priv_proto;

      for (keys %{$protocols}) {
         if ($proto =~ /^$_$/i) {
            $priv_proto = $protocols->{$_};
            last;
         }
      }

      if (!defined $priv_proto) {
         return $this->_error('The privProtocol "%s" is unknown', $proto);
      }

      # Validate the support of the AES cipher algorithm.  Attempt to 
      # load the Crypt::Rijndael module.  If this module is not found, 
      # do not provide support for the AES Cipher Algorithm.

      if (($priv_proto eq PRIV_PROTOCOL_AESCFB128)       ||
          ($priv_proto eq PRIV_PROTOCOL_DRAFT_AESCFB192) ||
          ($priv_proto eq PRIV_PROTOCOL_DRAFT_AESCFB256))
      {
         if (defined (my $error = load_module('Crypt::Rijndael'))) {
            return $this->_error(
               'Support for privProtocol "%s" is unavailable %s', $proto, $error
            );
         }
      }

      return $this->{_priv_protocol} = $priv_proto;
   }

}

sub _engine_boots
{
   return ($_[0]->{_synchronized}) ? $_[0]->{_engine_boots} : 0;
}

sub _engine_time
{
   my ($this) = @_;

   return 0 if (!$this->{_synchronized});

   $this->{_engine_time} = time() - $this->{_time_epoc};

   if ($this->{_engine_time} > 2147483647) {
      DEBUG_INFO('snmpEngineTime rollover');
      if (++$this->{_engine_boots} == 2147483647) {
         die 'FATAL: Unable to handle snmpEngineBoots value';
      }
      $this->{_engine_time} -= 2147483647;
      $this->{_time_epoc} = time() - $this->{_engine_time};
      if (!$this->{_authoritative}) {
         $this->{_synchronized} = FALSE;
         return $this->{_latest_engine_time} = 0;
      }
   }

   if ($this->{_engine_time} < 0) {
      die 'FATAL: Unable to handle negative snmpEngineTime value';
   }

   return $this->{_engine_time};
}

sub _security_params
{
   my ($this) = @_;

   # Clear any previous error messages
   $this->_error_clear();

   # We must have an usmUserName
   if ($this->{_user_name} eq q{}) {
      return $this->_error('The required userName was not specified');
   }

   # Define the authentication parameters

   if ((defined $this->{_auth_password}) && ($this->{_discovered})) {
      if (!defined $this->{_auth_key}) {
         return $this->_error() if !defined $this->_auth_key_generate();
      }
      $this->{_auth_password} = undef;
   }

   if (defined $this->{_auth_key}) {

      # Validate the key based on the protocol
      if (!defined $this->_auth_key_validate()) {
         return $this->_error('The authKey is invalid');
      }

      # Initialize the authentication data 
      if (!defined $this->_auth_data_init()) {
         return $this->_error('Failed to initialize the authentication data');
      }

      if ($this->{_discovered}) {
         $this->{_security_level} = SECURITY_LEVEL_AUTHNOPRIV;
      }

   }

   # You must have authentication to have privacy

   if (!defined ($this->{_auth_key}) && !defined $this->{_auth_password}) {
      if (defined ($this->{_priv_key}) || defined $this->{_priv_password}) {
         return $this->_error(
            'The securityLevel is unsupported (privacy requires authentication)'
         );
      }
   }

   # Define the privacy parameters

   if ((defined $this->{_priv_password}) && ($this->{_discovered})) {
      if (!defined $this->{_priv_key}) {
         return $this->_error() if !defined $this->_priv_key_generate();
      }
      $this->{_priv_password} = undef;
   }

   if (defined $this->{_priv_key}) {

      # Validate the key based on the protocol
      if (!defined $this->_priv_key_validate()) {
         return $this->_error('The privKey is invalid');
      }

      # Initialize the privacy data 
      if (!defined $this->_priv_data_init()) {
         return $this->_error('Failed to initialize the privacy data');
      }

      if ($this->{_discovered}) {
         $this->{_security_level} = SECURITY_LEVEL_AUTHPRIV;
      }

   }

   DEBUG_INFO('securityLevel = %d', $this->{_security_level});

   return $this->{_security_level};
}

sub _engine_id_discovery
{
   my ($this, $engine_id) = @_;

   return TRUE if ($this->{_authoritative});

   DEBUG_INFO('engineID = 0x%s', unpack 'H*', $engine_id || q{});

   if (length($engine_id) < 5 || length($engine_id) > 32) {
      return $this->_error(
         'The msgAuthoritativeEngineID length of %d is out of range (5..32)',
         length $engine_id
      );
   }

   $this->{_engine_id}  = $engine_id;
   $this->{_discovered} = TRUE;

   if (!defined $this->_security_params()) {
      $this->{_discovered} = FALSE;
      return $this->_error();
   }

   return TRUE;
}

sub _synchronize
{
   my ($this, $msg_boots, $msg_time) = @_;

   return TRUE if ($this->{_authoritative});
   return TRUE if ($this->{_security_level} < SECURITY_LEVEL_AUTHNOPRIV);

   if (($msg_boots > $this->_engine_boots()) ||
       (($msg_boots == $this->_engine_boots()) &&
        ($msg_time > $this->{_latest_engine_time})))
   {
      DEBUG_INFO(
         'update: engineBoots = %d, engineTime = %d', $msg_boots, $msg_time
      );

      $this->{_engine_boots} = $msg_boots;
      $this->{_latest_engine_time} = $this->{_engine_time} = $msg_time;
      $this->{_time_epoc} = time() - $this->{_engine_time};

      if (!$this->{_synchronized}) {
         $this->{_synchronized} = TRUE;
         if (!defined $this->_security_params()) {
            return ($this->{_synchronized} = FALSE);
         }
      }

      return TRUE;
   }

   DEBUG_INFO(
      'no update: engineBoots = %d, msgBoots = %d; ' .
      'latestTime = %d, msgTime = %d',
      $this->_engine_boots(), $msg_boots,
      $this->{_latest_engine_time}, $msg_time
   );

   return TRUE;
}

sub _timeliness
{
   my ($this, $msg_boots, $msg_time) = @_;

   return TRUE if ($this->{_security_level} < SECURITY_LEVEL_AUTHNOPRIV);

   # Retrieve a local copy of our snmpEngineBoots and snmpEngineTime 
   # to avoid the possibilty of using different values in each of 
   # the comparisons.

   my $engine_time  = $this->_engine_time();
   my $engine_boots = $this->_engine_boots();

   if ($engine_boots == 2147483647) {
      $this->{_synchronized} = FALSE;
      return $this->_error('The system is not in the time window');
   }

   if (!$this->{_authoritative}) {

      if ($msg_boots < $engine_boots) {
         return $this->_error('The message is not in the time window');
      }
      if (($msg_boots == $engine_boots) && ($msg_time < ($engine_time - 150))) {
         return $this->_error('The message is not in the time window');
      }

   } else {

      if ($msg_boots != $engine_boots) {
         return $this->_error('The message is not in the time window');
      }
      if (($msg_time < ($engine_time - 150)) ||
          ($msg_time > ($engine_time + 150)))
      {
         return $this->_error('The message is not in the time window');
      }

   }

   return TRUE;
}

sub _authenticate_outgoing_msg
{
   my ($this, $msg, $auth_location) = @_;

   if (!$auth_location) {
      return $this->_error(
         'Authentication failure (Unable to set msgAuthenticationParameters)'
      );
   }

   # Set the msgAuthenticationParameters
   substr ${$msg->reference}, -$auth_location, 12, $this->_auth_hmac($msg);

   return TRUE;
}

sub _authenticate_incoming_msg
{
   my ($this, $msg, $auth_params) = @_;

   # Authenticate the message
   if ($auth_params ne $this->_auth_hmac($msg)) {
      return $this->_error('Authentication failure');
   }

   DEBUG_INFO('authentication passed');

   return TRUE;
}

sub _auth_hmac
{
   my ($this, $msg) = @_;

   return q{} if (!defined($this->{_auth_data}) || !defined $msg);

   return substr
      $this->{_auth_data}->reset()->add(${$msg->reference()})->digest(), 0, 12;
}

sub _auth_data_init
{
   my ($this) = @_;

   if (!defined $this->{_auth_key}) {
      return $this->_error('The required authKey is not defined');
   }

   return TRUE if defined $this->{_auth_data};

   if ($this->{_auth_protocol} eq AUTH_PROTOCOL_HMACMD5) {

      $this->{_auth_data} =
         Digest::HMAC->new($this->{_auth_key}, 'Digest::MD5');

   } elsif ($this->{_auth_protocol} eq AUTH_PROTOCOL_HMACSHA) {

      $this->{_auth_data} =
         Digest::HMAC->new($this->{_auth_key}, 'Digest::SHA1');

   } else {

      return $this->_error(
         'The authProtocol "%s" is unknown', $this->{_auth_protocol}
      );

   }

   return TRUE;
}

{
   my $encrypt =
   {
      PRIV_PROTOCOL_DES,              \&_priv_encrypt_des,
      PRIV_PROTOCOL_DRAFT_3DESEDE,    \&_priv_encrypt_3desede,
      PRIV_PROTOCOL_AESCFB128,        \&_priv_encrypt_aescfbxxx,
      PRIV_PROTOCOL_DRAFT_AESCFB192,  \&_priv_encrypt_aescfbxxx,
      PRIV_PROTOCOL_DRAFT_AESCFB256,  \&_priv_encrypt_aescfbxxx
   };

   sub _encrypt_data
   {
   #  my ($this, $msg, $priv_params, $plain) = @_;

      if (!exists $encrypt->{$_[0]->{_priv_protocol}}) {
         return $_[0]->_error('Encryption error (Unknown protocol)');
      }

      if (!defined
            $_[1]->prepare(
               OCTET_STRING,
               $_[0]->${\$encrypt->{$_[0]->{_priv_protocol}}}($_[2], $_[3])
            )
         )
      {
         return $_[0]->_error('Encryption error');
      }

      # Set the PDU buffer equal to the encryptedPDU
      return $_[3] = $_[1]->clear();
   }
}

{
   my $decrypt =
   {
      PRIV_PROTOCOL_DES,              \&_priv_decrypt_des,
      PRIV_PROTOCOL_DRAFT_3DESEDE,    \&_priv_decrypt_3desede,
      PRIV_PROTOCOL_AESCFB128,        \&_priv_decrypt_aescfbxxx,
      PRIV_PROTOCOL_DRAFT_AESCFB192,  \&_priv_decrypt_aescfbxxx,
      PRIV_PROTOCOL_DRAFT_AESCFB256,  \&_priv_decrypt_aescfbxxx
   };

   sub _decrypt_data
   {
   #  my ($this, $msg, $priv_params, $cipher) = @_;

      # Make sure there is data to decrypt.
      if (!defined $_[3]) {
         return $_[0]->_error($_[1]->error() || 'Decryption error (No data)');
      }

      if (!exists $decrypt->{$_[0]->{_priv_protocol}}) {
         return $_[0]->_error('Decryption error (Unknown protocol)');
      }

      # Clear the Message buffer
      $_[1]->clear();

      # Put the decrypted data back into the Message buffer
      if (!defined
            $_[1]->prepend(
               $_[0]->${\$decrypt->{$_[0]->{_priv_protocol}}}($_[2], $_[3])
            )
         )
      {
         return $_[0]->_error($_[1]->error());
      }
      return $_[0]->_error($_[1]->error()) if (!$_[1]->length());

      # See if the decrypted data starts with a SEQUENCE 
      # and has a reasonable length.

      my $msglen = $_[1]->process(SEQUENCE);
      if ((!defined $msglen) || ($msglen > $_[1]->length())) {
         return $_[0]->_error('Decryption error');
      }
      $_[1]->index(0); # Reset the index

      DEBUG_INFO('privacy passed');

      return TRUE;
   }
}

sub _priv_data_init
{
   my ($this) = @_;

   if (!defined $this->{_priv_key}) {
      return $this->_error('The required privKey is not defined');
   }

   return TRUE if defined $this->{_priv_data};

   my $init =
   {
      PRIV_PROTOCOL_DES,              \&_priv_data_init_des,
      PRIV_PROTOCOL_DRAFT_3DESEDE,    \&_priv_data_init_3desede,
      PRIV_PROTOCOL_AESCFB128,        \&_priv_data_init_aescfbxxx,
      PRIV_PROTOCOL_DRAFT_AESCFB192,  \&_priv_data_init_aescfbxxx,
      PRIV_PROTOCOL_DRAFT_AESCFB256,  \&_priv_data_init_aescfbxxx
   };

   if (!exists $init->{$this->{_priv_protocol}}) {
      return $this->_error(
         'The privProtocol "%s" is unknown', $this->{_priv_protocol}
      );
   }

   return $this->${\$init->{$this->{_priv_protocol}}}();
}

sub _priv_data_init_des
{
   my ($this) = @_;

   if (!defined $this->{_priv_key}) {
      return $this->_error('The required privKey is not defined');
   }

   # Create the DES object
   $this->{_priv_data}->{des} =
      Crypt::DES->new(substr $this->{_priv_key}, 0, 8);

   # Extract the pre-IV
   $this->{_priv_data}->{pre_iv} = substr $this->{_priv_key}, 8, 8;

   # Initialize the salt
   $this->{_priv_data}->{salt} = int rand ~0;

   return TRUE;
}

sub _priv_encrypt_des
{
#  my ($this, $priv_params, $plain) = @_;

   if (!defined $_[0]->{_priv_data}) {
      return $_[0]->_error('The required privacy data is not defined');
   }

   # Always pad the plain text data.  "The actual pad value is 
   # irrelevant..." according RFC 3414 Section 8.1.1.2.  However,
   # there are some agents out there that expect "standard block
   # padding" where each of the padding byte(s) are set to the size 
   # of the padding (even for data that is a multiple of block size).

   my $pad = 8 - (length($_[2]) % 8);
   $_[2] .= pack('C', $pad) x $pad;

   # Create and set the salt
   if ($_[0]->{_priv_data}->{salt}++ == ~0) {
      $_[0]->{_priv_data}->{salt} = 0;
   }
   $_[1] = pack 'NN', $_[0]->{_engine_boots}, $_[0]->{_priv_data}->{salt};

   # Create the initial vector (IV)
   my $iv = $_[0]->{_priv_data}->{pre_iv} ^ $_[1];

   my $cipher = q{};

   # Perform Cipher Block Chaining (CBC) 
   while ($_[2] =~ /(.{8})/gs) {
      $cipher .= $iv = $_[0]->{_priv_data}->{des}->encrypt($1 ^ $iv);
   }

   return $cipher;
}

sub _priv_decrypt_des
{
#  my ($this, $priv_params, $cipher) = @_;

   if (!defined $_[0]->{_priv_data}) {
      return $_[0]->_error('The required privacy data is not defined');
   }

   if (length($_[1]) != 8) {
      return $_[0]->_error(
        'The msgPrivParameters length of %d is invalid', length $_[1]
      );
   }

   if (length($_[2]) % 8) {
      return $_[0]->_error(
         'The DES cipher length is not a multiple of the block size'
      );
   }

   # Create the initial vector (IV)
   my $iv = $_[0]->{_priv_data}->{pre_iv} ^ $_[1];

   my $plain = q{};

   # Perform Cipher Block Chaining (CBC) 
   while ($_[2] =~ /(.{8})/gs) {
      $plain .= $iv ^ $_[0]->{_priv_data}->{des}->decrypt($1);
      $iv = $1;
   }

   return $plain;
}

sub _priv_data_init_3desede
{
   my ($this) = @_;

   if (!defined $this->{_priv_key}) {
      return $this->_error('The required privKey is not defined');
   }

   # Create the 3 DES objects

   $this->{_priv_data}->{des1} =
      Crypt::DES->new(substr $this->{_priv_key}, 0, 8);
   $this->{_priv_data}->{des2} =
      Crypt::DES->new(substr $this->{_priv_key}, 8, 8);
   $this->{_priv_data}->{des3} =
      Crypt::DES->new(substr $this->{_priv_key}, 16, 8);

   # Extract the pre-IV
   $this->{_priv_data}->{pre_iv} = substr $this->{_priv_key}, 24, 8;

   # Initialize the salt
   $this->{_priv_data}->{salt} = int rand ~0;

   # Assign a hash algorithm to "bit spread" the salt

   if ($this->{_auth_protocol} eq AUTH_PROTOCOL_HMACMD5) {
      $this->{_priv_data}->{hash} = Digest::MD5->new();
   } elsif ($this->{_auth_protocol} eq AUTH_PROTOCOL_HMACSHA) {
      $this->{_priv_data}->{hash} = Digest::SHA1->new();
   }

   return TRUE;
}

sub _priv_encrypt_3desede
{
#  my ($this, $priv_params, $plain) = @_;

   if (!defined $_[0]->{_priv_data}) {
      return $_[0]->_error('The required privacy data is not defined');
   }

   # Pad the plain text data using "standard block padding". 
   my $pad = 8 - (length($_[2]) % 8);
   $_[2] .= pack('C', $pad) x $pad;

   # Create and set the salt
   if ($_[0]->{_priv_data}->{salt}++ == ~0) {
      $_[0]->{_priv_data}->{salt} = 0;
   }
   $_[1] = pack 'NN', $_[0]->{_engine_boots}, $_[0]->{_priv_data}->{salt};

   # Draft 3DES-EDE for USM Section 5.1.1.1.2 - "To achieve effective 
   # bit spreading, the complete 8-octet 'salt' value SHOULD be 
   # hashed using the usmUserAuthProtocol."

   if (exists $_[0]->{_priv_data}->{hash}) {
      $_[1] = substr $_[0]->{_priv_data}->{hash}->add($_[1])->digest(), 0, 8;
   }

   # Create the initial vector (IV)
   my $iv = $_[0]->{_priv_data}->{pre_iv} ^ $_[1];

   my $cipher = q{};

   # Perform Cipher Block Chaining (CBC)
   while ($_[2] =~ /(.{8})/gs) {
      $cipher .= $iv =
         $_[0]->{_priv_data}->{des3}->encrypt(
            $_[0]->{_priv_data}->{des2}->decrypt(
               $_[0]->{_priv_data}->{des1}->encrypt($1 ^ $iv)
            )
         );
   }

   return $cipher;
}

sub _priv_decrypt_3desede
{
#  my ($this, $priv_params, $cipher) = @_;

   if (!defined $_[0]->{_priv_data}) {
      return $_[0]->_error('The required privacy data is not defined');
   }

   if (length($_[1]) != 8) {
      return $_[0]->_error(
        'The msgPrivParameters length of %d is invalid', length $_[1]
      );
   }

   if (length($_[2]) % 8) {
      return $_[0]->_error(
         'The CBC-3DES-EDE cipher length is not a multiple of the block size'
      );
   }

   # Create the initial vector (IV)
   my $iv = $_[0]->{_priv_data}->{pre_iv} ^ $_[1];

   my $plain = q{};

   # Perform Cipher Block Chaining (CBC) 
   while ($_[2] =~ /(.{8})/gs) {
      $plain .=
         $iv ^ $_[0]->{_priv_data}->{des1}->decrypt(
                  $_[0]->{_priv_data}->{des2}->encrypt(
                     $_[0]->{_priv_data}->{des3}->decrypt($1)
                  )
               );
      $iv = $1;
   }

   return $plain;
}

sub _priv_data_init_aescfbxxx
{
   my ($this) = @_;

   if (!defined $this->{_priv_key}) {
      return $this->_error('The required privKey is not defined');
   }

   {
      # Avoid a "strict subs" error if Crypt::Rijndael is not loaded.
      no strict 'subs';

      # Create the AES (Rijndael) object with a 128, 192, or 256 bit key.

      $this->{_priv_data}->{aes} =
         Crypt::Rijndael->new($this->{_priv_key}, Crypt::Rijndael::MODE_CFB());
   }

   # Initialize the salt
   $this->{_priv_data}->{salt1} = int rand ~0;
   $this->{_priv_data}->{salt2} = int rand ~0;

   return TRUE;
}

sub _priv_encrypt_aescfbxxx
{
#  my ($this, $priv_params, $plain) = @_;

   if (!defined $_[0]->{_priv_data}) {
      return $_[0]->_error('The required privacy data is not defined');
   }

   # Validate the plain text length
   my $length = length $_[2];
   if ($length <= 16) {
      return $_[0]->_error(
         'The AES plain text length is not greater than the block size'
      );
   }

   # Create and set the salt
   if ($_[0]->{_priv_data}->{salt1}++ == ~0) {
      $_[0]->{_priv_data}->{salt1} = 0;
      if ($_[0]->{_priv_data}->{salt2}++ == ~0) {
         $_[0]->{_priv_data}->{salt2} = 0;
      }
   }
   $_[1] = pack 'NN', $_[0]->{_priv_data}->{salt2},
                      $_[0]->{_priv_data}->{salt1};

   # AES in the USM Section - Section 3.1.3 "The last ciphertext 
   # block is produced by exclusive-ORing the last plaintext segment 
   # of r bits (r is less or equal to 128) with the segment of the r 
   # most significant bits of the last output block."  

   # This operation is identical to those performed on the previous 
   # blocks except for the fact that the block can be less than the 
   # block size.  We can just pad the last block and operate on it as 
   # usual and then ignore the padding after encrypting.

   $_[2] .= "\000" x (16 - ($length % 16));

   # Create the IV by concatenating "...the generating SNMP engine's 
   # 32-bit snmpEngineBoots, the SNMP engine's 32-bit  snmpEngineTime, 
   # and a local 64-bit integer..." 

   $_[0]->{_priv_data}->{aes}->set_iv(
      pack('NN', $_[0]->{_engine_boots}, $_[0]->{_engine_time}) . $_[1]
   );

   # Let the Crypt::Rijndael module perform 128 bit Cipher Feedback 
   # (CFB) and return the result minus the "internal" padding.

   return substr $_[0]->{_priv_data}->{aes}->encrypt($_[2]), 0, $length;
}

sub _priv_decrypt_aescfbxxx
{
#  my ($this, $priv_params, $cipher) = @_;

   if (!defined $_[0]->{_priv_data}) {
      return $_[0]->_error('The required privacy data is not defined');
   }

   # Validate the msgPrivParameters length.  We assume that the
   # msgAuthoritativeEngineBoots and msgAuthoritativeEngineTime
   # have been prepended to the msgPrivParameters to create the
   # required 128 bit IV.

   if (length($_[1]) != 16) {
       return $_[0]->_error(
          'The AES IV length of %d is invalid', length $_[1]
       );
   }

   # Validate the cipher length
   my $length = length $_[2];
   if ($length <= 16) {
      return $_[0]->_error(
         'The AES cipher length is not greater than the block size'
      );
   }

   # AES in the USM Section - Section 3.1.4 "The last ciphertext 
   # block (whose size r is less or equal to 128) is less or equal 
   # to 128) is exclusive-ORed with the segment of the r most 
   # significant bits of the last output block to recover the last 
   # plaintext block of r bits."

   # This operation is identical to those performed on the previous
   # blocks except for the fact that the block can be less than the
   # block size.  We can just pad the last block and operate on it as
   # usual and then ignore the padding after decrypting.

   $_[2] .= "\000" x (16 - ($length % 16));

   # Use the msgPrivParameters as the IV.
   $_[0]->{_priv_data}->{aes}->set_iv($_[1]);

   # Let the Crypt::Rijndael module perform 128 bit Cipher Feedback
   # (CFB) and return the result minus the "internal" padding.

   return substr $_[0]->{_priv_data}->{aes}->decrypt($_[2]), 0, $length;
}

sub _auth_key_generate
{
   my ($this) = @_;

   if (!defined($this->{_engine_id}) || !defined $this->{_auth_password}) {
      return $this->_error('Unable to generate the authKey');
   }

   $this->{_auth_key} = $this->_password_localize($this->{_auth_password});

   return $this->{_auth_key};
}

sub _auth_key_validate
{
   my ($this) = @_;

   my $key_len =
   {
      AUTH_PROTOCOL_HMACMD5,    [ 16, 'HMAC-MD5'  ],
      AUTH_PROTOCOL_HMACSHA,    [ 20, 'HMAC-SHA1' ],
   };

   if (!exists $key_len->{$this->{_auth_protocol}}) {
      return $this->_error(
         'The authProtocol "%s" is unknown', $this->{_auth_protocol}
      );
   }

   if (length($this->{_auth_key}) != $key_len->{$this->{_auth_protocol}}->[0])
   {
      return $this->_error(
         'The %s authKey length of %d is invalid, expected %d',
         $key_len->{$this->{_auth_protocol}}->[1], length($this->{_auth_key}),
         $key_len->{$this->{_auth_protocol}}->[0]
      );
   }

   return TRUE;
}

sub _priv_key_generate
{
   my ($this) = @_;

   if (!defined($this->{_engine_id}) || !defined $this->{_priv_password}) {
      return $this->_error('Unable to generate the privKey');
   }

   $this->{_priv_key} = $this->_password_localize($this->{_priv_password});

   return $this->_error() if !defined $this->{_priv_key};

   if ($this->{_priv_protocol} eq PRIV_PROTOCOL_DRAFT_3DESEDE) {

      # Draft 3DES-EDE for USM Section 2.1 - "To acquire the necessary 
      # number of key bits, the password-to-key algorithm may be chained
      # using its output as further input in order to generate an
      # appropriate number of key bits."

      $this->{_priv_key} .= $this->_password_localize($this->{_priv_key});

   } elsif (($this->{_priv_protocol} eq PRIV_PROTOCOL_DRAFT_AESCFB192) ||
            ($this->{_priv_protocol} eq PRIV_PROTOCOL_DRAFT_AESCFB256))
   {
      # Draft AES in the USM Section 3.1.2.1 - "...if the size of the 
      # localized key is not large enough to generate an encryption 
      # key... ...set Kul = Kul || Hnnn(Kul) where Hnnn is the hash 
      # function for the authentication protocol..."

      my $hnnn;

      if ($this->{_auth_protocol} eq AUTH_PROTOCOL_HMACMD5) {
         $hnnn = Digest::MD5->new();
      } elsif ($this->{_auth_protocol} eq AUTH_PROTOCOL_HMACSHA) {
         $hnnn = Digest::SHA1->new();
      } else {
         return $this->_error(
            'The authProtocol "%s" is unknown', $this->{_auth_protocol}
         );
      }

      $this->{_priv_key} .= $hnnn->add($this->{_priv_key})->digest();

   }

   # Truncate the privKey to the appropriate length.

   my $key_len =
   {
      PRIV_PROTOCOL_DES,              16,  # RFC 3414 Section 8.2.1
      PRIV_PROTOCOL_DRAFT_3DESEDE,    32,  # Draft 3DES for USM Section 5.2.1
      PRIV_PROTOCOL_AESCFB128,        16,  # AES in the USM Section 3.2.1
      PRIV_PROTOCOL_DRAFT_AESCFB192,  24,  # Draft AES in the USM Section 3.2.1
      PRIV_PROTOCOL_DRAFT_AESCFB256,  32   # Draft AES in the USM Section 3.2.1
   };

   if (!exists $key_len->{$this->{_priv_protocol}}) {
      return $this->_error(
         'The privProtocol "%s" is unknown', $this->{_priv_protocol}
      );
   }

   $this->{_priv_key} =
      substr $this->{_priv_key}, 0, $key_len->{$this->{_priv_protocol}};

   return $this->{_priv_key};
}

sub _priv_key_validate
{
   my ($this) = @_;

   my $key_len =
   {
      PRIV_PROTOCOL_DES,              [ 16, 'CBC-DES'        ],
      PRIV_PROTOCOL_DRAFT_3DESEDE,    [ 32, 'CBC-3DES-EDE'   ],
      PRIV_PROTOCOL_AESCFB128,        [ 16, 'CFB128-AES-128' ],
      PRIV_PROTOCOL_DRAFT_AESCFB192,  [ 24, 'CFB128-AES-192' ],
      PRIV_PROTOCOL_DRAFT_AESCFB256,  [ 32, 'CFB128-AES-256' ]
   };

   if (!exists $key_len->{$this->{_priv_protocol}}) {
      return $this->_error(
         'The privProtocol "%s" is unknown', $this->{_priv_protocol}
      );
   }

   if (length($this->{_priv_key}) != $key_len->{$this->{_priv_protocol}}->[0])
   {
      return $this->_error(
         'The %s privKey length of %d is invalid, expected %d',
         $key_len->{$this->{_priv_protocol}}->[1], length($this->{_priv_key}),
         $key_len->{$this->{_priv_protocol}}->[0]
      );
   }

   if ($this->{_priv_protocol} eq PRIV_PROTOCOL_DRAFT_3DESEDE) {

      # Draft 3DES-EDE for USM Section 5.1.1.1.1 "The checks for difference 
      # and weakness... ...should be performed when the key is assigned.
      # If any of the mandated tests fail, then the whole key MUST be 
      # discarded and an appropriate exception noted."

      if (substr($this->{_priv_key}, 0, 8) eq substr $this->{_priv_key}, 8, 8)
      {
         return $this->_error(
            'The CBC-3DES-EDE privKey is invalid (K1 equals K2)'
         );
      }

      if (substr($this->{_priv_key}, 8, 8) eq substr $this->{_priv_key}, 16, 8)
      {
         return $this->_error(
            'The CBC-3DES-EDE privKey is invalid (K2 equals K3)'
         );
      }

      if (substr($this->{_priv_key}, 0, 8) eq substr $this->{_priv_key}, 16, 8)
      {
         return $this->_error(
            'The CBC-3DES-EDE privKey is invalid (K1 equals K3)'
         );
      }

   }

   return TRUE;
}

sub _password_localize
{
   my ($this, $password) = @_;

   my $digests =
   {
      AUTH_PROTOCOL_HMACMD5,  'Digest::MD5',
      AUTH_PROTOCOL_HMACSHA,  'Digest::SHA1',
   };

   if (!exists $digests->{$this->{_auth_protocol}}) {
      return $this->_error(
         'The authProtocol "%s" is unknown', $this->{_auth_protocol}
      );
   }

   my $digest = $digests->{$this->{_auth_protocol}}->new;

   # Create the initial digest using the password

   my $d = my $pad = $password x ((2048 / length $password) + 1);

   for (my $count = 0; $count < 2**20; $count += 2048) {
      $digest->add(substr $d, 0, 2048, q{});
      $d .= $pad;
   }
   $d = $digest->digest;

   # Localize the key with the authoritativeEngineID

   return $digest->add($d . $this->{_engine_id} . $d)->digest();
}

{
   my %modules;

   sub load_module
   {
      my ($module) = @_;

      # We attempt to load the required module under the protection of an
      # eval statement.  If there is a failure, typically it is due to a
      # missing module required by the requested module and we attempt to
      # simplify the error message by just listing that module.  We also
      # need to track failures since require() only produces an error on
      # the first attempt to load the module.

      # NOTE: Contrary to our typical convention, a return value of "undef"
      # actually means success and a defined value means error.

      return $modules{$module} if exists $modules{$module};

      if (!eval "require $module") {
         if ($@ =~ /locate (\S+\.pm)/) {
            $modules{$module} = sprintf '(Required module %s not found)', $1;
         } else {
            $modules{$module} = sprintf '(%s)', $@;
         }
      } else {
         $modules{$module} = undef;
      }

      return $modules{$module};
   }
}

# ============================================================================
1; # [end Net::SNMP::Security::USM]