rbnacl-5.0.0/0000755000004100000410000000000013120044713013000 5ustar www-datawww-datarbnacl-5.0.0/Rakefile0000644000004100000410000000031213120044713014441 0ustar www-datawww-data# frozen_string_literal: true require "bundler/gem_tasks" Dir[File.expand_path("../tasks/**/*.rake", __FILE__)].each { |task| load task } task default: %w(spec rubocop) task ci: %w(spec rubocop) rbnacl-5.0.0/Gemfile0000644000004100000410000000042613120044713014275 0ustar www-datawww-datasource "https://rubygems.org" gemspec group :development do gem "guard-rspec" end group :test do gem "rspec" gem "rubocop", "0.46.0" gem "coveralls", require: false gem "rbnacl-libsodium", ENV["LIBSODIUM_VERSION"] end group :development, :test do gem "rake" end rbnacl-5.0.0/.coveralls.yml0000644000004100000410000000003113120044713015565 0ustar www-datawww-dataservice-name: travis-pro rbnacl-5.0.0/images/0000755000004100000410000000000013120044713014245 5ustar www-datawww-datarbnacl-5.0.0/images/ed25519.png0000644000004100000410000026623313120044713015765 0ustar www-datawww-dataPNG  IHDRfS pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FaIDATxu|?>wWH.!!$b (RZ(V@)=AC BBܓs1w{{ّ׼Bp O#.?)D}zA}~wg,^mNW A\#}q#F4ѻ[ #T}:8+FaFx:O _gjAevF.+0::Tv6 _Gg|cQ˄5R eH)UF;ʌn3*ٷZV!1F 3YfM}C)3lesy6WsCzB!O{T$% rT U*++?~J]ځ vfkQѱI&uֵD P]]]ZZ*e2YMMSAQT,bX,D"P(\`mGW{0c40"YM*7#Toٸq=࿫Y|~ }}}kCA_S.gfffffz*===777??_my{{ԩS'88f[E9R{+ЯAt0̐29Cnj!6=鄿1 _VZ<(hL!l-`tp WnCB w>NO{]kܤ'fqFaׯ_{nzzRD%$$$44Y(D"H`&JRL&dRիWeeeUUUHԠAm5j eE<)tw4 9)+Z9,.ilwvQqdukqē'Olvxxx۶m4iΜ?ETegg?}ƍ)))WWרo[n666E0_(ШHńԦK]扖w EDV¤;mllZ &ѣ2ҚCt5hBq۵kW/yё#G:TZZخ](___E"Z{ݺuƍ#G0`@XXؿko盡D[dca"!Cʐq5cO*^`1^;M[7h~ruZqq'7q2 ?Ñ4֠OP^|~JZZߗ,aν=}tҥΝCQ}Fܹy2aOh6m~~~||}>|( Vb$Uz #5=7|I}䟆"=EpDAp|b}F@Pk/FK*{iX&Or.^"[n 2do޼!H8lٲ޽{k4Ç:uaÆ&z&ˏ\qqq׮][fͫW:d08iOui@Fzth(TA8V8G խ6Ji=]nTӡ'V3W39cA#F/9 T }JGHԝ #ӈ?QG_ zZ3ĀU$BѾ}>|̘1΍7СC tϚ5kʕ]tٵkB}&)@o6%M7bĈaÆIЙiTNMr|4B% voZl "#R^HEpO?bsPNu夃$]ҨI!U(55542{lV|ׯ_~}…!!![mݺʕ+W\wmݺU 0Hu w\4nի}?~ݠA"s͐~v(5h3-T_Ͷ@fQfjT/ꩇA 눜_P.`БO/Z Zj`D >ycbtܛ7o޼y{n >>h4<ҥKz\]]x~{!aؘ1cRRRa;SLy⅓Z[lY`Aaaa6m> k&:WRg<=RD噂E!$@lc*.)*NB!+9BP.9">9\^PV#8:G59b |a%kIBTj5jԣG:t]իGh܏=6x`C[J5kV\\9۷o\.@&x1)))--M^xqbIIĉGT* g.] ޽~ǦM τ 6mtYCEsTZMh}{3gp?=6;}0W:,=ޮG7 @! '`IjC={ey1cꅆzfU ōhfNί;wWUߦ̈́޽6۷v+rKJ6FCQ <ޱ?^8bDGAt?P4tp`ZǏ 6B('''T~~~PE(PrDSl6[Wp;qD֭#"";6l0b{lذaÆ ߼yc!44~jzD`5"YFߨ\$O$ Gb|jQ57Msr%eb0 DI+0D HO\//Yᑑbo_UQ1e&M& kúuf/(|y]/v-[NްYp>?CNN^ySi4 V(\6{߉Ξ]3iR7s0-gX}SRRRVVB:}"hȑ ,xbddd=4hp8YYYGr(d2H5oܸt͛oذ!))Yf8;99?g1b.Y$⢢3g,\tu:u<{L&1yqb..MUs[lCZDb2đ0d>)JzR`ocKDwahןpaP*;>Gv)&p(siݺ ._z?c8D-Z|Esss>} d׮]խ[ԩS͚5[֖b8p`Μ9Z2D-UUUÇ3] b0 !3 pfH`JwOfyZ4s }t03gzuvb3eJR9,m5jذ8[6n\RU%yPhk ˯<}j}]]Q0uNNtÆꊄ~9ׂ蘙Illllll,>zhV1uTBGFx9s&++ zuyf޼y V1bڵk?NCjjjƍgggn:*#V4R+ꂣ2l׭ۏLqB^ 8~ j\.A(N>o6i$44:gpܰ06ݭ[7ooozyxx4h@(#FO-R(555ݺusrr x[puu 6ݞzj„ QQQsaن1~6 2KDt*Gn7TĂ@w?ҼވI\L`ӟT_gS(}5\ zyp֬ݻTN×E٫{,r??RcMZ 45 @^ #>cY2L$ᔔ6oı#F;v_3[yE*v#ojR3Y  u+ 鉟!SD]E@/e#+P  ;+snd]0D. B_ǯՒ\?CW{ `gu*.Ќ54|'OlܸqŊ Q=撑$FT9֭ӥVTLQifn'K+͠&̻gkyl 24Na4XF+ aY3w_ z&]˗5QO٤2סJn U=|1EGgZvGyyY]]rJXd{:0'oϟ_`ɓ(HuiƇAɁM M i6ͤ<6_GeQ _Lg4@,#?z:ٟZp۠ 5w t+nMHm۶oݺ[n >SNϟ_t… u!j@KzGJ>dHa( 5mNڈlDFCϨ|WyH@(FTN:M=vu8J%(ih."_H[lYn]rrrLL F%MOO2dH޽R'̙ch?IcT͠fchE3ZZhH÷Fk2!I+hx(sFʗq^4xTek D.j7;_bw.)YP4(pO@tX?CQ=vhJbLJ4hڵׯ@~"jFFƂ Zly3f&|AGtX"''(Z)TJ$n4f ,]^=ncc3uf͚O9 Q:Q!3](n6SRB=F5G湮vݺӦ4 >^*fҖ>KQp8waMܽ}|2&ʥ>}Fjժ}vrr2/O"$''_z4lo8p`PPIKBM7]̑jLZ,PrTݤf<ʽ(*\&=v#GGGѣ|դ<חadfM=*hR*ڻ9}e˖|hJEEūW߿ڵ'OTVVr8X"mmmB!ǃJRTJ$DRPPUTT$H0 ssskݺu#""BBBH*H}a;F35GD SK3110DכC>L†%E6mެl6 aJ%Mlի!~ C-&;077799o߾}ׯl{{{ *++ tO "\^&Mº[ai :0iYBh9WnQg%C$dKxH&Hf5ȸsNNnL.h4Yl68ST)U*˽LJf 3hZsEQ6V.PTVlf>veN.(ժU6m<hFՠL&+++ήH$D._eԨQ666BP,D"{{{z f3)c~3YHn<@i\1j%Ŗ1f( ÌR //VUUeee:u趓B$7JiyH$"R3^R|???O)**WH$"O>mѢZBBBΜ9cvlZ1}"V3w3瀡C)Y%]ibRZס`菪ۙ-[9sfUV8p`ѢEׯ%.GEE@oݺ599dddX>_jʕ+ƍ۵kסC  .ѣw/^1bDuu={JKK7o޿?vpp>L>ή`Ȑ!&MH$aaa |/Y򼼼7VUU;ӳѣ2dHXXkjjZnݭ[7a [RϤ-$ۈh&Y̾IM2 !Bt%I|Ig9,Tynʱyi#`୪ C,i_~ٽ{w} tqF EѲ2 :uiӦ;w<~ð;v׫W7oׯ_zzÇ=1rH=q2)))..PEAxΝ`.䔓3s9s}7ŋh(ޢE ;;+Wd2>;u֪#F艷%dr[1'yM"s&* s>Հ((vx3Ԥ~q4; k$ѓVh1<lllz} x+j6m֭[wrrj֬CF|||]V^^~͛WWW߿_Po|L&#aw=y9sPmݺa?_'M}v'tZ9dȐr'\]]4hӪUw=zԤp9VIn=fv`%Cga̠$ג (bhm"xzJOIR2Sh h4"(::K.>jǎ˗/Dj"(666''gٲe(J(c6j믿~Lj={4mt饥@#ݻw7nܸdɒ)]eee``VU= н{]v*< g cQhW5:Y`%zkk6ʯ + =HxeffܹS,3FծYbݻ~)b;vvvv-[r7~ELL UVBd˖-۰aaqqq$!!Af͚Ϟ=ر#Zj*V{!XO?9sjZ&uťy?syyAU,++==gϞl62hFVgeeUWW$h0,Պ:$c)3K4X"%] Lo0ɝ(s/DǂQT*UjjjqqqPPP:u0 ̔JJR(B>ð\ӼysH$'''"ׯ )++{P(lܸJz9 -[DJrݺuƍsppb%HJ%oذayy/X,V- yj>DM4!:/˫?|hӦM1 +,,tsscXo޼888-[r8Z2[&T*mڴihh(Z-FЕ f`.ޱ%ɦ.77# Cf`˦kѵT=yԙEVKh,wkY~j HT"4kL` h-V ¯=y&0Z;m݊N_X I]h*"(u-z~_z 0͎g>l zyShrR\\|, T_ Lg7Z`U3Yi Aϙ%MYDâaLpfJRT ''uUweo]5vÇ'OVTl6VTT;41Ijc͡R[`,i=8\eA0.g  qg k׮={h4Z|ȑ.]Z|%_W{#&i'kM 2X0vz&X#6Y PNx}Kqqqf2 ð֭[ߺuˊid ~qc ,2#T6 (*m&htw6:TAU6m Di^+1,\rc%> Z_LϖB;CH~~6q"J bbcc[TUBTTY a6[CitfT6:dT9TDP/lɚCY**j9hjT* -A5i2AIoݺ5 }RC5Q% uRDji%(G*`lj0)ÜIÌr!49EBÄ9HCu$l44-^ #CEL:ЇE0ia7P5EjЛQeb7ү67mnnn4{LPi y%Vzf‹+f0fJa-R =@PV%=cP&ӠGv=egHs_+z;#忘 s(4b4IT9Yлb1(m&. iӆ~ؙ_'q0UzoIfψNIO.CuFOL:bxn3en.jZVT08NPHRLΧ Ibt*6FJKK xcA{{{.{ҥ]  TÇD"OOO+d%T3,|>>nnnujd{K""V{ڵk/^XZZJ ghVÆ 'LЮ];ooo>t,aW ^^^  lP@ڙ+yd9D e:R4w'=GZ&R7nɩN:ڵnܸ#"={vرUUU=zhڴ@ [+W8ѱΝ;QQQOgJMMÇ]VN#G4o?5/Lj#tx1M٤xs1 w޹sn߾sVkggѠAHDW Eyyﳲ޼yɓtWTTT.]zI!q|y\\Cq/,YRT*պuƎd˙3gFy-=SuiMJL t҃|5 ^7ln~~aRO>cƌ .+ٳgWٳ+yԩO<1!՟W>t ~ f\fS~ Eh6o}gt h^ѫ}GHi^P =>|?ΝT*:PPPp:ѥK+W0laj)**4hWxm?.|}}j_TiZnҤIӦMr2Ѡ=a6"%غssNtȑ#VrFCaOxԩS!~ȑ#333>,YO<_޺dž]y`x׷CHbZH ,GvssˣPDBk{DAs ЩSc^|ʕ+~-IT|REEE' ?~0m޼ڵk_x1y{][ha&4>/kLB re6)"Ҽ&)M o( mw52 X *R$aި7$#J:׫=z~֯_?<<w^zV\n:nngRXX]1ڭ[3fܼy3>>ڱcŋ#F|ܹݻϝ;7++?~O芲FM(` X(}׏Rbccz/// pqqyݳg***lvFڵ+//_n]UUᾅXtܹs[¦.׮]QՒn2eʋ/߿o HߨQȩS޻w~+V}vJJʅ fΜ 4 KՖ鲟z<|LLܹs ) ԭO#ljjuYYk׮޽{8q~4ÇxZM4T*ϝ;k}vRRyD5k@ m:RT˗/-[F|]yy9kC v(RP\\|?sDnҍPVV6nܸ޽{oݺw!Q-##˦⭒]lG R6kҺMf8yI?omiғV2lJ.tkݳ.;19 g%Do߾|rڴi<OW\:yӧO%ɢEx<޸qF$:tмyvN8m۶<z%K\pJAAA>>> t޼ys2dD"۷oӦMgϞp„77;B) Z ?SN|>֭[]tR113WWWgff޿޽{O<0>""b„ aaaQ QH$֭KJJ._|Igg?CR,_{ԩbX*nذڵk!!!򋳳seeƍSSS1 kӦ̈́ `Em۶=|PVs8 &P9AV(~q$@ pD"+fڵ$''̝;^^^vvv+V ܹ3-**Zf۷fϞ-??M|PT 4нpj///\eJJJJKK]\\f֭r\VO>}Dݻwo߾}vv-k֬nܸ!<~ˁ:t8u9m4\?q ͚5sqq۷/q 8ZT_^zl399Y$! al6M6={tww rss~px<D/_olSfgffk46dC|HI9'Nxub=z' *_~]VVH$;v_0jիWcakfʔ)ބK!uceTZUVF-!aÆ#G;͐Zc;JKK VQՊe˖hZ__HL/i莀)R"ɱwi"yQjQo95RHN9(zWmR7e|{h\$rjQ{_TTDlcYT7x%%%, Y8ZðϋD:l ]\\t(M8YÇ :/YӧSm6RԤIÇƆꉟWU}MrB kٲ,X R æMzݺuk"n32'%%nذJ吡 ,ȿX%KЬ>ݻW h4ggf͚) /88FUBDLP(<<<ڷo?qĉ'&''CRh4{+))Yvmxxx-tT*|ݻwn߾ `5-莆)J%I>|;6n8{l=gRIx^Էn8~vk#7o1)4_IkPmFS|'(Ä8e,!gԌhH~ ._~ҥ4JT9|ڵk߽{WVV6lذ#y:>~Ewҥo߾fz-rvv޶miOj+55uK,9{Aaeza;0apNyOѣ-޲e˲e,XpܠomTTf+Vl۶СC͚5#Ti>>>]t0駟&O 7S= ر.io۶m? V+&}H #>}@Q}ڵ7npBttttt4-Z(00cǎŋ9s&//~:uSEp~.]:o޼iӦ5k *aaaسٳ'4KS"## %͛Scbbtuuҥy:,&{P=PR?DõpmNSl;!g(D5ji~֙WOՔ [";4A`?sRh4>}(z%3 rrr}:uܼy񸸸[!C3&++bΝ-Z(***-->>ET*߿_"00)6mٲh4He ؿgCS ~bbb:u=zdTZŭ4)4l`/󀺤59׏xSSq`hYx˝'7"Yv2l'#CaޞZC_7̣Cm19X,ڵk48TE١u*s⃩ƻ_͛7ӧå[:t5 :" Ӫm``a:7Hӥ|2d46AQDDAN=zeooK ))AWXj*6uVè_4:ږlgQkR L64dF1=IUFҨm6Y]( Ĵr" eN٦^Au])+NO3#yr, j8 k̀54慦x |֭[,Y=eʔI&t~nnn>pt4it:t}bb\.oҤIrr+WlmmwI:VUUuܹŋgffwѢE 64XqËfrܔt2j*kv:i*b&P7Z"\gϘf*ksZrJYVpy{+Gp[ߎiU9o=^,y2^[P7ǿiC)[޼y3h 2dUƼdee-]ڶ:d1B1T!x01IhFj1gzehɊXf{yy3bd(d%jI%we57ND%$\tR9*fOiE!+V7OE_:uΒG` ϨLyƍCBf͚^ٳg2̌ECRL?~|С0%<<|ƍzXn0TMMwTTuXrŋuԑ4WVVzyyuؑR158i3eF~d{]\U^qi܌^}mI,}bCIuJYQ>.&᪢硌R+_^t)ZB^zѣG_~d[MAA͛7/^ܾ}{aoo?tЫWZ(̝;}vRVV  ņх&1LqF5j*^| 'Km,yxUYsӓV}rutLנ&_n9Y|wB^bgBӼS0t d"rWVV>}޽{7o|\.WKp8l6f( "++o߾χ AAAm۶m۶mDDl=.s-Kӧ!CMՂ13ǭ܅jпQR%%%͙3ݻ6m? 4{PC3lj,=h&.VaÆ7~{Ջ'c*t*{n ȟߙRU,WK5 (/zըj޿9WPߐ"1fC! WR͖QӆI n>LHHx߽{-Pp8|>_$D"%'+**d2T*Jx7oS~}==PEJmhF&;;{W\[.{Ӄ mDj0|adhN,*!]VVW\9ydF3gΜ+W(ڰaC[[[C:h%Ë I?nGRիW bĈ֭#qkqq ί(P[z.Ϳ:aҞ,*Ot wR#PbHLDMIyyy~~~ZZڻw T*U(gnWޅ v l."}#L3# {tL db e^ g4 YSW^ݻD"z*ad HUJFػyNGHzW.M}0o_ ۧ&43\գߌ\ozeӝ&Ş!$*~L%3djT= 3eRZCՆ0 K!CJJ-*̼UD֦M'O7$b \(qp 7뽙d<upyWq,UxCy7{pKcrD5Y=UU"i=+~W6r0`t}.]ݻqي)V{c"[w[ZVU?ӑ'pwix?Ԫ*/?Q DQ*LNWX1R&0 }qtTׯL&vQ~yW.S<#y;xBWv]{]eP`q?}!ߝBGN.*`SqT3z.?4X%:& *`tMx)JR}^h[^(#yMè?G,{p_i}g,/WC>Ko)?hHilFѩ遫L1E]$UU&U$UbsmZ8_.P  AP ;5 j0,KQp @V |Z{;l6jq`5ÿCLY)2SA (V} źlTe!(EY8Nhϔ3+҄bȓ/x׎wpQꬫIi}:l$ri^T5or.|QV Tq/ٹ41#@;p,6 (PA@PAA 'aqLժ0TZs@O!6NqLZe(A(APT  _p"%DP.lfsmMl kYcZVVUUJY841LI+V&iZQfsZp l|BwρPwN % 8x˗a0k6n4Ue)ٯvGzwlv_WPr\+~' xڻ4x*p-5ᗘ>6hPESg6H&D, buO^$}J.^YHCOHיS& =`zRjU-*K}*F Al_Gd9  @w8G8GTq!4DvA">Mb7aZV#U)Ur\)/KK_l4jSQD|@# (U9<{EL4PnժٳgGm'>o WZR!誒 H[pmKsE<⊒g.^ҟm:دe#lq\^Sd+(;S z3ӦM\R?~СC!m졩 ̳|/R;"БDž4NZQY]9"]lj+eri\#ɑVgI2J_hR;F,^|' S mvAB].X bsm\k!6)յsΧO 4oD"8%y~/rt~Egʼn<]YXtπ>Y)[Ri2ջcv,^I Ё9u>7pISg3-jT@WAG  8p|>bĉׯ__~,B%1:V$MU>5/ޏ/`s׶qdxTZhal``FR6~Z̯ިtyuv Yk!_ժ{:BƏs@O>u$Xd`UȊʋfLRb G@/ըkRϒI?DY|&4|ދӼ jjً3xj8ѕo*ռ%MQ#2t6 khHa/vM$i4PXQQ1hРGh`shѫ Ű_K;uLl_覻kW yT<҂iUuJQ.~'z+PG*OwWӲ<[zl"o}OZ;R+<$$UVΝcC`aZeyo(pK!/\a嬗 ~VVl>{zm$H*3Xl@xQUYjNQ %,cJhq UoHTLo76k?;;33!L ѵ_=^ gc:y6*YWfذL.L rYN\ؾD\gm'ȥytIe]u4*ؾN~Ӟ,"o]6P 7.߁Sѱ[gϜzhBqaZZU%ʪx]^8Ae"3FlWǤ!X|4 %IҪ 6ǦY܎z-{Ci=gh}/0M+!2cXhdZI3'zШͭE`bqLLU5IjO $5׉l8Q&VeZz<CW&̾DK0+p<][ʥy%ϊr/+eR..ޱ~$@<+8\a1vjPxiUV(ZJY;nY*Ke IBVj@ iJ6Uw iuvI-Ϙ_=^AMl ?Mۻ7F*53>z)M/c1 w:HISN'Oׯ1&iWFI̦ B!hfy3ˏNP t jyT2\.ɗVgT \V>8 UbZV P.goG^{jcy_DUz71Z\j5rFʵB" q'jmD6~NQvN ] mBO!Z&3k1YAP6d:=3mag..W6Ffb/ӳܑ:m&X+IZ ʴ@@(uOa1!eBk]*96lޥzi<]V++JB(QKԊRgyg./w TJy1?EBdx#l1_A/+AЖ!˵Z0c oTs豳gߐa ey*=am_IjT*,ŋd* II>dt0$]0FŒ2zՕU$AʪP'Rq֫WXҚG%y[ū%Miڋ/A}ӧL?TFqٔ__ϟs\Zck h 5ʕ+͛aX͝j ǐ߿?88 uFRVTTԶm۷ 3CeRJ&CͶdVUUM6}=!izb_1:@P|cN۷oG[ɻN:5`}}w}vjW׾v_mj&[RI?399&omm#G>yktڵ4jԨ{<6[5j!C~<8 _{kpŊRw N/*IX| |xҤI,رc0K.ݺuJeeeC=ww}ץKW^Z +Wk47nN1F!(y mRid6ߛ B ͅ\)u,pbϐ/2  Z-E@\AT.ziR|(GfkPI zolٲܹsgϞD$dP)IK\X4& z^!;Րn*WVV 1 mcmG7nLNNMKK9s& 33sٲeUUU666zOrrreee˴Z-*))3s S]TKS)/:޹ uze3ןu8&zςD|wDDhkZ\0Vb>qKe=@rr;d[臑ZJ# l-C嗄xӐH$ŕP(%IeeF |> B&1aXyVrɐXt-H2TT&*}=j4O* F8r kzxx4m̙3AAA^^^M6̜93((hѢEÆ ޫ(aF dL3 ֊y҂YMNE AznIE>@0Ԋ, 鈈@Ym>۸e~p@\2iL*߲}X#a7l2:::***((*RIbw<)nG炂/v===H"\opppppp:uիlggg\HEc(̯4C& %!>P:,f_V^Ç{{{gggj]!CM&ƏI[nnn߾}}}}Zl  &&fɒ%r[n(Yfݙ{s49itdlX8P+5j>[*y,(qsD!l%% @`:CėW.;qҜS<8r肳RBYlw#G[jպu-ZԫWO,3jMHY uAAARRݻwoݺc lvnn@ ڵX,DBb) T*JkjjS۷ooݺu1bbbbcc###CBBTG3yy  R?;=z";È}4v}w0[o;P(8FWT,_uZfF!.#Á$ 0\NyZ1+FPy| 8O/_|;w\r!6ٺu눈 1~xkjj^zС;wx:uL<9223%%oETVVV˗7nܸxy<^Æ ;`u뚴뗡<TTj&B3e˖߿oѢÁ[S;lذT6MJB'WT$*n>>k*6 Tp?(.\cQIɬ_j`^55(4$p5={u6K "=/36mf3Wu!陯֮ޫ~dF0v*:"G/=Ln(--k6BH@?gǎΝ={СC,N:-ZhӦMDDD:u3B#+dee:uj0K.qqqīcbb_xnnnnnn 4޽/w^reܹG>}Ff`*Q yM^EcT5bHi4DM  899vS 6lذEX >>>08=5_e46  _-:OnS\.o>{fϮSM} '&&['FZ]-up_’۷n޺MضQ7o:Y,9{oO7hBF"8뗵k+~ (9 YZZUv8Z-& Qqjx߷o=^bDžf]@$@T_ҩSN:8v֭۷EрM*q_CCCN U4`*ÿ[NNN>}8zΝC<C:tHOO?qĺu:u4jԨ @1yFcZWn@es2PɟDm@dTxa#_1cɓ'H@@rI m`*Y4ˈa40 |!b?4;vܱ}~QޏePy6'4Wo=]`:jOFpl, *3844vS(Yr7:tË/nݺ(6l2...<<<00P N0]vܹsbHͫWfΜyŰSNBݶ)~̝;wذaK,ٵk|1c0`~2ImX\8zd&mrBAD,pgP%ƪ*ݽVIM$jfӳNzj4(:MZf’,֩fqBG˘X!: YrFw F tM9;9(h'󯿬*/Ӕo_[dP- RTYvLs J&SϔU'ymF|޽s߿ݶmQFٳg{_˗/ B :Us@j =z kа ]V^~ H:3~RVM:ux Z@kB ݻ Ł*OOCz|Ap ưmV }¾k׮I*55oߎ߳gc{ܹ[n5jڴiP 8yýϝ;Hd5ʏ6wʕٳgoٲt޽h<;=)*LSI$ 4,9`қ9s/ٳgҥK, q\4idȐ!fΜ9ׯ={61/666k׮7o޽{j={BBBfΜ۷oݻbr56mlj֬Ybxڵ߾}`􎀙;ne 6O`ʰqf);g N€V T?ML |ْj\a%mIiYtt~v;!0F S\ :rM?i棇bINOC(qyիWwElkמ?~GNHH5jsgz6 =5mz~˖-K.uttܸq#a=d2S ! 6-TGU Eqq1Цݻ'NA{'[n7oޔJׯ_XPNdX~zFFs||\.GQnڴimڴ!DKXy{{#ruṯ"z+{.:)%"ѐFB@ xYs++j ֣{[ ()MhM.1ȨdAKXdRo߾4iҡCBS>YK`,YrӦM͛77nW 4ͦ[ǪU-[6rH>?:9pL2%((WT, ͮ9r$é_D"Q*0T(ْa(]2.Q>|P( MOo]NKb_KPԊB\5J}NU;;30"x.?ຨ5u'"=aÆs\.|>,ի۷o'M~T*?~\VVV^={{{VC8R^b\円r8^&إҔ\޸qcCG AG͛7SNHG̘A^pQwQiðJ+V̘1ϟ?匎;k.MvA4pnܸQPP#`>s "afڵkwΝ'OݻsZe>’6VK"[zʪ '3NKnH (999kPSb HNGVbccB! , x7z5j(ϟ0aByy[AAAddիsf͚Z3:u4~x;͛w=z+GYbEݺujۆ ܲ'O ۷܈sn~zſ[Ϟ=oYHqMj{T"7C &YTn߾=00K&X~֬Yu޽{@@?sʿ c&0 sww8y򤓓dzjvQוN>}nɒ% bL tju _cĸ:$t ѣ: I5@Bktk蟱zDR ǣ.]TRR? nٴiSyy+WBaEEq0TTT 3J̙3!!!UVh4b?cMM͏?H$_hQxx8_bŬYt"\s:wܤI'N̞=0cI籵o/Gm_`/55]rPi㸗׶mol6={>hʕ~~~/...2EQ0gGGǑ#Gv: <Օ~ ߽{1czFX ,jdO&Bz?)N`)'b":rbPK7N?ToEx%KR3*֭[vvv>KKKE"YC r ۷ ͛Ǐ[hQQ۷4ik:;;w)==]_G7l#::zӦMDur8ݻ/[,''0Qx/) ~4_F4䤡SN:uՀ W5g7 s#F>|^üyLcD?G=bhd[1gh3]L#8Zຘ{B4hpl:>7@!Wp@MF ̓1 >M4>jԨÇGDD4mڴk׮ݻwxc3fjZ611w٣GFDD uR۷o7nl)d4U;Nɰݻw )ݢX!gouhccūJ';G MRx`T+>H*ڰ?,!Eo޼)˩*ɓ'O<ٳg۷Yƍ4h Puڵŋs8TӧOn㤇tӧ ^zT{鸙~:>SLYԻأGE׬YCOTu&&l*Ck׮]twzV,>[]jQM̼gS[V{5) _{Eli+n&%**wssskjjoPf8hZFp)SZjɒ%֭ׯNfgϞ"X, V]r N< CIVrJ:u  $G芬ƍ۲e̙368Wk׮=i֬YVlVo0͌3V ZOղãŔ=~|//3Ҙ]]v3gΎ;ڴicxw޽/^ݺukܸT*5d۹\˗/߾}f͚zF9sfVV닊<<<1c .|Eiiѣ7nlٲ9s4lJbxٲelԽ{B5o5zW^]tNhEl6ɉ0[Q0>}zZ:uQ4eڵӧO0`Çkâu0]aLejj/_Pcɒ%ϼ2q˗eeehT+AcR2_l:UV OJggGC c cŚ6mjr ji]v_gMFP*%Dt-++#l%ʮFJOO?w9bĈ/^TTT̛7o۶m&L8w`^^ފ+h_גɈ#0-QN3ܺuA1{X?fc߿?==]v[nK ٳ={.]tРAGĨZ{bShm,S~TTTl%4:6WW^vڠk׮Λ7oݺuÇbB⫮޻w߽{WRRryxիw9zU4͡C~/rssW\hѢ|-bZ9wիWb1 Jc/\5qݻ'$$Hjii… ۴isŋ۷O/X?rLj>Phlj3,=cK\6I&|o߾, +_z fPYxL&۷o_qqq^^Ν;]]]<< P@1}ʖwa zd2ٕ+W5kFﻬZ-wY|yBBBLL?3ǰitKJJvhѢ6mܽ{wΆFIq&fAu& 'UaZwssݻ׽{OmGVשSgСÆ ?ׯ_oҤIhhK\\kr1 jժLB5s;{vӑ}&BhI2;j4୔]mfNN #7v5͖cB"iX,={vbbAlҼyN:=z 0HE3eF}uiӦկ_ᅦݼys&K^c?EӎQd42C/-$ԤR(A(9CxZԩS;w >ԩSW\Yj_^^^UUP(x<^޽'J  _~=wnooooosYfrpLH9\ÚfZm4kԩsZO<M&1c>>>fDѧzJOOsŋoܸxG٣GOh=LZ?%%.))YhQFV^j\.flԨBPV i~ZUDRSS3x%&]]rLlè?Og)ղfB ˗/2雩>FL23zmѢE-fϞ}o߾}ƍ-Z)ITLT?{ //~D"z#@KǨa Q)Z̝P +UnQ?RneF (-[  &xyy#G(JJ{888;vUV(V5kVZZO```ZZK``͋/bbbiΚ\G@|jd$[ ?h.\o>{l̘1pcqTi%n3 Q>a„ &}Ç7nHHH `ԩ.SaXr\&d2\.+**޽{_]]j[h1jԨm6jԈ* <5LdӴISFɪn \.W*>!=O\PTzS|>LJH`!@ap80SڵLJ`D{]t4www.LzUpy,_)/¼;l{IR*W 3Xæ"W6lXUU՛7oRRR^~͛*bAgEa팢X,vtt VTNNN֭kԨѬ_ƤԪ6sT*U(~-5: ,Xp!<wŊ 2Q7neFx݌pm\[ 8reVZYH12U6z2ٵlْ8MJ%4Ld2D"Jkjj H$*"H(zyyΘ1cݺu^`jӧG2:zT}fL ɔJ%p JrJ&`ӦM+VЅ bht@Dr*. y1i-Ca! Y)..uVX%0MR轎2o?K$ r8{tyfS+S.cȤAd ¨J$ѣGp:DF$%Cjz V@Q._*jlTd0"mÇeee49gjV(Mg"[倱۵kV]ha/Z|yH5MF?r!ae l3 " 2L,&4@vcDžE6f5s(ײV 8"W9smIsի":U1v u 9nJ4kDήT%Kp8>ͿO>֓j <0bhPRFHMP}'Den8/EdrF7!P4iO 1SG~V T ֢H:8L MuK.5k z*짚5糅!oFI$# G`r%b7~ q0h8sO>ЀN^FI!6۷' 6DzBOK E`TJ1U k٭[7=U{P3<*vX 8tR6 rIUл&X3 ތ5`1OCCh'ta a8`6m8دk+܀v r7[._ DgE btG<SNZ{uEEEQU$jGʼx_70+ P^=`ۼ=`3zH3%=IU`R8>swԿ2O>ӧnƦ۷5_ϤMԨ6erz͠v:Aڪ E!+ztIhVVӧO;t蠫`$ iW:wę3gԛ7o~Mm7naihih@ 6 B@Q n4K*pBرADJ2 /X, >>>..."b~09<{SZpdHh7MIYZ0|PwBBL&sh0 kŨ{3Bz})H FᙯϦ޼y3++K.AapȑM48s:u5k 0ƒf1H|@HFlL"rbAPGcfC˗/?~YPP@$b|.R njH$ W^˖-4hX1zñ%9Z31[L2hŒf͈ h6!C nƤaz&]B1o޼5k`fccx<>|CVk˗/q&U3FTc4&T4+)R_oo;m^|Ǐoܸqݬ,X, h߾}XX-,Z- B555999egg?|Notttv"""h¸@2DdPTF(L:-qqf*h1ܽ{m۶0)Xx004b"&aZZ3fÇ)SɨPd03|>۷o۶'N9=ځɷ tP?uP=?t4T7nHIIٻwo۶m$988-_͛UUUfCWWWܽ{wժU;wa%\.7**j۶m 髽~VaEI2@謙޾}1Ь@j#ѕTZ}̙04iZK.,[/o YM)SھT"pX_|9{lcʔ)gΜ65ɉ1c|׌3j5?JΛç6GNҘoQV|RlҤI``L&3MZݺuk??j|T:&,Tށ;vXqxÏ?ifz鼌oÇH)cƌ̴n)$~b勆+҄THӔZT^s3fUV1eh Q*\.Eo4DCO&T{nz sG%d!Ӻ/29 \[[_tÇFڵkWTT͛׆La}@j*jOoC6399۷fGY׆WR PjJ{ցhn&_fap惉jKy&Bu+V$ur0`QD"ӧ9 "M$. ݂kBшݻw_xgҤI{NMM5TdpG;:51h&$&rW؍7BcGg }aw,>@3\.da51'tysAp=D,_0*3} 2$;;{ i"U#"3\I.]nܸ_i:vh[8%uõghbbxbh2(&Zj/_nР!Ho7j*erZQ'GC?sܔ7ab[) Ʀ_Ćaeh6.{n^|ŋtBao!M6QY ϑFݻӧtBFlJYQu DFj<8BWDPG 6LyBd8?[3rz<2I}|㙄hԡ(73i R@!TnCr83gR+@<`{5&&ٳNJJb³sqҼ[wgTGwR*RL&$$p+T.2#'& F=, aXcǎ a42_ ֒Xƨvp3|WjIEBҁYyj~(((8|pddI70 8qBь?U!0ŒUgEuBEŋvvve^ߊ5{jwL O[{[1eH>Lz 9,RФAiӦ{֭{ӧsAٳg8p`ƏjIH)hJ:6Vg+ Uҩ_׌cwR.^جY8B9i4nT(kFd*a,)֣8FWkI w}ʜP5˜[C,(}K7ATOOmgggo+((ڵlٲo>oo#G޽ʕ+07ـ'VgϞɓ'c&HHXRRbkkgϞx??+WNoѣDZc`:Ԯ];]hfݯADeCZIrHQ =5% q1555--szF7.4IBz:jƜ]BomښVpj We8+Tb0Xf(A#Q4'Ԓ֭[]v@X\\\b`RM>l6op8#ׯ_UUUbbbvvvZZéz#L@~EU%I4y+T~^S Ikư&4x{{{xx\v͐a7smHXYPE5hJ&Ju3I3u9,ӃfavwbXAVfF53fյm۶G=|phh(zHK^^J]fxW## rcsmjL%=a˗ԩӨQ#ԫzwm|%iLrfosΉ'FW9r_yIX8h$y $`EI ?iӦ|>B#oڈD"7SRR;X,.,,Hq?u4={6,, OpyÇ^4(DU"= Yg,>|خ];?ˌ3ƌk׮*&H'N 6kΜ9(ht4V_o@@@ΝO>=g"Gqw}lٲ˗/c 3ta߾}]tڵɓB!bqYYٸq㪫 w [#̝7ʢz rBӧO-͛G5&&MڼyݣGAH=}_Mz>&Ψ.ayXz…ׯ_C0d3IKW.}!++\*JRɧO5XD"X,vtt ":={={677WR3 Օbyzzcر ǁf韥_4Sl4ER9z(^wGiiX,3fSa_uE狀իӧׯ_?// >|Q>^8)ɻ]Hr Dz˫W^Zy͛Ǐ t\x<@`kkfkk+t#τBLLĉnݚ(Hjr\&=}t̘1B]~=55sʔ)4`R &M20@ébwBIڟŋ?|ƌ" 2Ms,}4xٳGWyȱcF_/ḴgqVKJJRdgg?yOOOyh֭ۿ{{{P( aRT*r";;/_|]RRҖ-[P ԩSxxՅm۶m޼K.?cll,nnn.]믿HL6[Y^qQQH$?~!fȯ^/))̟?_V]\ àVƍsrr,I@ IUmfgg̘b^|i.Y%Q I ̟?ڵgOFbݥhJ +H P![@ q=Vn|eefv5/}dff82**##+VСΩ3ƍ2L] \*iii+Vzq ۷\¿7RQ+JnnŜ9sXfa)#Gt&گ3f~z^zeffP)e7>2PXgr<$$Yf u%233n{ L:ٳg\z'VOYy=LMM]b޳gO<M땤$ &( C6XvvydƍvvvK.2xk0TͯjcccaanHV1%BȓMUJP >_vu?eM4?߸qcÆ #s[L-nڴ p9T/5GW\D2dc^^⛌8>enn>|ÇRRR^y9mۆpӧy8>GqE`mݺp^zgϞᡝ͚5;H${yJ={|KV\  zڵkq3_.]^Ǐ9s.?2 U,"gFF… ) Zn 033;vlddD"c2,**jڴi8o&M?N+S}Y$n;'(~\g=zAOPD|r-[Jo~_R^^i&+++ hu2S5!׮]zÇ=ո1YAO>njjFM'GT*y1cpy]x1--V?(W^]p!1112d˗uF,9_ޞXV޸K𵕙iii9uWTT4tPk ;v,YPQQ1i$W1ciXgxU(,5{&55tɒ%,OкvVZv\kk]GDDdffR3E.gggyܹsWիkCCC?|uPЇe@KKK???___[[[+++kkkM\*VVV⊊ < ׷ݻwo߾}PPSn8{ER!~`OvPcH{TmllڶmQoP:ͻqƖ-[/^GUЀ9,w7n_tŋGՈ̊߿`jj-]vurr}(//dr@CP(=իҰaC___???-ӯV=xGw/Zl٨QWeKgϞpm۶^[bb޽{++,ggg<رcK.}pݺu?^ݻw{{{[[[6i$$$$((ɉpG/SL9s۷o"$N`+;S4%50ֶiӦdҢ"D"$\.w.\8p@3Mqpp&@Pu lyR A7666==}z4_'*((Xn]ƍ׬YcMRPPpڵY,ϙ3g۶mx= I-bʕ+sA!X-tRffݦMooo"ϐ>|Ço߾m\.u^jf3ƸdREBH }$ZEQDBZN DV_oQBEcy/6:=p]W/OڴiÕxǏ&&&iiiz {7ou֢E @||BUfeenݚ[XXGƞ={6(({M6csrr!> ]1  III"L7 ѱ5kL>UV :xyy]zuΜ9!k9o9a=;9$=Xn2kC:PSj>B]qT?\^ =g/,+\$#vPprCCC od_ӗ/_%CO?C=wǏ'O~ϟ?O4)::pȑ;vGEE<^UTDÇϟ'NXO<8pƍ322V^Oᯘ&&&Ι3'mbri%dF888tÇ88^AʫvpЕؚL/i_y+\CD#Cb"0L\w8סT+:VT?Z,2=LdO>}wT0h!^6˦eee޽k޼9Y'H$ϡC|||"##w޽ƍ x[RM8~+W?<<  K,EAAմiӌ5EgիWǎf)BVV) Uÿkʔ)iiibƩ\`x<Wh'O ׸B+ALLL9x`nݘJ$͛7ݻnݺ%ƍ2/Зkx'O>G3*F$bPz:&' i]zZeDdiK؃pCÇ_~Ӳ晰yE8B^ KSYݻwzbɰ0lƍ666ӧOOOO]r5>taҥ,u O>v(RRRfΜ~z+ݣ]ƴ3JGUs9:U*--uww5jpŰ넚Җ-[n޼Ν;oԨmB<-R+ wJz?//` gLjl2 j2(b,ø_U˖-C#0Й\j67n_n K"(88]vbQBg +ȺNrooaÆ1jИ-7| hgg=vP T '_ox8OL֢E&MtÐg(:MÇy<^@@=r>Z.\P # DBavh>vURdQZ3?Y2zjuG+W_rE. ХfXD&vv.*S3'(%: .:-5ZfMvŽ,q($&M:w\EEEW\Y\\"zDrmw2իFBt:\ݻR-\= S\u: @W$Ck1733=YE_=i'زeKvoZaw_팩@.-ŧLGZ:Ķ8dfȐ!QQQ Xvm6m"""&C\.TǏj?6lأGcBY͚5sqq!hθAz5SK <]"h 8轝h˺wsskӦ qy~~~_kz`6<>&RPj~'>F?088… ǏcƌٳYܾY("4G޽ ,--Ϝ9ax^rqqqiݺgψ $&\z,$O{BAtzi2dhYKsp&4*xر#SS?[^S:Lhb#X][LɓnݺA 0 7n\TTԊ+>|0a„vڭZӧOdxZO`vWUR޿ޮ]ɓ''''^Ç#Fs`y<^n233?̲s .; ׅ6DÕI>oiiIhѤ]:T^OFtԩNx<xGҞr(s1 ZPT L":Oɓ~i5+}[rB`zuݺuo߾]]]׬YӴiΝ;߾}esOϿy?ܹsf͚_VrrrbDZn GNׂ`f_,赣S#24l ˆrĈ111o޼!#1&=n6mt"wR!J;lc՝d'o%\V$B&9uP(^xqԩũT*''֭[w!00řO<... 6ڵѣ[nC8>,MXܰa;w0*'>ܸZ%^OKwW>RZRaPĀ>#",-VVVgNVxmC,fkkB( /i8tǁRᄀ^;YQQqݶm豠yv*aP(رcǎ%IBBo߾Sˬ[ةu <kvv"(@j7-&Z&f΄LfB&:,څHk^|E ṵƎ7.[٣/+ԩZPÒ=ѭcncNG[ݻw .ŠB?itZ91Yjڵgnn:k֬ĤR\T*q*>%]v S4:Gt"sG}Aa8cHLvshיJJ޽{w̨e!kҩ%8٪ɸ@P'aĭ3,a#dJfj`U!%ۂj B@ دOCG >\&ܖ~'^d2H$RkVZhѢYf BKKKSSS@`aaBpѹZlaXttt({y0:Sb+U*噜g@ [[uhP5F!P (׵ *}{<ɀ0פ6k9VMjPEd5u#lLuc~3gN&MB7uGd7o6jϡ`\rHetjFE|>C. 60@NClwݛ 66p[XX,\GZNCDZe˖T>eCeqףXZZjqXZ3T*o޸Ыub A|Fp-8$. 54^ .-H(mm45V%@<< 1X95* li\\0߁ iұiHFoE4 PbRTseuzRcҹbw5Pbr1aرcn݊55Ng#JKK߽{>/R}2P!@Nv cf^zEcm(kO9ăt’C3$?|~AAU5V* 61DN`$="v& jea%zQ"+/W*HV x@Ҽ!ӳ g ]߉(2'''ݛ%RVθ=Fwc̘1AAAd%&&j!0G>Bl(Dǹ5s$^P'#PZ[[[XXhqXj΄d`x'B 4;xKK,[_b L@W"xN>kX/LB "^ F&@ 浞=|** ӲV8:fv{[y* ZwpaN 3o/VVV8u+Wnݺs43PIg+:_~7$!<^x ԉ%giv|5;`yBXa XQ^P@P*e2 jT 00^vFv~m!bqr23R2 P PTI$RZE"x<0O\&OO?dd l$}H ZeiD-X@- "22qLfWqQǍeDror僾 TEƍҏPNjr-up9;;WVVj!3"TqY+*nr u?V^i\xgn @6yERDvrsX9hMǪ vˁ7izd/i_~,l,>llnmqM`*Woi÷yqojp8/WD~0ŧ҂.h3R|@:MG ?jyeYѝXxH$=z=G+Fc![PPホ ҨggC6eZiԨBO$AIKK * *g`Zg&]]]+++hg}1`M{SaaB[KBJ MU*>so͗=.ud6@KOulkZmfiNHU] .,L(*6nȚukETQ66J0fOoܡ24M>=вc+`T9SHK޽y|iO"@ZUݸq wh0~&7!!!![l۷͛7MLLpTNt06 &Bw:qt:mU5p]cՃ-,VUS –jV%ar [+B66EETiA PRȤҊԸT\!41@ UMijhIQUǴULԺ~qWff+F-SCu~fӋ[vlI kb+†NX%Y7D_:nLU]hܹ6d<`rI֭[_paСEEUb8!!AU}ԞJ'6FUM0;129Q4oޜ嘭<@i l_ {Gk;/E,ETQ~i~iRlR Pײw+BbBїC×^_3k/@* Xt-b)B9Y8i157s +,@sԵߪ! ۆ>|`y(^ʾ\ʋ>r;fu}iIII޽@:)!,Ov'N ńSft~ypppbbD"!X6 LfȓD|U\vV777 òtq YXֆs섬rts9L4UJw|G\s,}T$>`Ʋr췉~ڷlʚi[gnTJeg89^|={vqqM<<<{^ d:4KMS{WxFHr"%]N~pG_>oϦEO_}l8P]J'#F%L{5A0^XoF oܲf=zB)NNNnnn*,Q >CqUo-9L튯oeee^^NإBXB@-i&((? U'נ"j~DSØ/'Lqcg"IJڷo_xx?aؒ%KXf4?T\?4V׭[RtVsxecvif۾n yH -$$?D7bv1'S v1+ "88XRmE N8wD9Q\49~O TW7X (3H&ΪXO#\rWWC{ҥ[lapuV kwgϮn\D<T*U(Ν?hW9SQ:!=z[MTJLD$:U%`Mm\jIח&I0`XjS[DբNUZ)Rv#b@d_3\stʆ'!z*F]!sssGZIB"a@LRt#hB#ipqVKJA=D*$DTJob0 OhnggwɁ.\pI$T i#"RRk=^WZeh`o ~5OLd54I0Hl5Tbhč+,c@2t-@1$7]|US"F^xPW({TqccQ7PqTb̄<̐\Pءc脤D3`F]Nib>@/V? ѰTY,PǪIk9&H^U<dw-BwR)1I޶mז-looȑ={eC*,={sG i#\>'aD83vѕFcKŐb3U/*lrZ8jFXC!i2+A*Rd<P7TIXZhd&.Uۥ YK7|*j\%mC,2:5 <2k@bJ,P(lذaZZZaa! LbijϢyyB @ >0 @C 1AcL  |/XPsK M+|L(B!&bz0Rz4UPy? ,eYy%"Y3chggw̙^z-Yd֭^.2/0,d::QF3,Ю%pTj^ &_AoVCװF> 5#= Vb5 2`FҰtXX7&uC@J4σ1.;y0Q ЅdQ|&Te]vCmI' 'O溻kٳ6mis܉720!Y1qXۜXt|Zh`)Zxp j%:ԢT5b0G&*d6V(Uk<_M!W1eοLp H4)ey˗/&"06{ ٰaCZi{֭>VVT7Lѥ'~ՈC~`aa0a^=}||XggSN1j&"V}ZV׮]4hN#Z Ϟ=wǏ322 YtwwtvvqwwСCAAAeeH$&-<==}}};tХK-ZDb_W,BYrj.D*O*A kU%gzb5B&5TRUȵ,Ũ$ ZLLLqlm)C.,SHZm߿gYpSL`8/O<늑Bݜ6<v E"|P(vvv7߿```pppPPP($b{٥KBP(Riiiizzϟ֬YS؎;v޽W^&jzj&7SI|;J \)HKV$^.Ym-PkQIL[%y;hy0 `aP($n5n&E` 3)ެ5jJ+++&Eܾ}{qqߢE ;wLNNvpp;w. 8pիyq6mP'Vk\\짏~jrŋ.\`gQFfٳgf<== N6ߟ|/A***CͨXC"O ssTT̤*i .`fZ]vYYY,%d*zƍ[zuzzjuhh%Klll~71TݻW^jXL ׭[wUbJJJTTLITܿ2` =J:Ix?ciiy{+۷ogϞݤIŋ+ʍ7FGGرcРALԊXUbgkk-[.Xʕ+޽vϞ=ۣGΝ;ٳG,f/-&m85=2v"}۞ـU 5K_5 F2\- ookmlТ[FVQX} vH°UbUp  #S.Cm/mNԊJRh m66 <(ĕ+ׯ_׈4ў*Z|ӧOA&Mlll _>9iBBٳg ###ǎz]vA޽{>y'...""ѣk׮pBLLLe2Y>}óvNv'1b… ׯҥ ֧Vqqm6nܨR 2uԞ={@@ E~/99ԩSǎ5k޽{ׯ_ϒ~S`6{ Ee#&2TQ@bc -+"R;êzuI,߿O,-ҷ#PCc$+$;㷪+(#| (<tgCW#STOw&) 5nٳggʩu14.''Ν;YYYD,hBB©SfϞM}fΜxذ'\iz1>~> jSkmbUbFH$z t%rw ?uֱcڵ qƥK4'jL&4hеk׮_ޯ_?'qK$A Զmŋnz۶m;וl w'GÇ>}nڴNQQQ1ѣG+G^puL_@{Ѵi۵k۷oL7d}1m_*-F֣dRhedTCqg7}'̴ZO"`@;ޞ$j*P{"ZPq&СbUԒҐKbzDɞ#%%ݻw:(CݻӧO߹s֭[-Z(++2e aO>GDD>|7nj*===%%};v,##{%%%VVV/_lРA->~Hes]M|@\n~ {AwIIرcw9k֬ǏO0 =T nk߾}7nѣG%>hJ}I(WӢWɚ1Q7nhnkUH?D᯦skWÝKn&ijFrD@R W +{D.SP q޽;ǻuΞi7tsa33;ww̙Rt 6%c٭]V*fee^T  ðe˖xI&.]铙L&tߢҎ.97ݼyǍGoߞ#JǍwڵ7NzL="J֧O7nx\EQNRFaGe! "z.XMrFR0j jx@"5q@y•@($A `WxC *Fr'%EkY \1 a0ԇT*TjffWK\2cL~ qT 򖕕mڴ9Jj 1=jW^￝ MTyx`.i?8;7o^~?h}1952dȐG5k֌evqYZdi k)ۊ1`wZ1 2%O\';LT|Tj ļlj"h] &hi5dVY@Dm|; >lQnmkPIV&i& ի;cPv(A@O8lZʪ_~/^#Uӊ@TVʡ߶m M/227޾}͛OBȸ Scgѣ¹sJR@725 "::zʕڵ8pZ yQ-.iZW:Hf9H҂5*M ӡSRٲUƍUmNd#2!dƪja5%LR),,,>61% PMhmf2yF1rHJuYtU:W%4mڵ@Qd")w>60o|>ʔ)/_vuuutt"*}tر䀄{ >|#G6mPVYY{ɓ'O6@(--]jU;o}i~DmrVjݺu6m֮]kjj:w\ƱVIx d,Xļy&`uV # U C@@ GҴ+ww=ݛQ Շ<666LH>F;}AŴQF;Ȅ-꿧]Zjղe3g |;{X&Y11ʛLܖN J(^I~g rfdd͜9"7te˖ 2dCU(*J&?~%r֭X:uĕׯ_8p=zs/ &| f͢Qӧ۷ngH$_}|͚5k0 [jϣ1bpv: H$9#>I(Z P G@5 Frǩa512V PWhZBLF k iKKKq &"o -,ٷȆ֑iZA6( 'N\p۷"iH\]IBr%UF}hy@h27oT*Ǐ622e˖ӧO|7떖dʊP&fdd~z۶mO,.bִiS2$k޽iii="QFw;99 >Zx\cƌqss۶mpNi(q0R1 |z*?Yhm6뉏www}UV۴iӋ/,Nj~˗/]I .͞=ڵkEP٣GC^r܊X,fǟ={ₓ<hB*R]I?pBro ڵk[nݪUUV)+VxիWk֬i߾=Aj,jd$ }v/ U30iF1RLkVSB ^f @k&EN~ //@=9XV;W1El+i5WWӧ[ݺu{EyZ;ڬ_1.o2շTVJKKnM4I,=ztϞ=˖-;v,a<111jB,\~ʕ)S <ŋ&M0cǎvrqq9s&ޱW^m߾}ƍ8+Gpƞ*˗/Z?wٳgM0a޼yÇgzG_"𳒒~ .͟?Ç.]|˗/ 4ߺukH[[[;;,Vkh B$i`Q@H@ TFJR- V#rR^Z6Ĵ+ՙW'.ed} j ?3W[߿wqqAtc>ѹkP? `VVV RB+dnnnOjĄ {jxM=22ӧW\ٳgWzYlll~~KBBB:uꔝ=`WWנ˗/|7666ZUPPp=z&Nxqby&$$dڵ=zhڴ)8I~K.e]+=]\A5 WH$Uk0rf1*zW{{VS 0;?)=]P<ôBIZU9O=)$ɪ8&ϯeϘ1Q%Tj, ]'7Hiceavo߾W^SNy󦛛y,tF ~,3MF-(~zyWDYԩ=ZF۲2ˆLaRPz(fz S}B_BHz;v еk/_謄 {8uogOn^7nƍԨL&}c_KTJ$zω|c>~k3S&k-[ݽ{}B0!!-#Z VQ<Qu2";wΟ?M6Ǐ <#fx[%淏LnuobHUK,ٲe˪UX #%!!aȐ!999Ǐ533+xԯ<~Oq}L-Da_rۯR=<=M{%K [$򽪁Mf:| Uf_"EQQQz!S>*]CB~A@"0=I&KWP L؛H_~ri)H+֭[T*a-=:F> s?@H_;Tdd$`ӦM(W]BoP)QN:E$Z1A/_lnnCZ1XOK#@ԅ2I!|lذa & 2J9IDD!( uG!GL8RHÇoW-;33355A`YfqeI `Un~PxǏ{yy?~<8%?\R߂{g:HE_5eGD"Yd m׮]8--D[Dѣx".+@M %}j"/^3gΌ3Ν+>|8eʔٳg?>--xl߾}۶mcؿ[o‘nmm=dB}SrX BT@ 0"|C9sTTTԁDBu (/$riI›z \t ǙիWTTWdM !,((ؼy3n%7o^nn!l:+]ҦukbiR}yUiiiYYp;v  ,XsݻwW^ 7vٲe˝;wm۶m'OZb+w11XDW ZRgii̙3qt\\NFCKnJ$a%7ͣ7kh"+z:uTyy9"1b˗/t 2tfH zo?[* tk)Z4o200ӧ3׮]8qE1r̘1< 9rHǎ>|Ϟ=1c… ?իW s=zӧOOLL0` ^|MD{{ݻ*=uV2\+Q`M aNIzlmm]v=}k׮8.ن^3T_qUٙoPmn_\$/^XhчƌӼy_5::Z+ХA2HyܹaÆ5m~?rHdddǎ?rAR&OdN;vbC洔_7oޔ=zT*ʕ+[lId{ٹsiӦyEjǏw9rdz|}};tPXX뒒tYk֬YdddJJQ/_^ZZzj"=%d@R X @4WVK*jj7"##y058,Z{;ǷFeşSzVV֮]^(6h`'NHJJۯJ=zhӦM}3ηv.-񯍞,Jɓ'<e2eHHPPPPP#iHuJ2'''333111>>>))))) |}};uԵknݺxPs Ԓ߮]um٫x5"'QߵkWjj\./**?f̘ci :k׮c'N=a„D͛/_|ԩ?ŋϝ;W~}P8tھzj&wǽ{ sU#oc4L$ ]> cٗ͛WZZv%K`9}<IR[;'M#V`N"J^xq7odddqVVV666xJ MD2G1uss رcXXXÆ 4>!.ꐼ`I&j*Ɵ-j9k{`U*x>߲eK+++\kT*U* XEEX,yhѢݻwYYY>>> 4 0`T*}AF:^"o!+6)BK^vs6l-@( *Y›j󨩹 Ɐ"I+/_$&&~177'I*͛7bG777777 hԨ3 .Q(WoO>+'+u +{vΝ;3F=|رW\A :;Hh兽~\eKKK>j*}":Gɤ7?S!+=L-Y_nccӊ TjO-jCbO(|v ЀųEׁ:::߸q u} P,!;L8Q3дiH+XU&)Y\]Q ՁQ^'s玵۷o VdQ#x9٣[|j(H|ƨ˖{!@?!`b3A^r͝;ǒb<%QyZŞgkhU^nݲ !=RK6/9O }sȑ#LQ+SIt~4c,պ8~x?C-G')LݦRrQܾ}͛7uN>\oQ*BVQAo?<YfvK=Vn"NȈQhSW^J۵kwmxCM~ֽ.JxW=\eeest%##6>k1݀ϡ(/#4Ǐ3aD9 qw_;]?UtR+ooo$Ni-~THko?~gϞG):tr,l,Z޴ 8\(W<jÆ %%%dUӪA.+<q\Nv4 ўE |rKKKssŋ'&&qrӮ^zXDEYcV &\oڞP闂俣=H{zرc?vNlt6% (2Yr[ OL!á޼y;;;]|Ѓ#7[4y$ ZUw=777Zq{UDΠDZHRwCqqq?-a-H(p!Ieܥ2pm<k֭2IaI[Di{`Id=z\pVvvv111ktH|$>ɉ3] >=ԣKO<4i Cv lx, SD>PP:uE }~z<啕߿*夆f1JY&eه  .%!@*?~|ƍIII[駟,tOZVJ`r]@4 RVV/>|X._v (@(] p̬JRT* ._YobbbX)%%%"H*T*@gg8h{# yrL&ɔJ%^CӦM{ٽ{M[}9,N "g]h(N5C`Ȕ177WAAAΝ;kUUQvBS['4cܡ^58(7dddw***Zh1o޼`B #-8pY!!91pdqqqW\yD" СCPP@ Cq|>R eeeeee%ew&:V*,/UՖ8SDe9$,J[@杽tL>*F`ΨPu]7& S)))/xcƌy:K|g&_1P$$#!!SnZq͑;vt 1ss} Sx8뭇6"|Q\?MTMxi(Y5PTٳCf7&cS81^,,,ONkb!߽{?~~~}KKKώYfV)N 9}ݼy?x{Y--<QyI^:u*4Ȑu_TT޻woBŋT:eĎIs\*y˜5GGGgh Y;F >>>oܸq`` n2555d2THII~zqq1$*33BױcݻmV+~&vk$\?/)))ׯ?p… ͛ik"syvg͎N+'.ieL%`2N+KcF !.&&&.^ЩS<\t*8:#._ܰaCXX z4hЅ ȾJz똌EfD&8)S>~␭_*U?YONVx:k֙Fo&@ _(gG \}{t.Z ЬY6o̩[6iLrܹZT󧥥?o)WHHȦMt֍1䫔gϞZn}ݯToD%\..q÷gY aUv RjNѺ1f ^Z^|e֭x͛q!DҪU'jEV#Jw܉' ݼy3SZZ۶mñ𝝝nJ ?fVEE/9YX 7ΌbNZt`w:VǔeTZ\Lj?AErg}xVJOO߹s'k׮NQQQ8tDVΝ;"RD/^#i;vh a\-w:cbbpj,.?H>{0ANL= ;5  ."P7ܨj"+.vY|kXK$;|ʈ#p3\@@7TrJMP ϘV"l۶?Ŋ1ֺWT֭[TEWf\\>h]t rb`H,B"œ'gv[>cMNǏ'?JKK 6l;!P )))cƌׯP~eTrBX纑233Gh߾=;*Uu<<+i8 z"3p `?b򒸼H$QyN6}ˏ=62|a)))߿?_~圆S,Jr={ [Msϟ,]\ XGo=/L,I 2RG)փJ@уi#2Fn6>dG];s2ED1;j)lҨQ#&O>}ϙ3q"([n7NRՆf KKK.]^r+0WRIJ̹ ~R*jOެ@d,9QAD.Ŋx $qo=sR0sE'x7oܾ}ĉ7o^pQ:uhkk+ ...`@V'%%歓'O3UUTT4sL ?@KDЏzCH?-[ƏO yΉéSO$6|?hwvv|>GzǏ#"">\XXdee=~˗' D MMM/_꘍O?{iN_|uSdǍvڵ3fƲ$>۸E\qDZڧ3g`[z,Rh LIV:+Ԅ&,^g}|oї{o/ G4Ҏ$d?N8կ_?[4hիWMp .M:Yo˗Vƒ+r<+++%%ET^xqÆ C"h|P(Ξ= Νgjzweɉi;46 :W?z3gΌ?ɓ M$" J(:/;wIDDĨQt?О)v I-P+͸sZye ~qqV.󅦱G$<[h3߽{_]]xOߟXjk~-=Z8/0M:$(ª8,<5ڏinK&mxgbAUlZɈIagUQ%,*mʀϨU؇=657咭ccuC[> (X,9gB2E)< 9SKnQ.{8rǏqL"H. &W$%J$~aڴi!!!QQQdjEϕB}Q@}@]Zy<(g&v|s6Zmfv$_hjjXO\PJ*>s`NƓc Q8 0/4 ak`P6qj?V`nV ]M\YuN"ˤhZX*Fe! G{ȰϨV[LD͗*9Ur ǗUJ˾LR%IZ*0o 0r0wlj]Mwm eF-o+ZJ~ZZE dir#'e{p TFuwϞ=Z-ZDԏ⿧LKupp+++WHc0eiQoz~FZ.mGˤw"Z!Ȉ?ߚC[)))qqq/ݼyS+ Drׯ3ϵ:KרP(۷'C_ 4>Ҋ¤;s\M:|疩# ʰ3_:q'8aS|^eYB^zsfYC(Q}@nݺebbҨQO>+66@RRRfŋkVF㥴tҤI#FAi < 2$R.I3fggzbիWR)1;VZfٳg!EEE߰at˗/߻w/9Ǐw﮶I$;wPBcqn2:}YQ; >'6]?iJ %? i( .]6mZ.]ߟ_= 8q";{3gt&Xeh ;<{A0 zСCdB-qQ˿'[:dju늋 D=z~~cZWTT"pBII ޽{WTTDnݻww!B5o߾-J۶m(---** #::!*k.((7onaak׮sOeDBjUJS3N[X˷4eAS#Qb18 ԩ޽{ӧ?~رo'K[Kv"?|pǏ]v,9C/esiܸɓ'7n7)e,JʹNV727oJJʇƍDZ^reĉD={XYY-[_7o]\\RRRRSS;uuV@P\\`R;99}wDӧO۵kGu˫h۶-ҥ111Gܹsqw233ӎ;RSS#Gtvv>x`BBH$?~YjVSSSxٳgbbb,XPYYGT*8$''_~VMMM.]r3g|%33u&L2d!ӦM2eJ5XBX[=<*ˋ_Ү%y <L al1az#srg:ftpp4iҤI={vܹ{mݺu˖-vvvm۶m֬Yppp===]]]-,,T*KvvvRRRrrW^xQ\\  7oȑ#tZ]J|Qx[* zFT.Y䯿0`ѣG& " N I:Ɵ?#(w}6lسg.^XVV?|P4xYYY9k֬'NL6mǎֹ.6ben}"!̍Ѫ{[ 6tvvyZ5ڼysEE6 "[VV֥KӧѣG;88ٳرcSSӨVZݽ{{2L(իQF?sAA  IR|>..N"ԫWO&Ʀ?yQP1SNɸՋ/SSSq\%ommѣ֭[DCmٲeƌU8WRVvvV lX700t'o+HˤeE읛{x"3wi0Ż]XaٱY$&k!`0Zc999 >|HMM B&r\+|B __ߦMݻ۷˖-[d  @3(hYjT9\Z.))qtt+/((1cƥK-[fPU%IJ$hO&q`11q:W,Aob0wgzݻwǎS*&L^zG3cǎeddo9{lDDDDDKKK;|cbb2zh>aaNaJZdɔ)SBCC|O˗{暚N<9<<[,ifȑx/\P(rssgΜ|aÆUVVݻ700ݻwUӁ[E9ˊ?*9TO_Joavm<oV01P @5ja([&)UR*YQU?b[{[fCaY ^V H$ܨlnnnnnn)VVV~ʕkFGG/\pĉ`U{ԣܸqC,>Ə޽ 6,_Q䢒ǿ]~yXPכ6m4i-~7Kd/_l۶VGJ_*KJ ߙG2a<7i0Yo/,yjbbjR+ejL+d> &5hukA`w:HD"ѡCpرBXVVpB\^Q0$$lٲP++K.1u߇Xb[Y/Ξ lKb Qxj+t~mR{15NP1jl'Ňqo.~G38," 7XZZ>|4%%1:1"it>Y4K7]]L[{X )*\.;&N+ERb~:=s)JR[XX|+WFjR۷{%Woٳg3JsC\k [ l1}gKH!S2B:E 13`}l,}x<-ZԾ}ULex-;xChn$cK5ݕ ȉ '`_ѳSo+++ܹWjݲ677dD,b߿y]vmժ&w.J׬Y3pc'srݻǏX0:|=}B;8 Y'Xf8 ;šQչP-HQ_z+ژ%ƍ:#S>ˇgdd<|022iiiB/ ߿aÆ~~~&&&ZLP.>}KJJJIIILLT(vvvZիW=d2YZZڰaHtQ˫WMvA0E /:t 3 /U\`Y~Lg6dU`" M@`Mqa!,N:kRR8UzhmǬ@<Ȁ{)0ZU*Ϗ9r̙͛O>SN80`trr ԩa۷o?}Ν;uiѢEr`iM1y Z_vf(""">|(Ƚ!ݓ6Ԋ}m@fWrI] ,VC7,Tr37˃N7'g(Dw}7sr?Ʌʸ}_RÇ;w{l<̙3i.--Zx1{rt] kM7n,ė/_||}yuoL%Y^-{'zYqsqͣ5٢lRuU=xWrXÇuXۇsa...;v rGQ^vڅ3ڀS <yٿېa7Jl f|\= DZZ|֭[YA7nh׮s<J8&.۶mKҽ "rתc S{.8~x<忰*++;;;@aR_L+ Eu떖Fsί^3IhUQFL̞f,bl1Z(Y/;vL(PFSSΏMHHaMj/U:mP]bZ!LԣG&H5kb1gbCFkXmUe=m,}BׂCPgj}Z7Qɓ'-ZSm3z%ɼy~- ʺ,?~cx<^XX˗/sA7ʊewGT輫VTvX$H1@B}ͽiZ{'db.iVU Zh͛oQFϟ1ZG@cffc vl2txO" %AEuǠ&I0P`߼Y'O߉9j@ rvvx}UGjV:ӀV6VZl@vm>|uua,Kˈ0V^^; ,"3Ur r  kM 2k:uϟo8ɥK 0ց)$ %K ŵjժK.W\xFh<|͉B}B:dlmRĢbkzTyђ<19̙CVmEEEݻw244tԨQd#G$&& Uj7nܹse˖ Ч*.xl̘1w{.`цz0Qv75 }ke9 {5!>djR!0 6iLbTjj<#B hꬺ^sQᏵhd?LtR#$v;cүp "=EV0Vsd@K5[ޒUZw!CX{r`ZAZ:f!WLFTLֺuk _255_c@%K>|#""ѣG8m:zŋ]\\>|pAr42v>}bjv'#QT$aj^0 )`HU ;mS*ZEIjSZ @ev B)or{=b|LҴ:rXu?촐@KdL+GɮBUg2ļǜ$(b6V5D gY-ۈaXxxxeeeAAƍ׬Yyҥ{:u* ::Z.'6M.eee%%%>PٙjRᄡQ~Q0浞0 F$h5gtE|rrJ@ * FOzP: H`i1:tΕJ[3^OV9gKLQ|u+++nY@ԩT*nzu Oi\~}gF D7|ZO˦VZ-2PN#fU?IIU(#VGdA@S BVsy !]X?I\}Lp]P k]rbQ@ `Y~d>]3 *>>>͛7zTw˗ǎ۽{'֬YcbbT*b1@T*311x8qywX88---(88XV-.R$W) I%T kAZsiN 'k:,PEZZ*a ݢؑt.DFD*hf&Ep}Q 75{^Yz1(((44ҥK"HuRp󲲲}eggf͚x={TTT>}իWss-[_%%%޽;wN_xJ޽;AmcvZ hQz  sy\:N;k|u_qRlscTBh[ǡW>(TjhN@W&!lԩ?>w'~xM6};vѣJeϞ=!7n<~a"## ްaC׮]}}}Q1uس߰0\` jS"icOAm jjy:9&H&Y "KC!@:)Fc&쵱PJj8T}qUђ-&*+(Nk:󉭛bo nn.annnx Ůo'٠Vsssmll sӂ=… vza޽G[!ѻ08h~7`ܶ @x<@*HGn0j(_L]\&-C]Do>7Z:OPv,Z;> BDt&+ӛno&rcɉEˎ?үDq03g憅-^n#6DBWTjٲ#G-Zt }(NNN?ׯ7nܘ1rN:EDD( ݳgϾ[%tw~O/7^KEY@ ݢQh@PcKa4fCXM1ʨ?ҥeJg|&gAYW:QTbGW꡵ad8OE|x322O>=f̘ "cX%u85]>Vk/Z :tС?ۏ=z̘1M4A <Ի2,11͛xÇ3g$b9r X СÉ',,,yzQ@u{;@ <0{o!3ݭῳ8=&c66pF'(CqU3rrrZd̙3o߾oݻwo߾Gmڴ'}JT/_yߏ͚5۲eСCh; {xx $''}:::Zŋ~D"ԨBZ=@m+cÀPn]:s[YXZ>{xq)D%x5bBaB+PKbnCdᅓZCTKΉbq,  y*2`m˗/8zYjjjqq11`iihnnneeecc[[[d򊊊rLVZZZTTgfz7.?ϟp4IپU,"Fk8q+:"0PQVͫ "A][XሣF9TL"!Ĉk#(N1 􊝱b8C]V{P*%%%YYY>|HII)//L&fbaaѸq㠠zyzz:88XXXS)++IKK7o޿{+7n8r4 d&5-$7@/&U@.dVM A &{1t":#.BTrFŐ|i%@p$csSƅgggggMRu2L`Q;z-$!ʀ6lذo߾ƍB J+p28(꭪1RX^VQS cAJou;zt&;J9 -@%uBJ!uֆpqO# Ts)`!Eg=|> 45yLy9H3ʉ_!n2p]@:RG pP_@æ/(ʸqf;F?ڧ7'AUU*!RL!GzI E\ԷxZA|Hp *:GaBLQخE?V III1=ƺsvkN;enMbuP 'PWC,?ʓ<;A+O Ӂ̄NH;K"C V;o4BoWl6eclL)0 S9l~ӝB̦ 0j @@TM:gvX7d80y.mD0˒+j 4LiL\.m$;K.1Sm1`g'YFS ?˔1}hPDŽLpAMٵ͋{ -SM`U` :4TG- FN 4qM4CiRS }F䩨?0OD~JU195,hb,$l-ž튫D^:Y^i;V^{ux(Gõ"W?~ܥs6m;xʳ\ryH rviayif'ի%Ǧ熝0zG4 3^oAF5Q׿J4;m?~GI4hĴ&<1 kV, *+b=>O\rԩ~qL0(מг9n1)2CXuȑ%$&SS of1k^%͎Lf\\-,\-F57ly`((/KJJ=z(q4ZI`6jԸcǎtsNn'FpH57TL7-.m%:@͙uLQJ(NZ,y%NA`^xLmn3QF@I6?5WXQs+4%$g )]B78yP[YGŧONOO9rիW߾}kgg7j(gggʽ{~w<z1ݭ%^ EIB,WRCdt\- ES x[L8>(Gby[5s.kWےJ&&&;w믿ryjjjFrrrÉ@2ӧ8JHLJeEEʕ++=6_x1NkJ &OL_%"j;!qA004iW'`0h487FBHh!`7)wͯ#)ݻ{}ޞۊ T*uxxj:uVyC*[2a3~N{U|_ȼTawMӴm|~~nOOO\]FM۵,^ H$V5Jbxtt4L4MT*ac+ j!F(KxZXȂ(oP~853hRu}jpC<ȏUA<I( ۶///|>u=___?>>ڶf|>N_^^noorX8iL&>>>TUt:F! ^W.Q|B3> _PFVlmm-fcF!*`tg&b2:!J X#AI!BF+0 csssQ~E6,aH%D{FYc~NoWX;.؂Bh5p+elbuE>J\x27h̐. H0{HN-BJBH*+ό!=]| J}JP܀.,&!sq@f_yX/ bVmfGQْD3[@p|' *#x2@+d|O 2#w"Tr==H \V9҄⷇qKAhuDmMO]c ?IENDB`rbnacl-5.0.0/images/logo.png0000644000004100000410000005634013120044713015723 0ustar www-datawww-dataPNG  IHDRR=J pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FR IDATxwUz9KMAZ.T~MޫX@1)" UEDAPID@BO$c1';̚y#g̬wVK4E$I$hC I$II$I$I&͒$I$4K$Id,I$II$I$I&͒$I$4K$Id,I$II$I$I&͒$I$4K$I$fI$IL%I$I2i$I$ɤY$I\no&HCo,'Ӈ7Z)I$IHV'wQ x¤Y$I%8a&Z$I$|شq~x)`=͒$In4m Y 6 $IT.i^0#Ԑ=͒$In4k¼-pn$I$Jzšٍ^Y$Iѭ毚07iI$IR tW6S{%I$)i Vb$I$I\{_ mhĞfI$IF{?i<,XI$I"ɞ]sM[4K$IR4:)L$IT@-sޒN$IN ^lˁó%I$)"ioܖmJ$I"nOBV;ʶAXHm=͒$Iv{e;f$I$EՁ9K cG%M I$IC;=͇0wKJ$I"NO<ņN5 $Ivz3$ITMLC13J$I*4 algXPH$IRR~$OX$I$ɤy8 |X\.~7^$ITny np10bopx$I$Ec{;?pebyenZI$IREi:vWv! zE9[$I:Υ+r~;gx<0x܏Z$IY%$IdҜ6sVIva$I3iXLx>y>;iٹIR\ `($IIs^ Y9ܙÙVI50 PH$JXt}Uv[*jh WB$4@aOLɎOK$I& M^Ѕ^}_ Id+:WdyB $I& f[%$iHo. ITK~/F/!#Y8x;a{Ib7 xag u uw=<#=߳ ޤ9KsF^~j5;$/%IXxsy9aAI[Nl߀;[!;Isa~ .a[\/KR;,R!IY}^ l֎m}o5-J?觬5L xs=GOfBF Oب1!^2X0d+`φ?{6[XoFIv*iBԂ̀# ;lo8X'|з=׆I󓄱3p벽.y.\gNveoC`/C豮5e~7~iI,qj($Iôa;ռ}s¶O&&]N0aVsT{ |8 _nҝMK^² =&jfG[S9Ӱr?_flƊ$ &swԄsV&U{=R LK66$R0$SBXNºbX4K]!Y)LN˹qгs`wC!I:w5.6Y8چĤYնay)|,uEE$,\"IM _iuB'] Io=awIKv4TY pa(; G46hLU;פ4W'<6f%TEW'tvH&R> ! T*[joi($^62i=pcl^ \KRm\ | IҰȁؖ0yCC!Ip+pIT'pFT;7I*[p20 pȤY*8Kp .'Ie\J4Kgt~1 4KHTo,IQn-e,UQ)dR9FXb}C!fz>A*p$`C`UC!f« T*G2 T*eC!4V.Ja#C!ʇɆA I>n(`!P l PH2x8PHR.FYON}a`0x6{,m{kk/^ lb2i)T6_0 I8WU  x x :ifl¥ <`(R:_zIR73*0? \ ~+? \}mC豖Iu PHs4C!I%= oYqXu>oֵ4x/a}X;M 56/ZoMzH pN8_l8$c%;灋"LEYΑY"&*m<=CG)lll^|%+`%-KgKw0D/BwY9Bt`K쳱24bIHȯN`rx0 >ڙj-r!ɪyaV.yj_@~VwX=MUDO LI?YIu>m$ρ $䥄yF~$l=`Q9ˀ4 } L}a#, %! "T/c C׬Rœ^¼XmMUyiyz a_#:[BR =&GX5V?vN",Uw >M5iV-KO^zSՒJm#BBV+c0aHۀXTY a2iV0fN郖Tz[3 J{n-uaEӐ>2aҬ'7zyW$ I*V8HwYL688ۣäYN!lwpMnvӑTFՄ!ے`5^-gYQ#0iVgO햄T;g($ ~Wǀ I8p_A{6Kp)Ts} ӗvSOG }0iVuGoȖp F؃bC!'K ?C؇XOM CFK@}M%7"9[prR`20iV5},o7R%S0$a˓aZ, JzCaҬIK@wk\R=Q¼.Zg^Xa(LU=gey^lm N<,B=|8P4BXLNF_$^|4XLX!ۄ97iVclإʛ|0HoQKwW[2i(>ygZ"paT1o<&Y2iqMڐK5X "Pܶ}8Ir>6\ӁIa_"L|ݜ61R힁ZT9x?a>d,>yY'ц]sp:pTR6,で- 4K9Kʻs>F^U } YhBOd,䞜!ji | ^SA&RxϖkJBR _# L=6RKXBR6*G[ 2i!= IO,?d,@*yL`KFR+؏d,c! qp/pW$~`/`Y]d$FeٝA\۫=o 0*DuP|Ir<"i3I8O`~*Yn}Ya= M3R}k-":-"Eh^':`W`g-VMzj¶Dډr#o?#,(4KzoǛ󆽫F;u}is^;+ u*d ˯YD&ߒ%yc9xgt`EҶ7tz9͊3zq|W2Ia.55 JaF .TFwzGv^G-a ,H1ۣ&n0}gtn%ّ>nw^ZI?MWl<: FVٲ:aێ+R8!5,Rr?RFppp^(ԾC`s/!(g/$fCȒqQ9v1a/$K~ -G)L4y.j/zEM_',z9B^{elx;WbC/4+N_VW%.rщ~JxQ6pD,n ,[3j[ a LkGB/o,ҕzC|L_Nك"VykUil1 `)~WƴߪYRţ: au*{_Av/$l(ɤY%^/p ,퀬w fj`v |?Sk.G[0qwGmڐ P7%LxhE.4+k]zF\fp:Mf{ޘK-] Y6aWX2KbDe?0jݬ·ȜI|/u6~EX]Z ɚWdϚ t*l%fő|xJ`12;cxlZ-=[Z<Bh7<S>^8l_1]5[2iVۀ9X+IV`oק! R&윶{*&og(NjeҜ{%f|G V)tNKIKee688hu;9#,ţ",X y"^*'4+k> xw8͒Yi.$FbV6M"g񈰟8c( }^yuv*H&5<_1pJ[BC5N1+5ku2:9FXpM^KxXCbWdҬ'cR8( H DX4w߶VjG\4SDb,ac.&#{~$4 -MR8<Y-Ĺhq #jˆA' dҬpaP6P# D3O ض"O7IQq>lJ8a| ,8,8ӆ]ZsUU99jfsyZ<9m6vw_N$>@Z¾۱ETjVF/7f{'pa3Y}Xz{Xx9鄼6aLU#O$!S<$; >#s6 }+f]} `*Yq bz̷ /~bZw#?E*?^jВr>#A8<[U!Rad -8>ZHWK-b}QҗUyȞoxJ3;,n֒ [aubˡY~%$fpORvg%LB <7÷?aLSľĵ*yAO81Gx=F2tRwpc,ɤY*ƣMp >^HB=p*BDq գ4 8ϏVfdrBCl#%Ҝ7p8JVI&R=Z .DXci1[5op# `w'z$ y.}|.{!̧$f^ĕѭ@oK"v +)|R*7V^9\%@#:o+sJڍJVL(I&ͪ/%(ԅH=Iht |1SͥsppdLk["8 =G۲U+*\{C2iVE<%B_| |k* /_bJ<6x_ =}z0|NIsaLU~g;%aIk h$C4,r0GuiD~MFlL^w(SPB&͒IJ6`JiLJt8AfQ)] Y5pmxGc *O+da/܍-B%ǁJ;X ivԊdҬy0'l\Ӝy^YOcI 9\+? /0r. I|k^iX2iVfGc8-kں{F⼈0NwZJ;'#;WkZV)ZdڏuEVxr5 x[A^̞CXG+/4+T``NHB/tS|Ӱd`Jd70b7Pܜo7U4ô-;z)K&*Σ,86+=UsWQ<탬^0 8=s:S70az8Ť97[k{9K&*Ɠ |;qE̲EWfu@XeLIX,xn[!/ߢE[:x|̿S̨\X{ŬF/mɤ9,O64pcM?X'(fnPumOۊeE}Y s7I%ΫYZ{+^fC^i]guL,Axn/bV$$6!} db.0k%-ۀkgtínI%7|J(Kp0IBi# `[gX!vjfA}+UN&2R6CLaPBⰎ+m]蜶xVjya4: ɤY$axg <5RX) dZˆikZ`SgHEqaj lpaɤY W-4okq![<$OonɤYZ>q \Zi!zKL DrwF|ߞoF~ּA&ҊU]N݆1t ^{ztFtN$L򎉽<[߼cHx'4K0D蕬7~&߆HBҒ bf r| =S=c6ˤYZ!iOc/`ssr'zx"^tmi G[pL(vϢ0imr>޿ !%},y1YYUl-^E!fi$4؎ E:.X"6*4?h5LK q]h( | ; G mjQȤYZ1q'q,ajݰc.&͊W5Z"spMζsr俾&jo~{9xf8dvD:Ⱊ>c؋Wuy+`ې>LX 0EOäY8 sX8};b5NR x[M]H0c S8eu|>9Ms)pt4KɆN_5GS̛jꂻ}G Ek-=5hٻފs":_ԧ:#.cҬh"8cSx%Ҷm~h kK 87]G‚~yDr {{ID˥-n790sSaҬ֟Ep*O9k֏V ەV3um4]W&ԡBdRFj;G6 Z5lҬ ׹Hoeq)\ :V1&ª ELXϣʎvؗ_GvNetQ{uuyfI5pC|=q-Q7j u-&,!BnO|Ւ%-AV~|;gnU$:`Ҭ&WeH>4jބE*' IĻ :Cf .?o(:^z SWz>`B!8U/~G`NgҬp{ܴU4hT)a8SnR.K E]PqWΦz~8t  +}jhф%z I* E PpV SSya :gV*a&wnbwًu CxcauvՐv%O&ͪ\| qzfvΨo:W?ߡ*OwfY * [3g@wR"Ca׎5,$,JaҬ*p}i gҰ ƙV%EL|ig|seS F܋Bc8Bp4aGvI, Cf|^ kZ*m99 72&̇|%qD|0t̅C 2ʻUp;n0m.P0MgJy?ȪkҬ\Gp*;F4UeKS8{)Bq{NYLX6$^U a^GB]w𱒜/ {W9ׅIj8Cqtt]Q˰Z Ep{0TɄeԾ,F [nMB⇬:9xW,y3xF ;XEMc#8i)hۧSX7dy~LЩIܫJ%q=պoEt> C?c HG)b6eW7lOئjNNFe4iVf"cRZ9_eKC\ι?& ޝկFJTK ѨuFWbkuEہ>GTk+[! %wg.} Y%􄟝-ֲʕRUMð ?I ijC9|'aH~ 3-WB&aDa[&okIٜ4 >S 8;C04Q nN›vm$*bB8"U>@->3]IJtaoZ$]ƅiagPy7Odsaݗ $[w?+֢pB/;-ncԚk7F /??ò4ֵG2^txєL]Q݁- ׷jn`_QC1"zPd#?u(S/X6TF|:S:p;w;=ag8_C$ɥˁ LsPQS8l9)…4 764?T&fW JfdcS4$uOPwI5&ª ňHq6nóD%%lJෆC&Rxe kpHs7$n!%W=i(t'a*,=Ze,8EX,_NU5UY!kaSK ’O*#uCv}P4u-zC.pp`Ҭ.c p^ k$qMXAr~ wiPづb ݁?NXu# V+ڽu_ \g8LU-$Xg(\̚i! #tLf4qA$4ܔo!Z'7J`W4bgK/ ̫oЋԫ`2"箸 o`q1FfB k7ZоF{\_ ,uԲd eoWz>nbh%l}y:K! ڞC /(Y6i=IX !k_a,JŠϼ%I0ah9 a}g5w->%eG¶8nV$u 4Ж{c)Ș Kn F .o_ rq[‚x bˤYj9Q ?~-N"a〷$6OvOª?r޲{tph"`*R 9=.&ݎaxg:Cuz37N!LS,4mmbCm*y'7)|CL"9% <ե߹Խ4`+ oN+:ya0p%02cB _:EmoOZՁ{=58|sx~/ᰱZh77V<۰K4x[ܤYROj}!z/R;Jt1Š1ix%쾻e0K~C;-/v!xaq(f{G; S'DXVԹRdsnamwFD=v#̉ 4ZD虜OSLRF\m8:߾$,&4p{v5;DolA?C]w',hv;Oٽi.*Ŋ{?VY}2kgd ]591",N a4ţ#lg{ CX$a,$)6IWgaܦR{b5Sr!0Ig Co(b L $In7:&&$i8>&ΒX9yY btIJ@ $.IgpOG[$mn x+IRDzO$II!8KT*ceJTwlK;iL$I >$$I$I2i$I$ɤY$ITIX%y"37C,`a$Iy+b <{+ci nF<`h[vҼ6#UQz+gIpMlRJI]d{Cm̂c8:O˞ T y&{7RGLba$˓4Bۭ C+I(9 U=YJ&;07ֻG$rܧYc 2 ñT%}$㳄|>T)$4< MlOq~Ȭrys4?!;õ5sDw0 6 !Z7·͎!N~sL%Iʫ]OՌ'枪<⦕ d{\z/$IF 8N&͒r7'K*luC]z7Io%IIÊ=&Կ0\:镮eš-Id,)F$Ov&)tg8n,$I&͒"4W/v^s/5P_ݬwô-IL%EiN$J1IJc/vuO$4zfigngig~sOmq[NmRi'Rn+w VЩ!ڽYPe7kzgOqvNςσMcfgyT_o qN }*_U|L-}Z0uU(66 :ef4߅ kb ^d!M7 mwHc4 {|z  NفguzH }Be7ko ]:D9z[a[ӬVGx?Ԧw袙Mk+N}5(‚-6Ƴa5Ni]FWPߋsJ)3ut>5k^3>܉&P #m]m1<#eTY킺^U{ǞDxN oj'qc){[wgOsƱ-w'h4м7k2`ókPlu{5{{&DXv4M^4;7u_icGxM=7oz6uxvzPjf3C OflñG<Ui.]P뾔{MMNJsZy;\<-+֤yze3H#ioo'-i+2Rs:N橃4{#/fņ`xvbʋMmVΣ?6fϳ6uk>#)IsQ*_{044Kʰ&Hnn&\L51Ycr;yv^4i0F.\[szi34Ohz/۹mDŽ.4ڼڼvfڐ4Sos&Q_bEG+|6W :ĭHc Αk8{`?s,!bVʮ 6'c;XI |žS:\{J.uҩYʡ?kos鹸= y-<g54cZ4I,L;/6yPO(:ujf?/F޴.5X'4i ߬.g/\xOU^LU҇Q]+v*iyM6E?+VLFBj"ѿUl{ .$qy XE.nk"ׁF&aˬ|O+2:ٍEκZzOU^E =`՘9{v;|۠ڹYo3qvY7|4ĹkV^-f1vz&x~S'!)v= Qf vc>{H;7o6F=DyYo7ķ=}%ñG<cc$1C}]&&%xH5rĞ4oA9l6 1CN[継8vP:q#s[|f5qIG{66'ww&I|o !AI%{KZː4f6d<WsvG1yƇdl:@nq+tnykc.YGb 8s#H[_I2Isu,Isσ&E xݗl징ĹzX< >lPkN40n"雞y5[UlzY6tk8s{*RUvi  #0K CǛ88Mv0{:"rU,3Ijq9 Cb٩{yO^4KRkƳPYb٩u:!yO^mIRK,ګ`٩,4$ Lx=Ez1iТyY^Irۉ$H^/lIV\$hau;YÄGS$gKA=P}&ˮ8<=Mmqc)-4Kq7Л} eg‘^=ҽU=Z}H_/E\iMUv-;io(LUϏk@C|=EzYI$f ]TYtaUS$$/-,;c0.+ym$0{2b-I I^0_U{z7:dz$)gE&͒$I$ ْ$I$4K$Id,I$II$I$IU+T+IENDB`rbnacl-5.0.0/images/hash.png0000644000004100000410000013156613120044713015712 0ustar www-datawww-dataPNG  IHDRFziCCPICC ProfileX XwXM KZr^’s9,䜓DE%I " HA%0$(T{~uMMMOWUWW^DA0eoϿՍ; Ԁȑ)z_۷1n#R~M{DSBa _t] _9 Ob߸t{ x `{PђQ t8?,f SO`L(kS><8Ƣ!?0W&MÆ!_.4$^_i,f k_}?805ucpa6hxL pt}| pqIx09 F-7w#bl3,EcMȉrhle˦OTu s>dÿk ,00 B£hBQbo®Ĩ@~=#|%(Ғr `׿vyX7hj %hM?4!e؎K蘣Fڽfx R@( G <8 @ep 40F$` o` , C< iCF%dB^?B#P CP t uC!94-B_MA`F H*BapDC#"ILDqъF]$. _7ttft>tt%ttt= /3P3  Jn231mCO1^a| Db2ba:t.I$(#yf ss5&Eg,ӬHVkkk#&/I:auv.v]v_tzQM~#`S(NqN;xs94(\\\//pqD˳ʫě{wȗ_? hJ%V["NS8AUA?<!>!+BB/U {I"$q ;v3$ZxQHJgb1U`2AqxxS DDĐ$ZRM2LRr\VJO*NVjFUR:UG!7^J!e'RȋSK)>+J(*SP"(Y)WQVVQRS^TRR)UWeVU=P vKzz' )`+4E4}5/ji h*˵u:dJY]A]*=1 ze[ t" M ^ (72EZ明QjVU߳p(lBX[zi-lf}ؘٜڕؽ?`@ptQ1qI)֩Ǚݹy%ez̞{raݜݪ;>Od_¾G!==ɞM^h/+^?6J򚷙w ŀRHYY]{1@' `9 8si`PPЛaLaay‡"$""###W,}m1A/V4XLv\IFs|ScBXB_xą$KQ){>0sP`!d)&)OMMzHQ)G玙MKJ?q ԉ'N=3'qlFAƏSSO˝.:9u.=s917)wՙ<   c , >8xDdzO9suyg,,0h$U\\Kj82ê/_WRSsJV-6vk*Y3@Cl^-{TꚅK[-PkbʍmmC7oktHwT"*r;;;Ik]]s==w}v}?۫PGn>V}|>~֧*Oۇ4 wC0!ih Q@ϘE8k:-Oa(g'0k$ֳ-ps }BI:Qz1x.ArFjDLEl AJ*j}Guu%Y  PF&+̦',,[ݲnf{Ů5zM.M{]ݚ6_WQFttULgDD$('H=hֱ#OtK;tZ4;)&: C{>@PHQUS2ʹ1)ٕZ.>e+2W%ԟh(^XzƣC&osky}>~apmhsֳѵ`k{jrTO?y<;<7fq~mzG^pIzYGO+忰}Z7uS6[toڑFWaLg10=Yβ>csXq+,0&U-B%ӈoHJHK_9%'%o ȦFJjZ&IJkI{HM^~Aa%rK!ۇv=w:ۜZ\jTVs?GgW29;rƧԷگ-+aѐo–7"1Qlђ1{$&^M?|dD 0]*̑G[L:>tɶڌsOfgfK?RQxleqMICiK٭s=矔U\^%^yٺJDmku_m|yeu sy6dBVyݐ{T?ҠÈ3Q1q ^/ȓ)+ktL쁹7o;M-~xcG'"|^+vh}C;o#[ ?No4_w 5Ya?/O?X(pGpV'Q(*.&&."Ab2 rZ򕏨DSPWԤКmһ_nPboi|$4,ZFVN̞ (DtsƴڷskEAPsIA2!Jaj:&vQ1)9q&M_:qK=L QgzꏗH?Nΰ:zZ0>s;Cl܉3y & 'ώ?+)*{z@PXV.mV.kX֒J_SSi0nhdجߢ۪uCMBL-m;\]\ݼ=Bw`Q'}'s.)}V831~t/\_9q|:Bǻ_Q? }_lx7u -ۄ̮Avc)/q89K tpT,PP Pp"*p~l6>Mcb~˥6U8vԉtތӄ̜lƜ3yRgJ 8vA+We/77Dް)AskNKWZϞ{Z=u4Y457c;W?zm_ҿ2[ LP$X+ d Ci0cD0"сx$ +7("ZF+L8s<^ԏq**ӴH$@ ^qzgq Lfm@B69~Xt{\ua teAФp CDHdIE,ERMbNN*QXYf^M.SWAW[qKiJ.*ԊK4*5n[}.uCw5&Cy-t,=Zܱvwp:|eU{oS+|{G߯CbСǡa]Qq㱚q&}<@>8lҟjym C֧gf9* .,>UJ,8ZG[Zkĺ军m :}{J>c_y7T;972iҬޛbGU 6/a>h\@":ᚎ2YEK44`1%OX#l\Qqjf>@=µӈR?{@O͠ђqpE;W,vI>0Ng/7wO/? a~yA:'BV$FsJP15qDd4VL19yuMWJ-*;44g6uXu261F(R̲-fllݰ_turquteps[>׫"V؄ֆ"B#GcDJpLOiI58X@ڏČZsޞIȧ-X}vd{|.W_Y|CbKJ+WrzL??<6s.qB2(מo[Uo?OA~7zV.?Y%`;gggrggl+wm}@. \]hdQ"iTXtXML:com.adobe.xmp 732 275 ,t+@IDATx UՕ.Ͻܩ$6ڂ;zdzf:brғ@x ܉nG"KF>:րw*kGM7猎tnۇ޲uV+5K*T'/QM[ ?޼+߮<׭h<7ϚK^-lI WU>c%o]0Wn=w?9_3&%։&\G'g_.iPtn+/2ZEv[o>zBYc],YBN;w-YNHL[usZ-,O|G-M&[Vz gU[W= "k13m*l!oY2Z>;nY%_3&% &{d1)l7F RFѦկE)?}%_|lu_=fMiQί.g?t5k#%e@74-WquϽg UqI!FĤ8BAP?>xG#}Iq@nyqzj QSƉM\kٺ6'8{;]=.Z˔&kpFaF< גےJb7kI*yD>q kF#?c!>">%Qp|Ez[ʄ|rfu/&X;|ݡxviW`/ /~D];ODχ8Z($ Ʈ1~!PVuŨY96FyN)u4OQNgۿ#Mproϼվ9ˊ|5W1c Q3KŜ­[/g˼g}vu]'J]Ok$ /)&ǧ=)Ÿ6'Hq98{qQ#YbYkϏ(L/4~W37U}I]H)?_uEdfd\tƏt[TՈS5^|ѧ7)LJa#P{@ ~[̹BoWV ЕFG7͙4v xʦ)[8__!8@ë}Fs{M͝P+z\[k?Bt;6rvvHY늏UCxߩ/Գ X#0)Ɉ^Jߡb;ĭ9X\*L>Bh~};1VΣO_ɿSeSnB툴ou_W,)?e`%yt2rN eʏĠqx_ ;?6EApP}ok<+vjT<F C.ßNg?^k)ћ-:G?PQ}p'YIpk# KavޗSGw6DK]8CZcmV˛6mPw{G~X1o鞶֫٩fNhk1Wp M'O;`{ (f#Нug;\7#k4PoTw>MqZ;'8涢`a.IcZy5ѽ+/PC)x594#3tAYߴ'Z7UWKk[zW$*0)OFD?+7DjkE_΅?YQ{SwMx;9vVʄeI գ47(lm0i@m%F?$h-[Nw=i M薲&~>,zZ+Nʊxg[ɴ'0}n`&$~@蠠HZdňYO򕉤L1#{óXz3g_ٗڨKq$[ ׵$FzbyW2jen1Stsu_P@[3O^zvi Όow'M/L3!v)\\#O`M?y<h8$}GqY Vq.E U9KGwwJOĊokM^ÏiE_}kA_X1}܈cQ+e?i+mKXgߥkOR0A, P_j"{yΠN|67ըj ? 'c:K,O`z`=:t3'(y"/W=\*vGʠB8Q)IT滢;~yͺj󳾛ؽME>ӜeKOP(}fGrS:E㯹lW\z$? hW5pΥ'AD^ %}vƙD 퓺7m3݃5O Ug$*행^GW&]9O/8wպc 8OJ"W^.5?>?/!oKRW-kKDOeqE@w!z/^]^+m/p kZ1aҢYú"#уs d]։Kg6&v9pE@?AII?y<F̠G_=ALKzߔoLuI\ #\np@K0p§_bŚ*!w96VmFͱup.o7l8'#0H C ^v m3V=sq Lg 0Er:tAwYȕAڿ11]+%]$tlʅo^:TI{+T}Y0jN>'tv{M+Z,گ۷_V~-;.$*3_Id٤k/x&jwF#v'!spxx@ P7>NQFWE#{TWHc K~uZՁ@MTm_o,Ϝe͚讱#/Zo{{I/<6cZ1&% ];>9\SQe,)ַ+1ϼp;dܵ'+^ɞUV|;7g̤a3+i%'m1^fF`tا$ >gF`^CIIA 3#0@:LJsF`F50#0##$ >gF`^CIIA 3#0@:LJsF`F50#0##$ >gF`^CIIA 3#0@:LJsF`F50#0##$ >gF`^CIIA 3#0@:LJsF`F50#0##$ >gF`^CIIA 3#0@:LJsF`F50#0##$ >gF`^CIIA 3#0@:LJsF`F50#3ض_':-n%/[JLl]I2[udUMwodbN&OLLE*ȭU%/+0)+O0#scF` LJʓ~2#0&%F`򤸟#0@?GII?<w6cSzbkn5|z~9p#\LJ.4.0@HGm cGנ eY%on?g7t6#  Gez p}ZV@qI5oaB ٱĶY*ԳvF? 2pԑp}M)ԑQ=wK`EBE0'gءo﯏^֛g2{0݈nfp=sW~++hK(xŹ DQF|Z1O7m?T x#6LJƅSF=Xy-{W xSlӲu)A^H0qYB?$|m/ nOs`7?_#ЍT-Uc@LDa)$DK @-a{ w` &%]$ssۻc+PhpOUa2bO*`b As7 "𺺉#d!FӶܚ6bİege ݔ/^r9<x3@FI@~JTF#@lGռ>('R2"= 6Yu}0 ӴQ޶rG*fXOÝeDIIWu1e`#v;*Gnΰ SGA2# 2ʸ^PC oR5`HOSkibל?#b =(qF`"p a$ئ%2<ͺƍP AAp܈J[PyۣTO?~H^X5qLql%o4,DXf{;Ζp+0F>_`gz:&2=7F&4xx~|s_ӤgDNlnpiJ4h#sr3z.ÌcYj ;8_WF r;ޅrNF0)鷏t;xh<ۄ=VuY9.s|`p'f6pR=@[C0׋%8PPNPVsYYMQb02;`HW&li,+~̩wk`>>ȸÌ@!nbچPT[ `!#&$nU 9X# F}jEcn*ȡdM+j~m M,T2*Οٳgۯל9ryIgg0LJ3݊GC:t!wAӰ&~!AP?p@6h-1  욦HcRm>_~~0( `Zl>kH[4WdLJ2YpO Ba" ΁xoHP`I/l "f+C>!{\ ,wǍFFhm$]6 ^U vK؈=g wl{=ˮ]a &%,WmnkL( C=!=s/8xuq+_ժ#:jD~X|lۇ=}0TU)\w<{90&%N p}DBbFcF4lzXT FA,-("&R{ĕʃ.)"7G (Y6ҽǪw *1]1bV,x/o;1,.0ѽ1@o qM,2n3z -QlCr5#Ŧ ⴂ_wb"`0Ԅ~ )Dk\`%.%Xhj>Uu61'`&.~O # vڡ8ؘ6"lN\g\u#|6&#t#WݍrՌ@E)T0oY^D lօ;72` p11)q~Y@?dhy’?9P ց l TE؟OXl,Ё (4w.؏Kòc1+W՗ɀ#/X)_ϓGt'>@S=nTqe*Y>!9`! ¤} ZB]]9~A_{:b`,lѨY󘊪! x jLbbbH7aarmo;vhm Gs#R;d *VnW`ugyN2G@> <\C]L"WB4lBn%^SC t!-,0AMfRDΰb.@uW,jE^ѝݒQqgF`RrqAF"p-]0*jkDAv!iDi85 = vkK^^-@h cu#1ܪH Hͅ&%DP,5՟68oH0)HOs'ՊVzm&)C43KABl$~HfB2T xv6?b1y8kqH#!ZBM X,>e1pEEY%ر Dʀ(j:4 ]q( X 6yM4E,L5h]łH'&ӱ=,80 02v_{h=X>#dLJ2p^D 7TTAðMp'Ap㗞$x,Dv)vҫICr@Sn &qg{=4f fFT6 !Tڈd#:~dO49ޢ Dbfc}i~J#%}eC8 Fv)<;b 1@5Β`" (:'ow4nqhBAIPLS|N0Dw8,S:*0Db5G>}W.Pw3@W!zY S3Rn>*t-2)¹"_R Q*)DkL5 ."D\C jFe%H&F"C6RԄ(db4 ixw~eLJC.2='4]njm0/Q5GPCf R #"e!4grEX&+*DNWPV*b6|ke'}cظI 0@!JI=#Ћ4U >Q PM@"'I`ioCe5jC`#pHk( b $z"PDiy);uc)2H#p :@`hq Dx .m#&=1di?.5*rÑNX7EF270B !@D"DBxx> I\ %.B`hN& t08Bץ\\] %,;Ю h6Q nK|J['`O(,+D1ŭpDc-[62Y50@& JI&<#)h@ 4Dn T\dHfzW yiZ$^?tLc BB~%F,6V?6,/B:_2|2 ީ&*O'pOT۲ RM`dž \_Kmэm]ӝQ]@`.#0 pvFSQG(*ۀ8%;A9@ BrW8xQWZ, -, `@! H Q $΂re$@K _,:kH)vY5_38h0FmLB@ҽ1EހLrw< pЩ(p)_Q)@6"'3z &\ %pJBVAuc@Bph&T9V +,8v Th?dDu1Z~ۿ1"#Ы0)UqF РEjLGC"AUx"thQ |DD"e=@$TPUMw.\xUL`2I7'3"A%H&9 AC`p $5&ȦHz&xl7p_Upkuk`^CIIA 3u4.EǮyUL椒M3!)H&'ܥtB׸ Z*̀!'H1kY1%(u=Ԩm~bkBň€|'kBKL]" LX,YvDVl_  ;ݺӟw F]b{+`.ci$R]* 1JRIāsr0C 2:UCVqBЊAWH *#a.X@P!⻒i&Ti 1i(Ny$0r;ǞD*dfNɎж6p5B@+ ,+ s^Rz%/#Rӈs{@&#"1,T·D L/'{gORdb ҪjdT0isy_pNlv"zȅހ( v xL29>g %hk9 $P#JgC(8cOƽATFAX)A)F `EMځn!ި %ȄT @QIAh=z= ej.8Tanb` o}> јPvP =W$:a$9!) Q"Y ea$#cXT Kώۍ3q~+%q`N"JWV1=nWU,JFC GHXd4҉ ORw Q- ;鴇N\#A)i%AlF,*G2ZC,*֊ NRw02KB@n߮?yw~`W 0ݏǘ[`nM)V|˃y'/|$9$F8A>GfppJѫUe7P!؋Tjl7ZiCQQ7Ճr`^JH 9TNtwEANQ%v0h慰b+v:w;1rd`&%ݍ0% 1zvdĄ~W]@Ipl ҉;q "(TO'H^iY.HcAN0„+ܖRTb%72܀`JƔ8X I/Ha Gځ$ ҁWԏ=ϩiAj' V=٤B09D C7etx)R$ FԨuو$V:AK]KF#dVSz铟7?o舴)#t1LJP 껊:ְRhdU|.AJ#/K(P>]Q3rV74 s ­D (  hUJrSd衍$ 54TB($w%bP"Q:H޲R( r BˎaB?Bua?8V&^{=X0].+d0XقJ zgI 4%>., 4Bst5Dʀ wUJ¥ChpA$9pXⅦU?PP5Q!\Y+lSzbAT $[Ȋ.@*qH$!HP+ |w=)ZV6@pO[ Y&( ui_* #3LzF A_z`Л^H&fkr %!Y`BLDWb$TNDvP\^ R RBӞ| h<\BTƀ _4JUEZght)sH"$TEn#Ȉf1@W{b7h6(+d}oBueX'wP16gT@g"Y&Мܚ{4_ ÙyR!$s>y@zS~H$̋bU 80/cWHRInhPx<`A|HHA/?SzLJ˯5O!p{vaaaFu\ faFtÝ&KrP4LӒP1 @`P:&VV ^ `I{SAD4ˆ̀ۈ&WP"j"1Ԙ)4x%te@v}&}&/\p˥~N4KJ9O  e WJ!mz< uMM 2IDc=lU1YE^cTˌ˫F+Mi W̨vcG–EK\3Ă޲p1:Iʕ+/^{ʍ-,htO~}3kleH%ip #'ƍ_ùl#k7d qdG}DM[ozY^qPfhYE9&tY2E6@s N䞁/?[@ 蓇¶b/:USc1(/|D,)>)fѐCiA6!Y=Hl!XrlH.9F&lG"tvBfB@o ¦Uz#YӭnHKH˕p(JKW!) #H.LnyL!YAFrg8=j`_y}1$ aߑ=v#F|N|D'W_}&B'  @9T]ʽlX01!7"JI4`!^DG )d~AqHqӾ`!φX&)TFR* M$ЀwBiĶC,1РNoj[Q\p%:DAqi)83bXM 1T-vGԆw{AݍD"MZ-Pm߰#3ٯ'OB2vo} #N\9JDr"L{`$8C%W>xpk.76~ %Ί `G P}?CNex}uv໻eS<K^ݠ@:mY)JӷzB\48H\?JbuE K YA@!Hpq\yH f@uyd'2@NP4+ATNĄ LFH ci\$8y@yEGy/0X,QX(5-,F 7.//B9!lSo2i%29 [3RU4$>jP_` Xf7L6b F"m6pر\+&;9P8t}`'#؉/~%7,L)-\uftMybCU&97Y<%e!av{ |:VJHu+aŢXv\&%tdw |57n\!208 )` @"831 G%) `=Y:'_SNtR ´.Xk3! D- !t@%҂onp~zq9 zVW߹$)$rgǼ5R2A6}vbҬiݺfIxQ^ԃ˞{4zowu7Q |5qau_<sɼyFH+aϧ`'?Ǐ'|رc=0ژ4̖0_sČYQ%8hV5?XXcaz@XX(4@+dj@~뒖`Η,! K '(pa}n.V` -I"!ʵ,Oy|TQ+*$I!LC.ؕ '7XyµP$k a0L{0ӣz,zk[YِBmvBʌR.&Fc0\DR2S24Fc X}S4ph4BO. {5jܑ=/;馛GywM,Yn{v~`*uo .;tfXAv&lԎ P'1U]mt>nf1ĆģţpL<;Mgɳ5ix]Փ,5}x~K].sP :QO^MݲA<*,؀Em&Xr@JX.]",O4Ķ ⾹iw;O'~O$\8-bʃ|qB9ѣ=GKueJٹF[%@`X C-*^doVآBU!W( b9YJ^ `S@YT jH.q4IPHKэX4Ff H1D;(l^ ;}*2_ьTxGrz@:4aj2,DbFc,JDm@ j~#ڃj@(Æ4U՜;Y|&ɘ1c~^c' ESO(~;~l]C>e΃tUm<{`㆒矏12y}..dLl'>Ӈs ʄܜf3bCD%a1R?+۴C? Vwo 3}hz2Sοv?j!OqP+HxPI#~:~Cü`}Fr}ƌW,tKրq̰"] -`nrEp%&r"I99g4AS**=<_SU7NT8hHN91kܓE$>dUxuqoFBNHcYQ=ؤe.`AKr$eA t-bPuD | ` (QDS캰QfMQ.q7)J^dFFʦqN+Xr-?e[>,ƂAfmUx66o֖ﻷg̾ꠄ>t⬾5%7w.i61%zvoꑣ @${O;x"Dv[nMr* U:bYL/a,{v<.xaKp̸e\ϟxVXۛ۝'͏g޴Kмδ[lU 1URA1yBv# MΓ/HDg,;v]h6F0⒐ACN@"*a%+t8:Hi>dļ9yVEրW,Ѐ$d-m{~ a Cds7`'4ub)4iÂ}&}pA%DT Ft L%pAݠ́%,R-pnjO K. X)X@)QVK/)ǯIWLұ"P*&s|/#KTN$HEA)>$+UEŊbJtA&ӈ3M (&T!@B AG9̐XB60H[bэkmA` x , =с,mF<:77,;١&va8 @neho _e]ST>Fys/V,M=W晩0;snKLb֯CSN=X6DOS$X"'7,YW|A&i_y_̯(\VuwpY;uuf23Eψx(xb)J[G`f#vs%ETE΂"S&d2NDk)l[' u7'2%!^\+.mJŽSkȺW/@O,7^KGPô?'\{;*uoOsGu3ͅsL#{bA29 IЦ 3%ߏ=o^2[ːd'\scٹLvA@L ObY_Ɋ%D@vM%1jp\I *&u!B#_eVƣpGB(Lc-,@-*|7d, }C)9 PPw?$;9rcavL 1r|95﹪9nP׮<9_Ssp JL|ɕ Ԏ_Mqxtɛ0KcG߳e+1]CqY?0+BL*o'N'*Hh^2B#qROLNmT(8)Fd6(j(|IQ0NOSMCcǦ#q g&+u*=/\X_aQvcy'E}#k}Nde4~LsS.{*- dĉNVXq̙r]0 i !K`N'*$:ɛy% yY0[ze@>_4y;e #styC$.'D2 -KejrJ6&H s:J9~x!AXtR A^T-"p,)h (HJPТdB>Q]Hzu5 ~Oٳg#C=(܉~gLlS j??J Lr.Q&p+wĕy78{lf},p,Q|WbI*Ӭ@]tRcQcIdU[xB,u65uL[oN;(ĉ#lVo3$΋ᐸ-EJ^P՛=UEڟ3Q"[m0SL"Wrci99iӎ 3Qۉ#Eǰa_LNrƴN"fE\BASnM(M ES/M ($w3-#27e6JS( ֲ"1(??0b82 s3d*1g>|Xb"ǀă 80S,A 5aa%:>RbMbj RwYE 8BR5)X-u*$LjBiryp3A(z/ -i,(.6 DW[ 7P "x i %&GG ?SBnG_F^U,F<Ǧh6)t)QgU vل=p!pxxw+'V{f䴞{Oњ[tbl& c!1LtCbKq Jjq,$8 |xpx۟3ƚiO:OcXcZ=O0$iLN^?hj ACJ*B"aB 1 M1K@rG9)@\Q|-W ;T[W_e~/(Ocƌ%D0MǽHHd@E^$sB]XHafPzei7:&D5fCL74 \,6Q(+emCKB[CƀG*\wS=na14JbSxe[$OxP#փ[((`$qsaK5\zdR'h7"~;4 Q&L9^~Z\ ŝ ]Ι3Ԍ?`ڇUl\^N?UI&A۳@\(`FFrI. d}S>A $4 H4Pp#uALlH> =v {9CKh>(v<|C!;(Oyܒ?<˲A<ea;ŠLzk.a\a$(FƇRwK c[DmrGh#b”F1x2R ;B6ڤXC/V_ܩ֩q|1g#.nDb %kw\'r 7NKa _l<.|?S`eAI,( %cS)9|*1ƧO̼4>c>MҿrbœaiT9@J/_ޑ_$)qEZz퓥9W'/5!zU*bsn*:GXٞ;Gǒ8+[o׷D.y.J;+9U8GJwzH${8SI71vDŽ^/6=,Zz''"8|PB[^=$^D37+,^y,Q=q0_?1k# Y*Z\紧HIHNxMgǞm$$i~ŒZ]#P⮇H5Y6'8rbSjF\<>37rxP2_+g\i;7̴ir$vQD4XQhjAHU r[ 1oiv\Ob[f>o BS'9f࠵-&*^$N@eM/L$_ QxȲnJ5GHV !5Q ?CQ W9`(*(3 QP8쳃V#F-^c W(yR 2L!n\>M{PݚMs [ H!<d?)W6$t;w}wFqȕ|?m'?_D ds^|_xˁ1(ޔ?SG-;K.Kֹ<"'}ߐZzm~;r%jءf񦖉-$|4mZvF<@ēYh!JI:7gX|(mrHJj^)'o!c(5ݎ3u"uHjX R8"-.A:m0mͺTd IqTG@bpKTuyңzZ<r_0R,2\ȩPKHr^zwu& |%"<䗼K=Ɲk3'lp[sSAo[)>l@KZ@8L콧lO^7/`;ˏ뚟~\>N~o0bfv&qZ=H/y62Dy$*$:Hq I! r^8A!z446gW3U)6b0.PP FڡB@ DPXp8H0 p/D ^_oiTlE.6X*dR.1J@N!ڡs=<{W @<䆐4Hm `$RiAHDQ!d)ɲ\&-;eTXJ%2**/8r""ٖTEE$m^z$A33s _w߽` V HP*RXՂJY/Et\%.y=EvM)'>{: cwn˦$承{2cǦ˗9pT(_{fG8>G_;16^3?W~r>hsC#[۾p;mӗq;\L{-NrIq!L1ޓdjE_>s~k/Y[ך/>j Ͽ6}Ύpq3^;~Asqsjs#/p7Ẃ Yş,kG'}yvՏ7_x9gFT1~l<27r^y10?5֜puV|^P9-6ժHu*'ho6V+\ӻ8,d> bBL$.#lh&$Çh2` VR3 GD򑟒3XYHjB`L['T,04UΏL(gĠ#u‘tKK0`K0%0 XUొA0 ̉EK\cK x'i:gg=|\Kx.?Nsi=ɍF&p0(=Ѽq#+:͈fH $lGE]_؟ Q~%9Cm'LJmt\??љ l@5`./[zcxy윘Lt:YfV_ؙ|mSGwڵCRb|SG?l'/ku9y⇗( x^H򼸆]_3|_$EsYyEȧ#;8df'/M9~Ow<4.)YwT ҮuvϞk(Mc̑ywG̯`3c:z?`cGiG/V#y%2r'.W9t佅DYpKW,ksf oΨMph&6p^ (ۉj@!e0I;IG@jeVK@KRrʝVqu`I\H1$AHHWvr`Dr/@lBH LeuRC`d&7ZS{` 35L: :qSHL_!e8+Rha\?E5?"K1;Ƃ EihlqiCwZM-"f`$KHwʏvd=ɭm~8:~*d3ϓ'wUosG?}_w0okr>Őm|t-S=zꂑ]'G kc>׹G)krūԌ_N*B!(}rm y_)W+4qy+@b'hO8E`=Hm)n^OHN}_twA~ ]K@}3]_8Pu "n:y=No`cp}+`.~|G>eNkS?kBc~sURaMZl=aZWs՞:4U˕oe/};J,8N w VDRlXoXlyeLgݫqa現aSÇbTylxI2f/2㣨Lz hM$V*|F]4eTDNU66 ~3~!̚ܙfK/*;˓`2I"#5٢> r5ƆťFŷiHfzB~|V@aذ?@\crY:>-ǏtMe);"tzG/R_8<Cs3N?w}s߶ jƧ}n,,sɟ\6uK\ev4)e-#iFA]5O~Z|>n7cO-ouSOÓ<7W̄;PZۦl~vk `S-ȶQ;?$7?iVʸ8{'և"q(2JbvD@J`Wɵd2٧P0F.vyabaRua-O׬iE`]ֱI"9*{S(2yR-%((kk(’S0R_?YPFD3)L_TtA|{e/97Dߗ3ə3Ɉ$G/9Gȣarܞ R=) 5)ԗ͆=s94le%zTn <iܨ^S׉[^ӭn!Up,LSYZ6d ;lLF邋 (8pHr#0V8awL@,ӐDd[޲(}n#gJ/IZYMHD9qPuٴnPsKj±*CCB?0w4DhCEDv#JEOqk6lnt}|[onvCN\"pLqcqzu[^cZiŧ/cGrin 2?ߜ3G̚9k> }/nv$amp6N&tJQ.؉%E!C0odHoSv$bšR;NG!d:y:a$B6Nz_d9hlB VCXOm#mD>wd7+ e aA~5r~l+"6E`0i-g}𪛺Uߧ~o?ٹoa%m=}/SoF=0}]z KcvsPu̱96#_5_˫q /H(చIUDd9'{$| DBCP8 z.@ZDb E+|[X#H@1Pl2buQ$3,j҈hFR3f`92Jo8 Jv˼jSv=t%N|C{^UAc\9ydwkJS4)9f ?o}AB1| o*#D[ 0b$ *)"Bx?(X*"bɘ BL:2B 1B:< ̄oX [AW%Eza(ٙ "V›]SYdEVkcGȎe9?! q8#0L7p_qUxwE`aط)]Yw87wYX9}^$%#F}s,:2[C`χS6fTRZ\scڼ:zq ΄p\L$ڡUq8'خf&2MېwYЏLnz!BaB i79J c;@ %5٬i8e0)1q(< *sRD=a01@ҁpVC4 5O|ah;>DMP%%.08X~84&F6#Sx >!.:[koI.a_0W,Y5Xi$@cgb`H>l&IB^AAdnFˋ0Ēq5c*PDf_9 XRD.Xڶ7}}kF$Vզ(E[Rz_fRP#&_ eg*xg`[ٙ"1E.@`"2B(`"d7`6WpI@"<g9INXD!28wQtt .Td(2;a5ugX7+b I^˹;Үy-|)K^WVzbc GDpD3tX*D4H=_jOٳ2J#Ͽ Ǟ7hL/-!I]񄵓#iX╂VE&=BW$U`~?Tlp}ڸ9[QE`PRT뺊@/"hډyxYM l1.B4uoل>)h=d]Iv| Hzby2eUE2.*kYe)P\[\\FiАԁ\ )}sqsW.nys", JJw]UM(o+=Ģ%3!"pG,U ZdjYEF]-tw:qjEM%j U0-QX."[%%qIBαg\cI€$݈#^1%OCaq7PF'n2_I~t#2D MFG:Pr&hn,(,+yZ=W}MЯK_EPRӏGSvuhH[VY3Lzy?0Sckkh!DM\E@&Y,҄UX\T&nh4,ga;L$lF2`rK 5PI&nDuD"n(DxKu^iO">X9+P0:Gx950xHy!8f$ <ť[.o:p)ђ:2QcN =ЃIP(r\}Bz,?xN*"8bٙmxhMuIDAT0dOZmDm!CD!6eIƒ Α8DT't!CI8<و $1d>aJڈGVKѠ0jC24 !TWXܵŁKWQw%%,:" 2wx{te39 1Ұ>=]jPI mf629ceR=N!4#rNBj \IR"gE]$P$D&K( erG RXFW$Id!lM_?`6/E`!d=1WX`ZmTS6gLMk7U{ZBtGdGXL?$D+T<`c*Ia IF°9@hT3/!auw 7<򉑝W3J"dթ$觿 əvnt [fہrC!1Q`F2;t QұAscO ˅jm92+|GCE )2BuxXQܨ)^1`Ag@GDa92/KGEDd>qi26.A!}p004F=OLD`3a`$P)֪~ȎD9Ԧ(%%Ԋ!={^d$Rts3ӳ}A?4TѐājH}S;v 4#"dN*dI3V+n-submai:q/&.l00Ѹ<ܹ}<0`HĊ"()Ylu=EرG |jYB\rI4=>Q#K$Ua b0,ЎoP=H,)!zj;dYs%o|\Uen(͵b4!6wB DCp/+D+*w}3."PR ,I>C^EϑN~wi$EIxaiG|d\SMTmb6 'I|Xg1:iEԊ\zEGW$OyoΫXHȂ<~TX:,"ЫTvnkz:a' jujQ:PE]E$t֡ XeUj2IR.Pǭ8kRCO)j# t }oP**r*"\絬W4^(+æ}BƐn B^E`Irl߽Na\d{7$3sX5ד#{wu;(*A@I*yz[F/a ~Ba:l&ǧrIIf4I*,$mSu/N"nlHNFM$jLD&7\)|Bc7%se E`E dE6ޓ[WG9NV=B J8IgmSojZ]^wS*2@@I2xH*"$xC=FQ])lz[7RD/,34#$%THD}˽41 YWTBlFv}jT3NͷҶqC9dIn[UC@Ia++_ #{XZDg_ACH !9I$n5}D†)q#Uh+%_m6 Hxx̚ٳcqZmQkݡkٳQQE`PRԊFv3ΨG_ 1+w+E"\5sRZBI_=I:V~XBI1(n5΍ũSfxYsbin%}WUUD G hS/ 8 }E>ȉs07iJč@IL>?:jemL\cYli93&K7^l:LPV$JJVc՛RRůT!6DCt M]E`)(l醄h㍨\$80N6kwi$;}̹|]TdPڽ~M\D}!!rǴ\>JVr i}[޵)TV@@II<AUlԓ'rUI!m$N;Ţz֬k|.*$Q;?Ea[m#k A Tq} _;p)UzKMRjSUU7G8PV "}ykf'NτA_l+jޯt:`_TijR bI$<)^7V( mE@X()YOXO 3$2AN;VJifR\seRY[eXozJVγ>m",9KlpjCO]tI/UV JJVʓPچMq$M$J$I3L %\bZSa//#hH@OBW؈IEF3l <%j/:",3,*@6哨爓+\Aj CIҠPpR{3T!qnHŁlŤ8 zLs&əv~k4nbޗ(|.*"+ l8w&- AQ p ח!Ir6+rGFQȥM$9ͪ)!5$ SJ#&bAFhSՍqW׻W 0AMφa3 5={$O DZf * (UB_&!L.6f`ڠX|3A"|gw\ [NI>>L?VG.o!1yWJ3DcځUE`PR늊DD.בfۍ'q#b}_04gg 6 ,%(B4)m[GXXЊ"0()Y\uVE`%"ƧF8Xa<'VI$j3Sz V0 |>7~nb[T%q{R ^BP-sq1@=PtBoLW?4+O#Ac񩳣3mfwۭe(F@Iɥq^E@PڼX rgϞksj;2aЗpgs̓cI CWG'`0I{߻sڣ(%%+o[ؙѩR__3cQH(:$Wt'$/ɻPNxF-OٳgH&Y(z"hǫ7ݲc享/Jr_!ME> A;)M6N\_}gfvnp;it>E@X()YPo@XL G^;8+JuZ*ScIv]= 1JX'6'w1qbk)@@IɲxL*"Clر_j4#/աOO$+ /o<&MW_9y"phu_~,!pbVS{/zrժxn:t'>bpQw%%,:"*@qXkfr&"(CD/%|4#V: Ylؼ#ZHoRP:JJ:fz"S\O?^nĤ1G!5HpLWτW: "(CFEM{?b=³X`"m &ɓg4*8~ۑdF=(u__^H+}p﹂RE@X()YYoRX b#R?퍃Ce?o8%63}X tZE`E!0!nJoFP#Is;ל~~wO~Oˎ3}lݦ]א3T]Qt7Yrzok~E` d<)SxdD;/ vGfwOu;0j6L; eɜv%%´/]S?rnj:n㳞|{^on(=ozxABH{겑[UA@ozQ "(F@I~z"(@ >%=(TE@PE`u#E@PA@II< DPE@X()Y_^PE@̣PAE@PՍE@PA@II< DPE@X()Y_^PE@?IH"lIENDB`rbnacl-5.0.0/images/dragons.png0000644000004100000410000004055113120044713016415 0ustar www-datawww-dataPNG  IHDRDgcA pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_F6IDATxyxW}?]gv˻[V9!$ %Z%Rf}mB.^طK)b@J%"/]g?_5#q>΃f9)%uQGuQ+QGu1h\މ뺄C!b E٤Є *hQKaBq4 /o$L#HO`X%RžRGr&rHWH P@L=+1 Q?%eL:f#c)%J"ØMvI tijRC!2#8=GhhHQʣ*P6z:ꨣZ dM+yH251`n;ʂW*(!A % <xxRPПXҲ<)D粎:K Sc{**qv]D@UBPO:xDTEUpJ%Tե0M#eۍo[Ғ^9W#dL0H:E*Lt$1=J&;kplsBXC `&lbE]&eN|b*fĈt\1i;x:ѕ_Pvd!˫S@>$uԱpo"8\3"ăXmN"u*i2W f A +H!e݂4=>j!Rx&Yap'O3K)E?Np*:%hvj_rp-"+x[{R]dڢI3 ,F|vUc%6N^oie عoNdxO?՗%)w`'g;:U1]@fSyi#m]ZxLոI,(}hsU8Iks}H3\F!X+eiQ#O0XahɈ&Q:Vt BIЗ 48bA:d0N[إh|{Yt2/Yi <)>hOMNmkACW֗%î)ۨW%4q}2AB:0OGoo1_x=(!1~cq/l>U/fx[k Ɖ0rSb)QŢ,ohŠI@=\U!I{`+1 7U (sHm1D`}z F؄]Srƞd8ziJ?x?^_F}!EQtZ*c%)p\"4 р¢ Wj|aFn7w??xDIZ(0 4l*Xx:v9~tvT J< i<1`zK4[)uhVuQǥQqUbo/6 = վߎ+&燾q?5?؟r$2`@չgp1?_S(gF-t, SI غ8c )KpI㾡qRǪ`+b(<D5 3gdEj0ih䓼m'3{4b$h 5bH%ITj44-U,))ѯˍSG/kP_?B5ELtM:길5%ke'  yGg>e[D4*99Ƅ;MUG9 Q8f0_I/rs{fE#ǘ(Y \ sMWA5!F)x]㙂ǏY )4aVCi%n)qi YWcrrV=ȁʸm(d,DDc6W+"k`n92NP^8vX=S.Ξsx=c9rQǥ`njӖ ,_}ȗ.g۔ ]SDYw]~YwdY=W㼹hk. Ogfާ7}rcaGTN6t4[B7cM,}ÃdmϤI:9>ʓI㞂X :6 hR%bb +#,XY^O5" &#,If-ڢ0tl޷4F#ģ?P54%{>:0˒*f>7L':&21E9[4K]Sw߮yfާCԼH𵭫}SY\KJ* ,]\$k]훇oy(\34}N,QDS1}RUȦEфBJQLOҩ&n;elF\ɓcI.ZŨ) Xh);Kkk 9bљLlsm EQ<Ch,=T.on<Ob $7-¶`U$H+Ƹ˗O|ydL-Zy4Ni\--;Me?1y=# uДP( >LDs~XOu=SKSPvXYPm}f;bc Εݯzr)`VιF3ݜKb;ν֩~ȷz+@)ۦڹy+-RdURTJ /a{6)fG"_.mdc4AW9xT+H,5d9Rp ϧRJc$oj4h*c ],8Bra`Jm`hs8ͺK.cY806h&dRh j91Z|_mI\`C*~7dr©EXwEc,ޞibu5ֶZ[ZeBr? ޹$hIqTj;ڿwJ)B)eUBr2)iXR<3^i{q"Y 3(*zGK:MES'7*.)GWқ.q F3޾qT9i%FBE8ڃA~zc_'16U'x.C6KQR4" 3RP(,Ko>ڟyT )D%xN,>?W76B=NlDHg!=SO9eWn3̠ -G~;nmaT#)SwPBxSks!>eC۞وh&<ڌIQm`\yʳ8,ͧڣ<4oIidmuö0Y)pՒ&dFs D"L8vW47sd hkEJr[x^ǔ2iJ"r m>{;yO>}`o֪ 55eo3-򕮫j ]G~6k˄olvj4LḨM]S\X\RʽBN D "0*JʆhA r$'#y-jd\ FUqKe$' aRSX i, !յQ-WBgSe1g'X1mEkuI.hJLH{>xg%X"DiƳAEAw !gf_15l +xԅ j ]*/D=>*bǝs!m>X#@PoC̨uis*yVeUc Q~f@.$XRN ֜Igt`tD9Xebq`s4/uH()hFMsFd4D@.%&A5AаY h:$8:F8DAa_䉱 7eQS=b:OfOd4SqOx4:.C e 7Gܣgɫڶ )mAv3!ܜ a}$t$=_:^Sm O+);=̭OOaftYX"b(C=g_lMi~+# MID -(rd*M~&lAkX&V4`UH{Ey1kd_Ȱ^$O1,pT1YQp! %DwY^OpDA6 h}F6=jlsrfpT[eZ L=~mcDJr"Zyɑ >s |{S,o>2"xdf8İ1L' QOg?ɤ)yۊvc#kG,2D0(dh3Btcx`L1ŕx:n5Ǟyhu/ל#pxco,0묂dz|.ʈ)j> i-U6X*ZEew\ |:W@f H*d2z" *gxGݓGF<5^FaE=uU93LNL7 {TJXRrpt3Jc9v3674z!T#M%LxnqnÉzeDܔS}-\=&ErB$1ؚo?iwjq{Wjݞ[}2SAK9X2&BNX!<š!t2$Q'IzB-H5\U[o=xl0X+)\Ё=?$cI?A.㰺-Lghᛧ&zb߽߶ ϧm<PbYaIɋCi1L}. *Y?}-_-*޺8Nr.b35+1F9K4>s^Y9vM0¬v{kXx\O (PuBh9I+:lnj:o 4nc7z Zi,fm7 a}ly'7e1K輪UeqYU QF_휊%yǸ pW."|+3gEYh#mAeu9[p93Lj<yI6xۺPhby%ٜ=*([?hՌK\hCƟ^yB}~"osm<]X2Chs(z%ikba6!kp$=x޸;?Mo' 7rsu|Z"? d $nN":Yf1Lf/_c|E|xM\i0K8aLHnkmXhHH3'sNLR/ؾkU/Ykq#Ul.QoY۶HBp_ض  \ ^b͡o9WL5i)Čpr[Wqs$T~cz L7GM,?Mz5t5bǾq寬hU/A8h64mrhWo-/U2'l鴸uɢ%S|mf__?(<ް߸οѹ:Bm,kk滟ww̙qaZG\m1Y.۴k)Y|4Ac,M>wbb'7-Ioji~`ry  ϹF.l&+,xoQ7D73ɗ/ e!z|g ̬tmq1+nUi_Sc[:!$FE7g!Sݙ$?8?;l3?WwN?u{IӘ{}|̞r]oWcTPzx67{j?M|oJw<yYP'+*絼O[*Ëe}j"5s>!믺[=]aJ,:H4G9e|.r6."K:+pGKO,ν+[9pe+U_ai~# yr"E>$ed-b'.2`${nw%_zoi )w\O~0h,O"TmD~I+ZئyEo_Ŕr13iThW3},xʾrЬϷ8~|!aTIe7K9Y`%S]"龘ïYݍw, 00Hgk =GB t5bYSǡ^C|~BZ>+$)kJ^[ssZS#f,ìԝ[{*_;Qؾ*">ȧu)YtCEIֱ ģqTnAgZc̔]SW=B\,\-v=N?{AݝPv.is %Hrϯ&'{8W^d.G%Tc}5&TwW5XLs>*KswcTXRxwF@|#Xa<`#CybA#xFGKvpPgsb$): M~~;6)8(XIfR(aV5 7;~ Ba4Li]_Cl^JXchXn)&B"%>L˾+0Tg. -NŶ{ͫ\moleָ;>0YBu ΖNr.0ƒ|_ò>*ǸF˚tYE`]w̓ǼiZE#?ΚqwU˵ؙ (!5O dYXM^4{!.%quXesZ'g})V:}nJOח_)VBخV63c ]ksxDC'F1-[:a&ZD 'GŭELCgKg&0-}~q JQzȡ5>!+_TEjѾ;gk,Zkc<-x!C"$[8y%ȣ@^R1q1R9YV#cz+\FV-קsU4ÅR%ų<84]TUa\qWڤwt8r"5S-4qwA,%O=3L>JAO-s}.+gyZ jt"pE6iъ|Oēss{Wc|i,~Oi?ضo^zG򅐛ϭV6?4[Jg yk{wVٖZ);Hz6ٕ<69 汩>쬑<-J6%2YV de"^jY}k/1c/gSz+ jlW5^0UkyTF_V&y2fLʒx$INFĄݟt Mj_m_McmA}|I@?n^G{@E:$(;_x56Vv.ֈ46%x`$N城ޱ@C\ƛZ 7.Ejtև_h/;^5s3k5mʫ8|h2@ !0WO*߹7Ulee[o9,Qo&"ǹ-5mUZm*uw=Zu6yRִv_zD4]6*D(n Pq3L@Qp#}WYdzG-W!(ʮ/<: ֩ sl!\G_wN -~BCe&sEf>ֻ9* - {:Seu!S:8{`'gAkU S WrN&9 Yk]j澋kŕ7W}r\uD e 4qð7^çŒdx4 )@EASZS/> $._q`0pf6?\Mhfs\Q֟I[/[d,lgc3' ϥWXVq2S)<3XU*@cǯܞ `!\N_h,^WE_ˤljPUD6:92ފGX?[;K( Rhi(=R,~h) $[K ;wQ~I3[>L8_ƀf- ڂtW\쑬G8[|7g]o3q QGszuTFuʂIhQK Us/D#ф@!@Z.V0HG1 4M* L!MCgI1͸n-kiQw-?iڿt/q}ϓ9PEɏ.;º17t?wMKGLJVtF1-u:Nοz rɗKyhyg)88k"{J0I*p,NVĉ+w_x6q t$GI৬FYgQNbs Z1ɠ9W:PNYeu_ (Ui&(v _[Q4KfbωqF%ZUjt5YiS$r(% v 'R_ *z*s{yaݳX$Rl!5mVUD$HyTUhC 9VO޲2&bh[?L<o5.ㆻr4K*ẓ:C+G$stz/AYҳ:.xWZ/%"V \(JIIhNS m~z1OM4?/䪨F:TW={>d.(R40ͪte먣ÓRj5vsqn$!xxr6k@w$C;;=xs̴92|Gs%nq3Nxz8}0r jSRYP)H(թ:'_eHf:^?gd="tR*9z&a9Lfx^˯}s\;b=PPc0GJ2R WAu\Z•*ĴYX֮9HgTuTZPg&YbEi=0 (I%D4@H#d"Tro[8 UPG RbNaCl::Util.zeros(32)).to be true end it "can be compared lexicographically to a key larger than it" do expect(described_class.new(RbNaCl::Util.zeros(32)) < key).to be true end end end rbnacl-5.0.0/spec/shared/authenticator.rb0000644000004100000410000000622613120044713020405 0ustar www-datawww-data# encoding: binary # frozen_string_literal: true RSpec.shared_examples "authenticator" do let(:key) { vector "auth_key_#{described_class.key_bytes}".to_sym } let(:message) { vector :auth_message } context ".new" do it "accepts a key" do expect { described_class.new(key) }.to_not raise_error end it "requires a key" do expect { described_class.new }.to raise_error(ArgumentError) end it "raises TypeError on a nil key" do expect { described_class.new(nil) }.to raise_error(TypeError) end it "raises ArgumentError on a key which is too long" do expect { described_class.new("\0" * described_class.key_bytes.succ) }.to raise_error(ArgumentError) end it "raises ArgumentError on a key which is too short" do expect { described_class.new("\0" * described_class.key_bytes.pred) }.to raise_error(ArgumentError) end end context ".auth" do it "produces an authenticator" do expect(described_class.auth(key, message)).to eq tag end it "raises TypeError on a nil key" do expect { described_class.auth(nil, message) }.to raise_error(TypeError) end it "raises ArgumentError on a key which is too long" do expect { described_class.auth("\0" * described_class.key_bytes.succ, message) }.to raise_error(ArgumentError) end end context ".verify" do it "verify an authenticator" do expect(described_class.verify(key, tag, message)).to eq true end it "raises TypeError on a nil key" do expect { described_class.verify(nil, tag, message) }.to raise_error(TypeError) end it "raises ArgumentError on a key which is too long" do expect { described_class.verify("\0" * described_class.key_bytes.succ, tag, message) }.to raise_error(ArgumentError) end it "fails to validate an invalid authenticator" do expect { described_class.verify(key, tag, message + "\0") }.to raise_error(RbNaCl::BadAuthenticatorError) end it "fails to validate a short authenticator" do expect { described_class.verify(key, tag[0, tag.bytesize - 2], message) }.to raise_error(RbNaCl::LengthError) end it "fails to validate a long authenticator" do expect { described_class.verify(key, tag + "\0", message) }.to raise_error(RbNaCl::LengthError) end end context "Instance methods" do let(:authenticator) { described_class.new(key) } context "#auth" do it "produces an authenticator" do expect(authenticator.auth(message)).to eq tag end end context "#verify" do it "verifies an authenticator" do expect(authenticator.verify(tag, message)).to be true end it "fails to validate an invalid authenticator" do expect { authenticator.verify(tag, message + "\0") }.to raise_error(RbNaCl::BadAuthenticatorError) end it "fails to validate a short authenticator" do expect { authenticator.verify(tag[0, tag.bytesize - 2], message) }.to raise_error(RbNaCl::LengthError) end it "fails to validate a long authenticator" do expect { authenticator.verify(tag + "\0", message) }.to raise_error(RbNaCl::LengthError) end end end end rbnacl-5.0.0/spec/shared/box.rb0000644000004100000410000000327313120044713016322 0ustar www-datawww-data# encoding: binary # frozen_string_literal: true RSpec.shared_examples "box" do let(:nonce) { vector :box_nonce } let(:invalid_nonce) { nonce[0, 12] } # too short! let(:invalid_nonce_long) { nonce + nonce } # too long! let(:message) { vector :box_message } let(:ciphertext) { vector :box_ciphertext } let(:nonce_error_regex) { /Nonce.*(Expected #{box.nonce_bytes})/ } let(:corrupt_ciphertext) { ciphertext[80] = " " } # picked at random by fair diceroll context "box" do it "encrypts a message" do expect(box.box(nonce, message)).to eq ciphertext end it "raises on a short nonce" do expect { box.box(invalid_nonce, message) }.to raise_error(RbNaCl::LengthError, nonce_error_regex) end it "raises on a long nonce" do expect { box.box(invalid_nonce_long, message) }.to raise_error(RbNaCl::LengthError, nonce_error_regex) end end context "open" do it "decrypts a message" do expect(box.open(nonce, ciphertext)).to eq message end it "raises on a truncated message to decrypt" do expect { box.open(nonce, ciphertext[0, 64]) }.to raise_error(RbNaCl::CryptoError, /Decryption failed. Ciphertext failed verification./) end it "raises on a corrupt ciphertext" do expect { box.open(nonce, corrupt_ciphertext) }.to raise_error(RbNaCl::CryptoError, /Decryption failed. Ciphertext failed verification./) end it "raises on a short nonce" do expect { box.open(invalid_nonce, message) }.to raise_error(RbNaCl::LengthError, nonce_error_regex) end it "raises on a long nonce" do expect { box.open(invalid_nonce_long, message) }.to raise_error(RbNaCl::LengthError, nonce_error_regex) end end end rbnacl-5.0.0/.travis.yml0000644000004100000410000000057213120044713015115 0ustar www-datawww-datalanguage: ruby sudo: false cache: bundler script: bundle exec rake ci bundler_args: --without development branches: only: - master rvm: - jruby-9.1.6.0 - 2.2.6 - 2.3.3 - 2.4.0 env: - LIBSODIUM_VERSION=1.0.0 # Minimum supported - LIBSODIUM_VERSION=1.0.11 # Latest released matrix: fast_finish: true notifications: irc: "irc.freenode.org#cryptosphere" rbnacl-5.0.0/lib/0000755000004100000410000000000013120044713013546 5ustar www-datawww-datarbnacl-5.0.0/lib/rbnacl/0000755000004100000410000000000013120044713015007 5ustar www-datawww-datarbnacl-5.0.0/lib/rbnacl/password_hash/0000755000004100000410000000000013120044713017654 5ustar www-datawww-datarbnacl-5.0.0/lib/rbnacl/password_hash/argon2.rb0000644000004100000410000001757413120044713021407 0ustar www-datawww-data# encoding: binary # frozen_string_literal: true module RbNaCl module PasswordHash # Since version 1.0.9, Sodium provides a password hashing scheme called # Argon2. Argon2 summarizes the state of the art in the design of memory- # hard functions. It aims at the highest memory filling rate and effective # use of multiple computing units, while still providing defense against # tradeoff attacks. It prevents ASICs from having a significant advantage # over software implementations. class Argon2 extend Sodium sodium_type :pwhash sodium_primitive :argon2i sodium_constant :ALG_ARGON2I13 sodium_constant :SALTBYTES sodium_constant :STRBYTES sodium_constant :OPSLIMIT_INTERACTIVE # 4 sodium_constant :MEMLIMIT_INTERACTIVE # 2 ** 25 (32mb) sodium_constant :OPSLIMIT_MODERATE # 6 sodium_constant :MEMLIMIT_MODERATE # 2 ** 27 (128mb) sodium_constant :OPSLIMIT_SENSITIVE # 8 sodium_constant :MEMLIMIT_SENSITIVE # 2 ** 29 (512mb) ARGON2_MIN_OUTLEN = 16 ARGON2_MAX_OUTLEN = 0xFFFFFFFF MEMLIMIT_MIN = 8192 MEMLIMIT_MAX = 4_294_967_296 OPSLIMIT_MIN = 3 OPSLIMIT_MAX = 10 sodium_function_with_return_code( :pwhash, :crypto_pwhash_argon2i, [:pointer, :ulong_long, :pointer, :ulong_long, :pointer, :ulong_long, :size_t, :int] ) sodium_function( :pwhash_str, :crypto_pwhash_argon2i_str, [:pointer, :pointer, :ulong_long, :ulong_long, :size_t] ) sodium_function( :pwhash_str_verify, :crypto_pwhash_argon2i_str_verify, [:pointer, :pointer, :ulong_long] ) ALG_DEFAULT = ALG_ARGON2I13 ARGON_ERROR_CODES = { -1 => "ARGON2_OUTPUT_PTR_NULL", -2 => "ARGON2_OUTPUT_TOO_SHORT", -3 => "ARGON2_OUTPUT_TOO_LONG", -4 => "ARGON2_PWD_TOO_SHORT", -5 => "ARGON2_PWD_TOO_LONG", -6 => "ARGON2_SALT_TOO_SHORT", -7 => "ARGON2_SALT_TOO_LONG", -8 => "ARGON2_AD_TOO_SHORT", -9 => "ARGON2_AD_TOO_LONG", -10 => "ARGON2_SECRET_TOO_SHORT", -11 => "ARGON2_SECRET_TOO_LONG", -12 => "ARGON2_TIME_TOO_SMALL", -13 => "ARGON2_TIME_TOO_LARGE", -14 => "ARGON2_MEMORY_TOO_LITTLE", -15 => "ARGON2_MEMORY_TOO_MUCH", -16 => "ARGON2_LANES_TOO_FEW", -17 => "ARGON2_LANES_TOO_MANY", -18 => "ARGON2_PWD_PTR_MISMATCH", -19 => "ARGON2_SALT_PTR_MISMATCH", -20 => "ARGON2_SECRET_PTR_MISMATCH", -21 => "ARGON2_AD_PTR_MISMATCH", -22 => "ARGON2_MEMORY_ALLOCATION_ERROR", -23 => "ARGON2_FREE_MEMORY_CBK_NULL", -24 => "ARGON2_ALLOCATE_MEMORY_CBK_NULL", -25 => "ARGON2_INCORRECT_PARAMETER", -26 => "ARGON2_INCORRECT_TYPE", -27 => "ARGON2_OUT_PTR_MISMATCH", -28 => "ARGON2_THREADS_TOO_FEW", -29 => "ARGON2_THREADS_TOO_MANY", -30 => "ARGON2_MISSING_ARGS", -31 => "ARGON2_ENCODING_FAIL", -32 => "ARGON2_DECODING_FAIL", -33 => "ARGON2_THREAD_FAIL", -34 => "ARGON2_DECODING_LENGTH_FAIL", -35 => "ARGON2_VERIFY_MISMATCH" }.freeze # Create a new Argon2 password hash object # # opslimit and memlimit may be an integer, or one of the following # symbols: # # [:interactive] Suitable for interactive online operations. This # requires 32 Mb of dedicated RAM. # [:moderate] A compromise between interactive and sensitive. This # requires 128 Mb of dedicated RAM, and takes about 0.7 # seconds on a 2.8 Ghz Core i7 CPU. # [:sensitive] For highly sensitive and non-interactive operations. This # requires 128 Mb of dedicated RAM, and takes about 0.7 # seconds on a 2.8 Ghz Core i7 CPU # # @param [Integer] opslimit the CPU cost (1..10) # @param [Integer] memlimit the memory cost (e.g. 2**24) # @param [Integer] digest_size the byte length of the resulting digest # # @return [RbNaCl::PasswordHash::Argon2] An Argon2 password hasher object def initialize(opslimit, memlimit, digest_size = nil) @opslimit = self.class.opslimit_value(opslimit) @memlimit = self.class.memlimit_value(memlimit) @digest_size = self.class.digest_size_value(digest_size) if digest_size end # Calculate an Argon2 digest for a given password and salt # # @param [String] password to be hashed # @param [String] salt to make the digest unique # # @return [String] scrypt digest of the string as raw bytes def digest(password, salt) raise ArgumentError, "digest_size is required" unless @digest_size digest = Util.zeros(@digest_size) salt = Util.check_string(salt, SALTBYTES, "salt") status = self.class.pwhash( digest, @digest_size, password, password.bytesize, salt, @opslimit, @memlimit, ALG_DEFAULT ) raise CryptoError, ARGON_ERROR_CODES[status] if status.nonzero? digest end # Calculate an Argon2 digest in the form of a crypt-style string. # The resulting string encodes the parameters and salt. # # @param [String] password to be hashed # # @return [String] argon2 digest string def digest_str(password) raise ArgumentError, "password must be a String" unless password.is_a?(String) result = Util.zeros(STRBYTES) ok = self.class.pwhash_str( result, password, password.bytesize, @opslimit, @memlimit ) raise CryptoError, "unknown error in Argon2#digest_str" unless ok result.delete("\x00") end # Compares a password with a digest string # # @param [String] password to be hashed # @param [String] digest_string to compare to # # @return [boolean] true if password matches digest_string def self.digest_str_verify(password, digest_string) raise ArgumentError, "password must be a String" unless password.is_a?(String) raise ArgumentError, "digest_string must be a String" unless digest_string.is_a?(String) pwhash_str_verify( digest_string, password, password.bytesize ) end # Clamps opslimit to an acceptable range (3..10) # # @param [Integer] opslimit value to be checked # # @raise [ArgumentError] if the value is out of range # # @return [Integer] opslimit a valid value for opslimit def self.opslimit_value(opslimit) case opslimit when :interactive then OPSLIMIT_INTERACTIVE when :moderate then OPSLIMIT_MODERATE when :sensitive then OPSLIMIT_SENSITIVE when OPSLIMIT_MIN..OPSLIMIT_MAX then opslimit.to_i else raise ArgumentError, "opslimit must be within the range 3..10" end end # Clamps memlimit between 8192 bytes and 4 TB (eg. 2**32) # # @param [Integer] memlimit, in bytes # # @raise [ArgumentError] if the value is out of range # # @return [Integer] memlimit a valid value for memlimit def self.memlimit_value(memlimit) case memlimit when :interactive then MEMLIMIT_INTERACTIVE when :moderate then MEMLIMIT_MODERATE when :sensitive then MEMLIMIT_SENSITIVE when MEMLIMIT_MIN..MEMLIMIT_MAX then memlimit.to_i else raise ArgumentError, "memlimit must be within the range 2**(13..32)" end end # Clamps digest size between 16..4294967295 # # @raise [LengthError] if the value is out of range # # @return [Integer] digest_size a valid value for digest size def self.digest_size_value(digest_size) digest_size = digest_size.to_i raise LengthError, "digest size too short" if digest_size < ARGON2_MIN_OUTLEN raise LengthError, "digest size too long" if digest_size > ARGON2_MAX_OUTLEN digest_size end end end end rbnacl-5.0.0/lib/rbnacl/password_hash/scrypt.rb0000644000004100000410000000501613120044713021527 0ustar www-datawww-data# encoding: binary # frozen_string_literal: true module RbNaCl module PasswordHash # The scrypt sequential memory hard password hashing function # # scrypt is a password hash (or password based KDF). That is to say, where # most hash functions are designed to be fast because hashing is often a # bottleneck, scrypt is slow by design, because it's trying to "strengthen" # the password by combining it with a random "salt" value then perform a # series of operation on the result which are slow enough to defeat # brute-force password cracking attempts. # # scrypt is similar to the bcrypt and pbkdf2 password hashes in that it's # designed to strengthen passwords, but includes a new design element # called "sequential memory hardness" which helps defeat attempts by # attackers to compensate for their lack of memory (since they're typically # on GPUs or FPGAs) with additional computation. class SCrypt extend Sodium sodium_type :pwhash sodium_primitive :scryptsalsa208sha256 sodium_constant :SALTBYTES sodium_function :scrypt, :crypto_pwhash_scryptsalsa208sha256, [:pointer, :ulong_long, :pointer, :ulong_long, :pointer, :ulong_long, :size_t] # Create a new SCrypt password hash object # # @param [Integer] opslimit the CPU cost (e.g. 2**20) # @param [Integer] memlimit the memory cost (e.g. 2**24) # # @return [RbNaCl::PasswordHash::SCrypt] An SCrypt password hasher object def initialize(opslimit, memlimit, digest_size = 64) # TODO: sanity check these parameters @opslimit = opslimit @memlimit = memlimit # TODO: check digest size validity # raise LengthError, "digest size too short" if @digest_size < BYTES_MIN # raise LengthError, "digest size too long" if @digest_size > BYTES_MAX @digest_size = digest_size end # Calculate an scrypt digest for a given password and salt # # @param [String] password to be hashed # @param [String] salt to make the digest unique # # @return [String] scrypt digest of the string as raw bytes def digest(password, salt) digest = Util.zeros(@digest_size) salt = Util.check_string(salt, SALTBYTES, "salt") success = self.class.scrypt(digest, @digest_size, password, password.bytesize, salt, @opslimit, @memlimit) raise CryptoError, "scrypt failed!" unless success digest end end end end rbnacl-5.0.0/lib/rbnacl/aead/0000755000004100000410000000000013120044713015701 5ustar www-datawww-datarbnacl-5.0.0/lib/rbnacl/aead/chacha20poly1305_ietf.rb0000644000004100000410000000362713120044713022033 0ustar www-datawww-data# encoding: binary # frozen_string_literal: true module RbNaCl module AEAD # This class contains wrappers for the IETF implementation of # Authenticated Encryption with Additional Data using ChaCha20-Poly1305 class ChaCha20Poly1305IETF < RbNaCl::AEAD::Base extend Sodium if Sodium::Version.supported_version?("1.0.9") sodium_type :aead sodium_primitive :chacha20poly1305_ietf sodium_constant :KEYBYTES sodium_constant :NPUBBYTES sodium_constant :ABYTES sodium_function :aead_chacha20poly1305_ietf_encrypt, :crypto_aead_chacha20poly1305_ietf_encrypt, [:pointer, :pointer, :pointer, :ulong_long, :pointer, :ulong_long, :pointer, :pointer, :pointer] sodium_function :aead_chacha20poly1305_ietf_decrypt, :crypto_aead_chacha20poly1305_ietf_decrypt, [:pointer, :pointer, :pointer, :pointer, :ulong_long, :pointer, :ulong_long, :pointer, :pointer] private def do_encrypt(ciphertext, ciphertext_len, nonce, message, additional_data) self.class.aead_chacha20poly1305_ietf_encrypt(ciphertext, ciphertext_len, message, data_len(message), additional_data, data_len(additional_data), nil, nonce, @key) end def do_decrypt(message, message_len, nonce, ciphertext, additional_data) self.class.aead_chacha20poly1305_ietf_decrypt(message, message_len, nil, ciphertext, data_len(ciphertext), additional_data, data_len(additional_data), nonce, @key) end end end end end rbnacl-5.0.0/lib/rbnacl/aead/chacha20poly1305_legacy.rb0000644000004100000410000000336713120044713022351 0ustar www-datawww-data# encoding: binary # frozen_string_literal: true module RbNaCl module AEAD # This class contains wrappers for the original libsodium implementation of # Authenticated Encryption with Additional Data using ChaCha20-Poly1305 class ChaCha20Poly1305Legacy < RbNaCl::AEAD::Base extend Sodium sodium_type :aead sodium_primitive :chacha20poly1305 sodium_constant :KEYBYTES sodium_constant :NPUBBYTES sodium_constant :ABYTES sodium_function :aead_chacha20poly1305_encrypt, :crypto_aead_chacha20poly1305_encrypt, [:pointer, :pointer, :pointer, :ulong_long, :pointer, :ulong_long, :pointer, :pointer, :pointer] sodium_function :aead_chacha20poly1305_decrypt, :crypto_aead_chacha20poly1305_decrypt, [:pointer, :pointer, :pointer, :pointer, :ulong_long, :pointer, :ulong_long, :pointer, :pointer] private def do_encrypt(ciphertext, ciphertext_len, nonce, message, additional_data) self.class.aead_chacha20poly1305_encrypt(ciphertext, ciphertext_len, message, data_len(message), additional_data, data_len(additional_data), nil, nonce, @key) end def do_decrypt(message, message_len, nonce, ciphertext, additional_data) self.class.aead_chacha20poly1305_decrypt(message, message_len, nil, ciphertext, data_len(ciphertext), additional_data, data_len(additional_data), nonce, @key) end end end end rbnacl-5.0.0/lib/rbnacl/aead/base.rb0000644000004100000410000001075413120044713017147 0ustar www-datawww-data# encoding: binary # frozen_string_literal: true module RbNaCl module AEAD # Abstract base class for Authenticated Encryption with Additional Data # # This construction encrypts a message, and computes an authentication # tag for the encrypted message and some optional additional data # # RbNaCl provides wrappers for both ChaCha20-Poly1305 AEAD implementations # in libsodium: the original, and the IETF version. class Base # Number of bytes in a valid key KEYBYTES = 0 # Number of bytes in a valid nonce NPUBBYTES = 0 attr_reader :key private :key # Create a new AEAD using the IETF chacha20poly1305 construction # # Sets up AEAD with a secret key for encrypting and decrypting messages. # # @param key [String] The key to encrypt and decrypt with # # @raise [RbNaCl::LengthError] on invalid keys # # @return [RbNaCl::AEAD::Chacha20Poly1305IETF] The new AEAD construct, ready to use def initialize(key) @key = Util.check_string(key, key_bytes, "Secret key") end # Encrypts and authenticates a message with additional authenticated data # # @param nonce [String] An 8-byte string containing the nonce. # @param message [String] The message to be encrypted. # @param additional_data [String] The additional authenticated data # # @raise [RbNaCl::LengthError] If the nonce is not valid # @raise [RbNaCl::CryptoError] If the ciphertext cannot be authenticated. # # @return [String] The encrypted message with the authenticator tag appended def encrypt(nonce, message, additional_data) Util.check_length(nonce, nonce_bytes, "Nonce") ciphertext_len = Util.zeros(1) ciphertext = Util.zeros(data_len(message) + tag_bytes) success = do_encrypt(ciphertext, ciphertext_len, nonce, message, additional_data) raise CryptoError, "Encryption failed" unless success ciphertext end # Decrypts and verifies an encrypted message with additional authenticated data # # @param nonce [String] An 8-byte string containing the nonce. # @param ciphertext [String] The message to be decrypted. # @param additional_data [String] The additional authenticated data # # @raise [RbNaCl::LengthError] If the nonce is not valid # @raise [RbNaCl::CryptoError] If the ciphertext cannot be authenticated. # # @return [String] The decrypted message def decrypt(nonce, ciphertext, additional_data) Util.check_length(nonce, nonce_bytes, "Nonce") message_len = Util.zeros(1) message = Util.zeros(data_len(ciphertext) - tag_bytes) success = do_decrypt(message, message_len, nonce, ciphertext, additional_data) raise CryptoError, "Decryption failed. Ciphertext failed verification." unless success message end # The crypto primitive for this aead instance # # @return [Symbol] The primitive used def primitive self.class.primitive end # The nonce bytes for the AEAD class # # @return [Integer] The number of bytes in a valid nonce def self.nonce_bytes self::NPUBBYTES end # The nonce bytes for the AEAD instance # # @return [Integer] The number of bytes in a valid nonce def nonce_bytes self.class.nonce_bytes end # The key bytes for the AEAD class # # @return [Integer] The number of bytes in a valid key def self.key_bytes self::KEYBYTES end # The key bytes for the AEAD instance # # @return [Integer] The number of bytes in a valid key def key_bytes self.class.key_bytes end # The number bytes in the tag or authenticator from this AEAD class # # @return [Integer] number of tag bytes def self.tag_bytes self::ABYTES end # The number of bytes in the tag or authenticator for this AEAD instance # # @return [Integer] number of tag bytes def tag_bytes self.class.tag_bytes end private def data_len(data) return 0 if data.nil? data.bytesize end def do_encrypt(_ciphertext, _ciphertext_len, _nonce, _message, _additional_data) raise NotImplementedError end def do_decrypt(_message, _message_len, _nonce, _ciphertext, _additional_data) raise NotImplementedError end end end end rbnacl-5.0.0/lib/rbnacl/password_hash.rb0000644000004100000410000000664413120044713020213 0ustar www-datawww-data# encoding: binary # frozen_string_literal: true module RbNaCl # Password hashing functions # # These hash functions are designed specifically for the purposes of securely # storing passwords in a way that they can be checked against a supplied # password but an attacker who obtains a hash cannot easily reverse them back # into the original password. # # Unlike normal hash functions, which are intentionally designed to hash data # as quickly as they can while remaining secure, password hashing functions # are intentionally designed to be slow so they are hard for attackers to # brute force. # # All password hashing functions take a "salt" value which should be randomly # generated on a per-password basis (using RbNaCl::Random, accept no # substitutes) # # All of them also take a CPU work factor, which increases the amount of # computation needed to produce the digest. module PasswordHash # scrypt: the original sequential memory-hard password hashing function. # # @param [String] password to be hashed # @param [String] salt to make the digest unique # @param [Integer] opslimit the CPU cost (e.g. 2**20) # @param [Integer] memlimit the memory cost (e.g. 2**24) # @param [Integer] digest_size of the output # # @raise [CryptoError] If calculating the digest fails for some reason. # # @return [String] The scrypt digest as raw bytes def self.scrypt(password, salt, opslimit, memlimit, digest_size = 64) SCrypt.new(opslimit, memlimit, digest_size).digest(password, salt) end # argon2: state of the art in the design of memory-hard hashing functions. # # @param [String] password to be hashed # @param [String] salt to make the digest unique # @param [Integer] opslimit the CPU cost (3..10) # @param [Integer] memlimit the memory cost, in bytes # @param [Integer] digest_size of the output # # @raise [CryptoError] If calculating the digest fails for some reason. # # @return [String] The argon2i digest as raw bytes def self.argon2(password, salt, opslimit, memlimit, digest_size = 64) argon2_supported? && Argon2.new(opslimit, memlimit, digest_size).digest(password, salt) end # argon2_str: crypt-style password digest # # @param [String] password to be hashed # @param [Integer] opslimit the CPU cost (3..10) # @param [Integer] memlimit the memory cost, in bytes # # @raise [CryptoError] If calculating the digest fails for some reason. # # @return [String] The argon2i digest as crypt-style string def self.argon2_str(password, opslimit = :interactive, memlimit = :interactive) argon2_supported? && Argon2.new(opslimit, memlimit).digest_str(password) end # argon2_valid?: verify crypt-style password digest # # @param [String] password to verify # @param [String] str_digest to verify # # @return [Boolean] true if digest was created using password def self.argon2_valid?(password, str_digest) argon2_supported? && Argon2.digest_str_verify(password, str_digest) end class << self protected def argon2_supported? if RbNaCl::Sodium::Version::ARGON2_SUPPORTED true else raise NotImplementedError, "argon2 requires libsodium version >= 1.0.9" \ " (currently running #{RbNaCl::Sodium::Version::STRING})" end end end end end rbnacl-5.0.0/lib/rbnacl/hash.rb0000644000004100000410000000534013120044713016261 0ustar www-datawww-data# encoding: binary # frozen_string_literal: true module RbNaCl # Cryptographic hash functions # # Cryptographic hash functions take a variable length message and compute a # fixed length string, the message digest. Even a small change in the input # data should produce a large change in the digest, and it is 'very difficult' # to create two messages with the same digest. # # A cryptographic hash can be used for checking the integrity of data, but # there is no secret involved in the hashing, so anyone can create the hash of # a given message. # # RbNaCl provides the SHA-256,SHA-512 as well as the Blake2b hash functions. module Hash # Returns the SHA-256 hash of the given data # # There's no streaming done, just pass in the data and be done with it. # # @param [#to_str] data The data, as a collection of bytes # # @raise [CryptoError] If the hashing fails for some reason. # # @return [String] The SHA-256 hash digest as raw bytes def self.sha256(data) data = data.to_str digest = Util.zeros(SHA256::BYTES) SHA256.hash_sha256(digest, data, data.bytesize) || raise(CryptoError, "Hashing failed!") digest end # Returns the SHA-512 hash of the given data # # There's no streaming done, just pass in the data and be done with it. # # @param [#to_str] data The data, as a collection of bytes # # @raise [CryptoError] If the hashing fails for some reason. # # @return [String] The SHA-512 hash digest as raw bytes def self.sha512(data) digest = Util.zeros(SHA512::BYTES) SHA512.hash_sha512(digest, data, data.bytesize) || raise(CryptoError, "Hashing failed!") digest end # Returns the Blake2b hash of the given data # # There's no streaming done, just pass in the data and be done with it. # This method returns a 64-byte hash by default. # # @param [String] data The data, as a collection of bytes # @option options [Fixnum] digest_size Size in bytes (1-64, default 64) # @option options [String] key 64-byte (or less) key for keyed mode # @option options [String] salt Provide a salt to support randomised hashing. # This is mixed into the parameters block to start the hashing. # @option options [Personal] personal Provide personalisation string to allow pinning a hash for a particular purpose. # This is mixed into the parameters block to start the hashing # # @raise [CryptoError] If the hashing fails for some reason. # # @return [String] The Blake2b hash digest as raw bytes def self.blake2b(data, options = {}) Blake2b.digest(data, options) end end end rbnacl-5.0.0/lib/rbnacl/one_time_auths/0000755000004100000410000000000013120044713020012 5ustar www-datawww-datarbnacl-5.0.0/lib/rbnacl/one_time_auths/poly1305.rb0000644000004100000410000000330513120044713021634 0ustar www-datawww-data# encoding: binary # frozen_string_literal: true module RbNaCl module OneTimeAuths # Computes an authenticator using poly1305 # # The authenticator can be used at a later time to verify the provenance of # the message by recomputing the tag over the message and then comparing it to # the provided authenticator. The class provides methods for generating # signatures and also has a constant-time implementation for checking them. # # As the name suggests, this is a **ONE TIME** authenticator. Computing an # authenticator for two messages using the same key probably gives an # attacker enough information to forge further authenticators for the same # key. # # This is a secret key authenticator, i.e. anyone who can verify signatures # can also create them. # # @see http://nacl.cr.yp.to/onetimeauth.html class Poly1305 < Auth extend Sodium sodium_type :onetimeauth sodium_primitive :poly1305 sodium_constant :BYTES sodium_constant :KEYBYTES sodium_function :onetimeauth_poly1305, :crypto_onetimeauth_poly1305, [:pointer, :pointer, :ulong_long, :pointer] sodium_function :onetimeauth_poly1305_verify, :crypto_onetimeauth_poly1305_verify, [:pointer, :pointer, :ulong_long, :pointer] private def compute_authenticator(authenticator, message) self.class.onetimeauth_poly1305(authenticator, message, message.bytesize, key) end def verify_message(authenticator, message) self.class.onetimeauth_poly1305_verify(authenticator, message, message.bytesize, key) end end end end rbnacl-5.0.0/lib/rbnacl/random.rb0000644000004100000410000000136413120044713016620 0ustar www-datawww-data# encoding: binary # frozen_string_literal: true require "thread" module RbNaCl # Functions for random number generation # # This uses the underlying source of random number generation on the OS, so # /dev/urandom on UNIX-like systems, and the MS crypto providor on windows. module Random extend Sodium @mutex = Mutex.new sodium_function :c_random_bytes, :randombytes_buf, [:pointer, :ulong_long] # Returns a string of random bytes # # @param [Integer] n number of random bytes desired # # @return [String] random bytes. def self.random_bytes(n = 32) buf = RbNaCl::Util.zeros(n) @mutex.synchronize { c_random_bytes(buf, n) } buf end end end rbnacl-5.0.0/lib/rbnacl/group_elements/0000755000004100000410000000000013120044713020037 5ustar www-datawww-datarbnacl-5.0.0/lib/rbnacl/group_elements/curve25519.rb0000644000004100000410000000633213120044713022122 0ustar www-datawww-data# encoding: binary # frozen_string_literal: true module RbNaCl module GroupElements # Points provide the interface to NaCl's Curve25519 high-speed elliptic # curve cryptography, which can be used for implementing Diffie-Hellman # and other forms of public key cryptography (e.g. RbNaCl::Box) # # Objects of the Point class represent points on Edwards curves. NaCl # defines a base point (the "standard group element") which we can # multiply by an arbitrary integer. This is how NaCl computes public # keys from private keys. class Curve25519 # NaCl's Curve25519 base point (a.k.a. standard group element), serialized as hex STANDARD_GROUP_ELEMENT = ["0900000000000000000000000000000000000000000000000000000000000000"].pack("H*").freeze # Order of the standard group STANDARD_GROUP_ORDER = 2**252 + 27_742_317_777_372_353_535_851_937_790_883_648_493 # Degenerate key (all-zeroes, results in an all-zero shared secret) DEGENERATE_KEY = ("\0" * 32).freeze include KeyComparator include Serializable extend Sodium sodium_type :scalarmult sodium_primitive :curve25519 sodium_function :scalarmult_curve25519, :crypto_scalarmult_curve25519, [:pointer, :pointer, :pointer] # Number of bytes in a scalar on this curve SCALARBYTES = 32 BYTES = 32 # Number of bytes in a scalar on this curve # Creates a new Point from the given serialization # # @param [String] point location of a group element (32-bytes) # # @return [RbNaCl::Point] the Point at this location def initialize(point) @point = point.to_str raise CryptoError, "degenerate key detected" if @point == DEGENERATE_KEY # FIXME: really should have a separate constant here for group element size # Group elements and scalars are both 32-bits, but that's for convenience Util.check_length(@point, SCALARBYTES, "group element") end # Multiply the given integer by this point # This ordering is a bit confusing because traditionally the point # would be the right-hand operand. # # @param [String] integer value to multiply with this Point (32-bytes) # # @return [RbNaCl::Point] result as a Point object def mult(integer) integer = integer.to_str Util.check_length(integer, SCALARBYTES, "integer") result = Util.zeros(SCALARBYTES) raise CryptoError, "degenerate key detected" unless self.class.scalarmult_curve25519(result, integer, @point) self.class.new(result) end # Return the point serialized as bytes # # @return [String] 32-byte string representing this point def to_bytes @point end @base_point = new(STANDARD_GROUP_ELEMENT) # NaCl's standard base point for all Curve25519 public keys # # @return [RbNaCl::Point] standard base point (a.k.a. standard group element) def self.base # TODO: better support fixed-based scalar multiplication (this glosses over native support) @base_point end class << self attr_reader :base_point end end end end rbnacl-5.0.0/lib/rbnacl/serializable.rb0000644000004100000410000000062013120044713020000 0ustar www-datawww-data# encoding: binary # frozen_string_literal: true module RbNaCl # Serialization features shared across all "key-like" classes module Serializable def to_s to_bytes end def to_str to_bytes end # Inspect this key # # @return [String] a string representing this key def inspect "#<#{self.class}:#{Util.bin2hex(to_bytes)[0, 8]}>" end end end rbnacl-5.0.0/lib/rbnacl/sodium.rb0000644000004100000410000000261213120044713016635 0ustar www-datawww-data# encoding: binary # frozen_string_literal: true require "ffi" module RbNaCl # Provides helpers for defining the libsodium bindings module Sodium def self.extended(klass) klass.extend FFI::Library if defined?(RBNACL_LIBSODIUM_GEM_LIB_PATH) klass.ffi_lib RBNACL_LIBSODIUM_GEM_LIB_PATH else klass.ffi_lib "sodium" end end def sodium_type(type = nil) return @type if type.nil? @type = type end def sodium_primitive(primitive = nil) return @primitive if primitive.nil? @primitive = primitive end def primitive sodium_primitive end def sodium_constant(constant, name = constant) fn = "crypto_#{sodium_type}_#{sodium_primitive}_#{constant.to_s.downcase}" attach_function fn, [], :size_t const_set(name, public_send(fn)) end def sodium_function(name, function, arguments) module_eval <<-eos, __FILE__, __LINE__ + 1 attach_function #{function.inspect}, #{arguments.inspect}, :int def self.#{name}(*args) ret = #{function}(*args) ret == 0 end eos end def sodium_function_with_return_code(name, function, arguments) module_eval <<-eos, __FILE__, __LINE__ + 1 attach_function #{function.inspect}, #{arguments.inspect}, :int def self.#{name}(*args) #{function}(*args) end eos end end end rbnacl-5.0.0/lib/rbnacl/signatures/0000755000004100000410000000000013120044713017173 5ustar www-datawww-datarbnacl-5.0.0/lib/rbnacl/signatures/ed25519.rb0000644000004100000410000000074413120044713020523 0ustar www-datawww-data# encoding: binary # frozen_string_literal: true module RbNaCl module Signatures # The EdDSA signature system implemented using the Ed25519 elliptic curve module Ed25519 extend Sodium sodium_type :sign sodium_primitive :ed25519 sodium_constant :SEEDBYTES sodium_constant :PUBLICKEYBYTES, :VERIFYKEYBYTES sodium_constant :SECRETKEYBYTES, :SIGNINGKEYBYTES sodium_constant :BYTES, :SIGNATUREBYTES end end end rbnacl-5.0.0/lib/rbnacl/signatures/ed25519/0000755000004100000410000000000013120044713020171 5ustar www-datawww-datarbnacl-5.0.0/lib/rbnacl/signatures/ed25519/signing_key.rb0000644000004100000410000001000113120044713023014 0ustar www-datawww-data# encoding: binary # frozen_string_literal: true module RbNaCl module Signatures module Ed25519 # Private key for producing digital signatures using the Ed25519 algorithm. # Ed25519 provides a 128-bit security level, that is to say, all known attacks # take at least 2^128 operations, providing the same security level as # AES-128, NIST P-256, and RSA-3072. # # Signing keys are produced from a 32-byte (256-bit) random seed value. # This value can be passed into the SigningKey constructor as a String # whose bytesize is 32. # # The public VerifyKey can be computed from the private 32-byte seed value # as well, eliminating the need to store a "keypair". # # SigningKey produces 64-byte (512-bit) signatures. The signatures are # deterministic: signing the same message will always produce the same # signature. This prevents "entropy failure" seen in other signature # algorithms like DSA and ECDSA, where poor random number generators can # leak enough information to recover the private key. class SigningKey include KeyComparator include Serializable extend Sodium sodium_type :sign sodium_primitive :ed25519 sodium_function :sign_ed25519, :crypto_sign_ed25519, [:pointer, :pointer, :pointer, :ulong_long, :pointer] sodium_function :sign_ed25519_seed_keypair, :crypto_sign_ed25519_seed_keypair, [:pointer, :pointer, :pointer] attr_reader :verify_key # Generate a random SigningKey # # @return [RbNaCl::SigningKey] Freshly-generated random SigningKey def self.generate new RbNaCl::Random.random_bytes(Ed25519::SEEDBYTES) end # Create a SigningKey from a seed value # # @param seed [String] Random 32-byte value (i.e. private key) # # @return [RbNaCl::SigningKey] Key which can sign messages def initialize(seed) seed = seed.to_s Util.check_length(seed, Ed25519::SEEDBYTES, "seed") pk = Util.zeros(Ed25519::VERIFYKEYBYTES) sk = Util.zeros(Ed25519::SIGNINGKEYBYTES) self.class.sign_ed25519_seed_keypair(pk, sk, seed) || raise(CryptoError, "Failed to generate a key pair") @seed = seed @signing_key = sk @verify_key = VerifyKey.new(pk) end # Sign a message using this key # # @param message [String] Message to be signed by this key # # @return [String] Signature as bytes def sign(message) buffer = Util.prepend_zeros(signature_bytes, message) buffer_len = Util.zeros(FFI::Type::LONG_LONG.size) self.class.sign_ed25519(buffer, buffer_len, message, message.bytesize, @signing_key) buffer[0, signature_bytes] end # Return the raw seed value of this key # # @return [String] seed used to create this key def to_bytes @seed end # Return the raw 64 byte value of this key # # @return [String] The signature key bytes. Left half is 32-byte # curve25519 private scalar, right half is 32-byte group element def keypair_bytes @signing_key end # The crypto primitive this SigningKey class uses for signatures # # @return [Symbol] The primitive def primitive self.class.primitive end # The size of signatures generated by the SigningKey class # # @return [Integer] The number of bytes in a signature def self.signature_bytes Ed25519::SIGNATUREBYTES end # The size of signatures generated by the SigningKey instance # # @return [Integer] The number of bytes in a signature def signature_bytes Ed25519::SIGNATUREBYTES end end end end end rbnacl-5.0.0/lib/rbnacl/signatures/ed25519/verify_key.rb0000644000004100000410000000552513120044713022701 0ustar www-datawww-data# encoding: binary # frozen_string_literal: true module RbNaCl module Signatures module Ed25519 # The public key counterpart to an Ed25519 SigningKey for producing digital # signatures. Like the name says, VerifyKeys can be used to verify that a # given digital signature is authentic. # # For more information on the Ed25519 digital signature system, please see # the SigningKey documentation. class VerifyKey include KeyComparator include Serializable extend Sodium sodium_type :sign sodium_primitive :ed25519 sodium_function :sign_ed25519_open, :crypto_sign_ed25519_open, [:pointer, :pointer, :pointer, :ulong_long, :pointer] # Create a new VerifyKey object from a public key. # # @param key [String] Ed25519 public key # # @return [RbNaCl::VerifyKey] Key which can verify messages def initialize(key) @key = key.to_str Util.check_length(@key, Ed25519::VERIFYKEYBYTES, "key") end # Verify a signature for a given message # # Raises if the signature is invalid. # # @param signature [String] Alleged signature to be checked # @param message [String] Message to be authenticated # # @raise [BadSignatureError] if the signature check fails # @raise [LengthError] if the signature is of the wrong length # # @return [Boolean] was the signature authentic? def verify(signature, message) signature = signature.to_str Util.check_length(signature, signature_bytes, "signature") sig_and_msg = signature + message buffer = Util.zeros(sig_and_msg.bytesize) buffer_len = Util.zeros(FFI::Type::LONG_LONG.size) success = self.class.sign_ed25519_open(buffer, buffer_len, sig_and_msg, sig_and_msg.bytesize, @key) raise(BadSignatureError, "signature was forged/corrupt") unless success true end # Return the raw key in byte format # # @return [String] raw key as bytes def to_bytes @key end # The crypto primitive this VerifyKey class uses for signatures # # @return [Symbol] The primitive def primitive self.class.primitive end # The size of signatures verified by the VerifyKey class # # @return [Integer] The number of bytes in a signature def self.signature_bytes Ed25519::SIGNATUREBYTES end # The size of signatures verified by the VerifyKey instance # # @return [Integer] The number of bytes in a signature def signature_bytes Ed25519::SIGNATUREBYTES end end end end end rbnacl-5.0.0/lib/rbnacl/sodium/0000755000004100000410000000000013120044713016307 5ustar www-datawww-datarbnacl-5.0.0/lib/rbnacl/sodium/version.rb0000644000004100000410000000201113120044713020313 0ustar www-datawww-data# encoding: binary # frozen_string_literal: true require "rbnacl/sodium" module RbNaCl module Sodium # libsodium version API module Version MINIMUM_LIBSODIUM_VERSION = [0, 4, 3].freeze MINIMUM_LIBSODIUM_VERSION_FOR_ARGON2 = [1, 0, 9].freeze extend Sodium attach_function :sodium_version_string, [], :string STRING = sodium_version_string MAJOR, MINOR, PATCH = STRING.split(".").map(&:to_i) INSTALLED_VERSION = [MAJOR, MINOR, PATCH].freeze case INSTALLED_VERSION <=> MINIMUM_LIBSODIUM_VERSION when -1 raise "Sorry, you need to install libsodium #{MINIMUM_LIBSODIUM_VERSION}+. You have #{Version::STRING} installed" end ARGON2_SUPPORTED = (INSTALLED_VERSION <=> MINIMUM_LIBSODIUM_VERSION_FOR_ARGON2) == -1 ? false : true # Determine if a given feature is supported based on Sodium version def self.supported_version?(version) Gem::Version.new(sodium_version_string) >= Gem::Version.new(version) end end end end rbnacl-5.0.0/lib/rbnacl/init.rb0000644000004100000410000000050713120044713016301 0ustar www-datawww-data# encoding: binary # frozen_string_literal: true module RbNaCl # Defines the libsodium init function module Init extend FFI::Library if defined?(RBNACL_LIBSODIUM_GEM_LIB_PATH) ffi_lib RBNACL_LIBSODIUM_GEM_LIB_PATH else ffi_lib "sodium" end attach_function :sodium_init, [], :int end end rbnacl-5.0.0/lib/rbnacl/hash/0000755000004100000410000000000013120044713015732 5ustar www-datawww-datarbnacl-5.0.0/lib/rbnacl/hash/blake2b.rb0000644000004100000410000001653013120044713017566 0ustar www-datawww-data# encoding: binary # frozen_string_literal: true module RbNaCl module Hash # The Blake2b hash function # # Blake2b is based on Blake, a SHA3 finalist which was snubbed in favor of # Keccak, a much slower hash function but one sufficiently different from # SHA2 to let the SHA3 judges panel sleep easy. Back in the real world, # it'd be great if we can calculate hashes quickly if possible. # # Blake2b provides for up to 64-bit digests and also supports a keyed mode # similar to HMAC class Blake2b extend Sodium sodium_type :generichash sodium_primitive :blake2b sodium_constant :BYTES_MIN sodium_constant :BYTES_MAX sodium_constant :KEYBYTES_MIN sodium_constant :KEYBYTES_MAX sodium_constant :SALTBYTES sodium_constant :PERSONALBYTES sodium_function :generichash_blake2b, :crypto_generichash_blake2b_salt_personal, [:pointer, :size_t, :pointer, :ulong_long, :pointer, :size_t, :pointer, :pointer] sodium_function :generichash_blake2b_init, :crypto_generichash_blake2b_init_salt_personal, [:pointer, :pointer, :size_t, :size_t, :pointer, :pointer] sodium_function :generichash_blake2b_update, :crypto_generichash_blake2b_update, [:pointer, :pointer, :ulong_long] sodium_function :generichash_blake2b_final, :crypto_generichash_blake2b_final, [:pointer, :pointer, :size_t] EMPTY_PERSONAL = ("\0" * PERSONALBYTES).freeze EMPTY_SALT = ("\0" * SALTBYTES).freeze # Calculate a Blake2b digest # # @param [String] message Message to be hashed # @param [Hash] options Blake2b configuration # @option opts [String] :key for Blake2b keyed mode # @option opts [Integer] :digest_size size of output digest in bytes # @option opts [String] :salt Provide a salt to support randomised hashing. # This is mixed into the parameters block to start the hashing. # @option opts [Personal] :personal Provide personalisation string to allow pinning a hash for a particular purpose. # This is mixed into the parameters block to start the hashing # # @raise [RbNaCl::LengthError] Invalid length specified for one or more options # # @return [String] Blake2b digest of the string as raw bytes def self.digest(message, options) opts = validate_opts(options) digest = Util.zeros(opts[:digest_size]) generichash_blake2b(digest, opts[:digest_size], message, message.bytesize, opts[:key], opts[:key_size], opts[:salt], opts[:personal]) || raise(CryptoError, "Hashing failed!") digest end # Validate and sanitize values for Blake2b configuration # # @param [Hash] options Blake2b configuration # @option opts [String] :key for Blake2b keyed mode # @option opts [Integer] :digest_size size of output digest in bytes # @option opts [String] :salt Provide a salt to support randomised hashing. # This is mixed into the parameters block to start the hashing. # @option opts [Personal] :personal Provide personalisation string to allow pinning a hash for a particular purpose. # This is mixed into the parameters block to start the hashing # # @raise [RbNaCl::LengthError] Invalid length specified for one or more options # # @return [Hash] opts Configuration hash with sanitized values def self.validate_opts(opts) key = opts.fetch(:key, nil) if key key_size = key.bytesize raise LengthError, "key too short" if key_size < KEYBYTES_MIN raise LengthError, "key too long" if key_size > KEYBYTES_MAX else key_size = 0 end opts[:key_size] = key_size digest_size = opts.fetch(:digest_size, BYTES_MAX) raise LengthError, "digest size too short" if digest_size < BYTES_MIN raise LengthError, "digest size too long" if digest_size > BYTES_MAX opts[:digest_size] = digest_size personal = opts.fetch(:personal, EMPTY_PERSONAL) opts[:personal] = Util.zero_pad(PERSONALBYTES, personal) salt = opts.fetch(:salt, EMPTY_SALT) opts[:salt] = Util.zero_pad(SALTBYTES, salt) opts end private_class_method :validate_opts def self.new(opts = {}) opts = validate_opts(opts) super end # Create a new Blake2b hash object # # @param [Hash] opts Blake2b configuration # @option opts [String] :key for Blake2b keyed mode # @option opts [Integer] :digest_size size of output digest in bytes # @option opts [String] :salt Provide a salt to support randomised hashing. # This is mixed into the parameters block to start the hashing. # @option opts [Personal] :personal Provide personalisation string to allow pinning a hash for a particular purpose. # This is mixed into the parameters block to start the hashing # # @raise [RbNaCl::LengthError] Invalid length specified for one or more options # # @return [RbNaCl::Hash::Blake2b] A Blake2b hasher object def initialize(opts = {}) @key = opts[:key] @key_size = opts[:key_size] @digest_size = opts[:digest_size] @personal = opts[:personal] @salt = opts[:salt] @incycle = false @instate = nil end # Initialize state for Blake2b hash calculation, # this will be called automatically from #update if needed def reset @instate.release if @instate @instate = State.new self.class.generichash_blake2b_init(@instate.pointer, @key, @key_size, @digest_size, @salt, @personal) || raise(CryptoError, "Hash init failed!") @incycle = true @digest = nil end # Reentrant version of Blake2b digest calculation method # # @param [String] message Message to be hashed def update(message) reset unless @incycle self.class.generichash_blake2b_update(@instate.pointer, message, message.bytesize) || raise(CryptoError, "Hashing failed!") end alias << update # Finalize digest calculation, return cached digest if any # # @return [String] Blake2b digest of the string as raw bytes def digest raise(CryptoError, "No message to hash yet!") unless @incycle return @digest if @digest @digest = Util.zeros(@digest_size) self.class.generichash_blake2b_final(@instate.pointer, @digest, @digest_size) || raise(CryptoError, "Hash finalization failed!") @digest end # The crypto_generichash_blake2b_state struct representation # ref: jedisct1/libsodium/src/libsodium/include/sodium/crypto_generichash_blake2b.h#L23 class State < FFI::Struct layout :h, [:uint64, 8], :t, [:uint64, 2], :f, [:uint64, 2], :buf, [:uint8, 2 * 128], :buflen, :size_t, :last_node, :uint8 end end end end rbnacl-5.0.0/lib/rbnacl/hash/sha256.rb0000644000004100000410000000063713120044713017275 0ustar www-datawww-data# encoding: binary # frozen_string_literal: true module RbNaCl module Hash # Provides a binding for the SHA256 function in libsodium module SHA256 extend Sodium sodium_type :hash sodium_primitive :sha256 sodium_constant :BYTES sodium_function :hash_sha256, :crypto_hash_sha256, [:pointer, :pointer, :ulong_long] end end end rbnacl-5.0.0/lib/rbnacl/hash/sha512.rb0000644000004100000410000000063113120044713017262 0ustar www-datawww-data# encoding: binary # frozen_string_literal: true module RbNaCl module Hash # Provides the binding for the SHA512 hash function module SHA512 extend Sodium sodium_type :hash sodium_primitive :sha512 sodium_constant :BYTES sodium_function :hash_sha512, :crypto_hash_sha512, [:pointer, :pointer, :ulong_long] end end end rbnacl-5.0.0/lib/rbnacl/version.rb0000644000004100000410000000021413120044713017016 0ustar www-datawww-data# encoding: binary # frozen_string_literal: true # NaCl/libsodium for Ruby module RbNaCl # The library's version VERSION = "5.0.0" end rbnacl-5.0.0/lib/rbnacl/auth.rb0000644000004100000410000000671613120044713016307 0ustar www-datawww-data# encoding: binary # frozen_string_literal: true module RbNaCl # Secret Key Authenticators # # These provide a means of verifying the integrity of a message, but only # with the knowledge of a shared key. This can be a preshared key, or one # that is derived through some cryptographic protocol. class Auth # Number of bytes in a valid key KEYBYTES = 0 # Number of bytes in a valid authenticator BYTES = 0 attr_reader :key private :key # A new authenticator, ready for auth and verification # # @param [#to_str] key the key used for authenticators, 32 bytes. def initialize(key) @key = Util.check_string(key, key_bytes, "#{self.class} key") end # Compute authenticator for message # # @param [#to_str] key the key used for the authenticator # @param [#to_str] message message to construct an authenticator for # # @return [String] The authenticator, as raw bytes def self.auth(key, message) new(key).auth(message) end # Verifies the given authenticator with the message. # # @param [#to_str] key the key used for the authenticator # @param [#to_str] authenticator to be checked # @param [#to_str] message the message to be authenticated # # @raise [BadAuthenticatorError] if the tag isn't valid # @raise [LengthError] if the tag is of the wrong length # # @return [Boolean] Was it valid? def self.verify(key, authenticator, message) new(key).verify(authenticator, message) end # Compute authenticator for message # # @param [#to_str] message the message to authenticate # # @return [String] the authenticator as raw bytes def auth(message) authenticator = Util.zeros(tag_bytes) message = message.to_str compute_authenticator(authenticator, message) authenticator end # Verifies the given authenticator with the message. # # @param [#to_str] authenticator to be checked # @param [#to_str] message the message to be authenticated # # @raise [BadAuthenticatorError] if the tag isn't valid # @raise [LengthError] if the tag is of the wrong length # # @return [Boolean] Was it valid? def verify(authenticator, message) auth = authenticator.to_s Util.check_length(auth, tag_bytes, "Provided authenticator") verify_message(auth, message) || raise(BadAuthenticatorError, "Invalid authenticator provided, message is corrupt") end # The crypto primitive for this authenticator instance # # @return [Symbol] The primitive used def primitive self.class.primitive end # The number of key bytes for this Auth class # # @return [Integer] number of key bytes def self.key_bytes self::KEYBYTES end # The number of key bytes for this Auth instance # # @return [Integer] number of key bytes def key_bytes self.class.key_bytes end # The number bytes in the tag or authenticator from this Auth class # # @return [Integer] number of tag bytes def self.tag_bytes self::BYTES end # The number of bytes in the tag or authenticator for this Auth instance # # @return [Integer] number of tag bytes def tag_bytes self.class.tag_bytes end private def compute_authenticator(_authenticator, _message) raise NotImplementedError end def verify_message(_authenticator, _message) raise NotImplementedError end end end rbnacl-5.0.0/lib/rbnacl/test_vectors.rb0000644000004100000410000002645613120044713020075 0ustar www-datawww-data# encoding: binary # frozen_string_literal: true # NaCl/libsodium for Ruby module RbNaCl # Reference library of test vectors used to verify the software is correct TEST_VECTORS = { # # Curve25519 test vectors # Taken from the NaCl distribution # alice_private: "77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a", alice_public: "8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a", bob_private: "5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb", bob_public: "de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f", alice_mult_bob: "4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742", # # Box test vectors # Taken from the NaCl distribution # secret_key: "1b27556473e985d462cd51197a9a46c76009549eac6474f206c4ee0844f68389", box_nonce: "69696ee955b62b73cd62bda875fc73d68219e0036b7a0b37", box_message: "be075fc53c81f2d5cf141316ebeb0c7b5228c52a4c62cbd44b66849b64244ffc" \ "e5ecbaaf33bd751a1ac728d45e6c61296cdc3c01233561f41db66cce314adb31" \ "0e3be8250c46f06dceea3a7fa1348057e2f6556ad6b1318a024a838f21af1fde" \ "048977eb48f59ffd4924ca1c60902e52f0a089bc76897040e082f93776384864" \ "5e0705", box_ciphertext: "f3ffc7703f9400e52a7dfb4b3d3305d98e993b9f48681273c29650ba32fc76ce" \ "48332ea7164d96a4476fb8c531a1186ac0dfc17c98dce87b4da7f011ec48c972" \ "71d2c20f9b928fe2270d6fb863d51738b48eeee314a7cc8ab932164548e526ae" \ "90224368517acfeabd6bb3732bc0e9da99832b61ca01b6de56244a9e88d5f9b3" \ "7973f622a43d14a6599b1f654cb45a74e355a5", # # Ed25519 test vectors # Taken from the Python test vectors: http://ed25519.cr.yp.to/python/sign.input # sign_private: "b18e1d0045995ec3d010c387ccfeb984d783af8fbb0f40fa7db126d889f6dadd", sign_public: "77f48b59caeda77751ed138b0ec667ff50f8768c25d48309a8f386a2bad187fb", sign_message: "916c7d1d268fc0e77c1bef238432573c39be577bbea0998936add2b50a653171" \ "ce18a542b0b7f96c1691a3be6031522894a8634183eda38798a0c5d5d79fbd01" \ "dd04a8646d71873b77b221998a81922d8105f892316369d5224c9983372d2313" \ "c6b1f4556ea26ba49d46e8b561e0fc76633ac9766e68e21fba7edca93c4c7460" \ "376d7f3ac22ff372c18f613f2ae2e856af40", sign_signature: "6bd710a368c1249923fc7a1610747403040f0cc30815a00f9ff548a896bbda0b" \ "4eb2ca19ebcf917f0f34200a9edbad3901b64ab09cc5ef7b9bcc3c40c0ff7509", # # SHA256 test vectors # Taken from the NSRL test vectors: http://www.nsrl.nist.gov/testdata/ sha256_message: "6162636462636465636465666465666765666768666768696768696a68696a6b" \ "696a6b6c6a6b6c6d6b6c6d6e6c6d6e6f6d6e6f706e6f7071", sha256_digest: "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1", sha256_empty: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", # # SHA512 test vectors # self-created (FIXME: find standard test vectors) sha512_message: "54686520717569636b2062726f776e20666f78206a756d7073206f7665722074" \ "6865206c617a7920646f672e", sha512_digest: "91ea1245f20d46ae9a037a989f54f1f790f0a47607eeb8a14d12890cea77a1bb" \ "c6c7ed9cf205e67b7f2b8fd4c7dfd3a7a8617e45f3c463d481c7e586c39ac1ed", sha512_empty: "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce" \ "47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e", # Blake2b test vectors # self-created? (TODO: double check, fix) blake2b_message: "54686520717569636b2062726f776e20666f78206a756d7073206f7665722074" \ "6865206c617a7920646f67", blake2b_digest: "a8add4bdddfd93e4877d2746e62817b116364a1fa7bc148d95090bc7333b3673" \ "f82401cf7aa2e4cb1ecd90296e3f14cb5413f8ed77be73045b13914cdcd6a918", blake2b_empty: "786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419" \ "d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce", # from the Blake2 paper(?) (TODO: double check) blake2b_keyed_message: "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" \ "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f" \ "404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f" \ "606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f" \ "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f" \ "a0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf" \ "c0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf" \ "e0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe", blake2b_key: "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" \ "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", blake2b_keyed_digest: "142709d62e28fcccd0af97fad0f8465b971e82201dc51070faa0372aa43e9248" \ "4be1c1e73ba10906d5d1853db6a4106e0a7bf9800d373d6dee2d46d62ef2a461", # Generated using the blake2 reference code blake2b_personal: "000102030405060708090a0b0c0d0e0f", blake2b_personal_digest: "7c86d3f929c9ac7f08c7940095da7c1cad2cf29db2e7a25fb05d99163e587cbd" \ "f3564e8ce727b734a0559ee76f6ff5aeebd4e1e8872f1829174c9b1a9dab80e3", blake2b_salt: "000102030405060708090a0b0c0d0e0f", blake2b_salt_digest: "16e2e2cfb97e6061bccf2fcc1e605e117dee806c959ef2ad01249d4d12ce98cb" \ "c993f400003ba57449f60a7b071ffdaff9c0acb16891a01a9b397ffe89db96bb", blake2b_personal_short: "0001020304050607", blake2b_personal_short_digest: "41b984967f852308710a6042d25f5faf4a84900b2001039075dab13aecfab7c8" \ "40def9506326563fbb355b3da629181d97d2556e4624711d68f8f655b7cbb435", blake2b_salt_short: "0001020304050607", blake2b_salt_short_digest: "873f35a1ca28febc872d6f842a8cd23136f3a2c22c19e8f0dac4cc704ced3371"\ "abe5105f65d344cd48bad8aba755620f63f1e0b35ae4439bf871ffe72485a309", # scrypt test vectors # Taken from http://tools.ietf.org/html/draft-josefsson-scrypt-kdf-01#page-14 scrypt_password: "4a857e2ee8aa9b6056f2424e84d24a72473378906ee04a46cb05311502d5250b" \ "82ad86b83c8f20a23dbb74f6da60b0b6ecffd67134d45946ac8ebfb3064294bc" \ "097d43ced68642bfb8bbbdd0f50b30118f5e", scrypt_salt: "39d82eef32010b8b79cc5ba88ed539fbaba741100f2edbeca7cc171ffeabf258", scrypt_opslimit: 758_010, scrypt_memlimit: 5_432_947, scrypt_digest: "bcc5c2fd785e4781d1201ed43d84925537e2a540d3de55f5812f29e9dd0a4a00" \ "451a5c8ddbb4862c03d45c75bf91b7fb49265feb667ad5c899fdbf2ca19eac67", # argon2 vectors # from libsodium/test/default/pwhash.c argon2_password: "a347ae92bce9f80f6f595a4480fc9c2fe7e7d7148d371e9487d75f5c23008ffae0" \ "65577a928febd9b1973a5a95073acdbeb6a030cfc0d79caa2dc5cd011cef02c08d" \ "a232d76d52dfbca38ca8dcbd665b17d1665f7cf5fe59772ec909733b24de97d6f5" \ "8d220b20c60d7c07ec1fd93c52c31020300c6c1facd77937a597c7a6", argon2_salt: "5541fbc995d5c197ba290346d2c559de", argon2_outlen: 155, argon2_opslimit: 5, argon2_memlimit: 7_256_678, argon2_digest: "23b803c84eaa25f4b44634cc1e5e37792c53fcd9b1eb20f865329c68e09cbfa9f19" \ "68757901b383fce221afe27713f97914a041395bbe1fb70e079e5bed2c7145b1f61" \ "54046f5958e9b1b29055454e264d1f2231c316f26be2e3738e83a80315e9a0951ce" \ "4b137b52e7d5ee7b37f7d936dcee51362bcf792595e3c896ad5042734fc90c92cae" \ "572ce63ff659a2f7974a3bd730d04d525d253ccc38", # argon2_str vectors # from libsodium/test/default/pwhash.c argon2_str_digest: "$argon2i$v=19$m=4096,t=3,p=2$b2RpZHVlamRpc29kaXNrdw" \ "$TNnWIwlu1061JHrnCqIAmjs3huSxYIU+0jWipu7Kc9M", argon2_str_passwd: "password", # Auth test vectors # Taken from NaCl distribution # auth_key_32: "eea6a7251c1e72916d11c2cb214d3c252539121d8e234e652d651fa4c8cff880", auth_key_64: "eaaa4c73ef13e7e9a53011304c5be141da9c3713b5ca822037ed57aded31b70a" \ "50a0dd80843d580fe5b57e470bb534333e907a624cf02873c6b9eaba70e0fc7e", auth_message: "8e993b9f48681273c29650ba32fc76ce48332ea7164d96a4476fb8c531a1186a" \ "c0dfc17c98dce87b4da7f011ec48c97271d2c20f9b928fe2270d6fb863d51738" \ "b48eeee314a7cc8ab932164548e526ae90224368517acfeabd6bb3732bc0e9da" \ "99832b61ca01b6de56244a9e88d5f9b37973f622a43d14a6599b1f654cb45a74" \ "e355a5", auth_onetime: "f3ffc7703f9400e52a7dfb4b3d3305d9", # self-created (FIXME: find standard test vectors) auth_hmacsha256: "7f7b9b707e8790ca8620ff94df5e6533ddc8e994060ce310c9d7de04d44aabc3", auth_hmacsha512256: "b2a31b8d4e01afcab2ee545b5caf4e3d212a99d7b3a116a97cec8e83c32e107d", auth_hmacsha512: "b2a31b8d4e01afcab2ee545b5caf4e3d212a99d7b3a116a97cec8e83c32e107d" \ "270e3921f69016c267a63ab4b226449a0dee0dc7dcb897a9bce9d27d788f8e8d", # AEAD ChaCha20-Poly1305 original implementation test vectors # Taken from https://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04 aead_chacha20poly1305_orig_key: "4290bcb154173531f314af57f3be3b5006da371ece272afa1b5dbdd1100a1007", aead_chacha20poly1305_orig_message: "86d09974840bded2a5ca", aead_chacha20poly1305_orig_nonce: "cd7cf67be39c794a", aead_chacha20poly1305_orig_ad: "87e229d4500845a079c0", aead_chacha20poly1305_orig_ciphertext: "e3e446f7ede9a19b62a4677dabf4e3d24b876bb284753896e1d6", # AEAD ChaCha20-Poly1305 IETF test vectors # Taken from https://tools.ietf.org/html/rfc7539 aead_chacha20poly1305_ietf_key: "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", aead_chacha20poly1305_ietf_message: "4c616469657320616e642047656e746c656d656e206f662074686520636c6173" \ "73206f66202739393a204966204920636f756c64206f6666657220796f75206f" \ "6e6c79206f6e652074697020666f7220746865206675747572652c2073756e73" \ "637265656e20776f756c642062652069742e", aead_chacha20poly1305_ietf_nonce: "070000004041424344454647", aead_chacha20poly1305_ietf_ad: "50515253c0c1c2c3c4c5c6c7", aead_chacha20poly1305_ietf_ciphertext: "d31a8d34648e60db7b86afbc53ef7ec2a4aded51296e08fea9e2b5a736ee62d6" \ "3dbea45e8ca9671282fafb69da92728b1a71de0a9e060b2905d6a5b67ecd3b36" \ "92ddbd7f2d778b8c9803aee328091b58fab324e4fad675945585808b4831d7bc" \ "3ff4def08e4b7a9de576d26586cec64b61161ae10b594f09e26a7e902ecbd060" \ "0691" }.freeze end rbnacl-5.0.0/lib/rbnacl/simple_box.rb0000644000004100000410000000763513120044713017510 0ustar www-datawww-data# encoding: binary # frozen_string_literal: true require "forwardable" # NaCl/libsodium for Ruby module RbNaCl # The simplest nonce strategy that could possibly work # # This class implements the simplest possible nonce generation strategy to # wrap a RbNaCl::Box or RbNaCl::SecretBox. A 24-byte random nonce is used # for the encryption and is prepended to the message. When it is time to # open the box, the message is split into nonce and ciphertext, and then the # box is decrypted. # # Thanks to the size of the nonce, the chance of a collision is negligible. For # example, after encrypting 2^64 messages, the odds of their having been # repeated nonce is approximately 2^-64. As an additional convenience, the # ciphertexts may be encoded or decoded by any of the encoders implemented in # the library. # # The resulting ciphertexts are 40 bytes longer than the plain text (24 byte # nonce plus a 16 byte authenticator). This might be annoying if you're # encrypting tweets, but for files represents a fairly small overhead. # # Some caveats: # # * If your random source is broken, so is the security of the messages. You # have bigger problems than just this library at that point, but it's worth # saying. # * The confidentiality of your messages is assured with this strategy, but # there is no protection against messages being reordered and replayed by an # active adversary. class SimpleBox extend Forwardable def_delegators :@box, :nonce_bytes, :primitive # Create a new SimpleBox # # @param box [SecretBox, Box] the SecretBox or Box to use. # # @return [SimpleBox] Ready for use def initialize(box) @box = box end # Use a secret key to create a SimpleBox # # This is a convenience method. It takes a secret key and instantiates a # SecretBox under the hood, then returns the new SimpleBox. # # @param secret_key [String] The secret key, 32 bytes long. # # @return [SimpleBox] Ready for use def self.from_secret_key(secret_key) new(SecretBox.new(secret_key)) end # Use a pair of keys to create a SimpleBox # # This is a convenience method. It takes a pair of keys and instantiates a # Box under the hood, then returns the new SimpleBox. # # @param public_key [PublicKey, String] The RbNaCl public key, as class or string # @param private_key [PrivateKey, String] The RbNaCl private key, as class or string # # @return [SimpleBox] Ready for use def self.from_keypair(public_key, private_key) new(Box.new(public_key, private_key)) end # Encrypts the message with a random nonce # # Encrypts the message with a random nonce, then returns the ciphertext with # the nonce prepended. Optionally encodes the message using an encoder. # # @param message [String] The message to encrypt # # @return [String] The enciphered message def box(message) nonce = generate_nonce cipher_text = @box.box(nonce, message) nonce + cipher_text end alias encrypt box # Decrypts the ciphertext with a random nonce # # Takes a ciphertext, optionally decodes it, then splits the nonce off the # front and uses this to decrypt. Returns the message. # # @param enciphered_message [String] The message to decrypt. # # @raise [CryptoError] If the message has been tampered with. # # @return [String] The decoded message def open(enciphered_message) nonce, ciphertext = extract_nonce(enciphered_message.to_s) @box.open(nonce, ciphertext) end alias decrypt open private def generate_nonce Random.random_bytes(nonce_bytes) end def extract_nonce(bytes) nonce = bytes.slice(0, nonce_bytes) [nonce, bytes.slice(nonce_bytes..-1)] end end # Backwards compatibility with the old RandomNonceBox name RandomNonceBox = SimpleBox end rbnacl-5.0.0/lib/rbnacl/key_comparator.rb0000644000004100000410000000341513120044713020356 0ustar www-datawww-data# encoding: binary # frozen_string_literal: true module RbNaCl # Implements comparisons of keys # # This permits both timing invariant equality tests, as well as # lexicographical sorting. module KeyComparator include Comparable # spaceship operator # # @param other [KeyComparator,#to_str] The thing to compare # # @return [0] if the keys are equal # @return [1] if the key is larger than the other key # @return [-1] if the key is smaller than the other key # @return [nil] if comparison doesn't make sense def <=>(other) if KeyComparator > other.class other = other.to_bytes elsif other.respond_to?(:to_str) other = other.to_str else return nil end compare32(other) end # equality operator # # The equality operator is explicity defined, despite including Comparable # and having a spaceship operator, so that if equality tests are desired, # they can be timing invariant, without any chance that the further # comparisons for greater than and less than can leak information. Maybe # this is too paranoid, but I don't know how ruby works under the hood with # comparable. # # @param other [KeyComparator,#to_str] The thing to compare # # @return [true] if the keys are equal # @return [false] if they keys are not equal def ==(other) if KeyComparator > other.class other = other.to_bytes elsif other.respond_to?(:to_str) other = other.to_str else return false end Util.verify32(to_bytes, other) end private def compare32(other) if Util.verify32(to_bytes, other) 0 elsif to_bytes > other 1 else -1 end end end end rbnacl-5.0.0/lib/rbnacl/self_test.rb0000644000004100000410000000705113120044713017327 0ustar www-datawww-data# encoding: binary # frozen_string_literal: true start = Time.now if $DEBUG # NaCl/libsodium for Ruby module RbNaCl class SelfTestFailure < RbNaCl::CryptoError; end # Self-test performed at startup module SelfTest module_function def vector(name) [TEST_VECTORS[name]].pack("H*") end def box_test alicepk = RbNaCl::PublicKey.new(vector(:alice_public)) bobsk = RbNaCl::PrivateKey.new(vector(:bob_private)) box = RbNaCl::Box.new(alicepk, bobsk) box_common_test(box) end def secret_box_test box = SecretBox.new(vector(:secret_key)) box_common_test(box) end def box_common_test(box) nonce = vector :box_nonce message = vector :box_message ciphertext = vector :box_ciphertext raise SelfTestFailure, "failed to generate correct ciphertext" unless box.encrypt(nonce, message) == ciphertext raise SelfTestFailure, "failed to decrypt ciphertext correctly" unless box.decrypt(nonce, ciphertext) == message begin passed = false corrupt_ct = ciphertext.dup corrupt_ct[23] = " " box.decrypt(nonce, corrupt_ct) rescue CryptoError passed = true ensure passed || raise(SelfTestFailure, "failed to detect corrupt ciphertext") end end def digital_signature_test signing_key = SigningKey.new(vector(:sign_private)) verify_key = signing_key.verify_key unless verify_key.to_s == vector(:sign_public) #:nocov: raise SelfTestFailure, "failed to generate verify key correctly" #:nocov: end message = vector :sign_message signature = signing_key.sign(message) unless signature == vector(:sign_signature) #:nocov: raise SelfTestFailure, "failed to generate correct signature" #:nocov: end unless verify_key.verify(signature, message) #:nocov: raise SelfTestFailure, "failed to verify a valid signature" #:nocov: end begin passed = false bad_signature = signature[0, 63] + "0" verify_key.verify(bad_signature, message) rescue CryptoError passed = true ensure passed || raise(SelfTestFailure, "failed to detect corrupt ciphertext") end end def sha256_test message = vector :sha256_message digest = vector :sha256_digest raise SelfTestFailure, "failed to generate a correct SHA256 digest" unless RbNaCl::Hash.sha256(message) == digest end def hmac_test(klass, tag) authenticator = klass.new(vector("auth_key_#{klass.key_bytes}".to_sym)) message = vector :auth_message raise SelfTestFailure, "#{klass} generated incorrect authentication tag" unless authenticator.auth(message) == vector(tag) raise SelfTestFailure, "#{klass} failed to verify authentication tag" unless authenticator.verify(vector(tag), message) begin passed = false authenticator.verify(vector(tag), message + " ") rescue CryptoError passed = true ensure passed || raise(SelfTestFailure, "failed to detect corrupt ciphertext") end end end end RbNaCl::SelfTest.box_test RbNaCl::SelfTest.secret_box_test RbNaCl::SelfTest.digital_signature_test RbNaCl::SelfTest.sha256_test RbNaCl::SelfTest.hmac_test RbNaCl::HMAC::SHA256, :auth_hmacsha256 RbNaCl::SelfTest.hmac_test RbNaCl::HMAC::SHA512256, :auth_hmacsha512256 RbNaCl::SelfTest.hmac_test RbNaCl::OneTimeAuth, :auth_onetime puts "POST Completed in #{Time.now - start} s" if $DEBUG rbnacl-5.0.0/lib/rbnacl/hmac/0000755000004100000410000000000013120044713015717 5ustar www-datawww-datarbnacl-5.0.0/lib/rbnacl/hmac/sha256.rb0000644000004100000410000000263113120044713017256 0ustar www-datawww-data# encoding: binary # frozen_string_literal: true module RbNaCl module HMAC # Computes an authenticator as HMAC-SHA-256 # # The authenticator can be used at a later time to verify the provenance of # the message by recomputing the HMAC over the message and then comparing it to # the provided authenticator. The class provides methods for generating # signatures and also has a constant-time implementation for checking them. # # This is a secret key authenticator, i.e. anyone who can verify signatures # can also create them. # # @see http://nacl.cr.yp.to/auth.html class SHA256 < Auth extend Sodium sodium_type :auth sodium_primitive :hmacsha256 sodium_constant :BYTES sodium_constant :KEYBYTES sodium_function :auth_hmacsha256, :crypto_auth_hmacsha256, [:pointer, :pointer, :ulong_long, :pointer] sodium_function :auth_hmacsha256_verify, :crypto_auth_hmacsha256_verify, [:pointer, :pointer, :ulong_long, :pointer] private def compute_authenticator(authenticator, message) self.class.auth_hmacsha256(authenticator, message, message.bytesize, key) end def verify_message(authenticator, message) self.class.auth_hmacsha256_verify(authenticator, message, message.bytesize, key) end end end end rbnacl-5.0.0/lib/rbnacl/hmac/sha512256.rb0000644000004100000410000000270713120044713017512 0ustar www-datawww-data# encoding: binary # frozen_string_literal: true module RbNaCl module HMAC # Computes an authenticator as HMAC-SHA-512 truncated to 256-bits # # The authenticator can be used at a later time to verify the provenance of # the message by recomputing the HMAC over the message and then comparing it to # the provided authenticator. The class provides methods for generating # signatures and also has a constant-time implementation for checking them. # # This is a secret key authenticator, i.e. anyone who can verify signatures # can also create them. # # @see http://nacl.cr.yp.to/auth.html class SHA512256 < Auth extend Sodium sodium_type :auth sodium_primitive :hmacsha512256 sodium_constant :BYTES sodium_constant :KEYBYTES sodium_function :auth_hmacsha512256, :crypto_auth_hmacsha512256, [:pointer, :pointer, :ulong_long, :pointer] sodium_function :auth_hmacsha512256_verify, :crypto_auth_hmacsha512256_verify, [:pointer, :pointer, :ulong_long, :pointer] private def compute_authenticator(authenticator, message) self.class.auth_hmacsha512256(authenticator, message, message.bytesize, key) end def verify_message(authenticator, message) self.class.auth_hmacsha512256_verify(authenticator, message, message.bytesize, key) end end end end rbnacl-5.0.0/lib/rbnacl/hmac/sha512.rb0000644000004100000410000000263113120044713017251 0ustar www-datawww-data# encoding: binary # frozen_string_literal: true module RbNaCl module HMAC # Computes an authenticator as HMAC-SHA-512 # # The authenticator can be used at a later time to verify the provenance of # the message by recomputing the HMAC over the message and then comparing it to # the provided authenticator. The class provides methods for generating # signatures and also has a constant-time implementation for checking them. # # This is a secret key authenticator, i.e. anyone who can verify signatures # can also create them. # # @see http://nacl.cr.yp.to/auth.html class SHA512 < Auth extend Sodium sodium_type :auth sodium_primitive :hmacsha512 sodium_constant :BYTES sodium_constant :KEYBYTES sodium_function :auth_hmacsha512, :crypto_auth_hmacsha512, [:pointer, :pointer, :ulong_long, :pointer] sodium_function :auth_hmacsha512_verify, :crypto_auth_hmacsha512_verify, [:pointer, :pointer, :ulong_long, :pointer] private def compute_authenticator(authenticator, message) self.class.auth_hmacsha512(authenticator, message, message.bytesize, key) end def verify_message(authenticator, message) self.class.auth_hmacsha512_verify(authenticator, message, message.bytesize, key) end end end end rbnacl-5.0.0/lib/rbnacl/secret_boxes/0000755000004100000410000000000013120044713017474 5ustar www-datawww-datarbnacl-5.0.0/lib/rbnacl/secret_boxes/xsalsa20poly1305.rb0000644000004100000410000001173113120044713022676 0ustar www-datawww-data# encoding: binary # frozen_string_literal: true module RbNaCl module SecretBoxes # The SecretBox class boxes and unboxes messages # # This class uses the given secret key to encrypt and decrypt messages. # # It is VITALLY important that the nonce is a nonce, i.e. it is a number used # only once for any given pair of keys. If you fail to do this, you # compromise the privacy of the messages encrypted. Give your nonces a # different prefix, or have one side use an odd counter and one an even counter. # Just make sure they are different. # # The ciphertexts generated by this class include a 16-byte authenticator which # is checked as part of the decryption. An invalid authenticator will cause # the unbox function to raise. The authenticator is not a signature. Once # you've looked in the box, you've demonstrated the ability to create # arbitrary valid messages, so messages you send are repudiable. For # non-repudiable messages, sign them before or after encryption. class XSalsa20Poly1305 extend Sodium sodium_type :secretbox sodium_primitive :xsalsa20poly1305 sodium_constant :KEYBYTES sodium_constant :NONCEBYTES sodium_constant :ZEROBYTES sodium_constant :BOXZEROBYTES sodium_function :secretbox_xsalsa20poly1305, :crypto_secretbox_xsalsa20poly1305, [:pointer, :pointer, :ulong_long, :pointer, :pointer] sodium_function :secretbox_xsalsa20poly1305_open, :crypto_secretbox_xsalsa20poly1305_open, [:pointer, :pointer, :ulong_long, :pointer, :pointer] # Create a new SecretBox # # Sets up the Box with a secret key fro encrypting and decrypting messages. # # @param key [String] The key to encrypt and decrypt with # # @raise [RbNaCl::LengthError] on invalid keys # # @return [RbNaCl::SecretBox] The new Box, ready to use def initialize(key) @key = Util.check_string(key, KEYBYTES, "Secret key") end # Encrypts a message # # Encrypts the message with the given nonce to the key set up when # initializing the class. Make sure the nonce is unique for any given # key, or you might as well just send plain text. # # This function takes care of the padding required by the NaCL C API. # # @param nonce [String] A 24-byte string containing the nonce. # @param message [String] The message to be encrypted. # # @raise [RbNaCl::LengthError] If the nonce is not valid # # @return [String] The ciphertext without the nonce prepended (BINARY encoded) def box(nonce, message) Util.check_length(nonce, nonce_bytes, "Nonce") msg = Util.prepend_zeros(ZEROBYTES, message) ct = Util.zeros(msg.bytesize) success = self.class.secretbox_xsalsa20poly1305(ct, msg, msg.bytesize, nonce, @key) raise CryptoError, "Encryption failed" unless success Util.remove_zeros(BOXZEROBYTES, ct) end alias encrypt box # Decrypts a ciphertext # # Decrypts the ciphertext with the given nonce using the key setup when # initializing the class. # # This function takes care of the padding required by the NaCL C API. # # @param nonce [String] A 24-byte string containing the nonce. # @param ciphertext [String] The message to be decrypted. # # @raise [RbNaCl::LengthError] If the nonce is not valid # @raise [RbNaCl::CryptoError] If the ciphertext cannot be authenticated. # # @return [String] The decrypted message (BINARY encoded) def open(nonce, ciphertext) Util.check_length(nonce, nonce_bytes, "Nonce") ct = Util.prepend_zeros(BOXZEROBYTES, ciphertext) message = Util.zeros(ct.bytesize) success = self.class.secretbox_xsalsa20poly1305_open(message, ct, ct.bytesize, nonce, @key) raise CryptoError, "Decryption failed. Ciphertext failed verification." unless success Util.remove_zeros(ZEROBYTES, message) end alias decrypt open # The crypto primitive for the SecretBox instance # # @return [Symbol] The primitive used def primitive self.class.primitive end # The nonce bytes for the SecretBox class # # @return [Integer] The number of bytes in a valid nonce def self.nonce_bytes NONCEBYTES end # The nonce bytes for the SecretBox instance # # @return [Integer] The number of bytes in a valid nonce def nonce_bytes NONCEBYTES end # The key bytes for the SecretBox class # # @return [Integer] The number of bytes in a valid key def self.key_bytes KEYBYTES end # The key bytes for the SecretBox instance # # @return [Integer] The number of bytes in a valid key def key_bytes KEYBYTES end end end end rbnacl-5.0.0/lib/rbnacl/util.rb0000644000004100000410000001502113120044713016310 0ustar www-datawww-data# encoding: binary # frozen_string_literal: true module RbNaCl # Various utility functions module Util extend Sodium sodium_function :c_verify16, :crypto_verify_16, [:pointer, :pointer] sodium_function :c_verify32, :crypto_verify_32, [:pointer, :pointer] module_function # Returns a string of n zeros # # Lots of the functions require us to create strings to pass into functions of a specified size. # # @param [Integer] n the size of the string to make # # @return [String] A nice collection of zeros def zeros(n = 32) zeros = "\0" * n # make sure they're 8-bit zeros, not 7-bit zeros. Otherwise we might get # encoding errors later zeros.respond_to?(:force_encoding) ? zeros.force_encoding("ASCII-8BIT") : zeros end # Prepends a message with zeros # # Many functions require a string with some zeros prepended. # # @param [Integer] n The number of zeros to prepend # @param [String] message The string to be prepended # # @return [String] a bunch of zeros def prepend_zeros(n, message) zeros(n) + message end # Remove zeros from the start of a message # # Many functions require a string with some zeros prepended, then need them removing after. # Note: this modifies the passed in string # # @param [Integer] n The number of zeros to remove # @param [String] message The string to be slice # # @return [String] less a bunch of zeros def remove_zeros(n, message) message.slice!(n, message.bytesize - n) end # Pad a string out to n characters with zeros # # @param [Integer] n The length of the resulting string # @param [String] message the message to be padded # # @raise [RbNaCl::LengthError] If the string is too long # # @return [String] A string, n bytes long def zero_pad(n, message) len = message.bytesize if len == n message elsif len > n raise LengthError, "String too long for zero-padding to #{n} bytes" else message + zeros(n - len) end end # Check the length of the passed in string # # In several places through the codebase we have to be VERY strict with # what length of string we accept. This method supports that. # # @raise [RbNaCl::LengthError] If the string is not the right length # # @param string [String] The string to compare # @param length [Integer] The desired length # @param description [String] Description of the string (used in the error) def check_length(string, length, description) if string.nil? raise LengthError, "#{description} was nil (Expected #{length.to_int})", caller end if string.bytesize != length.to_int raise LengthError, "#{description} was #{string.bytesize} bytes (Expected #{length.to_int})", caller end true end # Check a passed in string, converting the argument if necessary # # In several places through the codebase we have to be VERY strict with # the strings we accept. This method supports that. # # @raise [ArgumentError] If we cannot convert to a string with #to_str # @raise [RbNaCl::LengthError] If the string is not the right length # # @param string [#to_str] The input string # @param length [Integer] The only acceptable length of the string # @param description [String] Description of the string (used in the error) def check_string(string, length, description) unless string.respond_to? :to_str raise TypeError, "can't convert #{string.class} into String with #to_str" end string = string.to_str unless string.encoding == Encoding::BINARY raise EncodingError, "strings must use BINARY encoding (got #{string.encoding})" end check_length(string, length, description) string end # Compare two 32 byte strings in constant time # # This should help to avoid timing attacks for string comparisons in your # application. Note that many of the functions (such as HmacSha256#verify) # use this method under the hood already. # # @param [String] one String #1 # @param [String] two String #2 # # @return [Boolean] Well, are they equal? def verify32(one, two) return false unless two.bytesize == 32 && one.bytesize == 32 c_verify32(one, two) end # Compare two 32 byte strings in constant time # # This should help to avoid timing attacks for string comparisons in your # application. Note that many of the functions (such as HmacSha256#verify) # use this method under the hood already. # # @param [String] one String #1 # @param [String] two String #2 # # @raise [ArgumentError] If the strings are not equal in length # # @return [Boolean] Well, are they equal? def verify32!(one, two) check_length(one, 32, "First message") check_length(two, 32, "Second message") c_verify32(one, two) end # Compare two 16 byte strings in constant time # # This should help to avoid timing attacks for string comparisons in your # application. Note that many of the functions (such as OneTime#verify) # use this method under the hood already. # # @param [String] one String #1 # @param [String] two String #2 # # @return [Boolean] Well, are they equal? def verify16(one, two) return false unless two.bytesize == 16 && one.bytesize == 16 c_verify16(one, two) end # Compare two 16 byte strings in constant time # # This should help to avoid timing attacks for string comparisons in your # application. Note that many of the functions (such as OneTime#verify) # use this method under the hood already. # # @param [String] one String #1 # @param [String] two String #2 # # @raise [ArgumentError] If the strings are not equal in length # # @return [Boolean] Well, are they equal? def verify16!(one, two) check_length(one, 16, "First message") check_length(two, 16, "Second message") c_verify16(one, two) end # Hex encodes a message # # @param [String] bytes The bytes to encode # # @return [String] Tasty, tasty hexadecimal def bin2hex(bytes) bytes.to_s.unpack("H*").first end # Hex decodes a message # # @param [String] hex hex to decode. # # @return [String] crisp and clean bytes def hex2bin(hex) [hex.to_s].pack("H*") end end end rbnacl-5.0.0/lib/rbnacl/boxes/0000755000004100000410000000000013120044713016127 5ustar www-datawww-datarbnacl-5.0.0/lib/rbnacl/boxes/curve25519xsalsa20poly1305.rb0000644000004100000410000001716513120044713023013 0ustar www-datawww-data# encoding: binary # frozen_string_literal: true module RbNaCl module Boxes # The Box class boxes and unboxes messages between a pair of keys # # This class uses the given public and secret keys to derive a shared key, # which is used with the nonce given to encrypt the given messages and # decrypt the given ciphertexts. The same shared key will generated from # both pairing of keys, so given two keypairs belonging to alice (pkalice, # skalice) and bob(pkbob, skbob), the key derived from (pkalice, skbob) with # equal that from (pkbob, skalice). This is how the system works: # # @example # # On bob's system # bobkey = RbNaCl::PrivateKey.generate # #=> # # # # send bobkey.public_key to alice # # recieve alice's public key, alicepk # # NB: This is actually the hard part of the system. How to do it securely # # is left as an exercise to for the reader. # alice_pubkey = "..." # # # make a box # alicebob_box = RbNaCl::Box.new(alice_pubkey, bobkey) # #=> # # # # encrypt a message to alice # cipher_text = alicebob_box.box("A bad example of a nonce", "Hello, Alice!") # #=> "..." # a string of bytes, 29 bytes long # # # send ["A bad example of a nonce", cipher_text] to alice # # note that nonces don't have to be secret # # receive [nonce, cipher_text_to_bob] from alice # # # decrypt the reply # # Alice has been a little more sensible than bob, and has a random nonce # # that is too fiddly to type here. But there are other choices than just # # random # plain_text = alicebob_box.open(nonce, cipher_text_to_bob) # #=> "Hey there, Bob!" # # # we have a new message! # # But Eve has tampered with this message, by flipping some bytes around! # # [nonce2, cipher_text_to_bob_honest_love_eve] # alicebob_box.open(nonce2, cipher_text_to_bob_honest_love_eve) # # # BOOM! # # Bob gets a RbNaCl::CryptoError to deal with! # # It is VITALLY important that the nonce is a nonce, i.e. it is a number used # only once for any given pair of keys. If you fail to do this, you # compromise the privacy of the the messages encrypted. Also, bear in mind # the property mentioned just above. Give your nonces a different prefix, or # have one side use an odd counter and one an even counter. Just make sure # they are different. # # The ciphertexts generated by this class include a 16-byte authenticator which # is checked as part of the decryption. An invalid authenticator will cause # the unbox function to raise. The authenticator is not a signature. Once # you've looked in the box, you've demonstrated the ability to create # arbitrary valid messages, so messages you send are repudiable. For # non-repudiable messages, sign them before or after encryption. class Curve25519XSalsa20Poly1305 extend Sodium sodium_type :box sodium_primitive :curve25519xsalsa20poly1305 sodium_constant :NONCEBYTES sodium_constant :ZEROBYTES sodium_constant :BOXZEROBYTES sodium_constant :BEFORENMBYTES sodium_constant :PUBLICKEYBYTES sodium_constant :SECRETKEYBYTES, :PRIVATEKEYBYTES sodium_function :box_curve25519xsalsa20poly1305_beforenm, :crypto_box_curve25519xsalsa20poly1305_beforenm, [:pointer, :pointer, :pointer] sodium_function :box_curve25519xsalsa20poly1305_open_afternm, :crypto_box_curve25519xsalsa20poly1305_open_afternm, [:pointer, :pointer, :ulong_long, :pointer, :pointer] sodium_function :box_curve25519xsalsa20poly1305_afternm, :crypto_box_curve25519xsalsa20poly1305_afternm, [:pointer, :pointer, :ulong_long, :pointer, :pointer] # Create a new Box # # Sets up the Box for deriving the shared key and encrypting and # decrypting messages. # # @param public_key [String,RbNaCl::PublicKey] The public key to encrypt to # @param private_key [String,RbNaCl::PrivateKey] The private key to encrypt with # # @raise [RbNaCl::LengthError] on invalid keys # # @return [RbNaCl::Box] The new Box, ready to use def initialize(public_key, private_key) @public_key = public_key.is_a?(PublicKey) ? public_key : PublicKey.new(public_key) @private_key = private_key.is_a?(PrivateKey) ? private_key : PrivateKey.new(private_key) raise IncorrectPrimitiveError unless @public_key.primitive == primitive && @private_key.primitive == primitive end # Encrypts a message # # Encrypts the message with the given nonce to the keypair set up when # initializing the class. Make sure the nonce is unique for any given # keypair, or you might as well just send plain text. # # This function takes care of the padding required by the NaCL C API. # # @param nonce [String] A 24-byte string containing the nonce. # @param message [String] The message to be encrypted. # # @raise [RbNaCl::LengthError] If the nonce is not valid # # @return [String] The ciphertext without the nonce prepended (BINARY encoded) def box(nonce, message) Util.check_length(nonce, nonce_bytes, "Nonce") msg = Util.prepend_zeros(ZEROBYTES, message) ct = Util.zeros(msg.bytesize) success = self.class.box_curve25519xsalsa20poly1305_afternm(ct, msg, msg.bytesize, nonce, beforenm) raise CryptoError, "Encryption failed" unless success Util.remove_zeros(BOXZEROBYTES, ct) end alias encrypt box # Decrypts a ciphertext # # Decrypts the ciphertext with the given nonce using the keypair setup when # initializing the class. # # This function takes care of the padding required by the NaCL C API. # # @param nonce [String] A 24-byte string containing the nonce. # @param ciphertext [String] The message to be decrypted. # # @raise [RbNaCl::LengthError] If the nonce is not valid # @raise [RbNaCl::CryptoError] If the ciphertext cannot be authenticated. # # @return [String] The decrypted message (BINARY encoded) def open(nonce, ciphertext) Util.check_length(nonce, nonce_bytes, "Nonce") ct = Util.prepend_zeros(BOXZEROBYTES, ciphertext) message = Util.zeros(ct.bytesize) success = self.class.box_curve25519xsalsa20poly1305_open_afternm(message, ct, ct.bytesize, nonce, beforenm) raise CryptoError, "Decryption failed. Ciphertext failed verification." unless success Util.remove_zeros(ZEROBYTES, message) end alias decrypt open # The crypto primitive for the box class # # @return [Symbol] The primitive used def primitive self.class.primitive end # The nonce bytes for the box class # # @return [Integer] The number of bytes in a valid nonce def self.nonce_bytes NONCEBYTES end # The nonce bytes for the box instance # # @return [Integer] The number of bytes in a valid nonce def nonce_bytes NONCEBYTES end private def beforenm @_key ||= begin key = Util.zeros(BEFORENMBYTES) success = self.class.box_curve25519xsalsa20poly1305_beforenm(key, @public_key.to_s, @private_key.to_s) raise CryptoError, "Failed to derive shared key" unless success key end end end end end rbnacl-5.0.0/lib/rbnacl/boxes/curve25519xsalsa20poly1305/0000755000004100000410000000000013120044713022454 5ustar www-datawww-datarbnacl-5.0.0/lib/rbnacl/boxes/curve25519xsalsa20poly1305/private_key.rb0000644000004100000410000000542413120044713025330 0ustar www-datawww-data# encoding: binary # frozen_string_literal: true module RbNaCl module Boxes class Curve25519XSalsa20Poly1305 # RbNaCl::Box private key. Keep it safe # # This class generates and stores NaCL private keys, as well as providing a # reference to the public key associated with this private key, if that's # provided. # # Note that the documentation for NaCl refers to this as a secret key, but in # this library its a private key, to avoid confusing the issue with the # SecretBox, which does symmetric encryption. class PrivateKey include KeyComparator include Serializable extend Sodium sodium_type :box sodium_primitive :curve25519xsalsa20poly1305 sodium_function :box_curve25519xsalsa20poly1305_keypair, :crypto_box_curve25519xsalsa20poly1305_keypair, [:pointer, :pointer] # The size of the key, in bytes BYTES = Boxes::Curve25519XSalsa20Poly1305::PRIVATEKEYBYTES # Initializes a new PrivateKey for key operations. # # Takes the (optionally encoded) private key bytes. This class can then be # used for various key operations, including deriving the corresponding # PublicKey # # @param private_key [String] The private key # # @raise [TypeError] If the key is nil # @raise [RbNaCl::LengthError] If the key is not valid after decoding. # # @return A new PrivateKey def initialize(private_key) @private_key = Util.check_string(private_key, BYTES, "Private key") end # Generates a new keypair # # @raise [RbNaCl::CryptoError] if key generation fails, due to insufficient randomness. # # @return [RbNaCl::PrivateKey] A new private key, with the associated public key also set. def self.generate pk = Util.zeros(Boxes::Curve25519XSalsa20Poly1305::PUBLICKEYBYTES) sk = Util.zeros(Boxes::Curve25519XSalsa20Poly1305::PRIVATEKEYBYTES) box_curve25519xsalsa20poly1305_keypair(pk, sk) || raise(CryptoError, "Failed to generate a key pair") new(sk) end # The raw bytes of the key # # @return [String] the raw bytes. def to_bytes @private_key end # the public key associated with this private key # # @return [PublicKey] the key def public_key @public_key ||= PublicKey.new GroupElements::Curve25519.base.mult(to_bytes) end # The crypto primitive this PrivateKey is to be used for. # # @return [Symbol] The primitive def primitive self.class.primitive end end end end end rbnacl-5.0.0/lib/rbnacl/boxes/curve25519xsalsa20poly1305/public_key.rb0000644000004100000410000000317113120044713025131 0ustar www-datawww-data# encoding: binary # frozen_string_literal: true module RbNaCl module Boxes class Curve25519XSalsa20Poly1305 # RbNaCl::Box public key. Send it (securely!) to your friends. # # This class stores the NaCL public key, and provides some convenience # functions for working with it. class PublicKey include KeyComparator include Serializable # The size of the key, in bytes BYTES = Boxes::Curve25519XSalsa20Poly1305::PUBLICKEYBYTES # Initializes a new PublicKey for key operations. # # Takes the (optionally encoded) public key bytes. This can be shared with # many people and used to establish key pairs with their private key, for # the exchanging of messages using a RbNaCl::Box # # @param public_key [String] The public key # # @raise [RbNaCl::LengthError] If the key is not valid after decoding. # # @return A new PublicKey def initialize(public_key) @public_key = Util.check_string(public_key, BYTES, "Public key") end # The raw bytes of the key # # @return [String] the raw bytes. def to_bytes @public_key end # The crypto primitive the PublicKey class is to be used for # # @return [Symbol] The primitive def self.primitive :curve25519xsalsa20poly1305 end # The crypto primitive this PublicKey is to be used for. # # @return [Symbol] The primitive def primitive self.class.primitive end end end end end rbnacl-5.0.0/lib/rbnacl.rb0000644000004100000410000000636613120044713015347 0ustar www-datawww-data# encoding: binary # frozen_string_literal: true require "rbnacl/version" require "rbnacl/sodium" require "rbnacl/sodium/version" require "rbnacl/serializable" require "rbnacl/key_comparator" require "rbnacl/auth" require "rbnacl/util" require "rbnacl/random" require "rbnacl/simple_box" require "rbnacl/test_vectors" require "rbnacl/init" require "rbnacl/aead/base" # NaCl/libsodium for Ruby module RbNaCl # Oh no, something went wrong! # # This indicates a failure in the operation of a cryptographic primitive such # as authentication failing on an attempt to decrypt a ciphertext. Classes # in the library may define more specific subclasses. class CryptoError < StandardError; end # Something, probably a key, is the wrong length # # This indicates some argument with an expected length was not that length. # Since this is probably a cryptographic key, you should check that! class LengthError < ArgumentError; end # An incorrect primitive has been passed to a method # # This indicates that an attempt has been made to use something (probably a key) # with an incorrect primitive class IncorrectPrimitiveError < ArgumentError; end # The signature was forged or otherwise corrupt class BadSignatureError < CryptoError; end # The authenticator was forged or otherwise corrupt class BadAuthenticatorError < CryptoError; end # Public Key Encryption (Box): Curve25519XSalsa20Poly1305 require "rbnacl/boxes/curve25519xsalsa20poly1305" require "rbnacl/boxes/curve25519xsalsa20poly1305/private_key" require "rbnacl/boxes/curve25519xsalsa20poly1305/public_key" # Secret Key Encryption (SecretBox): XSalsa20Poly1305 require "rbnacl/secret_boxes/xsalsa20poly1305" # Digital Signatures: Ed25519 require "rbnacl/signatures/ed25519" require "rbnacl/signatures/ed25519/signing_key" require "rbnacl/signatures/ed25519/verify_key" # Group Elements: Curve25519 require "rbnacl/group_elements/curve25519" # One-time Authentication: Poly1305 require "rbnacl/one_time_auths/poly1305" # Hash functions: SHA256/512 and Blake2b require "rbnacl/hash" require "rbnacl/hash/sha256" require "rbnacl/hash/sha512" require "rbnacl/hash/blake2b" # Password hash functions require "rbnacl/password_hash" require "rbnacl/password_hash/scrypt" if RbNaCl::Sodium::Version::ARGON2_SUPPORTED require "rbnacl/password_hash/argon2" end # HMAC: SHA256/512 and SHA512256 require "rbnacl/hmac/sha256" require "rbnacl/hmac/sha512256" require "rbnacl/hmac/sha512" # AEAD: ChaCha20-Poly1305 require "rbnacl/aead/chacha20poly1305_legacy" require "rbnacl/aead/chacha20poly1305_ietf" # # Bind aliases used by the public API # Box = Boxes::Curve25519XSalsa20Poly1305 PrivateKey = Boxes::Curve25519XSalsa20Poly1305::PrivateKey PublicKey = Boxes::Curve25519XSalsa20Poly1305::PublicKey SecretBox = SecretBoxes::XSalsa20Poly1305 SigningKey = Signatures::Ed25519::SigningKey VerifyKey = Signatures::Ed25519::VerifyKey GroupElement = GroupElements::Curve25519 OneTimeAuth = OneTimeAuths::Poly1305 end # Select platform-optimized versions of algorithms RbNaCl::Init.sodium_init # Perform self test on load require "rbnacl/self_test" unless defined?($RBNACL_SELF_TEST) && $RBNACL_SELF_TEST == false rbnacl-5.0.0/.rubocop.yml0000644000004100000410000000072513120044713015256 0ustar www-datawww-dataAllCops: DisplayCopNames: true Include: - '**/Rakefile' Exclude: - 'spec/**/*' - 'vendor/**/*' - 'lib/rbnacl/test_vectors.rb' # # Metrics # Metrics/AbcSize: Max: 20 Metrics/ClassLength: Max: 150 Metrics/MethodLength: Max: 25 # # Style # LineLength: Max: 128 Style/StringLiterals: EnforcedStyle: double_quotes Style/SpaceBeforeFirstArg: Enabled: false Style/GlobalVars: Enabled: false Style/SafeNavigation: Enabled: false rbnacl-5.0.0/.gitignore0000644000004100000410000000026713120044713014775 0ustar www-datawww-data*.gem *.rbc .bundle .config .yardoc Gemfile.lock InstalledFiles _yardoc coverage doc/ lib/bundler/man pkg rdoc spec/reports test/tmp test/version_tmp tmp libsodium libsodium-*.tar.gz rbnacl-5.0.0/rbnacl.gemspec0000644000004100000410000000226413120044713015612 0ustar www-datawww-data# -*- encoding: utf-8 -*- lib = File.expand_path("../lib", __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require "rbnacl/version" Gem::Specification.new do |spec| spec.name = "rbnacl" spec.version = RbNaCl::VERSION spec.authors = ["Tony Arcieri", "Jonathan Stott"] spec.email = ["bascule@gmail.com", "jonathan.stott@gmail.com"] spec.homepage = "https://github.com/cryptosphere/rbnacl" spec.licenses = ["MIT"] spec.summary = "Ruby binding to the Networking and Cryptography (NaCl) library" spec.description = <<-DESCRIPTION.strip.gsub(/\s+/, " ") The Networking and Cryptography (NaCl) library provides a high-level toolkit for building cryptographic systems and protocols DESCRIPTION spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR) spec.executables = spec.files.grep(%r{^bin/}).map { |f| File.basename(f) } spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] spec.required_ruby_version = ">= 2.2.6" spec.platform = "jruby" if defined? JRUBY_VERSION spec.add_runtime_dependency "ffi" spec.add_development_dependency "bundler" end rbnacl-5.0.0/.yardopts0000644000004100000410000000010413120044713014641 0ustar www-datawww-data--markup markdown --no-private lib/**/*.rb - README.md LICENSE.txt rbnacl-5.0.0/tasks/0000755000004100000410000000000013120044713014125 5ustar www-datawww-datarbnacl-5.0.0/tasks/rubocop.rake0000644000004100000410000000014513120044713016442 0ustar www-datawww-data# encoding: binary # frozen_string_literal: true require "rubocop/rake_task" RuboCop::RakeTask.new rbnacl-5.0.0/tasks/rspec.rake0000644000004100000410000000027313120044713016107 0ustar www-datawww-data# encoding: binary # frozen_string_literal: true gem "rspec" require "rspec/core/rake_task" RSpec::Core::RakeTask.new RSpec::Core::RakeTask.new(:rcov) do |task| task.rcov = true end rbnacl-5.0.0/CHANGES.md0000644000004100000410000000725613120044713014404 0ustar www-datawww-data## 5.0.0 (2017-06-13) * [#159](https://github.com/cryptosphere/rbnacl/pull/159) Support the BLAKE2b Initialize-Update-Finalize API. ([@fudanchii]) ## 4.0.2 (2017-03-12) * [#157](https://github.com/cryptosphere/rbnacl/pull/157) Raise error on degenerate keys (fixes #152). ([@paragonie-scott], [@tarcieri]) ## 4.0.1 (2016-12-04) * [#148](https://github.com/cryptosphere/rbnacl/pull/148) Last minute changes to the ChaCha20Poly1305 API. ([@tarcieri]) ## 4.0.0 (2016-12-04) * [#141](https://github.com/cryptosphere/rbnacl/pull/141) Add wrappers for ChaCha20Poly1305 AEAD ciphers. ([@aadavids]) * [#142](https://github.com/cryptosphere/rbnacl/pull/142) Added support for Argon2 password hash. ([@elijh]) * [#143](https://github.com/cryptosphere/rbnacl/pull/143) Require Ruby 2.2.6+. ([@tarcieri]) ## 3.4.0 (2015-05-07) * [#135](https://github.com/cryptosphere/rbnacl/pull/135) Expose `RbNaCl::Signatures::Ed25519#keypair_bytes`. ([@grempe]) * [#137](https://github.com/cryptosphere/rbnacl/pull/137) Expose HMAC-SHA512 (with 64-byte keys) ([@mwpastore]) ## 3.3.0 (2015-12-29) * [#105](https://github.com/cryptosphere/rbnacl/pull/105) Add salt/personalisation strings for Blake2b. ([@namelessjon]) * [#128](https://github.com/cryptosphere/rbnacl/pull/128) Remove use of Thread.exclusive when initializing library. ([@tarcieri]) ## 3.2.0 (2015-05-31) * Fix method signature for blake2b * RuboCop-friendly codebase ## 3.1.2 (2014-08-30) * Fix scrypt support with libsodium 0.7.0 (scryptsalsa208sha256) ## 3.1.1 (2014-06-14) * Fix undefined variable warning * RSpec 3 fixups * RuboCop ## 3.1.0 (2014-05-22) * The scrypt password hashing function: `RbNaCl::PasswordHash.scrypt` ## 3.0.1 (2014-05-12) * Load gem from `RBNACL_LIBSODIUM_GEM_LIB_PATH` if set. Used by rbnacl-libsodium gem to use libsodium compiled from a gem. ## 3.0.0 (2014-04-22) * Rename RandomNonceBox to SimpleBox (backwards compatibility preserved) * Reverse documented order of SimpleBox/RandomNonceBox initialize parameters. Technically backwards compatible, but confusing. * Ensure all strings are ASCII-8BIT/BINARY encoding prior to use ## 2.0.0 (2013-11-07) * Add encrypt/decrypt aliases for Crypto::RandomNonceBox * Rename Crypto module to RbNaCl module * RbNaCl::VerifyKey#verify operand order was reversed. New operand order is signature, message instead of message, signature * RbNaCL::SecretBox#open, RbNaCl::Box#open, Auth#verify and VerifyKey#verify all now raise a (descendent of) CryptoError if the check fails. This ensures failures are handled by the program. * RbNaCl::SecretBox, Box, etc. are all now aliases for the real implementations, which are named after the primitives they provide * Encoders have now gone. * Add support for the Blake2b cryptographic hash algorithm. * Add checks that we have a sufficiently recent version of libsodium (0.4.3+) * Dropped ruby-1.8 support * Call the `sodium_init()` function, to select the best algorithms. * Fix some typos in the documentation * Changes in the low level binding for libsodium and removal of the NaCl module * Add a mutex around calls to randombytes in libsodium ## 1.1.0 (2013-04-19) * Provide API for querying primitives and details about them, such as key lengths, nonce lengths, etc. * Fixed bug on passing null bytes to sha256, sha512 functions. ## 1.0.0 (2013-03-08) * Initial release [@namelessjon]: https://github.com/namelessjon [@tarcieri]: https://github.com/tarcieri [@aadavids]: https://github.com/aadavids [@grempe]: https://github.com/grempe [@mwpastore]: https://github.com/mwpastore [@elijh]: https://github.com/elijh [@paragonie-scott]: https://github.com/paragonie-scott [@fudanchii]: https://github.com/fudanchii rbnacl-5.0.0/README.md0000644000004100000410000002010613120044713014256 0ustar www-datawww-data![RbNaCl](https://raw.github.com/cryptosphere/rbnacl/master/images/logo.png) ====== [![Gem Version](https://badge.fury.io/rb/rbnacl.svg)](http://badge.fury.io/rb/rbnacl) [![Build Status](https://travis-ci.org/cryptosphere/rbnacl.svg?branch=master)](https://travis-ci.org/cryptosphere/rbnacl) [![Code Climate](https://codeclimate.com/github/cryptosphere/rbnacl.svg)](https://codeclimate.com/github/cryptosphere/rbnacl) [![Coverage Status](https://coveralls.io/repos/cryptosphere/rbnacl/badge.svg?branch=master)](https://coveralls.io/r/cryptosphere/rbnacl) [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/cryptosphere/rbnacl/blob/master/LICENSE.txt) _NOTE: This is the 5.x **stable** branch of RbNaCl. For the 4.x **legacy** branch, please see:_ https://github.com/cryptosphere/rbnacl/tree/4-x-stable A Ruby binding to the state-of-the-art [Networking and Cryptography][nacl] library by [Daniel J. Bernstein][djb]. This is **NOT** Google Native Client. This is a crypto library. On a completely unrelated topic, RbNaCl is also the empirical formula for Rubidium Sodium Chloride. Need help with RbNaCl? Join the [RbNaCl Google Group][group]. We're also on IRC at #cryptosphere on irc.freenode.net [nacl]: http://nacl.cr.yp.to/ [djb]: http://cr.yp.to/djb.html [group]: http://groups.google.com/group/rbnacl ## Why NaCl? NaCl is a different kind of cryptographic library. In the past crypto libraries were kitchen sinks of little bits and pieces, like ciphers, MACs, signature algorithms, and hash functions. To accomplish anything you had to make a lot of decisions about which specific pieces to use, and if any of your decisions were wrong, the result was an insecure system. The choices are also not easy: EAX? GCM? CCM? AES-CTR? CMAC? OMAC1? AEAD? NIST? CBC? CFB? CTR? ECB? OMGWTFBBQ! NaCl puts cryptography on Rails! Instead of making you choose which cryptographic primitives to use, NaCl provides convention over configuration in the form of expertly-assembled high-level cryptographic APIs that ensure not only the confidentiality of your data, but also detect tampering. These high-level, easy-to-use APIs are designed to be hard to attack by default in ways primitives exposed by libraries like OpenSSL are not. This approach makes NaCl a lot closer to a system like GPG than it is to the cryptographic primitive APIs in a library like OpenSSL. In addition, NaCl also uses state-of-the-art encryption, including Curve25519 elliptic curves and the XSalsa20 stream cipher. This means with NaCl you not only get a system which is designed to be secure-by-default, you also get one which is extremely fast with comparatively small cryptographic keys. For more information on NaCl's goals, see Dan Bernstein's presentation [Blaming the Cryptographic User](http://cr.yp.to/talks/2012.08.08/slides.pdf) ### Is it any good? [Yes.](http://news.ycombinator.com/item?id=3067434) ## Supported platforms You can use RbNaCl on platforms libsodium is supported (see below). This library aims to support and is [tested against][travis] the following Ruby versions: * Ruby 2.2.6+ * Ruby 2.3.0+ * JRuby 9.1.6.0+ If something doesn't work on one of these versions, it's a bug. This library may inadvertently work (or seem to work) on other Ruby versions, however support will only be provided for the versions listed above. If you would like this library to support another Ruby version or implementation, you may volunteer to be a maintainer. Being a maintainer entails making sure all tests run and pass on that implementation. When something breaks on your implementation, you will be responsible for providing patches in a timely fashion. If critical issues for a particular implementation exist at the time of a major release, support for that Ruby version may be dropped. [travis]: http://travis-ci.org/cryptosphere/rbnacl ## Installation Note: [Windows installation instructions are available](https://github.com/cryptosphere/rbnacl/wiki/Windows-Installation). ### libsodium **NOTE: Want to avoid the hassle of installing libsodium? Use the [rbnacl-libsodium](https://github.com/cryptosphere/rbnacl-libsodium) gem** To use RbNaCl, you will need to install libsodium: https://github.com/jedisct1/libsodium At least version `1.0.0` is required. For OS X users, libsodium is available via homebrew and can be installed with: brew install libsodium For FreeBSD users, libsodium is available both via pkgng and ports. To install a binary package: pkg install libsodium To install from ports on FreeBSD, use your favorite ports front end (e.g. portmaster or portupgrade), or use make as follows: cd /usr/ports/security/libsodium; make install clean ### RbNaCl gem Once you have libsodium installed, add this line to your application's Gemfile: gem 'rbnacl' And then execute: $ bundle Or install it yourself as: $ gem install rbnacl Inside of your Ruby program do: require 'rbnacl' ...to pull it in as a dependency. ## Documentation RbNaCl's documentation can be found [in the Wiki][wiki]. The following features are supported: * [SimpleBox]: easy-to-use public-key or secret-key encryption "on Rails" * [Secret-key Encryption][secretkey]: authenticated symmetric encryption using a single key shared among parties * [Public-key Encryption][publickey]: securely send messages to a given public key which can only be decrypted by a secret key * [Digital Signatures][signatures]: sign messages with a private key which can be verified by a public one * [Authenticators][macs]: create codes which can be used to check the authenticity of messages * [Hash Functions][hashes]: compute a secure, fixed-length code from a message which does not reveal the contents of the message Additional power-user features are available. Please see the Wiki for further information. [YARD API documentation][yard] is also available. [wiki]: https://github.com/cryptosphere/rbnacl/wiki [simplebox]: https://github.com/cryptosphere/rbnacl/wiki/SimpleBox [secretkey]: https://github.com/cryptosphere/rbnacl/wiki/Secret-Key-Encryption [publickey]: https://github.com/cryptosphere/rbnacl/wiki/Public-Key-Encryption [signatures]: https://github.com/cryptosphere/rbnacl/wiki/Digital-Signatures [macs]: https://github.com/cryptosphere/rbnacl/wiki/Authenticators [hashes]: https://github.com/cryptosphere/rbnacl/wiki/Hash-Functions [yard]: http://www.rubydoc.info/gems/rbnacl ## Learn More While NaCl has designed to be easier-than-usual to use for a crypto library, cryptography is an incredibly difficult subject and it's always helpful to know as much as you can about it before applying it to a particular use case. That said, the creator of NaCl, Dan Bernstein, has published a number of papers about NaCl. If you are interested in learning more about how NaCl works, it's recommended that you read them: * [Cryptography in NaCl](http://cr.yp.to/highspeed/naclcrypto-20090310.pdf) * [Salsa20 Design](https://cr.yp.to/snuffle/design.pdf) * [Curve25519: new Diffie-Hellman speed records](http://cr.yp.to/ecdh/curve25519-20060209.pdf) * [Ed25519: High-speed high-security signatures](http://ed25519.cr.yp.to/ed25519-20110926.pdf) For more information on libsodium, please check out the [Introducing Sodium blog post](http://labs.umbrella.com/2013/03/06/announcing-sodium-a-new-cryptographic-library/) Have a general interest in cryptography? Check out the free course Coursera offers from Stanford University Professor Dan Boneh: [http://crypto-class.org](http://crypto-class.org) ## Important Questions ### Is it "Military Grade™"? Only if your military understands twisted Edwards curves ### Is it "Bank Grade™"? No, that means 3DES, which this library doesn't support, sorry ### Does it have a lock with a checkmark? Sure, here you go: ![Checkmarked Lock](http://i.imgur.com/dwA0Ffi.png) ## Contributing * Fork this repository on Github * Make your changes and send a pull request * If your changes look good, we'll merge 'em ## License Copyright (c) 2012-2017 Tony Arcieri, Jonathan Stott. Distributed under the MIT License. See [LICENSE.txt] for further details. [LICENSE.txt]: https://github.com/cryptosphere/rbnacl/blob/master/LICENSE.txt rbnacl-5.0.0/Guardfile0000644000004100000410000000035113120044713014624 0ustar www-datawww-data# A sample Guardfile # More info at https://github.com/guard/guard#readme guard :rspec do watch(%r{^spec/.+_spec\.rb$}) watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" } watch("spec/spec_helper.rb") { "spec" } end