From 4a1093f495aa8f2cf57c1cdc540474c73eb9dcc2 Mon Sep 17 00:00:00 2001 From: Mystikfluu Date: Wed, 28 Dec 2022 15:17:47 +0100 Subject: [PATCH] better error handling Co-authored-by: Alpisc --- Cargo.lock | 39 ++++++++++++++- Cargo.toml | 5 +- IPass_logo.png | Bin 0 -> 49115 bytes src/main.rs | 126 ++++++++++++++++++++++++++++++++++++++++--------- src/utils.rs | 78 +++++++++++++++++++++++++----- 5 files changed, 211 insertions(+), 37 deletions(-) create mode 100644 IPass_logo.png diff --git a/Cargo.lock b/Cargo.lock index 1abae61..93d44d9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -37,6 +37,21 @@ dependencies = [ "subtle", ] +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + [[package]] name = "block-buffer" version = "0.10.3" @@ -46,6 +61,27 @@ dependencies = [ "generic-array", ] +[[package]] +name = "brotli" +version = "3.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a0b1dbcc8ae29329621f8d4f0d835787c1c38bb1401979b49d13b0b305ff68" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ad2d4653bf5ca36ae797b1f4bb4dbddb60ce49ca4aed8a2ce4829f60425b80" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -158,9 +194,10 @@ dependencies = [ [[package]] name = "ipass" -version = "0.1.0" +version = "0.3.0" dependencies = [ "aes-gcm", + "brotli", "hex", "home", "rand", diff --git a/Cargo.toml b/Cargo.toml index bd90d8c..a218feb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ipass" -version = "0.1.0" +version = "0.3.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -11,4 +11,5 @@ rpassword = "7.2" rand = "0.8.5" aes-gcm = "0.10.1" sha2 = "0.10.6" -hex = "0.4.3" \ No newline at end of file +hex = "0.4.3" +brotli = "3.3.4" \ No newline at end of file diff --git a/IPass_logo.png b/IPass_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..21a741975e59cd355fe4c9fcd42ca27a322d2927 GIT binary patch literal 49115 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

pM73@T>+t%MKR6;dR;jBg?&vUARQIa-ZW^ar zSlPrMVP7$+6S=_)`3sI7;*Q%}I08i^el>OkIu2gqjr&>Ke1_s^eFMHCakTiyQEipK zdRA>|!W-Xd#YHK%_6@RQ(~;bM7I^>c@x3L4nT*eldUUdgBeDXE$+yT)w^O@ubzX;M ziW)T^q+Y4;+$WV?C7a~A)ZYOz%|A-o=T?$7aN-3Yd+dS;9`yXl z4V%d36qGIzl~ymMDMoVuqj}=w2Dx}TO%=UQ`bTAwJ>~eFzM4~qtAMkSYE{JE-6wyJ z)!grQ(mEmfr1!_kZ=K#M$N6{*Hc28~;|FIs)b^NE;^3vipH0==no33F2}QN?6&80%g*SUc z8b>q~JRN5@rEe*Ga`>L0N``cb6}u%N|8^>F&ga&^VJtV!mCnMu`SQpG_q|!dBN!`n z@VTLiW;5z-7axn-f8V=;B|HqR-&8u=GcPPd4xb4fL21bbHOfYX7q=NcHn`Y=UNz$^ z-o}k!RgcYa8@oiL44Fd*sqw7;YZ_Nb^mW8{;G;0V)wfQf0(h(VXo~Cy`jhcA5$3T? z^V0dIu7$U zJqi73yQSPpG~olNAT1DfV{|{5_po+|LnZMzMd?QcVgELtJD~1%rX2hqmHf#v?jOk< zyD0;d(Wkx>P5kuaQ`%@II8o$=&NboXbLl$l`S7}BGpx;!H`q!(h8(^SM_C2cy|z>* z9GGV0EG*cI;3)8@_b}!t@VFQLQuUdBIW;AV)|z!v7;zy=ruXw#Lf+fJAGPNlwL$f# z=e-?w6)7u4WzAiHLQcahbrAMLiQ#uq+g(ujsDpXT0?cw^o z%OIVeM8(mqPe-MqX?Cze%;N?;BsM^ z6PnG}_eR6O`$X}k)ScoA;=u*B&;%xKnuxV?I9CY&e41lap=j@F>{I^WOk6*n!!iR< z`#ngCULy}lZv<}+W|!>Lq2S+_3)Vpzw{HcqD}c|X{s1H`@a9=2ZZ zr`7-1S-Uo@zry5QlCmhaFB+RV6$V!22JcAihN{}@;Hta6c3Bv7el<#<2zXa zXmu@|xh}~?2tF{HHCYA6ltq)3MMQkAT>5ci__PKGb;){oy84N}XlS42gs(){K(|(W zGF6ACE;=(8c>p_FOsf6h-zOCx1lYv6FMqid8~+CN2DA?FE@u)vEL^pvMypaK-nl#X zS#r4AoRP6RyYi+_Ft57#qx#`+-=i%ohYZqjuYCDRO4%f?)s13Bm3(qi7oCJnX8m*N z3uTd7oCK;#ACGq_Xc|;E(4)AVILdjHg92JtQej5Ibmz5W_6*3gLUUQ1v%i{}Zj1=Q z+_vw-V)Qe7mb^>;uh*R}Tcp;CuaNwl_sQn3f!}pD!sg!h3KuHla{P2GW}r}FL)S># zE3>!K0-;UA+!Gdmb*^fJc!jlogM#74>ki$=94wi`vwN|qR@w1;t&FdDw)Wa$9j116 z&%D4FHrwZrP2Dm+miKyt9#=SnLh}L@>|i)|Uipw|E&l&n6)no5P<@}d;OTRo!1qvd zS9OPMyk;~0u33>krq1TZnz?8d*n9ScRh+$rwxcp0GtO!YB|6_4>z_2+XOWJ(yZI~s zZ@VUWw10eHb@wG)wN0iHcS0!;hEnm*t9N5D0@%#cLvr`_i(Bx$ThN4N{ubYMY=enF^j%8vWj=e#4D)m7F-S=H}Cw4v!ZsEK$D0?VUSlg0J`do3=m24aS4B54^c+b~fpXbn;aAmLo+O zyoO$PR3;X)8m~j(5XO3&v2*g|9C}dchoR_^mJ?Oc#EQCK0$GBGKXdfrYn92`dbed8$vaGtOI{n32v;{p|%2geC0ajM8&1Agr;!W+e;8;11QziTG(zubQV{} z7Ypv>{MXpmpE(^&2LV!+xj^n>2MI-GwT5xBeeVFSSuy{UYZo_}yCkB4SZ4u5e__>+6+EL!8N zUzY&Qgkt*ca9B^_GmZWSWN3oC`u{NZ9#Bnm-NNvhNdh6X&=E^UMMOcxhNx%;?ASo1 zs3<`ceH0a}s6Yn9?qjeaDk|6uO0c3LL@ZzfY>0}0f}(ZZj()~j-oK9ve4j95%L2Pw}Uf}I_Im-l=gxd&k_Y0m4FXPbYuYykaDTGY`M z8xIMT1EK34D?*-09x6P-&3zs^M&mKCGu7o+Qmw#qrP5Zqr|Z#f_Gkj6PK+8W1qa%i zC5tn}VFf~;mwfDUxEW;%iJh&m?P+{C;}m3CO>tcP5FcP=wdNUkDeT^?=BAz5Jq zGb_z0O+4dF6&RD(VEIZGFd4XfI5uxz4c4c?Z!_biJlaMr%M-rTj#T zDPa$5Nv2a_fzXPTUEdl~rTC?|0Cw6AYb-%>pe0V%Q(-R_95YV%vQ3xPdv zA-T{mr$G3QmHkm(dQrCGx{)cJ9;w@U1u8nL6~AmqpFGDXJfXG83A)bZ|5lb z7audMfLHguTAPQ!l*NSHd?C+rfDOSN(E3W#8XOO0{cVbXroVufrmxj~gKvqQ*$t8h zEMg%jL*n`LOF_W*j`wk2q$m&#K-wc%NMO#XFfyY+4jcAty$7SVB@Cx zK533r21qCKVuPeVHAxP=l9s!`emHDD#%$lx~{vXH(X40VymN)@g@Lq)_8zux`8M0bR%!DneP1t^&HcUKi^f zn6LvHTaW_68}45J({Ve`I^SEM#*l$f!B(VB2b3MEm~@v#P5ZDBOzX>&MCTc zYLMvzk>#n@i``mQkRb||r^~0v{D~%gdd<9WqN(Zdm2tE60U~G%CoABo;8d$bS}dHK zUIh_A&;<@CQ87S;FazN|2R1sjLcf$&(+l(;Yu8%(_&x0KyRCa)#v$tM@7aYLm zx=X7hrK=ky($1pe?M)fV(CNj(k?|iuE^G`h8~`hi9vR~Op-4_s^z^D`&`(G{^{3ES zp0X5=PV{1dIsvr1;8Zfai-kD?ipz)CpbyCO-8X@>NPHVB+aoXi_DNCTh-Lb;BYaYL zNeWKLsS}$U`~9omRA{Sxki^eq<;1{T9iFdZ#!Yhd}*%ex2R(a$xo zT|QzlHP9_?sis@uru!`78nD-)a4~rP4mjI(e5IiHgE;zFD>Q!4G};!5ohut8qs3u1 z*|nI>EDfJw^TEAXc$C8(co76f&$p;H`M6HhXmMH@jh=gQ<%fxGqg)`>lf?Nt+Pzbt za>uHCU%ko8&hPPYFR%PmS~YHv8s$L+z|c`+)O>Q;NMkia^Dr$~>9dO#&80IJp)+vKJ1 zjqJy2TB9;p*&|xBykTK}m2#M+f!|iF_|^P_Ykoz8-`zlEQ)r7KBzWD%ZO7HhC8|aF zV1Y`}3M1mFsfI^Gy2yH0(cn4UmzuX-KCxo5Dg{r*{6S)2Etb8KX4;JBYV8XSR2CE~ zeQIdBlQg9GRQY{KQ~rv8s_SN&Mcx_|MwM7WixAV2j2Jiq)#=sUaIu=U*u8NAh{t| z{2EBEY0X;Ue0Q`4<(PLuukgy&(zdq!FpV@gXa5kC%Tt1s;%lAo=z|(|CEOC*i{A<( z=n3ks8Nj_z7+WcqNPsd=4P|Ws%Ftewf>xMaJIyp~ZG0=NX#|Z~sp09}6AhAnxfg_9 zQ2^ww8VIqJlRXABVkqcHT)U1qvc|7d76_?`D=@Qui~a|ew4*`tUK=F!Th5O-G0R)C z_7n?a0VQ7zN`-KIQ!e@3w>7Iq!8U0}0hFSp%XM*@EJ|yKaSJFYS-s@ zAzA@-res`ijow}=63#s~wFNqPX@IoPh_bzq{hn;Q^{#{46>6l;{%)(3w}XypJaaXOpSZ0k5R0$H*Z-*rsY{eiyWa{8vL^q7 z5wlyfvR1=6mP!HbGoQAPBl#hi_(@TE9@u`bo#yF{lKJBJBGGY;w8#GtY-*K!PJ&(o z+j~{~6dGI*?z%ynm3A&TuT%Q4vgNdLp4YtXgTeHDVVF=wPX!;fTEyJI%N~Ik=>_3$ zfj^cs`ZY*;+=QeUjOwFdo9~BX{aO`g>7e3~M%PNf7$0jiJSOTtv%Ong)?3A|gK$b} z%N((NPhIYbc4S|(h<+gOy^vziptXoL!kgQLTF**?vfV4}31C6qbez;a*)v8`>2HO7 zwV-}Vvfw>nKx=#E5J;`PAWb*=n_j^MTK3a|mAC7Dc<};;Kl14|bfJYn*p)Gzw`nm= zZOB^V`VUyOT@sSpoC;vDbM54mzL5v43sgGg32-)SLL7sL=*Lxg>E+M=P$1KmfZ-?p z!7>4)3x`AS@Y_JZ8LVOGt(VyMC_NA7x6i{OVFRT0=&pS1iYn1 z{M!pOD^3*tNw~_*X(r7JFRHRJE1Zf?gW4`B4VSJc(B3 zra(xCS#6!?uydYQ$tc0FJnhB|H={D%rjU0h4R*(0*&#W`r`>-{1eIP>x=oQPCT;)V zLqkhxsPb$hXl=WCWd9VrnRpXWZ=V+~fy&e>$r5_5ywnhRcS)5nwv;ww7MiJfP+n>$ z>?>>?M|T{paeniZsU6Al@{~Kv|6rr@JHbuuuQ%Rj{Zgdp{5CLG&4_Z)LL1Uj!xr98 zFu=6AX}6&9*Tj{aZ0KUAk!8+XVFNvD+TDlqO|fskaq<-BtH4XGc2PZ6c>F^z@|eJ> z-3nh2uEEP5zyyw54*OoY5Cg2q6Wj)S`9ty}+~g0_kNek5{?&u3%c^9L^-<-42Eh#TTmR;I6J{!~u{$Cx2@ zihA(7DT`k8Ydljz2^f&iP%emPF4DZ@4#l&r^3rFJ9YFq4jp+%kR{Z?DN*orhl>Xs? z!rm)V;){j8t_$0hFtkGQ84hcGZu!lRB3{gX0hs>@jajwJl&QSmY7_NZVAvp847u?e zG_FuP$%O`XEJo&pkH z_`e8lri0sJLALAN5}QWhsY{1!rvxT#cb``Etk+1zw`Bn)`@u%#F#4l*GAH0kBqq*qksPr(gX6*qcgzu72l2`I&rpjJPeHrj#h zx_kt@;|kg_w$Hw|@)HqIf#EPmW2&5&!f{lXP0hbqc=rm(sRVpAp_x0ETeB{viR~9+ znUge;@JA`M7rVQF1wntX4S{RS?+huI1&-&E^>Yo3we8~!*74j6Vn2etw zp7uZ@Ax}_G57oXh2x`nyV0Ay}wsw~|qcz}%;7J1=i1*bf&CfgHv}A3fRsO5(w{E$7 zLM>HQ!d-{yDVV02g0^5Qv6#K=w?9CeG%6N$d2N=HKD)o=STl=N`q;_W zNk=kw7vFwx#_woiiH`i2=yTS7@GyGqCc0eyYu#$4)6WC6xo`If`>KR%Mxu4Xp`^th zLG{!k;jRnd^W)n6gd1$a(zLZZQaIT5Bu9~=#J2viqG)<4cv1y=sCFvt%qdfO{@+tj zizc+)*8EFQHDBCG@R|nJu151)K<73=6RA%L3vjG z!990aSNyC%Wpref`T-)Q$Dn3caX9Vc+?QA=4;cWf@!!$iYr)M{$?|b2e_BA7XeGUjlu^2^>OBpjUW9HwL6^hQve{8Nxy z(+Y{Xbj=)3xGtX58W2a170^CE-wTgE0KrwZuToC8W_`Oz^J|HwkHdT6l)YfG&>Tv7 zL#-|2J7Rm@3AcmL3ffmEI~7e$pnL`GW{u(6;r98!NMX8W)ZmN4qkcmmT!u(e>Hyh{ zIbR{gRCohe1qlk8QI10?MLI}91`GWC5Na;HpY@vcb#0~KsOYY`>Bq{}tZFGG3xS;B z0?0`nt{eAFnKd675y6lGqz75!uZ+_p4g!hQkT0^M^B$k&0eS>YbT#B2>6Td=<)ud2 zmA0Bm1NIWgn6452q}J{!;`q5REoz|L`Q%vseUi_XE1!?F)XLuyc=6E8C$^s~COq#G zdmeIXd4L(2CWiA;q|bRe>$U~9J&NTa%{zb+=?r~HI~c&mqJcc5P*}tI>J8-nes3k5 z=ck=Vdkb8y@aJ1vv>`vW7)D$LDW~ca!oC7JPdD*3Y?OQjSS7(oUPNkZFS<2QUnBV4*egtY%iyjz3;2 zY*3We(5WVSPZp%KAm0^vK;hH{t)o|5zcKi`Q81N`QGbI|WVE~jwfM~qxx92w!=GL5 zR|#wWF)-G;1%2FcXSmbfwp$}@xsNlOdoB*^Q2IoA_CUsf^N&A0f4ZoB{Ipx*PF1we zX-7U4uHV4wEWq&m=J!82=Rk*(zv-aC{YJR0138jjq5l>_2eOC`^;1mU?;tsf;vbpZb~M zS6Y_2advp=;6vX&r4W-L$z%-^wAxo3b_#UbX)zBp4|IG}6zEy-D^t)qDA?2G&=Nb_ z3$!~LVdgwKitv~Y3a5kN_DpQw1f^e!fv_*#> zZb;PGC_^^XxWR-K5p+D~a~@Ys9~Q<;IWyI?wB$lf0_dD-Cd`hizN`4yVm9w)`*1sAvVp79agk6H2#icrO|H&g_QchX@xFP9u zkue?)F?oXPuij;U>DYg500L`5H{d6|Ze8@&ARMhIxVp_eW9BWsH#Ir8qmu+-T8ejq z#y6m!AOCvO^ZFlzHkF`WX`Me-Z9&c8_?C9png@>W;4S@nzY`}`&MOtIoLX~c*=cqp zCa@h(8(T*$*;6xGt)eOQlVbNB z8}kzv=c}b!Q2kZXpe=1!&~Mv=AUhw9${#pd*NuC(%cpR9;iWm)r!G7Pm^1H;Te^26 z!=^PJi4OW;MwEG=Qc#mViNbF9$?(rGWSRBB;6LeBz{2{=oLeQ8IaH8~P<&X?$iH zg*HOHcb^hPh~oT#lyCi?w0wyT=*76KAW+cw>)(fl@kboR*_Jd2tWol2S?KwVTdG~m z2z8|B!XNY6BqmtDRpD*z$}W(8cRU-|cxT{@JimZ-{1lb!l5xe4W~ z2Z3)^Vyb@q>jNS8Lp-m^*KimI92u?n>KoD_t7B!lzk2E=3L8cgi5m5F8P~6K|DMJ8Nz3tHC7!uWk~xlRlh1$!1lx7BM_tzWg#)Gx`g(gy z^6d6r%dtHn2k+{7F39oKw1GY1%=q$Djg0_SP>OBnqw#=+eHmWrvD3s!p2r4E8PK$% z;o5JM&VRpN;=0UJO_^~E%Lz6j0p#potX4|saq1A>yHU!y~kZlyp24ENu!@9Jw zVs*IgTyAxVKst$}TuDk>NiD$f|DT2;O!x(Vpb#05yK5U zMxlSlRAry-rw`d&i#qoTgLL_qyH!wN-|ysjwEV3LH4-AWy~kg`ejK(7Dys!@Y|zkW zx>tb)i2}WwM38$CcIc8(gJHtq&g`bq6gqbDY@K#>pM@#Z2+)sHLd3z_4#5N(eM(4m z57WV}gN}O81M)BkJ7r_x8;)r~vCq4~<6zTa-)OD_814KzZK*pUv&7ka-p9JxP-tful?fDX>|)Gf*3h^`yRIz8aQ{j^)Q4L6J8$nM?v}(Ewnoo=PGi=%N`bWem_=^ij zM8>B4u@reUOa9B8khSr$yOG4xw^Lj1Aw20gMd0ie@yEgzwQBRrZ}pXIBw^EY=&n-w z|ML_dXV0rERiwTZQlCxvE)=}W&3!mfhZugK~qJi#; z{=CQ%r*hG=cM<(|d>Z(&PX1tv0$s1RVt-WkqEs^lNh zFzG`y6K7{e0NMM>zvlHIiOPv$v_zjze@g@IW2@wt^If{8Ei zAv0po4}okZ)9t-P?vJpdAO_Xeb;>+}Y{$QJ0o7IEQQ2H&s4b-e7z!GkWV-5M;UZDh zu$9j>!)~b4K#zEPJHTYyaZ7Gg_PU&0Veu7Rxmg>7>eF7?^X(AmsqrT^3Y_91Bm`-L1fap9$ZsZ)yO^0vWPy{r2Fkfj@IJP~=gaV-4i<*YB zw;xfUK&A;S_W$wOf6)EErKx0LhWe7=A5h5IY04syLc%_ZOlhLIG7ZNcd=ki#SZ+F8 z(;c+XYeIS(NjfuJI)WoUdJ9@yF4jZE-C|Jy!~%7-Ojh;l(! z-wW$jIwf+{tkbaYf6wPp*v!6Y9ev!^?b0YC@hU}aMcV1sfrhTGxye#Hir?DAJeiRrtz#!$Q--?j&&lMT$5U zI|nQOTK-JAk88o@XM-8`xjP{|t?qUDU%M>C^Fv^kFmJ`bdT%m4DtM3j-%c;G%7?rk z7+{(835tqo&tz9{Q8{@1`}qbP?{tFHKQs35`U7jCxce&6 zWWfZA1ov>4cm3O)&2=;;VkT0gG-~PC+hnBlZ#Bfq{w=e9Z2rv4giECvx}*<$ zestO36Qlu_TR(K+?Zs1cG6U2ds`D(vueLr6*8130jX-FEc-D_Iddp2}wxVV_A?9R? zdzkk4JnjA?WoxnD%T*@XlO_`*ofcYHTy zh4A+yc#ax!u0J;@vE{x8|3`~JG&SQ=Rff3jR$u+hE*kptvckuPx7{@34%hU0t8=eB zd0A`dTSCpUCoNr%CC_#GI|m|1hOIuYtJZW=N_{{w3{NNS`XR};FI%u%%@_M`rB|CJ zJ>N@xtkpg244g)MwI6m2{6F)?%k@=RBgHi;{mw1Kzd-3Y` zn;+P>_jvVVrG-lqED#wa;s#D@IWc%6@zh8yn-1YSWAF8|Kdn)johREIw&z%5nT8u0 zS5!FYjq>0pxEZB|K(ao3jmxB&PkaMT?_jxEt28AztfId5;YXXU9o2j&DbnjmwM`=m zP1NIeTvuzTBnvc3x@32);caPwM$}7I+T;k+GUsj7@ZkO~q2dnDOT*Q_cKJfZq#E}{ zJu)&{R<7UpK%=`?FP95`8m-V!KkFB^q6-I(RWi7JeO8S+lwaL>;HY}+L6?V!hH7VO zo-Z1aD12D0w_}(3@v1LFkIDwj9j4Z~Jf}TFs#~_@XsL%wt%)%t#u7;yKJ9OgR|HF9 zG;fD(7owV%vcRr>s5(nm9F(vP-#z2~k` z^T5G`D159kL?Rn9bw7*CSe2yJRKfix8Y}el;fYfHn3pd{tP0e;wjttLAUkuC8abO2 z>nO`e7)xag4qWO9#p25~$egR!UL}zhX#V|Xj(zGhtRIXdm#$rY!^jn4)iEa9KObAI zXnyN_l~~SxHe*4^{iP5er^r5U2p_pz8frr1ftg8>y+0woTVcW#u(@@{;5mb>e&J7e z(Qv8F9S9Y43k_zg4%$@^-Cjq8toaoUFsvf*qs8`e_|g#HizXiX-7%;rTtJBw2Ak8rq$`EP!X z5#Nr#Og&7@MPdE~HWw@^gWdc*{g``_AwEHx$069|bbOl{*=}UqVkV?9hpngcOaS9U zuxObJX?$b+8h)1CPb9V`5`DJR{Cd<}eq-}$V^`0a&T(B_Qpg6ptlo>TR#P_q^jP43 zvyqBy^71u&o^G^2jRx!)gOdklLP={rMu%9s?M-COx*s9YQ#1%JtTM63GJ!INokt&7 z_*~}#K@961Zn%EsMvK)Dp^ggT9AF641JU6Pu=hXLWT_uBwL~QZo3-2wY~D6LhF_zR zsyJF+`>xK5=mf}CXI<(X13&%IU$3mfuP$!qi{TcL?)FI?J4;7mnJOA8p!Nd7DF|MC zdXkoF$X*!!AafX&DOF*U4>b@fvZYffhe`(Grqy_~zxF;_k&;XZz2OW2{zs61!T;ot zaNT+PsueO@Hal9PAT*Xgcy$o+YV`q2YB*^r`-Ey zu8@CWj7?MJ60ZC%xH0-+CY(lH_1*yrQ~7fbbZZ*-Gb01(blvt%^ypyUF1yl7-;BKh zjsP4cGw(LfnG)HAxq_Es%t+hY(`21?2hB9Btl>_iVjvB`K)q*!ed3r~sJr^^(&v@E z5@^g?jQ>=%aJ6z}TS^Q-hhQV>895o~0+U^xlVgxw>27zT+v_m z?xsZh!w~(q zkJJKivKMo`W88 ziSypR+Z%#BH-2rc)xbISqo-*}o%jTu*a<2jHt0;Gg29miCIzy)9|}h)hsmCRcLKEh zS>x6Q^PX_#=CHY|AfE2%G{JnUq6C0~Z;(cwD6+X_nD`i*9d&Qh<~{CWDJTzv#=CH_;%JUy=mwm9 z3-S^AZ6zN$HLD{I_-O`jtm|c8e|M^8a1$c!DTmH=bS(Il zsN<}Ak&QRsEn#QE&LkbHF0s5ll02Hs{s{Zo^crpO&JO}2hjdcwh<*Sby_CnqUiadV z|6!b7w&Xk5kOili<>KI-I{fRsn1D_{;D)lRDG{%Z?l<>0`TgYB6G5UbKSzUtdnO)> zK1Qn{>MdYrPmP2z31GZPe^dvoCQW~oN|Ev%tjU5iDo}bl{Dx-WM^8OAn5dNGeZj|0 zDitrH(g%RfI4)w$ZJ%g1w^5Ioott9N$D>T-a-DBX6b@vpp}8v_y+MmM)&;KPso-2D zl{0(movGNU==&Ua;zDH3p?T^9Q|0e{01^^{j3-s-yZP3(F5ElXymN{0X4*sJlsfS8)EPr?1JS`9``*aP{S8}+mYDL<%VdY3ElzM3%g-ng9 zT4KppMbw*bNYT)Y&9F(%z=m+x-ctp;2#O-qu!lfxf?5?P!+yL9_J7y+6v{CMvjW4FRDB1O*A z_3)#dlku(^37vHyyI`(ryRW+&eLSz$3LX9>wLJb`8l`W`|LRUcjT{|e26Z(id(orm)2H)T_jT#@1S7sU9lp7pYAz4NGUuuF1&;M4sYLRW&E27v=|SY5 zqP7Jfb1#+5`*wk2km|P@(t7TLnB;b7}t>M3?6H&e-PGb!~A!n&g8JF*+ zM!*EiT&x}n_U$jK5SRo9)6MwbbBj91J0Yk-8vV}QTpZS1zSd}wyo<;FG52I#;6U~7 z7o4q?d>%hSW0DTEu7#`*s3wEP$B$ZuQs2bd9cnw|Txjy6H-D}MNmBt){!+7a+`Jq4 z-&PY0p@O%1g5l9W)W&fVM#Y@QlhI**h_+UT3ug)4y z%y*&UYdwjb8Wb*fBX=QxbxKAbShD^%OOj!J1zMM)=g?QNuF%RNb zBGNqwqRgbVj9JvThI6F`j9H94Dc<5vRqs{BP=7x78WQzjg*IawNogr(G!+BRkG7Pg z)Ed;<edmCr^vxMl83ndLOPBF@cegYSjYc0e38G02MOo~vo%tix;&NG=r+l4ptXF&T zA@7J8kz1j&Lz7iowFRPkb|!4CsX{0$vxgeS6e@U|N|XsEgzr2?aqPabxd+sk!TRvu zyNaZ_JpS-@?MN63Hz1{P;KV0AdF>YM(v2xDN|(YYpxxD3koa-nN0WS0U)xx?=7m%q zm?#1_F^fA{y7}WR^h+z#P7?t@I#q4#T`1`owHE(>Y5Z2ZVOe)FPNTlJlA$!NpZdR# zs}@jUX7u8L2{Ym^>G6B=ZPX|@>G1PyRhFkGmT9G84dgO>PN?QgJe#{jjUpTm`dc-7 zAbpbLp_+&W-Uf-n+#8VZU}6Z`M?Qq#@r)v3d7l#`@#2W`@@cz7u`n;ji+DvpGb%_t z8Y(ZD6ma#L`QkX&a_}5Xj(XJ@t?c?$AHZKGO? zV2K{nE>^wEDur8xN4u!SexP+_=LuA`I?~~MkozuN5B@@vW4#?9kftBKEo#rZ?PZm% zz!NngH;ledH9GXLT%dwppu-D&p@MsS;px<4a;8ycx)>^K%Jf6+I-Tg}2YTxgD z{o`555XjJAQFK2QGo>cP&S@%U7G26saqd<&S#7)5jGIbE+~`23ix@9a5T4w+(qSN9 zy>`i$b{YXU&}Bl#g=S~I6iRy40U?x6#;HgD&o>Tl(cLN}($q=0d)-J&b);!uT+ul6 zo%~w#12w<&C8hmH-$)HR^`Yp*Nv%0h09bI^a_@^}4r9!jeegwYOa?qjpu>yEC1a>G8$J+A8%H1F z%@w$;J3a4_=CNg?Nwv9EaUb_Anz8@EsI7q~R2ZZ}xTw$g2q3jU6_j#-3F`5Jb@+~|a4B+$ z2W8ohatuH3xA~ac&&fVlTCiA?dVzGyc~wRZ(wwhVNfZc7zN?Ueh+;M4uHY8Hq+u#C zK&+xl+(F=cL6zYnHe#Pfk5@Y{UF)h@lye3w98}&**ZSDP+@7$Ig|dtPayqg-d-Xf* zRiMZ3?y62fMWz)ml%S7vU__Xr{-*P5?~c8{qoMd1r0iDx;y!`!k4eL<6K+iB!SQ(} zVKd2q$gR~?CBkH7=o9aVCRKt*&s3!!2{KMi_kXVG1op|0tH~@m?@f_BYgM&d4*2}rZty;1 zNH{|&!vD5U=hoFE3+dqw_6c7UW^=P#D~@~MxW6t_Px3$WIe98%cd#6*gb8HA|Mm0R z6+i9KTo$P(Yx=b5qhHs8uY*H&k5j2o9=tpgRH^;*+qw?iIv!Lq3=3zgl;{YXeTY`3 zfBP(gq6ZwbZ3>P*`;R`|{IJMCS}l8Ek&EfiQvj3(<_O}%;!1a^Q@W6-A7~6!sTA2KfkBp_zxPBVF2Ye4JD@^p@rXJnt;n|Xcl(H zBG=QOM>Kt&0aNOK)s?tyyJ4~=+DOhN?cj!vid%$$GO|{ z;PxLg72K6>m*I!!9~|#@BpJ)^O+0PZhx?UpT8S>=@Z0w6n!z$yv_fc8wuHLI<8~)S zl$Os0${zNmScBlv{Gnp(I7xW3l03;+Z%T&rrC!)fi0D;MjR*%9`q92x0l{Ohpa&Zb zZJ8g?Y%~66Gs0mMtFq3N|Jj&u7{^LlVZkp42r*hdrzq!gJJ=1=%R>`Q`QM=9({Q1_ z!~0t27S^;mIQz?A)LUM54^nhqD=xd1mwBWLc@Ped>6CLJ30vdAvL%~_omcWY4~>Zr z-M6)^;KwKZ@gl_TT@rKyLfMzAU5O|0FF(z3g3`rB=_gnoJf~tvs2=Ih5;>4K`{8%+l5ASq_vaJ{leFr(9DIR3}Y>E^< zBxGINhHoLk&Re0|cEs~q$c()}1@G_Gu?Aec0chquLIu;Ylw}xx@!2=GVbc9s@nZ%2 zAJDA`6^z5)9>v))gv^}xRdh#tD;}AyCChkZSTgVif|^TdB)%RZk?JM4rgFo!?bw+2 zu*C!zd#huclum_ibUCg@F7GHan zS5|h)eAf{qjso~1)8%trg2zy3VUQhZaachWz?D){u59D@etc~wn%Uq*+45xr%=7l2 zSXpuoZWnYk4~C19`a`GD-0CS|=-#V9x<_AgVg!W&YNlMsO;C5?d8I$LvFR79X~ugM zq>|pHh9zgYd~y@IP={>b>Z!c0R1LB@=ZBAuM9W-5r7N`^nbO)ZUM}zCJqHb5Jg=MC z?8WDXF5GcAOB-sVeW70Sz8;?RrKHnYK8IUPjG|6eb|Futm;(3s1e~0h^#rP3jp=WW z=Fq`y6MDfacu2k;>sCvIGNp&;BP2Y~7#%x{zM3ju(gdVdN z+3501ZuDJP@)eg&3$P#7i(it%rq2ZY?E*Kp&*uV%Umr(U!*1sXQLLKJX3`f<7686r z)-YC<5%K~A3TC=Z*i4qcu!fu3p*)#ia zT};FZh+jF4AUluZ`oapobhE$Z_NYMx+ee*K@>V|UvD>E0A#~^cgTuL4V=amk!P%Td z6uWYnMI7wy5a*CrN(6e@$pUyxtuFksX-wgJ&KJ0DXMA7kMoJjEazws-awM9-uHIXN z7F*%)V=3q}wlAN*^S8?v@z`g-d~-4f>`y?6``;a0OR$*7Z>#9`Nt>&;rN6y>;yu^Zl8UmN z^;TNv=f5wk+|+T}6wAO{p_vac=}D-nXW8;|Owf|W)3@W;8uyQEZ!vS5(EYP~brPDkJ+3DWajYeqQDhw~_8Xb=l z$DRk8aqv!wnJqYRu`h{Cxo_E|*NMq?Jhs!MO|RMHnmO>xdq!Rs=(gq3xiCOtynPwe zNmaX95GdnzTg-#*IJ;!i^DegV!kkSvZsDSV@Wyx}^V0K#H2L{aXqUZpRUBStCt3&( z3Z^bJ!=$&Lcj?7rmuSDS=$d(KPI1zPu*)mQ-PDJ=a?ahL2R21Y-UYX%!acpQ?+-9rsHSqX~5LgQ8*TSJg0bF5a(%cT|TuupMR83poMxwFIzrUbDMvr zYvJrSukUC(T(KwNXEAj@`W1|z-u^h}^n~nZOZaOhRe>)f(MF6 zpRV=6s|g)GHPYUfWS3N9)*KFAeQz}cya}$0`KDZ1=RN~&aLGidzyG=i@8HHxa9?Z$ z%}aic>T8cPqxSb1J?zueIh5Ci#m1&wa>U+jSvRx=&@VAIx7L=ShF3O#V|r-9YU#7? zc2vJ|zXZU=X#Ade`36+a9A#AFbin1qdvo5*;Z34qgI$(da1i#fN54GrmP9O&80u4S zdrsQ!kq+LU(4IsR8CXxyDmS1y_b~qoFnko3 z%HKMV;2kZS+tyme$Fm&{BtC!>coUY~Til04i1H}pw7fh;f7`I%983yA`HSJlDAA;D z+CH8K01NbU#yqf7fYvz%s7_~*=N!6U)1_%Fu%Spcj2TEUbbm+Y{n&G543<*2g(@<} zOkuFw(-PU(ndxDS4({)ijp`ew%m@fNO2 z%te_{d*GbPfM)Cj?PC#ob=U}aAsb>LAY+-AWP#Qru;V~V!n^U^;DeGt^>Yx@LWQHh z7>=N8W4oNEO+O;fd5c#;We@P#ZQxXz(N5ni$tkMtvkjK2M#{-f?8`vgcW|8v0pQM` z7v?aJN{%gMt@mfpwjFSJ5pC@e8a&o(zv{*NPsdUSp|xv%7HoI1+!`gS-e6Scmw3bp zODwOR@^n?oBgNGU$vWo!&=UhKCNsPwSvS91F+6(Q@#bO`Keu6I!R?51B-4dKy!`W@A`3EfvdQ+;#kZg@*2j1{|_|DppbgtiZ@fm1*T zb=FQ3(~cZbM~=)aAXK~!30$y+VXFJX?8oLRlD6(XHA2NYo$gBxv$MC1*l^Q@&^k_R z9dtA4kG$9R|zyod=uHu?9kyCVZ9DGvo3Xyz+HtF!wp| zT(Elf86b>!c-G8=fhC&kCic~3UN#zgQAhP{&NXty9t;)9=_s1D!i&{T24$VCNq?f(&4c0|5g^(|s$rO|b3 z7R=;Xt=;Je6eiI%xshs8J^cDZa#h99LO}>aW5Nw#jSA#}oDq*=7lJ5YEK9-|M!i*J zxLGBy#|kbTRU@>Zy!^T9J*jY>N~(V1GL_(l#sl??e zj)uWL&MP(;kElpXt#=2-n@Sg)EWF9RiqOu&ToYzoQE5bxidiO&wd1MrFy-S?#WPeW z%H-Wu5_D`iVZ;z#(S_QdT;i1dd0qj4vj_b#;WL3HM z9fl7vNBlUf(NQoFS4Zqu&7Nn?o&%Xq*5%&M8DY7{`z=sOb=|pU&mp8WH>}|YrG-$m zf1LQu*QP~t?6nbfr#Zlhd9Ui(GrNHn-P~rQa0pv?YM5tK*@T!g1;FI+WL=B^Pu`(P2;#;%#eblTtOm3#j78= zf>TV#tDG+qI&KBw8P|@pXZm^WZad4Ng^_SJBwR%#Ir($T`3|&@so1=>=QQD+hOZ^x zBRX{ua%*+d|dM5Sx7KFYwMa)v{QNY{8wRD~~Gfu*_bT<_(w?q-q4F7^>Ef4oJ zWC)n^tZggPujF)D%i^vKDUGk@R`q3&D2e;}D{B%%e&27Gk^^y?`@v(Jgg_oloBK1$ObwZwl&rlRY>8LC_X&pgp^s)n zOws~$n^KmK>xEqW`C}giR*E<6+w=9)w$>qVWbHx+bZhrY#drf6Yf|u{rn@7j$%3X+OyjfOu4KxIVmDdCngRgT|(x2Gx+lN z3NUQXGyoWXUybIxn(kf6SXw8?Nz;w#SwzLPW$VOjhxbGeTNb-eujq#zAE~Ix_VMeZ z6JrjOUuK|GF(D>caTYGHpt*Lp<-9eiMfdN}{JcA9L))AF6&Gx<0o{QUO)>CWjPq~n zLUfVL9`6Q}RmTXr<7I3MaO8eEG5gpUbvS&u1MQk@zwQZqA>QPe5ZZ0evka(6tHi}! zh3xDlw36O$`M$!C9=oPgADd>4Q00^6QB;^Uzz9|}ZAZOK5OSOkTsQ9p+>Th?g&VVg zwqWHFPyD0@LIsD>|2n>c3^o@K}@+iIY5khZ_i|PCc0~`Djw{{!5 zw`oIT|CbLU=lD=U-SH;q>&(068??%1@I)~Mi zPG+!}laxG?8{-Pvv8AoRh}aMB=~P@8flcd;kZFvdm_7ai5OJjbbwBKd9hgrWp#e80 z5CmvnabXmGau!(4Z`VV{Q~Pugs&x{_8x!?_xkF89JempGWO={2bUT^}?2F56`F;q^ z90lLV2&jqkGD6hw;cHjDb2nvqxKBJZL20%*COKM^q3alX;9y^ar29{1`hDm@t_N&Q zYEEPZ?*|oLG%tMhHgwJyp}w{2Z)66$f~03i({H&^SAnBzvn4k7x`MV;7j+WH^NGI! z6a8JnLwX%y4CPF}{T;kOvbl}Tce`TKfS9(=O($}tV?li$w4BeAj)yN@g|qYzL+|DJ z-q$wI!4cJF>lg$wPfA{_-wJYW_E_u#GV}3UZRN#7$dC}`cZh{Ae)Md?UErA4>_N41 z@A0Eu-ByeYyahk4;rF3u{CR-&XCiO`iU zQwEGKn1-kkIf7A{hj6sh1E&239&)(fR$#1UhD4iM7Eg|wfhf+GtfLOVx$<+#TAOBa zkkhlaxVn_(+Jp5_e?4~R{gaxz+izB^_@%dFe`W9+gwme0d_Nof79nT12w$^h9TBAs z3S_nmzP!nnJjq{a8`707Lx9+_msE?!pz<+nCyB4Lq4E1ZnlFET9PdpkoX)-g(%#! z=PE0}ylmXDyu}D97KCp?E|CZ=Z;4oB8d3TFxen~4kG?yn@4+WRS0j$mLRr7J{^u`; z=6xt&qtM)D78V7zaq%9(QvE}R5$fk8CP-MPkrVR0FY3o3)T=oXxq!+qnCZ8A2nkhj{kD2)}y)zW| zLZ~?CV_>0plh>CriE{TC5~}Bdd%8d%)&^x8K`=lGS3Q9HUcr1kdKcglC2QHPkGW0- zWQOd4lOQm9jE`Rm6dZd1T2xyCeA@#AKWW|=i~J`+H~n(MS$MQ7xI5};%XS^iwJZ1- znK-P7AX9=fP*}wQ;-u-__(XgF zh9ovZz!B^;-~;6?ofiCBP;9UmcKR*3k7o_|D5>r;j%Z#Nc){oDe3tAKa4UKUj3Wwv z0N)%jw5+xhpGdO-t5$AUWK0a=pEz1FbyyQLu;hzu9U+lTA`q!KaTVkhCdjJ zM-Ro(q=rsT<0yv-h&mT9Y=XNQjUoEzc|)HKDf%%^9m)lX!p;7j?Jmx>GJpjN#ZK5$ zZW0@dpt(5dn zP@t#d80R!gs@SQQ-$4JW1`Xsm2|GzXx2A47RM+Ew4BCJQa40R zFVkM)ThiH>9RW*YlW3gS9Mv)Cp~BA8nY(?fpL@Q;%Lg0T2~qWCRmLf0i%+V zzYdgofn#tv9C-%^mGcM`T|r2&TM#r@I-AlRzSf=9A2!VMyy#%}UT{X}jqMM{|G?b+ zLSkfDW~eE*RkoHhG8s|7w9jz$Y>}w#ZU@|ihb+axQL@cA`fg{y_whio=DTUKUG%>X zIkBcFIKrWi!zo0yY+n%BpM%nh7I=$*(t5KCIM`Arp{1G63?ysXI3aQ}2kjJEn)-O6 z)cBU|VkgJK2{eT7I@|C zzRapPfpcZaY8EB-@cVUjW1~J&+-oZd(dMwIWA1*xZf)F*$qeHJX;s8LN4mFY(KK16=r3;rAJ^JEMv+=OLZ$A`lTw(Xq5sYZCUi010TD{q5 z7fj=Ntzm4`upy&lJ*y9nZVwf^`zMS8o8RoRCDI0?PQ?os^>^<<+kCSTT94VZ%^%d^ ztO;%NxA$hjlv$E(_*f6-J=pv)Mt_p55B0Y z7la3*MQ~p{h8we~LrKY&AJ|~=jB8kM`52h&dLO??gz4ZAjLW7_vN2J}eQUiLmk$R1 zewj9uIu!yIy`||;^#<=}WE(PAK$LxV3?OR(r!+Sa`; zTt9YA-A$9Ty?1Qx5C^sMdl|Hz>hQ+{$Z7fB4zP;^q5IcIhKr!y2Otg0_s)Y`CZJ!T zTLc4B2K2zKSxF$h!qQr}lG_)cE1ksTcuH2vL-9hAlEtvGk}b8`Wo&L`0__9XTs?3- zeQQ`p8*%6ja0pT);K-hrX><8;;5&f+ZWunh7cC%?B4HII3g_s;>!*>^-!%EU%xht9 z!R~BM1iQ1fcxP*}Lp1wXxbMY?-0MpT!s5{fu6BUdF0wW*cvfi{Rh*FVsGYz?xLv>C{2BfwiDMdF7 z#>t@-c3|j93CaU8L;Z^lmaxB8yNx~|fO=qu{|H#k@%0jLn_t<)~PO#@T8`(~XkU_owGY%bYlmHtVaI$|5D@TWKNg&gC z;T$mSllbU-Xa;46vje%v%tzZI=5A?u&kpH0+ zC2!76_rfrLVr0&@Pp-HwPgVt^#-WZp+0+`A0ngz$3R@V?vEamcprY{+vS>7lC$p%q zOYJ4wgI5~}`iVwUeK-lL71R|x33Ngb>_v^>$eLNZcn)W2U73v8n4xCOf@A3kw!Y*j zy27K{s3mx1Z_usaNz?*P$<#d-4!va8@oHaV9sDr9(+(N-*v5|Zz`mGKTbjGNz{L&X z1mv7;sDt0bk5#_8JJ}lJk4f=v?s;d68MiCHQ~Ap^=7E`5b;tctvWdC{!Km%5n@h29yN8 zbig(mP(wU<_;B33Gpdri^L>n^-%u(%>VsV{DES6o2E%n0CC|^BvcItPhErMRr|~UK z9e$uL=U~KXvQ$>+QTQ@Lc8e7n2faP9R)Z23=)H#1#?h`em7gJ`y-NhGt5=v{d^VOe zYp*!R*$8iFn*8S7%+z}*EGld|zn~Yn4Lf__m?_(l=N^F0e;&e#49877P;;C2M#&tp z^Lpf&wuvsH189y`f_s-~`~qw8FlM{=j48W;o3{`hDl_BZv*D*P(cl`up))?iisG#U zc4%R4`m*)w4n+MwJzaZT6IT{KGsz^w1e$;$Ak;Ec)F5cls*5d}8R`pB3~GI$7(`dB zib(4gEyc-zSRVyis7gf|Kr61Tf={|y9YICy{v_Sil~$^ws9;5nn$?sAvnO$Xn?EvV zzWd$poIAPqPG;_%J5n!sB}Diw+p3^x3o8w}FyJ+o6-0QL{RJX6mOI*Z7f8}bo1==ugALYBE1f44%K=I(U)qygt~!EuLxgmjq(zrvN?hv^vWG2p_pJ} zrDxd_5%5*!%*%Ww(Q;`FrBs@A^3>7$yrG^PwDgtt<9CjbF9{>l|xB# ztC6nT&hbc85MMjnd7h<|M#MIGF$%GoL`)Sni?F39eH<^D1knmn&YEhLWjUS~=5`Nz z&qEMJn9-B|rHEywC}MJVy+=S8rnuFV*Y7%uSEvxWtow0!8N9LJ><2B68XZ$imxF0s zYb#%boA;aDfth{qko2?*9)%WPT;s`f-q;p_zgA~+IE96)jKnc?)A`7@HxY9+!-goO z#6j7QnD&DgDAmu>2v+DGmY8uHQ1Go8j8FSOVf?xy=2O1ENw)C!hcaxD3p*7}@j_A$u?Xjl4kM~`wwiiEC z9e5KqiO^ktLmH`+cVYl&1uEr@=(+wTcN%)8>p0W%P-Bo_^2D8|xJqUv^3dD53TM4# z|Aa*_#@GjgI}+Vds&^>45;c-7wq$rC8FMneP9Nz#71=p4-#VPgnSePnV(>3bOC+sh zeDyw(Mn!Ja3{cHe$|EUj{)RUd{YXcVC{@LmTUA8PG+rK8)grAUTm@EXb^KoIUDDB) zvetMSO0!=Rr<0x+_P(fCIU*JdCn&H}gHPg6_v@aHxI~7#g45D+m^hvi>tlJM)`pSN8j&Gd>-M)^^ zc{a8y3cgagiak~|>`}Q&*L$rWRj%`;k;C)-8n1ezvd*eZfQ0z5>&#+3yTOwxUxVM5Wckl~BplV5CzhnL?>drZI`g z*+4k+cZzjpxUmTN*lyZle498Zt?K>wk!ixOcdq77$vhK9g(nezuLrk0nZGa{N~1Yb ztC3D%EZe9~btn}+f!KN&+?E=4u~FD$u!XJA7bvNie~`}nU83V-D(31dt4P2K?@}j( z^eC{Qni}@9areToW9{ZERRm$y70q%WzhJ$WRTZMdwiY|%On;^k7tph@wy=oh4lPi( zqN#&M`h`--VaM>z4<_tAqQ?JMaf<8k?&ziZ*>w=gA2`Df#C3aGRV zQt;h6u&^t1!FwtKL0|9(*>tf>7iP`BT|LkH~DxH&1cqTL3Mx^djoP&1X#~I)6Aj zK1qW?&C2>``vG(?i~4v>5rZ-_v#z&SBQSyL7<1VN88aL{#VI6u)aRaW{}NLS)WWB+ z{m}t`=2~1Pjg)DQ@x=-crBCkF^aU8yhobhADH>Fp>R4U8fUvMzeat0uvFs~wyWsy@ z3>Np(a6cNiO9(U5xD}{!1FUBn3(m`;OU zE)U~O*sWCzfG)Fcn@e3bYA#l`y?t$AS=0C=e>m2czUEZS+Y#;13jO`;T9oOf@Xg-1 ze51MK2FdkPI28utr1V>7^7`p{e2N8@Gw#Zk#y0J?v*Tf4q+Od+#imd~8sk)?8ZS&H zES02N2x1N;LGV6wy-Dl}fNuvQt(x7U6wYjZM#d$V3?QYfb1R9|$O|gMa`JW^xXmR` zNJ(`rnRvN}t)YY=o41g@c9&X8I5(0g%y25?>f2?IKjI~kmAra_kQU;GhT4X3Dem(; zm_IXjp-_%UY^|#S(noe9=SdndVR?@y*M4%!wNACfpBqHA+J4sNbzLL;C5~3qWxHO) zg|_c(pv@UMNkWvt4aO011Ec|4@~~&Q;V)6}GRe0`p4OQE0h7l(5}!q!5Ew6a&6VY?cs`5)L-j zWUyTv`7z5aDgg2lR`N!GYH!z+7^RY{)JFi7(X17d(XWp+czILpTXTy@Wr= zhO81)Fwim=kAz=X;onHO#E+saYvV~_Ju`ehoahsP)wtvg*;-Koa+JC}QaGydfxP6B z)nrCTJn2e5{aU83aFJ2C`|$LLKNS>=J#swa=<%5Ox7b~J>3T(>9@?tdse0-74Zld} zWINUNiX5n$T&J}#5AcSzB@9Q8?y*6gY5-?omja+jf9LF2oWfG@zoc(G;CY|t1b}JM zXP*K{8EbK@0A6P6{=gX!KR-260BsTBPmyqV$#G0scX2vG&ax#8KsH{8D zyFDq4u^kRurWux^^+ykz9^r_RrThSn8Cbs048ZTxmvKl*j_5ZSz3h7@0p+!w@7_b< w=v*8D5e_~uJpmkF`F3^$Zi-_uX78uXMJ(c(%CEn_xY@IxFg|JAfq0AfU*PgbaR2}S literal 0 HcmV?d00001 diff --git a/src/main.rs b/src/main.rs index 5363d35..d6269a7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,10 +2,11 @@ use std::collections::HashMap; use rand::rngs::OsRng; use rand::RngCore; use std::io::Write; +use std::io::Read; mod utils; fn main() { - let version = "0.2.1"; + let version = option_env!("CARGO_PKG_VERSION").unwrap_or("x.x.x"); println!("IPass v{}\n", version); let args = utils::get_args(); @@ -21,12 +22,14 @@ fn main() { "list" => list(), "add" => add(&args), "get" => get(&args), - "edit" => edit(&args), + "changepw" => changepw(&args), + "changeuser" => changeuser(&args), "remove" => remove(&args), "import" => import(&args), "export" => export(&args), "rename" => rename(&args), "version" => version_help(version), + "clear" => clear(), _ => help_message(&args), } } @@ -39,7 +42,6 @@ fn version_help(version: &str) { } fn help_message(args: &Vec) { - let mut help_messages:HashMap = HashMap::new(); help_messages.insert( "list".to_string(), @@ -58,8 +60,8 @@ fn help_message(args: &Vec) { "tells you this message, takes an optional {command name}".to_string(), ); help_messages.insert( - "edit".to_string(), - "lets you edit an existing entry, given the name and the new password".to_string(), + "changepw".to_string(), + "changes the password of the specified entry".to_string(), ); help_messages.insert( "remove".to_string(), @@ -81,12 +83,19 @@ fn help_message(args: &Vec) { "version".to_string(), "explains the current version".to_string() ); - + help_messages.insert( + "changeuser".to_string(), + "changes the username of the specified entry".to_string(), + ); + help_messages.insert( + "clear".to_string(), + "clears all entries".to_string(), + ); if args.len() < 3 { println!("You can use the following commands:"); - for i in help_messages.keys() { - println!("\"{i}\"{}- {}"," ".repeat(8-i.len()),help_messages[i]); + for (cmd, expl) in &help_messages { + println!("\"{cmd}\"{}- {expl}"," ".repeat(12-cmd.len())); } return; } @@ -110,19 +119,20 @@ fn list() { } } -fn add(args: &Vec) { //TODO: format: [specifier] [email or username] {password} +fn add(args: &Vec) { - if args.len() < 3 || args.len() > 4 { + if args.len() < 4 || args.len() > 5 { println!("Incorrect usage of \"add\""); return; } let pw: String; + let username:String = args[3].to_string(); - if args.len() > 3 { - pw = args[3].trim().to_owned(); + if args.len() > 4 { + pw = username+";"+args[4].trim(); } else { - let alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!\"§$%&/()=?´`²³{[]}\\,.-;:_><|+*#'"; + let alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!\"$%&/()=?{[]}\\,.-;:_><|+*#'"; let alph_len = alphabet.chars().count(); let char_set:Vec = alphabet.chars().collect(); let mut chars_index: Vec = vec![0;20]; @@ -133,7 +143,7 @@ fn add(args: &Vec) { //TODO: format: [specifier] [email or username] {pa // println!("{} - {} - {}",index,(index as usize)%(alph_len-1),alph_len); chars += &char_set[(index as usize)%(alph_len-1)].to_string(); } - pw = chars; + pw = username+";"+chars.as_str(); println!("Using auto generated password"); // println!("pw: {pw}"); @@ -158,16 +168,18 @@ fn get(args: &Vec) { let filepath = &(utils::get_ipass_folder()+name+".ipass"); if std::path::Path::new(filepath).exists() { println!("Getting entry"); - println!("{}",utils::get_entry(name)); + let entry = utils::get_entry(name); + let mut data = entry.split(";"); + println!("Username: '{}' Password: '{}'",data.next().unwrap(),data.next().unwrap()); } else { println!("No such entry!"); return; } } -fn edit(args: &Vec) { +fn changepw(args: &Vec) { //rename func to changepw if args.len() < 3 { - println!("Invalid usage of \"edit\""); + println!("Invalid usage of \"changepw\""); return; } let filepath = &(utils::get_ipass_folder()+&args[2]+".ipass"); @@ -179,8 +191,7 @@ fn edit(args: &Vec) { output = args[3].clone(); } - let mut file = std::fs::File::create(format!("{}/{}.ipass",utils::get_ipass_folder(),args[2])).unwrap(); - file.write_all(output.replace("\n", "").replace("\r","").as_bytes()).unwrap(); + utils::edit_password(&args[2], output); println!("Changed Password of {}!", args[2]); } else { @@ -188,6 +199,28 @@ fn edit(args: &Vec) { } } +fn changeuser(args: &Vec) { + if args.len() < 3 { + println!("Invalid usage of \"changeuser\""); + return; + } + let filepath = &(utils::get_ipass_folder()+&args[2]+".ipass"); + if std::path::Path::new(filepath).exists() { + let output: String; + if args.len() != 4 { + output = utils::prompt_answer("Enter new Username: ".to_string()); + } else { + output = args[3].clone(); + } + + utils::edit_username(&args[2], output); + + println!("Changed Username of {}!", args[2]); + } else { + println!("No such file!"); + } +} + fn rename(args: &Vec) { // prog ren old new if args.len() < 4 { println!("Invalid usage of \"rename\""); @@ -233,7 +266,29 @@ fn import(args: &Vec) { } } if std::path::Path::new(&(location.clone()+"/export.ipassx")).exists() { - let content = &mut std::fs::read_to_string(location.clone()+"/export.ipassx").expect("Should have been able to read the file"); + let mut reader = brotli::Decompressor::new( + std::fs::File::open(location.clone()+"/export.ipassx").unwrap(), + 4096, // buffer size + ); + let mut content: String = String::new(); + let mut buf = [0u8; 4096]; + loop { + match reader.read(&mut buf[..]) { + Err(e) => { + if let std::io::ErrorKind::Interrupted = e.kind() { + continue; + } + panic!("{}", e); + } + Ok(size) => { + if size == 0 { + break; + } + content += &std::str::from_utf8(&buf[..size]).unwrap(); + } + } + } + let lines = content.lines(); let mut name = ""; for i in lines { @@ -275,12 +330,39 @@ fn export(args: &Vec) { //TODO: compress data } } - if let Ok(mut file) = std::fs::File::create(location.clone()+"/export.ipassx") { - file.write_all(collected_data.as_bytes()).unwrap(); + + + if let Ok(file) = std::fs::File::create(location.clone()+"/export.ipassx") { + let mut writer = brotli::CompressorWriter::new( + file, + 4096, + 11, + 22); + + match writer.write_all(collected_data.as_bytes()) { + Err(e) => panic!("{}", e), + Ok(_) => {}, + } println!("Saved at: '{}/export.ipassx'", location); } else { println!("Failed saving at '{}/export.ipassx' does it exist?",location) } +} + +fn clear() { + if utils::prompt_answer("Are you sure you want to clear everything? [y/N] ".to_string()) != "y" { + println!("operation cancelled!"); + return; + } + + let paths = std::fs::read_dir(utils::get_ipass_folder()).unwrap(); + + for path in paths { + if let Ok(p) = path { + std::fs::remove_file(utils::get_ipass_folder()+"/"+p.file_name().into_string().unwrap().as_str()).unwrap(); + } + } + println!("Cleared all entries!"); } \ No newline at end of file diff --git a/src/utils.rs b/src/utils.rs index 61ebf0b..132dd7b 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -10,15 +10,23 @@ pub fn get_args() -> Vec { } fn vecu8_to_string(vec: Vec) -> String { + let mut do_print_warning = false; let mut out: String = String::new(); for ind in vec { if let Ok(a) = std::str::from_utf8(&[ind]) { out += a; } else { - panic!("malformed character"); + do_print_warning = true; + eprintln!("[WARNING] malformed character {}",ind); + let mut temp_vec: Vec = Vec::new(); + temp_vec.insert(0,ind%128); + out += vecu8_to_string(temp_vec).as_str(); } } - return out + if do_print_warning { + println!("[WARNING] Output may be corrupt"); + } + return out; } fn encrypt_pass(nonce_arg:String, pass: String,mpw: String) -> String { @@ -29,13 +37,20 @@ fn encrypt_pass(nonce_arg:String, pass: String,mpw: String) -> String { if nonce_arg.len() > 12 { nonce_argument = nonce_arg[0..12].to_string(); } + + let mut nonce_hasher = Sha256::new(); + nonce_hasher.update(nonce_argument.as_bytes()); + + let nonce_final = &nonce_hasher.finalize()[0..12]; + + let mut hasher = Sha256::new(); hasher.update(mpw.as_bytes()); let master_pw = &hasher.finalize(); let cipher = Aes256Gcm::new(master_pw); - let nonce = Nonce::from_slice(nonce_argument.as_bytes()); // 96-bits; unique per message + let nonce = Nonce::from_slice(nonce_final); // 96-bits; unique per message let ciphertext = cipher.encrypt(nonce, pass.as_ref()).unwrap(); return hex::encode(ciphertext); } @@ -48,15 +63,29 @@ fn decrypt_pass(nonce_arg:String, pass: Vec,mpw: String) -> String { if nonce_arg.len() > 12 { nonce_argument = nonce_arg[0..12].to_string(); } + + let mut nonce_hasher = Sha256::new(); + nonce_hasher.update(nonce_argument.as_bytes()); + + let nonce_final = &nonce_hasher.finalize()[0..12]; + let mut hasher = Sha256::new(); hasher.update(mpw.as_bytes()); let master_pw = &hasher.finalize(); let cipher = Aes256Gcm::new(master_pw); - let nonce = Nonce::from_slice(nonce_argument.as_bytes()); // 96-bits; unique per message + let nonce = Nonce::from_slice(nonce_final); // 96-bits; unique per message - let plaintext = cipher.decrypt(nonce, pass.as_ref()).unwrap(); - return vecu8_to_string(plaintext); + let plaintext = cipher.decrypt(nonce, pass.as_ref()); + match plaintext { + Ok(res) => { + return vecu8_to_string(res); + } + Err(_) => { + eprintln!("[ERROR] Error decrypting data, check your master password"); + std::process::exit(1); + } + } } pub fn get_home_folder_str() -> String { @@ -82,21 +111,46 @@ pub fn create_entry(name: &String, pw: String) -> bool { if std::path::Path::new(&(get_ipass_folder()+name+".ipass")).exists() { return false; } - edit_entry(name, pw); + let mpw = ask_for_pw(); + // println!("{pw}"); + let pw = encrypt_pass(name.to_owned(), pw,mpw); + let mut file = std::fs::File::create(get_ipass_folder()+name+".ipass").unwrap(); + file.write_all(pw.as_bytes()).unwrap(); return true; } -pub fn get_entry(name:&String) -> String { +fn read_entry(name:&String,mpw:String) -> String { let content = &mut std::fs::read_to_string(get_ipass_folder()+name+".ipass").expect("Should have been able to read the file"); - let mpw = ask_for_pw(); return decrypt_pass(name.to_owned(),hex::decode(content).unwrap(),mpw).to_owned(); } -pub fn edit_entry(name:&String,mut pw:String) { +pub fn get_entry(name:&String) -> String { let mpw = ask_for_pw(); - pw = encrypt_pass(name.to_owned(), pw,mpw); + return read_entry(name,mpw); +} + +pub fn edit_password(name:&String, password:String) { + let mpw = ask_for_pw(); + let entry = read_entry(name, mpw.clone()); + // println!("entry: {entry}"); + let mut parts = entry.split(";"); + let username = parts.next().unwrap().to_string(); + let _old_password = parts.next().unwrap(); + let data = encrypt_pass(name.to_owned(), username+";"+password.as_str(),mpw); let mut file = std::fs::File::create(get_ipass_folder()+name+".ipass").unwrap(); - file.write_all(pw.as_bytes()).unwrap(); + file.write_all(data.as_bytes()).unwrap(); +} + +pub fn edit_username(name:&String, username: String) { + let mpw = ask_for_pw(); + let entry = read_entry(name, mpw.clone()); + // println!("entry: {entry}"); + let mut parts = entry.split(";"); + let _old_username = parts.next().unwrap(); + let password = parts.next().unwrap(); + let data = encrypt_pass(name.to_owned(), username+";"+password,mpw); + let mut file = std::fs::File::create(get_ipass_folder()+name+".ipass").unwrap(); + file.write_all(data.as_bytes()).unwrap(); } fn ask_for_pw() -> String {