From 6016d5d67b5e4cc71f91f5b8b2a0ce3fabe03455 Mon Sep 17 00:00:00 2001 From: Iwan Date: Mon, 4 Mar 2024 12:36:07 +0100 Subject: [PATCH] Draft to allow run in FIPS compliace mode Signed-off-by: Iwan Igonin --- build.gradle | 1 - buildSrc/build.gradle | 2 + .../opensearch/gradle/info/BuildParams.java | 1 + .../gradle/testclusters/OpenSearchNode.java | 1 + buildSrc/src/main/resources/cacerts.bcfks | Bin 103905 -> 0 bytes .../main/resources/fips_java_bcjsse_11.policy | 29 - .../main/resources/fips_java_bcjsse_8.policy | 34 - .../resources/fips_java_bcjsse_8.security | 134 ---- .../main/resources/fips_java_sunjsse.policy | 29 - .../main/resources/fips_java_sunjsse.security | 134 ---- .../src/config/fips_java.security | 10 +- .../tools/launchers/JvmOptionsParser.java | 2 +- .../tools/launchers/SystemJvmOptions.java | 9 +- .../licenses/bouncycastle-LICENSE.txt | 25 +- gradle/fips.gradle | 96 --- libs/ssl-config/build.gradle | 8 + .../licenses/bc-fips-1.0.2.5.jar.sha1 | 1 + .../licenses/bcpkix-fips-1.0.7.jar.sha1 | 1 + .../common/ssl/DefaultJdkTrustConfig.java | 22 +- .../opensearch/common/ssl/KeyStoreUtil.java | 2 + .../opensearch/common/ssl/PemKeyConfig.java | 11 +- .../org/opensearch/common/ssl/PemUtils.java | 653 ++---------------- .../common/ssl/SslConfiguration.java | 9 +- plugins/identity-shiro/build.gradle | 3 +- .../shiro/realm/BCryptPasswordMatcher.java | 41 +- plugins/ingest-attachment/build.gradle | 3 - .../licenses/bcmail-jdk18on-1.78.jar.sha1 | 1 - .../licenses/bcmail-jdk18on-LICENSE.txt | 23 - .../licenses/bcmail-jdk18on-NOTICE.txt | 0 .../licenses/bcpkix-jdk18on-1.78.jar.sha1 | 1 - .../licenses/bcpkix-jdk18on-LICENSE.txt | 23 - .../licenses/bcpkix-jdk18on-NOTICE.txt | 0 .../licenses/bcprov-jdk18on-1.78.jar.sha1 | 1 - .../licenses/bcprov-jdk18on-LICENSE.txt | 22 - .../licenses/bcprov-jdk18on-NOTICE.txt | 0 server/build.gradle | 4 + .../org/opensearch/bootstrap/Bootstrap.java | 13 + .../common/settings/ClusterSettings.java | 1 + .../common/settings/FipsSettings.java | 23 + .../org/opensearch/bootstrap/security.policy | 22 + 40 files changed, 251 insertions(+), 1144 deletions(-) delete mode 100644 buildSrc/src/main/resources/cacerts.bcfks delete mode 100644 buildSrc/src/main/resources/fips_java_bcjsse_11.policy delete mode 100644 buildSrc/src/main/resources/fips_java_bcjsse_8.policy delete mode 100644 buildSrc/src/main/resources/fips_java_bcjsse_8.security delete mode 100644 buildSrc/src/main/resources/fips_java_sunjsse.policy delete mode 100644 buildSrc/src/main/resources/fips_java_sunjsse.security rename buildSrc/src/main/resources/fips_java_bcjsse_11.security => distribution/src/config/fips_java.security (84%) delete mode 100644 gradle/fips.gradle create mode 100644 libs/ssl-config/licenses/bc-fips-1.0.2.5.jar.sha1 create mode 100644 libs/ssl-config/licenses/bcpkix-fips-1.0.7.jar.sha1 delete mode 100644 plugins/ingest-attachment/licenses/bcmail-jdk18on-1.78.jar.sha1 delete mode 100644 plugins/ingest-attachment/licenses/bcmail-jdk18on-LICENSE.txt delete mode 100644 plugins/ingest-attachment/licenses/bcmail-jdk18on-NOTICE.txt delete mode 100644 plugins/ingest-attachment/licenses/bcpkix-jdk18on-1.78.jar.sha1 delete mode 100644 plugins/ingest-attachment/licenses/bcpkix-jdk18on-LICENSE.txt delete mode 100644 plugins/ingest-attachment/licenses/bcpkix-jdk18on-NOTICE.txt delete mode 100644 plugins/ingest-attachment/licenses/bcprov-jdk18on-1.78.jar.sha1 delete mode 100644 plugins/ingest-attachment/licenses/bcprov-jdk18on-LICENSE.txt delete mode 100644 plugins/ingest-attachment/licenses/bcprov-jdk18on-NOTICE.txt create mode 100644 server/src/main/java/org/opensearch/common/settings/FipsSettings.java diff --git a/build.gradle b/build.gradle index 55b31ca816214..502d0fc58f673 100644 --- a/build.gradle +++ b/build.gradle @@ -65,7 +65,6 @@ apply from: 'gradle/ide.gradle' apply from: 'gradle/forbidden-dependencies.gradle' apply from: 'gradle/formatting.gradle' apply from: 'gradle/local-distribution.gradle' -apply from: 'gradle/fips.gradle' apply from: 'gradle/run.gradle' apply from: 'gradle/missing-javadoc.gradle' apply from: 'gradle/code-coverage.gradle' diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index b984ef3800490..a9f2351994048 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -123,6 +123,8 @@ dependencies { api 'org.jruby.joni:joni:2.2.1' api "com.fasterxml.jackson.core:jackson-databind:${props.getProperty('jackson_databind')}" api "org.ajoberstar.grgit:grgit-core:5.2.1" + api "org.bouncycastle:bc-fips:1.0.2.5" + testFixturesApi "junit:junit:${props.getProperty('junit')}" testFixturesApi "com.carrotsearch.randomizedtesting:randomizedtesting-runner:${props.getProperty('randomizedrunner')}" diff --git a/buildSrc/src/main/java/org/opensearch/gradle/info/BuildParams.java b/buildSrc/src/main/java/org/opensearch/gradle/info/BuildParams.java index a9ccb75569002..bbd06c84b30c9 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/info/BuildParams.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/info/BuildParams.java @@ -52,6 +52,7 @@ public class BuildParams { private static JavaVersion gradleJavaVersion; private static JavaVersion runtimeJavaVersion; private static String runtimeJavaDetails; + @Deprecated private static Boolean inFipsJvm; private static String gitRevision; private static String gitOrigin; diff --git a/buildSrc/src/main/java/org/opensearch/gradle/testclusters/OpenSearchNode.java b/buildSrc/src/main/java/org/opensearch/gradle/testclusters/OpenSearchNode.java index 268de50340cbf..e8bec036bf5a9 100644 --- a/buildSrc/src/main/java/org/opensearch/gradle/testclusters/OpenSearchNode.java +++ b/buildSrc/src/main/java/org/opensearch/gradle/testclusters/OpenSearchNode.java @@ -1182,6 +1182,7 @@ private void createConfiguration() { baseConfig.put("indices.breaker.total.use_real_memory", "false"); // Don't wait for state, just start up quickly. This will also allow new and old nodes in the BWC case to become the master baseConfig.put("discovery.initial_state_timeout", "0s"); + baseConfig.put("fips.approved", "true"); // TODO: Remove these once https://github.com/elastic/elasticsearch/issues/46091 is fixed baseConfig.put("logger.org.opensearch.action.support.master", "DEBUG"); diff --git a/buildSrc/src/main/resources/cacerts.bcfks b/buildSrc/src/main/resources/cacerts.bcfks deleted file mode 100644 index 9c3863eda60c51ca08b3a5eb73434b1af6508b5a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 103905 zcmV(*K;FMFg8`M?FoOY=9x#EF1_>&LNQU&LNQU&R|G&g z$@QPqz+fw9ASDQun{EE9+gFB9)}eY8-=Fuj-g2w67eG4I6yGBP0t5g80U$681_&yKNQUk1cL#Tf|(M)aDRZga$c>|CtbEA0ge__^=a&~8~X59dK+^G^2&kP zELWdfKCTvkNsy!cdGP%jcIt@eli)x=z^9{Ir{oQs!Z_@3%0%PiTwIfZTtYmZ)y4%%IeHodijv``GTH~qj^*sru zKi>8)GZ>K?u2dCbLgcJQViiyW8ooph;^5mdXoS9n56JqjD;uWK$-n=kr_R5Z8Rdd_aER}l9C#U=SgL`j0p z8*7ZTqVBDI8ep{H@}QsXXKey(KoOjjr`7Xo7PuZExoKQ`0&OV~*2Tr8dl4*BUqxUSJ*z#Y~XR(>58(Pwlk>l

x#1KDo;&5Kbu!>#` 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

T+*0Qk9FXWS4Lp5$ExwhEHB&l-e1 zunHJKM}t7dKv#GmC{l?nn)ZKM1}$RyZ$oc;L9i$}Okp3BVsO3p-~jkyjt{s%$HXAJ z%yA`jmcC&z41$vqu&e=|!VSp5hdP5g5E5Kk@=L)X15QJANFNn+jmVuog`ex&HDldC zR)ulBNPT#+A^>~{uA(nxC3FGh2?$U$a~N@n>zI@Il&iWtpqc_pYU!67H;qYDg~~e6 zPVn2TqQ($KLyZKocG|+fRk2o+5Eqo7C;#X4o!h{xafDF$NC>!4KuEIyxlY>FR>ND+ z4p;Sx-}09dVgChqPqET@XZ-jZC$weCO`RC&f8&N=JbC;pLxwYqu#TP)yU>~IdQsVZ9f07M< zRO~YoPp1?`x9Oh{5z5T!v0;MxhnB8%yCRj59iW*>)=rCcU}(uA2Lc`X;^oID{|sW z_UQlQ)FRRz4;$IUWKZPX4cQ4UIyT3fBrYz)BTW);Syu!!oe)3co`Orm?eU}qsAP~X z$q=&-q|~b!q^Ap$J8;Gbe6m&xNeW~rUuwSFkr3{OJtE^s>vvL;sR)$E2_8-jzls7& zP77Qr0d1Gtb3XHB7ie-ql}Zz;;|yU4bRW=lS(Hi;N876zk(XuC5T{R+SQZc)x7)eusyRgS%B?UoN+GSFWUVg6})w zR`hsu;W$yk?Cc;m(uGe6@4u*6$!v0BVQo==3cozQ$2#-=JSH*|c-=~LlU{V_<|HzX zQVCQ5R$pSzB@cLKzY+hYRa8UqR_{|ZlC75JG(SE*w%U@xUQoPmL2I|w2iGfDSPcIO z*>w^*@>Jp899&aIP;j;w6q{WjI*&^3eytjQ{)ar%f@{dPDyP68FN);;45K|{rL#CG zxow9VkBq5hMqQ7_yxVqyMAM0m-y?Ydrp9|@oJ_lR-Kgu%WmCfJq2T{vL27VdOxs`^ zm@CkPomt7GaxPJ5agi8Pyah8zUN#ue3Wrq0h{AZSzU!rEYxB~9eoro+=0*C%<+qmr z+a(Ftv3AfDt9nH|H0B~Mek5vz_xXk~sFmBFWFPQa=}Rg~8JorI?fgZo`~;#T^ayBK z-hr@+-Rq?UeO(7Uh1LlA(qukQp2rgQiZvu?sn8;4{3;~oP41l(G(3rFS*FLN1KVTI zJqK)SUge`Z54OMvYiLmlotP_Fw}RWS*+p}3!)AnbA7mS|PJcZPtg^HbF$~Oj;7=P< z&LNpZo*%F=1-rK}RuEMs}Ahm7T2eX0?|g^oop2oT$OdZ@KP z5Drz+-lSBR!Bm;haNa@xK%?5Jce4MX(PG&Zftw=ta z)q<%F!*opI3G^;pA{bxI4$*Y4LWw_^<|mS*G`*S7n7G0`XD3Pb3A+jg>OJ=15JgGk z!eY>A?^*1D92?yMh6==MeWy{A;>C1lEPZrM4uvc1t(G3*7nef)WEg`0*24X&2u~hh zpht7#kfwuwiIJ9?s^s{GGC zYcsb3t<@LC?)1DhaF3!H@$qrS$y`74xlTAHqV7Bky@lZ_ieLAI(-O!J88w0S{%-Pb z*KWL#ucdT7I-Zp^GqfIfKD{U$H&~=ts?ya2+M-EwHcEGso|n?JFgOP$j(;*;Uk$yk z0VBAFwSV!}&p-0x%W{Xe#tbTRG@JK=vy6xsZ=N9J3kTlEAc5Vibk3>t12A(vlU&n=?NAwjP)hki&fr9$RYyHc@0R zix+7FtrMoXDwRC^HVZVCq#{Xvah~Z2C_5BLp_bhRZuT-G-hRu=JAQMKEz|pG0Y(~~ z#Gws2s@^AL8FVmGV&~3;sHY51x&AhN% zq^nTSBiJf(udaFm}P+I!ycZM_XM{yl6LOp}!R1N-Cx1I9XzSxg!2{#OjBT*t`|OA zoXN2Qgc)YE+1DNJ7d@MLvH95RmS~l+1+`ZT>VnmYLVg@F&=8!d#v`gh!uq4DfJQ z^-q^n9rFdv(whnn%;h4kL%%-f)))H$HJp^s?TdUiYXG|rS`(L`hZ*}tRjh7YaW@1L zu3*pJp;#*pJ+|61YB(3wi@bS)g_SKLR}LxTOG>u(f6zKFME+Ovi5QNOZo|WczAN!I z(a5x$q=;MuC!2g9jUdBV;U5yws(LkDFif-Fgg6(a`AHQY&)Y@K(|LW!tA6w)s%FeN zsAC>d&!O?t(tRu!{h&>hNG%PsPicll*$H2Cnz5+VLap6YjJO;z3rFLy%LU`&^Ifx` zI6lPJ%R>$EKBgw@^-!YR&FG@?tfNcUKDB*>!3~MGRj#e?jVA`D1cg`X={2{LL0qohsGHAB)?QH4elRYo`70KG(vO7oErmhLn83_+~wqp)8d9H z4pd8eGd)~72A`fg%#alFAHAFsl%lRvyH6*lQsStJ9n$Q;9qP|5b0Wo}gb$SFgNy+5VVDwT{>rEAcemo)T*pwEX`{k4 z!!(er!V?^b8O@}gR)#j&rAU~4jd>BOAy(KVacHD{vT0MGzd8QqC(!!fNUw^H=7n6P z0*7(^Bs8lc?Yse_N{;qFeV!pyOX%?@TJKn%R38Rw>%GyJ6>zcR^G!MFTFqu@7c4;X zr$bzT>+6EX3&;P+NewPdN9V3d^4l*gj09AD_U0K7$>ka+F*lu(>6k{{kj`TyU{izbJR zu6>{5qg)J4UTgo8$b`90q+)}z(=Ii=SJLV;|v zf%BG&(BVFD5Nms4A2@qL)QXcmH*$4TZ*#wZ35 z+Gpx9aK&>H)ePM;xhmX(8Bv~+|1J)Y=~xp@^snQc z>ZDSXoEU-03C9cKKwcbuWce=0QqS-6j}vsq_xAjoBo8fRfbFdB9crbnTrn?Wf1n4F z|F|TA-}D=J8lrSboX-4&F#*G1jJ1_hV&DFJH&SybHXp6^II5{}{-CQPd%=;U>5dZV zZL$1rGjOHXH-z{}T^Ptk@38p>n$DJ(vvT$;SJgR$&QDcIkpE6vW`ROP#^2-i@a@uj zQdbk?%P1SLIQY8aa(xPPzO6BVX@yKO(W|gSLvw6O?$UvY9gpx>O^c_qBeXr4 zf9;=}tDH%uOG3}`0&kc4K452ne2FnWpb(P? zYGqrCQ7?iRDeOOMt2EPpG~l=hz?^KXZ8QJgg6X~|d745?8eJ!Yb~Ka!yQW$I7F|o7 z(BEUrkY=_nzq@EPFnvMY`U^%)*>f~0beyigxal?Vl&JN6vlhRo_ZK>*-+qt|*N|IF zt^&qknAA!abk9P*K$<>!IIZM!iHv1(4o&Y4Z@LyDMc+(m>xW2X1FyvzD-90+{OCWo z^^45m{^zf1tX5+SoHL>;279L>=WAWCf$v1e+t(Bo3M)lN6=q4_=1oYfju21iBmP63 zrh)vIFkVVXiO8n@r9^_hLy4S`ni&p&8sjsWuK66kKK%VY9!;d2?;&ZX8xhmtblR!R zVM0*sy6O;3Xpm*jRkoDKi4x0q!@X^YN<2^OV3ngMuXShvvpbea)vu8YKYQbw^-LcR zF!D@mx=7u&rP6h)X)Xf|r8@8i%`3Iu18g@2*PMEYp#~IKZnl8U@FSgR{g{s^Uj{ov zBe9!0l(5%UWhxX2l@w%)HTwNMM%}}_3>~vBbsx&vtXX8K;4*goJq{D;GU|0ZQo1ux z`|PQV=K3w9WN(~cTr2N|a1Z|;RfU2tF3Gl|`D%Asl_8ul!Sx2%!zC4!x>l2Vh*9p5gI&5l-%X=<4od&`yW>EwGth;aQ3Y+bOeWK5feY6XuLS}RH6D$lK4U$; zEPn-HCv}l8r@}j|iEN6w<22Eaky3CYje+%8nvt z`R4LUdzQttw*iXW{`(J4pe`p}Yk5Kc;)Xu?$mzCWoZsKE-I1_pa_ad&Z%lQC>@zO} z!*l~XdlVp58)O5IoO95(v{t>m6EM5yG3~LqK&|w@CUC?jO*a?KuN`-J!jZ}0NX7WYO3y4{B~ z*Dvf8?F}hXP65SEMiEmtT27h?0H_!q9pokg;e_Z)A+sr0rcXDav0T-E3GToNmpFSw zXwAeX?uq89p*^%s>dy-AnxK7))@fzKf(~$?h#X;{t>njq)ME{n+!URkv{O>WU;CVp z=;;_KP4^r73fO_S!2sF*5G=fihRU;)vG7e+oWs>hG9s_iQ zOluK}jhVPzVBcnFvI@kgm}=cMUU<{+p;O)21$=wj$Dp;AVyl77Q^c$Sk!BhA|d_S#Yy)*B%!tNcXl{US4O zxDP2`i$P+^dm6H>oxoB9x71p(z(`G(CAFmT7hQ&N63=wvVr{oB%EWAOYrs<$W+p<3 zAN@?Ex(JOJE}7B@4Frxj+o8`QhnQ@UOye9^USDzCa7=79`+^4|98SSnIt zheb$6P{Ml}kDRwWhy?5(6K!jE-zhTkN%ZiMtz1aX7`yXgJJKr(-yNpXC_2Qv ze^4Zci>MJ+aL+7t00tVdwp}W%8TAJ9SwD9OCfHEjS(% z{Yk2|7*xyjVy@5aIZlos%KkxJlfMtzf*%>fyB#SeO%@tLPVXe06k7-nGxPSObfxmh^LzdE9)6s?(0iUbA23qHE-nkV zt?aR@sB17LY2wmV*34msPFSARJHd99@Rm+3;;L||?}WMn(+l`EnQOjsX$_JlZ+Pw1bfTn%)47Pna#5A9?^b^B%Q;%0QhXj zIlVPRI~I4g@#hXXRnQS{U*q2ZcOw!(E@5kwC%BgeCT}->SLkPk2vG0(L%XTGs+}EQ z(I_0D^`z>(!ZLR$#Z;VKe>NS31E?EBIz@BqJ>*o6`5{J562mipo!$v^mMoz$cn9Pr z)v23<`NX_myDwJp9F#lUJlTAr+z}ZzMUKMd5f6A)$oC5fje$%ca#@=EJ%BNW9^yM7 zkvm^m55M_Vqz6;oWO~zURn`bUj_07!lb(D!=mQmsi;?HShlpBr?|*I5#}M*k?Apah zj`q8(S{g80bkDhN9%eiXLRT0<)fF)-30^?5tdMj!n$8xVr~bo_Kh~_&AjX*IZDlAk zkvxdzFc3Cf80jm_GH;K13EAJShPjVkFa5z)KfQv&f5#hVkV$KF_FS^Os5Ip*?uQVe z%;|nH<5dmy;GJ^$BVo`LIb)udY?$@afv$aQ(1EiBKT;~DMXrch=<~+Tbix`0 zpGB!qL=yXxUQ=0&8M`@`BP?|Kf9!?_bwrgytV=XjkZgw((9+#t{sBx|l~+IT&V128 zp05bC*XEY9tA!iESysK}IS;bx(*T^RZfoSfD(F;Z12DLKfUzoMHpbQ<$N;Z99LypQ zv0DY>#iz7H+}9?MX;aF!hijRFxbDe844gMGZk%C*(0MsL`auWuKc1_8jbe19GZ6m} zyl-XPB5e4b{dd_7sZ7C@=E1BMna=x@%{0DZdt@{h(AZZRPD^-=5C^+%vjZYef)j~tXuA^G zX^TLSdO97P5H|S>rJZ{G=?^;CnJ|kcQ|Awo((+iAl~xrTS(=y@EQ_yx>1M>JQJU!K zE=-C7wHYC+n*x^bJy?-#WYX{>QRT4(G088|XHBrRrnC zs;JX^$6<>I^53RXN9JoGPGjClhnPN3HEX+^BQ5&YGC)xq1uPDf4^in_7dB_c{OM)& zew`t=?rpB5K*1rP2EkV{=EqQKhx)ltl&ijJ51hW~imW#w)#JEt^HX@68$nA4*0!Nh z>CgW^kA}*@tf$z=y*r95awA=D}ske=@Zl!nxzU67+Qn-;!2Amzl-TU;uFTw z+;s2k{a~9doJ4vCWegLe?W%Tzp`~D+NZ- zla!V!6lBbD%HC5+2Z2r+x0bJzVj46z%MV~Lkqc0(5Q-T2xS;BWW%bVis}X9P%ERIc zLAjn=<}0&H70V8K0fnW~Dl8Nscb1}HEQ7h9$kuGBdSb3I_{*~aWqW99Ysc(o{trWj zjPojgm=^vmnYB@d85Q=o94GjbKPrQfT?&U+n(?@qHn75qbi2r@#l8j}ySpneoRn;Z5wOVhaDJ9V;Sv z1BbYBy3DLIO{A)0l+y%vYwPIU||Zd6uaK+m1&K$`^KJ+fX)u+EU_MZhaR zta>g*l+gW6Nnd7aPs@rW%eS-#n8VPx)0DiSOz9@*9jH`y6evp9P2WD)fg%(2S_K4y zT8C{>f8e(8}6DwEd(BnTE=)UvZTw%_`BndPh%FqR$Z4OCiXd8mIqmEy$ z%GCqHT?N8s>{r)QcEQ%~uUXv+j8#2%Cz-=2D21o%?Wu^S8(t{mrBoJxxHF9U!AlnV{c34@BwV0b^2KM!vxlr0mB9YZk(wXvL^ezDlB*%IQ!^ z{a}(pkGn(#jl;&pKmP5EJvV_n-#my3YxzVLwZaA!bXPfx3RPMJ1T}`*fJN8|O5$xi zw#NVf2pZZGtB`hR{dX(|;X!udJacYG0C+^Jw3a8$2kCSs}xgzn87(JD0a6lY16w~|w zU3auwX$^MxhQ1pVubv)J(!2@~9hgOw7sjz2M+e;Wz@b?mV+R2w>|!05^qViFqLO#b zK{3;R#k?tWa%T6OLGn)1{EiK}<~oxn-`hH&G;>|vTYa#c&huQErom`z-S~fQ0~Qo50r`HKhOkfssQ5w9qB8-|pvEzS zQy*&H^%KqJETBgQCWZ5xn9sXwb18ijh8mpuinO(JWsN-`)42rCyl|HuDH)nsqsa4D zHlk&M_OUe;6C_y`rfw&0=`Ae z6lu50xCfIaqImGp8`$h@IVYksgx?z*FAm3mGR-|b)xx^}gEis#yHayQq_b#|b@$<1 z7)}fGO(Avn2pS6)iio}^JX6r?FUC*ETV^QrT^o*Ykotv^%gyX47V5*ZGj}6#kR?uc zz?T4%nElciD9KsHt^X(Hd{Y@RCon2;S>(FE;2(kP&_F5LoV1Woxn zNcPEuo85_Sn7-Z5G;9QvyS1tC)4v4SnF$$qX@l2oBD$$EI3ZpS7Wh;R=5~wx2mJzS z80@o@go(-6)o{kpvm_Y)gEt~k587j@&-<9uL8_-KB$H=GMp(oG8xdf93}HicG)*!W zY=a6BJq3s7#l49QEQLOxBS?rNGePxmL2YR^lWM=ed-#IP=zh}lAcDQLYS+hHS&f%Q z^V&TyFQgRxCb^R5mvHtgo1g;93K~-fexl5qqYcG&} zEp?smv>$pe5A(&U&57Ud`1}!py$XSl_!tUVOH6BAH*s%7@cr)2l4$YO13(5FL_a#~1gr6x+f&!EXgV7>J{6|9XN*3K(R3#By1tAw%DWT}_aZDwVmj|nTL*ktbr z`(0rCY-*j0#%>p>3~uc6+OR4dQKKVZ4>6DfFYO|SLdhmpGZ{YOjGmWv)j{3aR;Q#~ zvM%zffK5h%9^Qx}!&NyEW}YpR*zcuaVd8Rr9tf({k)xM(Lxd#R8-SOZ;H692&JB5Y z!FR4AOJUQW0PxD5pZ?pO1#r_K{drD{E_Xbt4+YO#z(>-?$H$>M*r+rVs{r0QKsDPk z0cj<6z)?xL*NiCzGsZcRQ9HEkg4nO-q)fL`RV2$`LVSW;Rf&W-=u(Wkz07 zsQrWtE@=YL>M>|C-kwf{J0!W!xN>R=KMuR^>RvpZwG}r-yG|+ngQv_^P*5Nrv7*OY z@;0XPA{F`C-p&3O62e&&{SarrxQ1_;s)lN7c1y`mRm4C4qs(M!oZkBmLaea7N3D{m zXtP1|C7Sa2;o9fxW`p<=_03?UZNn=tW}BuMxP1iq128r9!Bt6ejL#onVjTEfr+H4t zB$HzEVXt`T^}h(L&q*Rh|BH=kEgA9jvL8XQU)Z?|!y52%kNtbA0zOyTHR@W>@OzoX zXWScCprGehPjGEKFO{?C!ryharr8VeKLqNoiL>Aia4-^+S3!`#Qj!4P#M@R`!Tc}8 zFVeE!{~YfpObPqe`XrwcO@C2os1Sq+bcdK26xwgoVw@W;KLC1y7|uysFkAdXJ`KgE zC6RwptIOtvQH8vP^w0jb6#8i9SFFYVAJ|y&U=@B!Y?`=cm~*7~lXdch@#c3kuY!hc z45niwl5SAh;6>Zu9?|ffUKMANiW8=&55&Woi9~Mxr(^xlybS%KA-|a~M}KpnV?qxj zvJumy)DH1zq@wpq4;%#Vv6BbE373%6jN!M}ulN8yrc_sT$=S@dnGW=W?iU*9V1Wxa zmJ2h@dW>7|8;(0Yo$J2pshQX5GEaE^`{?F8X-7ptybx^zibDNO8%O5`z&ev4iRNpQ z?9Mt*`&wq)sAD70O#O`vKf}c#f1Dvlv5}$R%g3W2>EloSrdR+TYo;tC-QS8;fe|W7 z+AA*I_DnJK7a*MZ_?A&?H=v1$ru6Bzn=069AffZ?|1wWP^ig_OgM|ztn- znC|ACA}5U&XCFx_3h%Ak^js`yY$so$5sr)~IkmI=p&a9|i84MQ#}ocdv4?cQwpCSo zW-b=lx-#xG-BTGnBdulfTx@bcV>GkA`_hGkKW?7MJ9e1wTr+*Ir8(n%4 zlnV6;vESqkgtR5H+3XKXhpzCWM_z)$^dq+}g|g@xTL#1OcCWT9{xUhJ6zJnV63~yi z-^Qw_5iB#kitd6gpS;F)(M82;R>yNJKd;3KZJq`t}rg3R!)X+cxV0A9*q&mymOTW!FO?^giigi9K;cNnL z(tN7}?PyeTvndLw$Om69u8&HQNl|zvgD5DUS`aQ%>s758sRS5jzGKa6Fz5zI_HBtB zF2@W;dhuLt*I^h4)1^rNEhJ88 z_1bZ(f_lbppw-D>-!g|UWiB9P_h}I=eVWJBbJhjk0HfW8rzy=~DUlsA9C4dcaAHA4 z!LS=%6!Y;8&g${)JKz|ZV^*l%|`0K`?4D|xvQ=Hbo zjS||fN&Nz#Ud=&4BDBk~9 z3!=LkOATqUF~FA-?<*SX&34m!{mZz@6haa`5?VH$x$%0xT=pj`2?yc%18LmxWrQV1KrwW2HP(H?du2!(S@267oLqH#NxS}hEKMP z6}&L&19J}1BMwOg>mdEMLnjW~^$Pu#YJuy(D*zxC=GTZ(w|*NqcBKavR}$bI2D0Ip zRMbKm>}6wfUGD>ZWcc103T-N9r_YHUJgLGXaw_eNh6#o$#5zO2TG##dKi_4x;YL2n z=S#ljUvmO)R4$10+?TU{0q+kG6ktE(E(d}c4?;1hcA`%GBp(gs%UKcm#h-wQ{V;B~KuK<+%IEfE)z?V~qcIh}^dV^L2 zEK_Mka?Kcyh5a69i2mb=hx1L&lODt81;GaJPEboA<@>&{Jv!2Nqi@4aQ;Gc@?AYQO z?^xLeeG)fTt+3sZ4Ry9_t_7Sjk`Q_FTu|t9vGZwj0Z+?D?b-wWkYOfcFg*1650AF- z;@c6S4v(o#IL^CIpUhy>t5ytiNZ{ORbM@Ijq04UIbaOMB)%^e@)D0pWDu*!#?T=ek z|4^@(qvt-1qpwazHjr(D2~S2dGP=x4w;rh=@E{61$I) z*jyVks}nZ}hTI|gnujfH4(&|)>&NkYf6hRr!+nywfbOnB^s*PMeG!`t#WR|n)C98+ zz%;VI9^yaR80QTBN!yemSF~m6Zn@tEf_g{E?G}8dVY}TGsYa}8u@A5i+(tz-bfa}p zBoQ9l%_9a8eNoVNp!nQ?a+q~<^UnERv$#2t&DX4_vUnmqN0o6v$mEP=q~S?_chx3T z&PtNF@lN{kf|PM%IHEqect4f7@Wo6o0%wVXB}Bz@FCahnUTCHX2UWqqQ@+cM9_B@z z($GgJ72P5awzmwMy>J2rJ-qx1`L6fqAmW{%5#O>$gi_xv_l&r7X?(wQf)K%?l1t^S zXma-4lgG#gW3Sn|5qEVnlTL;^VwgaIJCkr?4i2hx>S=td7G`Vfl=`l>nc`{`XZMR9 z7eZhl&;U-Xt16cZ5D~Ljr-(gTct_|1Qxz`hR;V#(Acu;21sBB6G05F2k^%a%M1rN+ z-BSz1KlUu|D0WxT0XBQ^5|}?^Sy;;xAty}qI)0vD+5JUny_{9u2Q_*DCk!g~Sa`OJ$VjB`kK+4UW?iNi(O#!gcx zG=VR@?J})Zv?&a;NGVf@i#;$Gh#pR~&OB3#t5_)-D|%4z?v6nPmD!Da-yqnc5%oQJ zu7jBFleUvh z*TEG=(r8dDH*^vP_{AM##TTs{z(IW9fZF^WV^e`%X?1q0=(%(6*umlB&1}}SSpL5_ zKcvj-uSnH6(nig<977?0S#j_E!)QpysU16hsI!2u{zS2`Azaf$+hkF(5cnVQ^1E8H&DHh@OQKl?J&be0x(6~PnR=g zuXWSv&(KKha(z49sakBj0G%nXL_&d5Y{W$QONwF{QZ`kY zEu<`qd_WJOC<&)Ru}Sx^QPN+3bzo!SWxAt0CBTm4ae#_5^Eh-Nv(YA&x56w<;Y zoXv^b)Dml|YEd6 zcm=>tKXeM{lX6et>YthW&X~q-isDzeQf!>fTLQi3R!AtO#B@foAd1=bTxCoB8mw{b zjj*j?=7bhMJpzI;Em&$*{`LB^yV(*F|4%#Mz2wm>SCB`b$&I3I_cGDqG;rW2*{LR1 zjPNdBT`rEgvnoNY{Pzo7F&}E;l^u=NjVLGf>O<>Y~7Y_>h94B8{ko%N(_!G?6V~Q z?}LTuI(M(CiKV{T@KLWqxDGyEk*VKqEMmW?l1s8bRE>8BJxhL!f!zA4#Kq0#VHVdE zgCjG@N%^KNHqYPqbyTM~7x~MBX3PLJK+37lPH$YRqritwB^RH!_}${aHFLC z$Or&4Qlhj@{Y|(ok=t%?!G6~eVrGMlShb6hK_@S#vr+b~Q!z_F z6~rB@j-1SmWVVPQ-spOEG>^H~I$j5=rzK^{wqk3d<)u9|$uyFQB`rtWV@?q6;}$~X z?j^EOmj*`Ri*G2o((HSHQ(ETi zNVv$8PR(V-pyBkP=#=Y+TwmP>Wq*I9E=%^RP%a-!2e0b?qT(YdO?@9=5E>yYBbz{=NTU9`E~C&bp5L!rQZF>A}VJ} z9%+TXlAG5YL_=*(%~vgUt{F~}^DQ#Tb>>kli%m%~^-sz!7lku^R0M{6ZO;y1-)^cR znpAL1Yh$&=_@p&8HQY0-Kw38-hj1+*ufHvOQQH1FiDoGN`1FSwoZ*~3Gc)GX@0t+r z@0cS`$AqVQhs?|K@eY1-z{+UJj5KJFJkFDF&q3fvI!|DYKb`fEKB|)g^eL^gP6sPHvdtR^{aOSYbHQUV`6pP2B zgqLjPOu-#Vx)c+y$O|D2%*zUog)2%7gIbWBuba7r%aOa-F^`_0-%F-aJV^$5iWuwM6dqkvJ~VcE z?m;5b^(Gt1p=pg3IgW)bJ95d6et*9sMsobyW2(wS&&lb2F5ffsU`06iz7^|-cnBME zN=hp_N_rgh_-D`AlpTtw@aQpq_>WTj7A^=x{oC%^8nBIVIE$)za7b{W|-V zdTL_q>tgub_C}tmeIXcslBy=M=qhM%+HWH>i^$^54)u$t%txTlK2UfHhZW(z-zJHn z82T1FVf&4TllEpgQxcPD7M#;ECJ;jwpixtv_h|Jw@ysf&3(?|+z>17!_9 znZ4#Y)lw{Xt6#@KAZrDa(VP}xjw&@^Rm&8$hH5JY^)&`5S>%qTvhDlgClt{ajesx` z=d|tUT^R*42ZY2}u32!cQV86 z;88)_ipXpwYT`&;8lxuqAR9Om8%J2+|KuctaI=l(?35xXZPH8{@Pnx$i8b@DRV`BO z^5ivVGF@5YR#X?3;poJ-%lQDd0A&be668 z&Zr(=@mUf-tLX@f`tA9t#MJX(vDc_Wdr_tBU=BefLzgNntBZody>xeT^4;M+%8 z$6J5zOINXZa44u%YjtZtjYlaPeUU?Zq#F}Mx7|IX1J^uQo zhUvJsbNJC)%gs;_tS1(<1~`z?3=j>?4<4{=rLFadfgkxbDK7bhLQED~dVz_01r4{g;_QJL98Gv97UAnx4tHp$1=HN__Yf1TqRkC+W0jAA!xUZWR<4S@9YO7 zYwCQkbm9y|j3&dlb&L%`D$j}m0#QC!N;LjrL$Bm|)d*ucDBjZEHR}FnDL9*Qt~6Vk z|H)Ohi!Pn5|8RCSwBhcX~_8jL(0FP z;owR!aS>;Hgb|1j4^v9KO`s*bhE5$NY|-wF8wU=p9YuTGsp)Nt1vt>u z4UUN~E{a?WkQI4J4y#w;?e!d7u)W)_CB#NX?_WT76eli>8Clggv&v=EW zH)G>y3k%_*XfkZ&=$G&W)fidDk&~{@Au$b$Y2{MUFxi`t_Ml2Ka-kjg z5+TeD+{f1N6G4x&H}74T=Ud^Y#Q6m~^vMVwM@mNUjI$Q2#`eP?S@6$|8It#6?cEvdYK)U!tr2rfU_*Q!DkxlB?+=PfqO0utYGHo7@PbD zlxi}};x*a80GYk>Lhz?H21-Gp4G5$Zb)(vr5aqXZfI;4=hP8uU_v!AH2!8BL$El>G zQIe%#I&3rDe**quh#sFsoAcQ@2Hvodd(y#tcwLl|-@b$ptjp;YBzeG<&Z~n9m|i(p zwE;9*@S#FiYX{^QNKIFyJQ~KV37-6!=VFWRN4p}xckwDHKsTaChNYefaYqne z@n0|c&O0p8-mvm4%41_bQsrlQXn)&?%3BhhezeGZwxc$rkg8Oie!f?vqohmGQa9#K zwFQzsF0f>70~GneGtbru;835Dgv5I4SA=G9TfR~+&pV~&Cbv)$8Zef)s8eZYHo*h) z>1J-Rb0&C(u05Jo1x}{9vOd$3IMDmi%%pp`U0FTXL+otO-50+#Pb_!P4 z;iKXRCZ>jT4l&kxc>+nOf||-Mv5v2U-YnR?-p}XJ-rf-b5S@B9p@PYbST8f%mR-@rET^CqSskTv23EB3>+zo> zX&tk|4IDT`VfhF8jDvhlpw1vMYpV!W#FZem5}BX^*xtIUW;%ov>glqh!Qhc$I_ zj7D6ox-f8ODl6sj&dF2#V+~V{r{9dO7$%JJ-*knLE!*T7r_>i=4VV)LAhdqf9PBRE zfNztup>5%sY*``p^puA9d&!L8HXU9W&<5WQgTpuLO-$zmBPZ4n0pg!uLif8jG0%l~ zrDIYmgVd9Q)ein3rfoG`yar((XmjVk$0BJ$Wy*#OEpcmq6N$`q`b1eeV&N3RM39(< zTqTsQN|u?M;Ebq*w(mbtI87!*@P&IM8XnBwQiD_Oqmir+j9>nccizM>kOTP;*1;tm zF>rnav;sdh5N(#cpcNEwqjV;~BNG6ZNBvDM`q@zun2?2Quzs zwgbR(TPgEP%+q?3RjL1~Dq^2_{EOVib@cZ4-|QQ))K=n2P;GI)UCYm-1U1qE@;Oo% z2;Eg|(OC-M)qY*<*fUaIIaW9z0__;^8%qvdaf#)hd@dC z*eS+o)lR2f|D$TG_>@`&Vk2KoLL@=;F(xI5B3ogHcaLELz<fVBFI9Gzj~J^?tfrd|&+63B=gjz&#aTc~lBDm}>kJ|{+3o@fdTi5m-va+nm}$gQx+Tw{yZ34qhYp9QAM2@n?`b>=nBpC&w*x5 zqU?)*;}gA)*?VU4^qPRYFRvJx&=@R`SAkY{7P)^re+ycAgiWO+DRW1z#m5}i2-hxz zbY)=3YuE2M;jPwa(8G-=Pgjua6sSqi^(~3P2>UR?m56pZTp^Pl%M`p|<%mH+b%SpgeaBn3W)K2ggOE}r!(+b`PnUBwp1fwuIq3;+X_=* z(DEpd)tz8N9U_c@UNVNYL_hmDKI)#QUv!~Oq93J zCd<>izG~s~stKe+I7!l!!d+8m!Qo~yFjEg4EV+zF?QBvASko+>dGk;mEp^p_!Quo% z-T;L1c*f$Qp8$!~SCsz7&1X};%)zWHPRf@QIr=ZBqt5Oiw+#~GRMma`mzakBAL;Kd z#X3hHUctOKS{&%Wf{&~3@1M>vYX5JSK#A1j#n%g}(GfFZW$2V;aZr-_Nt+i#EB?CY ze=(+Q%zaiw`_y^4J?6kY#Qx!x$mK5Z|8#FypJ46@EA$7u6R^}@!vwv;zT*{`X?NWJVEdnADMnFd$l z4w$~p6~BB^9h4XM;k^7qZ$?N?l}qtJPBVK|^sCo*j&3B-eK9=V}F z5PgY2jHxR0;BFg4nVc8~DrxkO@MD3Ky|)Vq7n!ps)R}r!MA}I;!5tv*;}7H5YG&Is zjQt76)aSr-Io8Vetzj<9?|us`*_OL9xR2nF*g};x;Qu35 z1e@7bxh=w|!eIDFvy*7vJ`U;HoJw-z!A(!Yy^&ETu68z$+V-2_#(C|tm;Q~$Her`h zUROpm8VKjYd=*)a#xD1oALRj7KV{jGr3=@1`b{4X3si6iQehjx`Ze`_f&phJ-LZk=J{}M_5E$@1YGlDb8#YOukf~Mv#PF#upf5h1~?9S&ln_&Y+iwe`G>Wh)%c+%F3}?GDybqHq=t5+ z4N4Jq9}`erqF)sS0mcD|-cN0Nv@tLCnf*MT!zgk2vJ58*S;axbL>5#Pghm6D zfFxUAzo}qC2H4Moc-;O;3SW7b(0_i&q^sCwYnxVFx#7ZBg+c4}!(WJLL8tLq0L$Rg zA`!L@pcR_|9Fvz8Jg(TR2Mn{9Gi%aOJKG68c$13pUvM1fmzs1JaHtVkVnGB{+?Tyt zjq=3M7rl`}8Bns+@|=U**RSbcmQj0|nbg5cq=Gk-$yY@k2yNZn@tm1b7UHRRwxrhx zsCQ(Rk!rf);w_|}gAc59&?UTR5{56&9kJ&9WuR)O0}L!(09H?{U&a#an+vCBY=mb@ zvfVgL8S0X`(J`hZz*Q;%OBDo{51+o@+qfK=%Oiq+-~V$Kz%usVs`uc{nKy(T7#}e5 zftppyc{XUfeOn@W=c5;KfL(F8ygdVw0%@{fCHkQum|9F&TPQ}`W{&OT^li+On(EpM z3AVyoPq=(5lYyRkUaf^mWZVsKa%oM0qYmNI zEZFnd2clttDHZDL{EU--eIl~r0kPjWfx6rNU=sv=E`Y$j5YTj5G&KI^vj~JaFgVuj zSSF!DbS<69Fq8?5M6j>nD->$k=-@k zcwlumO(}IX0>D6g47^H5=ZcM<`G!@aLj~4SB9z&tSrCkf|D?mxp=Lgm&8rJWRI%ou zdT>c@AhP+#rsUk~DAkLv;8p1=XwbaOi$-g1RR52W9w1o#{ z1&ey)@`-&f7Dzdr`V$9!54Hx|>d*ofc8$mNiT_XkoSgj!Nkv2IDt6IJn8I^hmQ3aH zO|DPnOw%RjVd9$|mY{{TjqO@z)Y9PrB=pKqWbkdBTd<)DC+i!){Tthb0e@A56b+QnFWZ#P&9&s{%iY#>z_Z!vXLP zOli5@XVtV>6)KS{Z8@nsw|La^ULO%PBsz0OIFCqXo+ z43(`j7UdvLEr*o34OD?&3A7dc$<%>+d853;uZi;WNT#^-rucTG7ZPnC z*pUe*=$fl7E|m7GgeU9Sbsxc%(7q3lTOGRbPILuya(d;O5uYZOi5p0A#>C8Dc3@lN z0lY6?+^L`Nc*_v-{G+XG1D&k&T47NhIht1?5{%>3-H_mWGG^nCj(-FMN+shvVG z1UUOoDXc+GGmNTHy*(3cmcfJ#3WZC{AZ>@xuPY7)3W!^UYK4$;E^7b)l5tujn1Us2 zm_s5Y;T`^rI=3rD{|{}5V$xn#z}lwx?$P8)ME-(cV=YTzS&Zzy<6k~Z5i=HQhWvqx z-xCL%F|kTH&s2@Q-~C^)`2GnRFS*Tq-;_M|Tz{h8of0B$wiqVFu|$CmiscEp zax#J(Z1zU1e*%cw3QG)FXX!)$hAC*5kBwIlpt0R^wp~FDWTQa2c>s+Jj+&}P8*&aR z#7FMg^}QLVFTr2Tb4BoaJC;)G1n=k;Gn45S9?wimA3Eja=0{8}F|QYM1}%PW$j9Wg|zAeMWv z?JYS8%L}d3rbyt^kP~>ro!GEk7MoZ2nogo3`WvBaNS^D6$u~=8Jn=DcbZW!6SmpEB z4mP!V!+cm54RjSWY$@N956Y$#QtRq{NDS(WIT0L_bpW4$^4s?2AYkp#uYTHY}WOGuB z%Sq22ZC3-<;)pTK3|q(S&l}}5fXGA7s2!p8Hk*(r`$HnGErO3DGkGR#)&zs*F;F=X z>)ARJyF?I>eCzSLn<8t7mS1N&E<5mx^{*3szI!u>F&4g=3#!8^GO$g7y@ENZAMgJ| zcWUCX+yOFU&-82g!fw@)yx>5<^H*#)-pJ)M$E zw}@0@5?Qrc4h5u!PdrqCs!(dbn()pVoq*0{yAuguKrLqs{?^P2 zkUcJpP{_%(<4i?k0gh;Cn#j&*mh7be>qFNTvi>iZi#cTXpZ})zjW^VAqxcv=<0hM+ zJ$S6SQj1s!V(+mDDu*f8o0bjRtSwTJ%;J;*iORGqHxjsX9IeRkDjB~Te=U)%;P)QC zB7hrW;T_&`Jd>?F9xd*q=_3lvJ}AV8r8V!#=hI@J>=pX{M!%~`MeItrT{3uz_(i}& z3KyA0H>5L?_&)l+2mo@qxfBc zCqFhiVPAfu$}ECu(tjg!d;?c%e)GG2@#01(ntPMsTi=A0d3yl>ueC%yp+?PGC7Y_BV>Vak$yZ`bn$+CA^kSw}-W0mlS zOuq?P&zQkJ7lB-D-5f6&z>-YK6K5hxo5VP6Zp zR>HfZ-8k<(9Yz;_p)Ni^_{$A!5vsVHPw|=fvP;*<-8(IQ^Df-5H#4tH<2q{T%mkCW zc17}8W*Lk6F_A2SRhga7fBx?OEs?o*@|~+Q4Na{P!5yujaY7zoM43tV1TvSF6Ed+fSiNP}3^8K@coq+}3(IB^G zCUa!`lQCp9zib&I_&7>Gw6omFyLKK`w=Q$_8(OQWQ(Iz>=qyNXIHtm{h=jXy-Jp-R z2%NR=#sRUFj1d4fW3nlH(%pmTxBmiLviMK6=J`mg?Rk(rfboAZh^bn5COz0DfAN(8 za`Vl2><5WH^z~Z&;JHSdI4|D`1$ZO-jh^}mdezc`jbbB@ktUIJ=yeSAF)X*nz3z1L zIe$+18%pqfv@nh^6ppW727)-=T=f=K!$M{klQ6SUl_B%s&aR%X$L4_BM#(naCLz5E zUa}*ojY^86u9Az^jtm5i@*fIRE|zLt#b+sUkF(u0?TWFfz$HK13lY2{jL~dG=eC3j z@wZWzrI@}M+G9s^w%#Ye_Qkb9-)n{6AhBLBF3j&7TtjZ4QG%t8nP?LbqyM#|-2~)E zA5qYe76_oPWP-egKM}K`cB&XIEAaYbhD>b{eg*4+9H`05id4~q-EFi->uRL~(*;6R zyH67qi)wo&(rdDC7pJ)|sv?k=SY{sD4W}dGJqeppn>Es~51~CE8+;GIUh0lVbl}2R ziPr`dqcb6DOPNJrn!m@xzvUBua~=q~z0R~DlZnL*TgtCkZi|i5S737#_VPwlgmbo+YySi!_A2_G~I)^}473U{x}7 z@d`e_?U2MZ@^nuBZOzWs*jkO`7p-cW`souU)I2<$T(Vaj_w{%>xUYU%qu^b9E(`#Z zM3%MeqU6?N#U#3BWL%R@B-0bLDH{ycD|z`!65U^JjCQ|!B42&uWdOgMV zy@(QCTejAo#4656adUdPS6+?v8>Vn#iU5rhOU}P6^mC?V!w=`F6$NwU9D<9m16@SQ z>`!6T(GyHeEmM!FGh6{+bOl_~HamB(yY_%@Xx8701`h@OQkIxl!qZIE&vdJ3l*tb6 zHH|S9w^*##eQbWOxcR~ClPC!HP$^`;3 z*22H);U6}$DhE#oE(R(H-HC4CbI7CHE2d2IvDv$)NR;xaT}r#*&-$ai6+y<1Y`dWo zX2mA>MdB_BUQ0tWp8%>fP+`}*wjC^+9MjBc;Y5@jjk4?I>H!J#zZn7E)rdq*?G^4M zHw&@HxQ)gzaZMte6vlHQ6HV4=2i|e1g~S-N1~+P8;MkGbrNs9;VW>T)odBTDqD#fg zBFB5*h^vf1iq?|uZzp)?p9_8gD`D~eVnTwQ=>d?*DIb#- z^_D#e)+$cElqhJyuE(BE675dDNrAY{qlh^+B~XqHmH~T?S;H$Ht+Tx<5W8Gfm8vq>>O=H!1=vHBPD|Y--EoyE zc?w6mOMClusA0r`pR1w)5=RUGTRIKdSjSSIxmI7OGcWmDR8D&EZ-jRU9YIvo;dRL4 zUfyL4HdXR-ZQJmd6o3~==!ajV*By!L<|*`QZ^aZ>2rNAIj8Vkds#I56CR;%#H&e>y z$bOwz2sRIIt*_u&dFT%1fSikih4YPk3D{b~?o%+Izd{n1MTsRue+pn0k z@H0UsG*`_Pr&4mJMq5K8O?+o)b!UZ**lCh9eUhofcIz&2l>1lXFCCfdr|d5jPKRLY z{o`dM_*LnEAf~UbS`iMTFQg<@qY~I7hx#y6rXmn5k&d@KWnC#rsGC?EV0Vx3;ZH&at7ocP+HAy{U88ic9Eq0> zTAWhz12R8m+Jk0n`&&iz{j*|6>vikEBbG%1^Qzimj0{5G@s8;=urU$ zPYL)BS)?YRFcm#xzS=bOzbbRH3qicEv z1ap;50y)!3Qi%_xt;X>i_cl7!TwFdVBlFl!@OeCuL7V7ag)EnA$kv~|mKB$G7Y5}C zUDTVMmNWYdg;12iUd+nB)~VDMCm2EFf+v+^HH;%t;-PHQ_$B5M^6^LP8c6V{NR+mm zBblreGyTwnul%%Rus_v?e&h0K~x_oJXn{nX;yKHdS*j+<-%sHNxCP{(oadskUvViJRt9+o6v zDeTT~Nx3Cedfbj;uPHQW#xkfzO^N3yDs`uc3T9??4$T5aT}QnI`F4b}__z=iGg+J~ z@fGa$+@za;!YJipKhTI?&9N4b!oaIsr)RaxV7|q8Os0(B$bp8hB@;NL!B}A+4;`8` zc1*$tv}E*uk;dTiGXOMTQvevCA;VPR|xbU}OQAW0| z`}rWzc-Bb8)VE+lnIenT<9AmD6}KgWoOJV?y@8e6V(fXx9}th-S9aH4Yz0&#=tJ`Y z+7?GM@ko-5NRHJCAg8!#mJbH}PVKer@5DJAVTWOv8Li;q^Y&2^ts>UT0+s=lzmC&1 zx!yWFXDr_1h9Ljd&qqTzu$~$nN_M>Ut`lcyR@u4RyQY={DKH&nb242Td?V&t1zj0O zS_gx|{P3XPV48u=Ght6K(XLsRO7XRMd0=z6=DvJxNR5YvTA=31Ic`0@0VpK396QP<^z0Z8D><0b9}1LJd)?wMim|MS9SBR=n#ntXza7>O(KnX^8(ZGX8up-( zcgGA^h|95%Z%C^yz0Dg?%g$J!@5Iur%aE$sXD zyFf}-Zw`LjwCpTxY1*mXbZ-g2qjdtq_H@8`qkR`TUa1}8pcX8Zq#JNNJfU9v!8mMh zuA{vmZ7hywSp!#gtE){O;(F@}U-Pz4bq>9b1O~9vDP7*hnLT~!Wg189kUheP{8P$F z9GD>qkSIecp%b-V;}1>#09#W~d4un~SZ$>rONv3oQmQDcq<>I{IsW8z=SuDtT zN4Fvy=dUTPUmB07sbw22!`y`K(A3^%^pPpFn!ePG1o53{GZ5k3-qprvnRbXE?l1)_ zy}lycT1D6K?UTc!CNTlrp2w1Gl4E!{GK5yh<(M#X`9?i6m7+@im*h>vv*3viXq zOC?oDM}uZ{vI#ik3tqZg)TpghfQ{Wi$=9Kmw8IH%Fxic^yhh={2_hN7fyP@@1%+xI z`-%Nm5K{#j%UKdS$+7!KSyhb;ev4u6C9iY#KseK{zb(pcf;pF+Z$s6aZ4ca>94dH( z@K-q9E%|`JMyJb3v1g=YF<^-H*p0%d=ZXj31#=rVmMSQ95M+4{BkA!csi20FmCZaB z>EFa2{SDyKjA(H%&qz+8tdv}w;N-1%>RI-aqx86uTrWD zD58>QL?<4@!D_tD<&Fb&9MH+oG#&Ny?cSY+lqi#q+YU~+b;bK3fLi$6FuhCwGTcM)Q-?8q6lRtyQQVKM z&oh4 z3*k(WokWQ~nF!xbl$V3aHufs$WqHA z%{85g8V>O01+%#`ktd(DoH7acIop%S`!2el`{M|^#5uCoiEql!_F**a(>(8nC5K62 z8l)_QTJ?B^2S_p-rI%^uvzY5H?f9xZVxzTXOlJx53>{65gf;3T<~ekF=D~`pl)GiA zFLU8@VSDUz?PNm+RVP_pPajGYVt)wFzmq+|-^30rpP)kOEJug*zvUmuE0SuazIBr+ioH{2s6&}AyVTb~gG7l( z)F=wQm{Z{9RQ)ZvS)8Dt>&qM)8W{)$z)-H&>QZXx@6mjH_HP;mFMr)&LVMfSv0i+_v zGvXL8tc#uZv^Td6>Z2DszEid%7L~dHnfR~YLoh#i=3YFAf$Vf@XZs`Mii%<&y#01G zY-=ko=_Otx1S66%9pYFR5X*ih4XxD=S=4II(sVNCGQMT>mBK@E$EUx@F?{hd)p!Ab@j*kkr<_7`u><+Mrem zw17oNKkW@sOCn8=E|C}wtoEh_7!SmPU-enHdec!bq`s_{AA7owT+ix*ggu6|;7q1SV=NN-1HnqrCuz*|G# zg4TjfHkI*(c8h4FifQFQS^(@}SSaeHq38oV%uNi|*c_njRD_>90;&Eave1ZECb=`O z({sv!%cPB?HqgG~iXT+$lMpmq@Vs=^;z81LVKwwVf~gk(<-Dq96#@G7T0IpesEA|o zw4RsS6iRy}g*c*4lv8z8FSz-*iIs6mBIPcyfrjYXk|OKlpZs^Gx5L3kjKG;f)y9-7 zGZ}7t_cAwq3ACx==*xdB04}WF8PaZUV?Z@YLF1P;?HcE)PMvm{HjZG&Jo{bWtV>RQ zA#B^#^}6drL*2={gc#?P+DJlyt zbbh%P5Q%np$Ul+gERe6Nf?!NtMkg~7BPh@k50JgsBjbWQ;aIYX(S}HF2YcVx{VqQq3bN>bQXpP#Z-e;Nu-C5^*|+9@|{%M;dJaKvn{;>_!mFq{zlgD z%NC7ypyKOZ2S;RzPRE)PFqnIna-Ir(UM6!#owq;16aG{`V^ju_bJ4f)rj6=UYo4MB z2x!hj;7c8lbq-p+0TJ5Wk1b@*hbtuu+WRY+ukXl|ei~f@&a$s##W}nlw)}6lqk6&N z^7VOge=i|mR3p~Sew{Rree}PQD&k({9MtMa3%X_nf`n*}P;Hjxt^&(g?jp~E`0q%1bu;>i09e7R#WOLnbLpn|sbWd;G#;`qwZ_}HE1wLQc?4)DS z4TX2GttpICWrjF(sWr=)_JoE0)oE$D0OY8?uI3mnT5f#H5?P>{GdP)>9bo3gmQm*8 zX~i6?q`lO{S;NG2;hom3Q@a?g*gx5%ErI?Q3MAzZ|Iyn7X(>vL&Lv{4(=vVs${1uV*lc9kyq8HG%OAoA)6A7OXLcMo53Gooxt(xsgjy z=18cH4-97yQ(cUIif3L(vlEVfFW0y>gdcFk^!5JTkgdw^W<TB)+WbOZ z&cqH~fOlT+!6BEH8odjMH6R_u8V}-ACbe58-@C_97WaPNXOI;Am?1PD{!`_h2k#r-hG#Pd%Et-^3G%C2O3=BQ@k3(Sf!>m?a9!r#t9$}bZ< zD5+2)AxJ}ObdV_}*z6A{Ux51!lX83~r)IO9_FdSTP*~TO0g8X}k+L z3t_FxFT5w$^Xb{Lsg~gvt^~n%m*qhrrGM@$l6q}wA}?K|IzZ$i!Q;WP@kw_$j)l&Z zZ}e41L>?;jGl$)$^60cas16t=IzU#5pB%X3LdH4O`$Z}WC-|Uz*_S}+^WLXt)a$*03UtrWgE`O08-qr{wU_;--vF^#S159HuC@+d7m(f>+}qjhco+xqf+~kT)56#kbSrkSpjF#Vn4BIc$po1S zm2y|VXMO@YIm7heHCLCkY8Nw$MfxCOEtc6i*c=n>%t^8G&`C4~p^;(eYYv)bQW+jL z^Y#xN|ACg1*PX|AFW&!8jYu^Rg*6$_y(S3`uhM_w`Ww?xXy;QnD|{(f8o@>ZCj1*G zVRw46T0t5oamdU!aeJEUZe$5inqLPgjG* z5TT^I4%Wy1PHCP`$eCOMQfTzoa4(O$V7A-n8!H;dgev%?(aJi4L*~)C&IL#*!LjHm zI;XNBomuf%G4bkA(NMx3W2(6x6w5dHCiBv5OsKJ|;5-SGkGzG=P znjZF3JIM(!eCbl6s<#lvH$PghV>N&5H>kM1VouHCSY0_0Tyc5ndrj+B9&74H+%LcK zy_#UP{O8%Dbt#h^f4nH%2(PliDYlTgJypl~ubtK6zzcOA?8;l0e(uapNrN(u|K#Z7 zI-E(Eoz-Kka#{PSqzYVLSE3)ARfI`1+y@TTrdmU> z?bmed9OvFKb9EveZSYr!1GnNycYHsnYHIG@7xRhYhCHJ<&Ud9U>=Y*ST~*&h zVUUn$dEqrcv;OaUDZ)WvX`RN;Vp6xA09u?vA*9dgd~;^mHd^{~>orBj=TR0am6^fc z-9828YFQ@zkpDskJuUzH5|Fv zr)hBxCGwP4Of^+T*+YHy^zf2eKcNP^Ar>=5(c>2On>~o&{)@8@fH6*)S+!TzpKtb} z7A}G7NIzw!NkJq3DHNw*fA*^I9)t>HuG&t7v96kj{2K3kGEn20crZ! z0T>cClRwKMlxL3s4$FhBIFrewR2|sSPSC?jC%lC-F#kkk)}u7z1-Jf^O(#2f4eS(u z))^hT=>)0WA=4C0*aT5X>RRWLv>s~ijJ3g~cT^|SsA9s(P# z!Up|H9Rl8%;urXpiSjMu3+~og(2a*4s#%V#Xj1WUmV6L>}@urU3c zb6`zz2?Eo^FP&v}xqP1fH7e(^r#gdC_4*F^fPl~1<~a=dPfaQ#gxjOIpi^2IxIqL? z@ZH@{VfsdC+x~l*#%c&a1qkkxolseGPG`Cpj-IEtpD*}LvI1Gq)r##4ovV3Ts@`)R zLN(15YeyIHF$CZ&tv+&-2F0+3@Aq1_lC2S3wC+N?Y# z7PLz~SbMpbN$QOO8B|eBYuG?-we^Faj6=-w*I|ZTqD9FT+V>^97;q(i3=l+YEfnC|)m@&BLj*Vcq*$W9An5Y9MG1VxoMnG9NHQXK|(ymC2Fh?~2%cD?-(tMVQ zr7y2;gpe+M>vam)_)mbYHUv+}+M)URpZw5odutpErJd{;t*HQIP91tXgug$%20Y3& zO#IUx|7{`IRmF^`qFuWfs?iE`SvrT>0TI8~=CCWApNSO{?BpvA{PdA39$F&CM&}XT ziT0k!ETqKw05`I}i`_qty2^%idTPws?bwJ6uqE zinK6^DU<$eyhDf}T+drjuk+2t41%hmc$M{vb7*n)8%^vF{{M)N{|;1ip$_{T^%@5* zIaOd_m^aYEF9UMwmqD;Q%qJL%a?5Q0`;B@_buWgT05L$$zu%nVK(`p1qQgg7SzV58 z9=nC#7OtI}p_sBWnFcy1av!#cKX7k}6Ej`uG?;?upfeF!uueSEJj>FxMT(NwM*vJn zAzLjzt8oJISPZ5qxlm-o8BX94wNZH`ZKRl_%@FK@q7g+dRo*~UR5+&v%AV%{O$f|y z1dT>bmtdoFDgZ%t)lEu->^+8=w0LSJQy3ms4Z)SRC*z9CVF^8-Eh28R4&X*xNcC=i zw?ix6bcrE`Kg;}wijZ&^{V&1#U_N}9c#B4oI!u(S{@I4k5Z6*T`NIh!9Yy+}szrW2 z-=VdyK#I`9E^awU@@S=I1OjmdkjFlaGJj-XH5`@6KF<@{i8N%uPc++5(kIULby$_u zZr}r==9`i0qp{tsAp?uhT?sc&(h@4((jFBuyTU8*7U+3TmI(2}jOXQx+@y&QwVW_*<)RB)n*Kn9jTz7z=zE7vA>KxgB`e8O zIT!}l0hCDXP*}3lIw@$Lq)Nv-RwPTJ31d7YcK;RC6ir9k#BWcP@9BvAq_CM>#IF{9 z|70{(-D0m4@>2?+XpC!J5}qp-@(^cuPZoTj%r5$D8I|j%l6>TZP?uHT7@wFF>xsM8 z> zsY!AQN9QB~qvFE>m&hhmg#QX<9`wGbP-$8`EsWzcgyzt3wO#?BYI3a{8eu7O<-6`E z&2ga>-mLmmToZ|RF=Nw8p!g6V2sS*fcl0yXKhgA!a(>siz>frN4d8y1i6qR|T_hT& zcGQ{$4n8S!vRxk_OQiXr9kjA7u2GYNa1xu_+LV~^&x3s{0MQJlt{HhES>D2H!aGb# zG~-NB(W2i`s|y4q)xUoiD%Z}dJ0wFef4jK*8|x#>;}|iwPk@Z9kz%?Pf4j3TT&%vg zJAd#E(SA>ymoX9VzfE3n5Zdo2Xc+8oKSHbsHS@L9--0E#!Yc9oMJk*LOIap#HW}=j z`Wf5HqXVbUZ=7PJYtC-aq1rKA_Z$I^_S2+5@%3ens5&h7)`VTk>0c%ZMR-k1540O5 z|J%4bHzu{yCo$FrQ7fW5V(G+Uz)1lI3P3DG0BZ|=60a{;$1#;2QJY&;CBToO)%9!g zG?qY^e&+ zU8|{rt!o6FH2pyuaXQix9`Xre$IlCyXFR(bRLfV{7`CxBrc#KJxOi>zPh_MC#f`4V zRl|Mx!D#S=f5-c&b!yH98*U-Aiyx=9R%%Ik=UJUDwnS0z0QVR6TOw_g!$}ti+DA;( zi|BcLKAirk?Mo$ z%+M2+zF(MC$v9EJKEt}#m8IgH?PQsZj(?1iR%}0T4D9r(OTw=RF(^mesZw4XyDy?C z4o`?CAX4Z)KV9q0k_2PItaZpJV{(^!t~}PX?(J5LV&rAf>0*%#$Hyme`;7qmMwiwe zIzsT1!hGdH<$iS{6lvJ9c1IYT;T8|r=5^e?`pMv$fzU?#q2{~Nv9KD(S_W}<;jf%`Q+U69B;IFpCmp~7 z%fQLKi+&XrwGH-wF^)*@<5g=ER)DhmVhe-Beqb~PHXQ=w$C&>twYqcRy~FWJ%rsRP z`lTu%mh5O{Ysl%AZWJ%*=_UZHmN`&zeN(G8v~a8Xk9v=em)M$LKiZ(M0;AJ9eUpLN zHEWW%6rlBw??wh|83a>RbZEHWX6+T696tloo1LW#eWUM%FwRR~kB}Im6GrN6z+Z1` zxrR&R5WLx;Aqod`48a*`Tf`1L?f(&&1ooZ&i||h;r%g)j@&z2HrkK~Yf5!G$lfh_m@zGRe&1~;H3x(SnS?mvx+#u8s`g5t4x z3vQ`1HU*)nWzn4nZU3rKk&wVG6NUEy^hL;HIe>cia&%R*T>w)m%H@97AQ{pSpe+%}!Tc}X@+L}^d3 zX9nCmMfzV2mcZ10F_pS#ISJuk>H?mab`Ko?5lBS{$4~tI;)k82ox&EX%7a(4+Q_vr zH)_@Bdk0VnHZlWPOsL<$AmqREgG{0QVFER;xZv!fD&;*&($q0GqE7HuNw5LgnCxRjzlHyqd!z@j)_!}(ZA5J{M&{|FAy5@-%%+(Y$2`JVgHqvD z;&P=v&FGorW2xttQ6yex>6ALiWVS2w%rp$!D`^V9@^-h2{&}&nU87fU!hBsQM{KJ$ zf#;Ip+fqsW`twtdvTwz4XbP_!H!ceEm_RnHy^mMiN8t!I%EBOjd*rWbkjxDZJJ;x~ zuSU9IxL{m`7h1zp2e@!kNY*r={6Bw>s_JXlINhms4uIP<+ zeflTxs+ubxq2JbUd9Bh>F-Iz@hiC=3rDSjCy@J1CGjP{&`o=U-{mi-{6bu~I!>q%K zj*tEU0S3Rr$`KWAWXX?Jl>N7CInW`hmyR_}smR62_IR5&sHvwMuY7=ZWmLZYu zvOtu~vH}ua{A~u-JI9xCRzzM}UAA=e7jV%@PBo>0@i(;u^cd1Wsr&w%mUqa?7+;2& zO!IWhwzD~0>~$jIZ^|ipAy<9`cXd4L>6bnES5F=8z(qsoK}JkG%&vPG7-H(SEYmq3 z7n_K+CKLFN0eihn>9(!se+eyeC%FAelUAB3nE6uzpkl^bf$pHC0K5k0Uz%|&kkJ6Z zYvoc66aC9^q^5mm!6(oQO0{xYYIs->BZourI8|g;NBv@i&>V5{eV-HU>TLe#6lDv{ zfY#mlx`M)z@Xp@A3MY<7`(vj;VdSY;`dn;0{9l(urRRpMG7Fvp3sr!uKH^3eH-02a z_LIM;0XYd1UAoPwj(Du{NOtq56~=A@CFEG>5$HAd38ws;&FRiZYitf5ytK^oEl#X* zuW)2Zmf!;-^w)(%n8OJSvj=#HAdvw|!YgEZ^XkR=7IQ_#Ws9Mrk$4mErE(P7T8Y@P zVt208FP3ES6H?11OX*iq5&V|9^xO<%m#Crzp!ATAI}ZKY zShc8g%v%CBp~>1FAoW2G_L-5a9AcY2^|*5VMKE!HU;e%+1bKRvnRN z(t{p#s)@27C+og*hx)Z(x_MtomLtiM;LY=G!*$R?86H{_XTqC9>HDHZ!8jymwygdQ z>kFZ~s!gzD>I;mEh1Ena3ied^dRnivmmp8x7dpz2M{Vh6;BF(4QqYSTriu0)@5=*63;%uqnRXXQcU&8j;>}j{T7y#Suq(b6sHU8p`V#UQ-C*8 zQHb51g-1kHy=DLuU~5xLUrrD z5-M6+_=Zx#>&HgJ+Jo{@M3@Kx)~wteLy_73hEU*ZQ#kU5eV@GYZh9>1EQ*atl#T@j zdQIzGq$cL`MmfMuo6cty1LK_2UcAzj8# zqX5^gv;`+>7fi)eo+5$qW8HG1%4tzSClzD1!#i|@&=oLq4;@;rzZ*kuUIbq3Lqkc- zpG{JOwK?UdVYI;dXYRjS6hesZz@?+)Fvz-5;3|HkJ~*LWsp(vN0?4XChrsBgHpbdWFw^|#=`$%c}ZUSN0nVujUhH8bBt&M6ydL>_}_S#j(;D{ES z1k(WQg@u4PVidnyai!l%OOd$8ZXwg#R*agFiGhp962hy|Md=gE_2Zmw{M3*&sw?G= zM9a^GY+J}#Jivj1Kf~Q)q=Vt5m7e_R5nVQEV%#}JPI5!WB*rWj09@{yhZQ$9Q9>kg zMh*u?lY<)KpU(|&d>Xv=#9MfkOI!k(;80!UH5v)7yn##I<};&sTEmS^_$}V!f^=pfPD*6W|0>9u{1vJ>4SGw`99Bqk;lb!3b#*fUJ$x?h|cvN#73r* zz1fzRUvsHFHS!t2;$8O$i~W3bmN^Fg0*8EnJvSRk5-C&C`vR7U(t{ipB%^jghNWMY zlUURVL3tkgUyehnHX0;y&_*S4o_2=we|c^0BiH+pu6$Z=`ZeIhPN2L7N~9}Ho#$!D zZ1Qi5u)%dFjd*H{c58jhv`sEc4zWzvEr|nKv?Tu)K|4mlD zG$l^!^SUciuKRNkv@M5XUT%cahj$?pMD_yb%o4-81U3H?y!QgQjSzy=x4m1PBPk0| zVS!#@9AHPdrZ7gt{y!V@q;57LdpBDf+o1a!T#%dCQVCu$`wbHrU!`<*TqC4eI!SSGjL|R?oFOrJH&>QkyEmOEzM^WXV z>wN&D!|#&$w)r*^8V~+LOEGBO#z%RNyA2@rB;ZB25fqDAk%FFxln;K+wTyw?m4aG@ z57`(bF129xX9AXGUogaDQG#x33wV1d1b;Ew(xKTJ_`fVW1<57M^T;33V9`MHlW_|MNW~q$17(%-05nO8?AkHU zUSI{8o&0)@-(&`l@E~7E8rF}rVn&U<>A-U{N&M9?V4hyN<}(z#LIb0T15Cf;sqRuw zU`AIO?Z?N3Z{=}#3hyob88~THoW*&R0~KZ2{|iP3=~AbgH$ZkTnTh|w23BOc9Q7n4;vkP1+UBh?lR?+U>UI zgLS~HVO7Kg+*8QdRj!`q!Z-^TI|qRVU}ZGX?|s zJ`frh>nFS+vuJIE!R;=^`4PoGN&lh_ff+SCf#f;z=_B9^E*-+$9P4Dtl9Lp0y(V3+ zY>XCp^ZjS~9ext4?D7dt)Mn8%Q-u@No5j>ci0R80T__v3GAo!8>LJ#oWcRk(tk+<^ zpR{z-wIbOLxKLu6e-QGyL%HegV{OPlZ%S!6l`Uf(`^1-J49A!xR-hx*xn%HQJI$?~ z=%NJiL&_bgKr_0#=8-3=yF6p|J^K|@m?ihle_OOey>BVFsS`Bk>1 zuq+oSrU(LujNS3cP}oflGnw(={roafr$$7i9(7WfJn0>jV4$u@VS zlK5J>)n)Kf`Ng-bE14A~K4DSHcT*ews%0y>p4q{?fUB^32-~=3=A1G&Gwg~0ue;5f z!~o#FT-I41*)tjAd3wO_>U#p3CNW4vC9$ZJ+C2OFZ+@1}JJ#2V(aA=wZ;b5JxlHqaDmNNCI&c zVCk(`9xNqcLOK^4(VZb730rcUv0@hq&m%;+E1?pA5hKC)XHOv~Lp04zX>sU{?Jt5rncOWhM_OVG)6C=u z2kb1!hUPWJ!~#Y=n!p*dBcY<&MH#hhf-lsa(4X|LOg{e2mCcwlZRRxKz$gJp-}%nD zMV5*2Cwls)r&h&_ly5>SJ;%$Qx<0cLv<>BWE!0+0v8m)Kp1EPca}MMW=TKxUfXtR1 zIQvrW>_fFi)E~g?Eo6q`v?G+-wT|`p%8!GgRD?A^M3gS_`C*b{!}oFzLEqXSc1vkyuyL{pnD_e z;-6g4y@9?3!ZERDI%{RGS|`=u`a7?WBL!b;bW7D9GC2v9P4Bs7;r^$5Ew4l{n1tLe zfJm_mwiC!lYvm!dVgaKck?A+@Ur^~>RH_5GNu98pMx*WO_%%ucz7`BF8s^7bV^x4X)uai(SCTNKbq=EpZ(REd1AB(BD{-aVKJY~;PkK4kTBc44 z_#i$yOjVC=hI|ZK`Ic%1=#V)a!kb)CiZ$`YjaRR$ zG74HbfB7}u50{ne{gfK8TPCJO>I9&oKr+0XOHBLV&eqb$5r!T%Bnb2g8aF2`U_jPy z@?OY7$kFi^+>Kx>OY4**554iPhR}g^Zl{Wev(+_2fHlb|rBMI%VoyDvvE-s>DulAUt-sW5m4>1<&O(K zAmE6KZMk8?V@&d6idu)i>%cZ&Nk_(Nu)XDwf_UmrFk#8gv20b7VUj&|OA_X7cqjlT zk5AP5qO4zWMIZorfAXLOJtzVH#96Q*j#(#AtZ%iD!W@eKQ|D?{P2W9PGYomoYW4nBSoT<}GJtVj&(5g7 zsVqEArN1qpWl%AbL<{#6kN0bY-~x&hGFW1{oK`p|P8XHBX@E3VxDg#$Hpamo_AWo+ zPc{gnmaaR>Xt|rAtIQLZkJC3X^{n|M!!@N50SU3w+(-a!`AY!Chy#hKIj#?B=I^S}n3eI5XN@Qg0kd-)^=qPSJfn#S3Wu7IV<$8t|OCRcNq_k2$vVIi!K?9l8 zp>9SB7RQ}HtGMA{H1yusro>2}=CTUAo9+!74zI;b?4dG&^1dN?97`hJCGU@8ST1E& z{2VK4g<7((isYd2!YS{^gT>_^2AF(Y)FN`fH3no$;bzGPS{muNo2a>STE}^Lyg+rr zY}{2r2{5Bz`L@lDH%O6>`w9HMxmS_%kvB#8bN5)qWV%tih~NV>Hmb6Lv?v8herb(wO$E zbqLY#Vg6VPG#=3Z|3B^>liD;dNbUz~gawgtNjH3PSW;@bMJ9}Q1Mm$<{^Iht!8h&z zb^`UCt2fb6g3bNCw4gR)A~XTLZ!O4w`(e#nrE6`sTi?r%Cux<8rpux3O0zfT0llxnJHYA}|8gK$&@{FC7{k$^II?ox(HyW4Nx1Cakn8Vp1gzDQ|9*bA6}mDeTDOG$FKwobo_(|Ygr9qkI|8u zo+FTwbm}k`)s^G8Tc-vuMjbfoA--H6XA}8Fr!gG_^50@mzIrGD4*;|NSpZM_6B9E? zRgvhs$0M|g9h0K1MS$XwK2ip@8IhLlFPU`{z4HSo)0x9N;gfcFskUp}TIM4&6AC~| zBjfy~!>96K1Ki>xTIW@wt;X1Iny2w>FYlj3R?j7;Yh{6H%^ctRZ*8(QuBfK3i8LQQ z7|0Zr5nblq!Ax1S3HgSeL119D3J=JWcPu*>93pDXZy%B$Ty^&CXbBY}@N%{@@B>a- zgh1HfK93qqx9I9ZW`}bY`jOrS$E_IWd{0pygpPL)q^qT}zE96fZyW4?ag1BJL%yny z(m8PMqJ(lGEJA~LxZhN;ryDhqc@?P%?}~qYB@lYJ{EJZT9-N`ANM#sXmKZ z90-||=I9y5l~p}xtypv12EW+cdSA34lVFWXQ-JX?#9*u#c>=WY4Z<3wz2J&Y13I)jFC=(a_(axfP;fT5`!bCl~hd0a;v>?aZ8Jh2_!=Aq*G=4sVraE47epUNa&LMz0 zp(V7RnQPxo{WoGLxl$&d%l2L8E1>H;3Ph!|;D8GpjU-Ttj~ChR*O=3b zGUwx_qfRLlWt|5%wQ5%(^N){pO86K9F+1A)Z zeBJ03lUhNoi(yy;oWG6`5=sVbJ7@WiNnU6s8O^d<>7IkrRv19GLn9LA zw?#qZeuvW5`?qA)YI$M6edC}j_(|XzKni)L zmAEF^k6a>F=0L%NT!QbH!b0`s918kL{n7LZoQx;=AI4RdIp*@oCn5b{ol#)5T>Th_ z+!(fYy^}oqJoRG~Xg&s8S;y>VV3&s%w%$=!tN%p0xtSbvB0>R)Mc9MN9xz6O-j^d@ zvP*LJiD@ym=TJ<1QwxQ$7OJiTt6+bt571F&IRPbf)=~2EotP+%0yhKXk@-(`VVaX%=pg=`FNkQf?as>$E>ZA%s% zLBdG3*awt86v-Sc3P7vs42b1uCWO5YC1#+#yc0<%>sPX{5_kb65ouNZ!q~{?G9`i= zQZ;M*VQkDy)-D_-?1U>2C+~Wk&olhL?C9J~6@FbIh%TJYLN8F=06ia|!$K;G$cb2Z z9C&tG3+4hD*T@oLa@l0Np&9bVzZTFyHO~YR z(F@ez>K;;`9jcNEea{00XlA^S;(JaV`WO0D< zNEI$ou$jL?XvZb%LBxLjoO&1Dd4GvLBJiHV!wJtpXT!bQ8pR$yec+kjxb~@<1*0ac z{S%W<^j&9w@KUG5U$?#G)eG>-y` zG*2qv@$jPT8HoH%eZ#0)e~@#CArMYIn)Lj709X_WTTYANcJa+>6bLwG9jo=%J;8|U zlt{iInnj{}xZAP|DCKHv9=^3H_eA7_wC8gOy(5fVCg&?+4fex{+j>7OOH{VJLrYEh zX8iEK-yQgpp?HEC^Qhb`I%@&rGrnAIti39UV?F|v7kLmhCH6N+ZLRR z`YtGUl-VQ*EfuLy$H_0forl9mR&LD0Pc7^1stLWchBq7onnj3S%$Rq-gLVr36qig% z$EG^e9L9^X>@8`>qNbjxrz=X*Dg6uqlMlg^X092yoCqo3n@hyj$B?y->HCb0T~02| zJt9277CJRsj!t!iZ9KI^kcDDciZzm`MpeG|jW7jB^eTbya=$^t8(1p!*2T6s zNAdfB@e)|On9sd|K%Iy~88Wr2B$wmt4(+kmTFnlF0%l-Ux(KO$m;3vL?c@kjE0z`7 z(1eDy-okzFowQ^^rpF!w+sOh6q8rZ^IKjW&erB%8k(<>)wh6i@(Q~KzZd@P*2u2*e zHP{?}&&O4%W7Zz%*v1+?^&f_$fV@$h4Ikd7udU+^mqznisFH6j2|_aa1Q zy7{4^Ojg`Ci1>7Tts~?=+O;Y2=#yNQNJ-R-xM>ShSko-yA7uyi#e@K z753kxkAQ?uju)NpPVh2s{N>q}4d*?eEE`xot;=b5-bezg!qP0Blr`{Rrj0MgQ2-R> zO`;K{rkrIL3&qrCb4@xCTHVmNm4mYvLsnAL&pvrTVbCEM!x%*6 z!2a<9Q0K0taS=Ri4}mZ# z5RcCXN>FrlK?k;m@C9&^VY};c5VUl!xiFRNr^Vj@!82;TQ#PkIq@sA>yKcR+*CB{~ zA8&E&INN5VD~Av;o#Wx+`*nd; z@X|0cc+?`Bt^eFq7Ta*UM1`0{x zYL(-{{;PzR3NvdI46V)Ip@fS_v6fWAp@~ccD~cIf!nl2RYGno{bcXQ{$DbzEp$ks7 zmb{Oh8KtYH;S$)m()V21G zV9QEjeVAh|LqjqK?;cSGh!zvVmm86vlY{!EAR0s?=!DRkP0=OkpP%FxTVoD>!BFi~ zOA|!~IH`%oe{bv&BI~;4b}U1)aBI~QUF*7k|-{Jih@&S-yvVVvj z#$&nSzWOQZPxj(*-jMOvAYmfZeKmSa;d%)?2G&`nJxbQRgheJ5$L2llIma)MV-tz} z=TQ{LF(=_O?m$MbeN<(zOQ@;H?TZ?`a69CY!#p!`gxyhp`z%Uc9Zi;DQIk)0Y=C~g z(#D2+X^88tWeQ9jK@39vI<^x?aonb~_iZvQ|28H>q3}HJkR%bk{7F!@{s8_cZ~@pR z5DDT@7mu$N(V93dD)>1(JTTysx{HlB&znQd<2+}VNE8*bno5{NR&p-%5Y<$uYgrUnOe6!T&OGIr z-TGJZlo$R3bh2X?Jnyn3K|7+nx)^HG0%t#`tYa~~SYzR-zpq&~gvq9c8%X!t4egv^ zhkI0+AN_}`f`>~G%9h}mSBIm(lJuR9OdCk!AXE3Mp z4^~yfN2PMJv2XGcBz++C=P0Zi*mS5+ODNhTgrE1{U~$YrA*=(I1_`->1Az0@mtVW0 zNpm;cs)je z&VW)B&nS;4npoHP5=X}yrFT_AMDbr9*D6_W_!pG3P^3c5ZC7}B$0DfC&aWahUZ}F} zTpKwfLZQFIG)%s=3LDs?Vl+wlK!{|}z#eq)X4 z1VJ7HNX6^p(3Y~$V<_AUwXW`xd>5@5=+3Hm2o8oQ%!YwIkBdr$-(J7;mqpm6qE_dI zDpzxsAHh)T3Ma#<`;c;yH$4+@s5H7Lx9g#&^!$yo-`6|;rEf|UlC|ZIdLGyIfAq&K z+;tpoMi=(8K}j>df{09rLb9 z=4nDgkSl3u3M`TJ`ud*dpadzqen?+i6v`6!m?ufxro#tXAIhk#E~#X3QEW0$Y->AZ z!BV6BGCbG~@@!ksn5$2;p3-k(ZU7`PXXfkq#4@FfU0{>Q)J(`%UWu96t!G~fl>M&r z2_tJfTk)!53IhNiWtwnR8FiG--rkQ{_LyFF95p6#UA$SdE0X6APt@x$nyRbcof={J zG_X(@RY??8p)g1HU zp!pdWSlk86 zSAH6+^{Nf`8Sf4qgV45`hAo#Cpg;TJjrfWG6~;X+wiai_j&p@yP};7oOR+L|TVdkf z;s98ep&@<1v+g^&yqDZD!`V#A56kD$TlKsHc7ol`f$m5a^yQc?87fO2nx1gB6cLFv zwrdg8f9;yO@d$@w+IqS3Ja^na&1Nq|l+sc5I`J{!8qc z;FxLAc=tu3m2P0CWVHaG1gBJmq(IKbu^bJv8q<@xWlqr4xKeRZ*{~#nr29;<(`$pw zOSn!-`#XXO69OWlhgU=082SC#5v_A&Za9)03IN2MaX5l1+`$dzD~lU$Gfd9od}zT} zkZ2_x8p3W1tFnNG?`ToK)N!2J+SvL!QgD-7>!A$5_`}`b?Xqdu2B#SQmlnqETsBaS zg`SkEXal69W~5@D1+zo7+-8rd+;Q{KzM)`1Qr1rkw`>4ytUyA)yC0;;jaH0~Pc^;a zA|7vqPp~pWXMi$@<^4uLv-sQKCG2LgwWux1ZzI_myaZ9(4ly_LhVjhQT{`kaXd)be z+1Uy2(YUWaLm-W)X+8)K@~4pQp+VID-&hWd)5nSm4MphhkUqg#mb;7A6hEa5>00+;2;k;7__$67Js+CdaHsg3BN)6Wcxy_lIAi2S*Rq!}{%hyr`|z z?})eP0{6J&_dfct7`EbZml+c_fzsP5Xy9%!;F=tHiNlJWy||v8oa-`=wIg9~#_B6V zHfGBMqPHw#Tv{%cfO4E;_ITr^#V)|?gSD5_1@yOttryNw=EeD(=za#es=6#^g^1i5 z4j@=E;lB)aEk7a&lIV_tjG6nSh)T0twY2rgPf`}8;6vXQ>=yFOb_rp58$yw;<|Ort z0LS1+MH&Uc5c9kvHYZ92s35y}+c^5h00SbK_)j$FV^_$%pEw0MZ(y33yUdi@Sz!E` z;`+j~Gsl^x$F*+HepRx~8)~6UBQ-O!VAetD%c1lb6$&uQgf33TCK9AGZ z8XBvW0WiNPF4+qY<{BQ8vqkyC8BHhBkzGRZC zsw}_7+EhOhAurVpRsk6XU21{StMzM<71* z(?#DMfvZELd1YIrfyd&)V}4l^a>k(F$FzPjOHgO+o&@FsK-!Wmg5-jC;kz9i9y9(0 zt)9hHUh=8eb{%-*9saF1y%`@<7>PpiB%BeRDZM0Xb`SpucZnP}WsVE4n6gvl47dmW zhjHUOhM)D^;KIgvtd>((34RQl!ScjuKB)V|m@!HA&*DJoGNz z@6DCz8*3OpTz9ZZ$bu5rKT3$VIhbw^8g^;|f>;=57m|s9r1>B~UbG8+VD(2bIC{>< z=UvEYii(Y?LuG~*6vV5$&&MVAmpBR&?-{~ddzpyfoNRr^xj{&W(2s8Nr^OQ4U|qkG zwkTZhKI7cJqbr)^5)dXd(c)2-=#O;ExOO-JpBi4bPKW|?w}p_rcT}6mK6kigH^1H zWpZ!6-IVJk`4&`r)GHVTcW<-mA)>s)aY3p%ZTu6c#5s=;@7_Rf{gREW{lvAIE4Avw zODK+!7?I>xCbw4SIJxFJL81G~kXi{G_1DeTVe$fS*MluEDsqW)qEG(a88UYh*0j4|q0R0?=Cd53#S1t1NlQv( zf~d+(qOG_Q9yx(PmgKg&R>`@6o=8Kwlhs{>EB4!9U+wX_Ddys?up_2T z6=8~_*jH3B?*lGL@3ZBw{a$+Vn?+%|(zI4iMz>lu%zZ7TmptBJ^=C9{hJHlBf(_Z5#s~LktS7B?I2;tAQ`vPa9Zdr z=Qze|Aq1>h3!M&R!e{9=n=5AvbnJPzIdKsSHW4g(=hih-8uGrlx%oNB)|s~8$Q}Ki z^KOEX4Y$(DRXLA=M01%@PNymEG zKo*dOrBleRQSphHuBu*jOAte-Up?!+Cl^xb!{EeE1XFIJEskIv2R{fCW6z&HyqOzK z%#pO7HN$5*Cwtjb$&xXQ!felyDtUh2R{r#Cn7uGoidzPpM~8)&UcHl7=IL|R8DQO0 z7*i!YDpf8%Wta93m4&O^PPcKxsBQdfbIyQH6nsAFRaU?{8kNCvEOLvX`VJ(xDnUF` z9IbEOz4}E~gKJ4U(Z@#agHC{Znim&x2hN)P;OZVi=_+UDSE&R{4`W*p*6F3}79M~X zaW$h)@TJ5DxbMa%haRk4(nKQY3M8!J=#N+qDM4i>W$niPFg$>5C!$ zJ<9{aS(9aX1Jm5&()~rwTH*$0C@7O#zV}2M8wiJaz~&|7KN1{!|9+UWCd%PSZcONf zpNb7-3}3w7IUx>{a7^`3v%La_^P1dwU>7m_5&QsGIq;~&_-&#g=l93>iSc$gM4sq6 z!&5s@(J`O9J^Xl;0e9pcNp&S05#+B8^w~GK3c9m(>ie7dy1VVM7v#3!E3%5ikX}rh zHdnR~*VFIdt`ggyHO4A((%ue%^J}&tD~gr@7m}D(@tL_jU>twiH5hhn3Z1RP~33QAAyBxKqO<>M+U(EnLM17{_in&`t!lj3cq(r2WP6U69;E5;*Avu|r zRWdO>LSb5$_X{jaW@XSqQ%WtImBh32`zz*Xd!r_oJSl@yk?I z%+$~W5Z`J8ks!G-`^q5*otJI>A9tr3ZgAyx^PdH$$n?!Nl^P~7W`SB~&Di+$v5r1P z$?A15!@18QVwGql9?EBj4~vpqD|gl?UIU<}b$TlXYtB)&rLxN`R(i3mu6W+7GRUrt zt|9pw9R3_>8>^!t>+1yu-p(w(Op(hmtoid3s4_O_4upMZnbN(n`zAED+X!0IqJXnR zRVOYczzVf_CXWwo1R{X!ncJ8G5Asv4blVg4hg0JZ2iU!IqR}m)SW`^V^2(dN5=~4L z8VW0huu}P=%AI?5Oj!CrazE-9d}MNcrB8eltu~E=-zSzyBe+*4md!CbXO_m!mo39ifBfLAANwI@Q8}B?B@~|KZ{2iASHuqJEwUv*&GUU%bV9bNw zB3{k|gO+E-0hbbtx|Z@0#^KGWVmeJOIrUn*x_Pv{SY*p{nS>vEJexRIq`y1y;*U6< zMCp=kLjGP=k{V@QZUY;%#7=(&dq7#75OK5Kk*nMDg^z|nczmfll+XQL*FB14~ z80cAqVIfP>)R=y2k^ur#yvnYx2mlt&nuafyK|VqL8`vvXCsE z36A7KE6q_7JOXh%0)TjW1RHpkX%n2BzlPAP@@;~ugq16gwk8vx1Mu+vSe{A;?=m}i z27Wfv2&>Q;Abu^#Bv#O8+yM8zNlY}GR-lDZcX*z6;TL4UWfCq^j=NgbH`uiOP%Jh* z@J*pQ{}p~bU0QCJ-2h0B?o31T>&GNBNuQ?}&^1m?2U+&MbP__IUtmt&SyyCU*f(>Z zt0ARki)DW>Ert4LBA^uu?@bWg3o_h_KGbZYb17$7ufMu7t~j^6jX2Mt6Ru;xQf!dd zRQc;84|We$Zo19Nz7wB`HSpw3ke-#dO}KTW4dl`yZ)-re5ok_sHL35ny%i5vFgMDZ z7a%t%5_CoWYive#KeA@zS_Dv3kYRlnC+uh;4ouR*j}FgHezYS^$lHOMzQA8633Q9? zjb>xq2Zq28i+!mZKgc>JId7sV5$-=jm-z_hR~dS(+puWsjp{G^ITc|lfMbH43IZ8W z7qF2t;hQ6xd~}6&8qmSMb{Any(a|2V!!$YZ{M{r2#LnnBb8{#fsgG3MJ)KI|eYhm< zepE4dQua9*Q*iDG0ft61Xn4otgdmfIIXOrg9-j_E@X~M}ZM5`l+QR96FgCfeJ7!@T zBndTV=jU0#-~lelLIz^B;Xb5;JB>pID$!4+9|b}T2)1;3u2BVpv6~hDSM%umE3z>@ zZ<^e#G^g3oy89cNzb+5eF*xLop#Q7M;}}yE_oDH`5=es0DXubI54s+tc_G}%!a+)t zN#F}xhr*=hfoB9;s*3&E8_Hrj%O2Ct=V5zqt>vL3@6OisPB*&W0kUxAuXW9brB{{diQbuP3^o7urzK~WcGfVO( zX2&lbosTiPK~t%jZVo;ZH#%7=TEU2eH*`%pbxD@dOLMDbhb<3R=SU;1D!rQpHQ z{~M9%;A{?-OA&~e}Qx8{7lL%1~pM)-o%7+VFUvlTRv3xBY zF{puK03lWx;dtbj`|p{0r$bD)1mApd+^j>LDgg%Dppj8TIDuCG())Z|!T@pBNB7ZK zS)alxAgp1pId!&?Ib0e5#L)UN-|#^uqgR)hd!bP5`)XLqaMoa1dH8a==wH%!utQ^3 z&x(fE^+4o7-#&n`wS{+3N0@`FB+;5*?!G@>L+Lei2(%K(-+yc1-XuIVMeM!*{s#m< z5?owJUu&fRlx84J)!H6Yb!#NX@x;>29Ca8^S3wj&%&jb>JppOZoC%W{#g@O^o%9&k zTW*>P2?~+`eMN5N!$|76wfrdtB2xwyMroa)I5^d7AcA88IQH9Lj`@_`acy1^QpvpE zsYS2|pn?#K8((46FXPgVvE6?X^u|(t<7aI>0&x+={sNQ2CR&oh(=>DZW&uw9EY-PO z1=)5wSnTrO70%A+3~}4_-R0+neoa$Qz%oa)nbWF6*+8>qrS8eG!>}BnIeH@I{wZ-{ zm=qpD14P2!q^fZE*0ZRAbhbr2HfriZ6K@{@&7Kaar%{-xEnufb<^oXb3*aXz>o=DNL$r&Una;<8kn>m`qYzW~Y6!l10=-+^CAXrk!AZ9#_iorZCzBE1Um)mH0 zc7R`RvGr8@s^y5CD zdvy!z8>+_CSzBnyDL+J0s=9K)HmFvm6po8)SC~TlaOx?7oQw?E7PM7}Ve{+Qxl3>1 z;PLa*=TOMcbMh;gUIOw6)6ZdzuQOnxhO6uZdR(UfWXdk^LZ?yORwxzqg~Rex!1NkQ zIp_~{aGhKzQ7ZNlHMv*dF_p~8{{VS11Fdk&-tp*r%)!aKd zka^TrcJ`SAi4;)$*C>!@wi3f5Xbkh#;E$^NeR|+$c0FNX0u5%@&++elWnSH;mHiu5 z0F5CxH%3{r$cGOLYb1Ci$VBK`00(LH2 z`wEN7V3!GtMJM152h?#a1-jtw>|Q~0|E);lV@t+!THOmRYoC4&kLP3=oMZ$u(?Qi+ zv6m|U_A(wB(HJ05&B zc}4~C#3Q2vj3<7^sy0+-Lx#N)s@H$6AB`z%43*r~MW>xuC7BH~tv460HRakoB$hFd zPA~Y=u-C>z>S0hVkAvY#w@jVhts8~HNE9H~2iGt9flzPYD)p6dW$U;(pc@-iC zevSAQ>`;xmSg)S}{ZQB_7E45QrDp!BMPaNBTjvJJU2$C+;1tLdq~5!$QIVh&HE7-i zZjaZPSFiAV?DoZ2ngPgX#9<*NWVb;P6t@y6kR6b!B}=FLEF`A=&V0mSXPM+woyom|Gyx*NK5kg^76hOcOGAcf|3N@JT%FXDt>SWEWoDTwJh5r3K$ z_TX|yh3r5ZK{l=qh3W7oJCmZj0{{;8*QnrE=s zhx?9`FuraOZ| zqc=+xtSLFg`&C**F@bav(BOPZlIc-wgvo$zG3rq}G6TVItj`5%_M_~z0vENs&NXK_ zD1@1Yox0m8uYOXvu3iJUS2xs-idU+}+qg1rJ`Q&Y`qz4<#&=jm?v~qE`^eUPPReoeI?wX+erBP8|^Fu5D)(&W!?2kgWxc9RuBSP*7H9<1Wrx7FW&p<-oGqOz~=HhLV&}bFQ)-=b-k2W?i0X~Tj zeMBxssQyKp_q9GKHL#}Jd;IZ*mG2jT(4=j#3@?m~s4A57(rDRIiELiIy-cVc`cfmQ zmtnyl=aGgKVlL!{L+T*;#(^&3gFGRUAkZxG0T;s0YI~Ckdmf?EZ!_R~-5@nvoTe?_FvnR`Nm)v}3bmkPvIF8zz=xPh zqJc@tr+%?Uvi$DhiQti2-dUJU4|rH>K1m z!zZ9w+bppmxg^Q}LWh8U;!}oW>m;H8XHRe12S_R1MSP4bq@IQFWD-sN!ZlPbe|ZGH zP@~m&#waB7iBqH8D||sxU>BCL5tis=7D0i1eI#$%s(OaH?&!(;kv>Q4vv9(AT#q0KgHg$nw$RXo0P$k@7EL z+@X+|oCH!uGX+6{6|`G2cv4VLJ{Gt(N!d_~m&tzrJlhu@K~NkzSJD5s;+k6q2j0L4 zHC^eAjfxghn1ZjPbPzm~^;>0r`fsSI!ZXVDn6QW=0wddNw;o&5U>TP3c_`2U=#w;9 z5(OX!AXgW%KZ=r~+MaYsM$%vX#?CXeI%L45?GF^lP7jHu>uv_`{r=K5Xy|)&4h?~g z)CKZU2l(z@?e~0(U>DZ=B4>kL0K?o^VXzqxlHlBLUy9? z=~ojF@qxQ_T+aV4OW=iy>G}}HV0UC`ov6%$$THy)kA!|^5gDRW1$T96a>4A;=-toh z2bz|yL}f#PP)80N{300pIQb9f2VDQ!temW^WeG4!Is#DwAOV2>=KK~h6Soyg1-QH(SNve^o9u7R9uL8OkRZau!kEo~=n`&c$p=R-+24 zy;!DP$e<(ke4SAINs5}xz%jYE$ekX^Lo7-VZ&19`sp$eqi$d&vgag-?aG?{k6z zpQC*R((fRQa?BMb!$Wz>*g4jVaGH0Ua4K-&d{qy8u|)-4bCwO|w8tCFYrH?je zRYIC_%BvJsfP;V0Io_jVqV9fM!8rt;TX@C_SkioWA;S{7x$WhXO zehDaoGA_j9BGRYhhePAgG%$g-FboC=Duzgg_YDFI1pqK)1_>&LNQU&R|G(V zdf*#t%!-H;kdbBlY$9AR1FJFvAk`Joo&}|_@+ONEIKd&3iT&}KG+ee#t8F~x28f{C zsoY5O&qqknz*w>Z0t5g80YESe1_&yKNQU jvmOptions(final Path config, final String opensearchJavaOp final List substitutedJvmOptions = substitutePlaceholders(jvmOptions, Collections.unmodifiableMap(substitutions)); final List ergonomicJvmOptions = JvmErgonomics.choose(substitutedJvmOptions); - final List systemJvmOptions = SystemJvmOptions.systemJvmOptions(); + final List systemJvmOptions = SystemJvmOptions.systemJvmOptions(config); final List finalJvmOptions = new ArrayList<>( systemJvmOptions.size() + substitutedJvmOptions.size() + ergonomicJvmOptions.size() ); diff --git a/distribution/tools/launchers/src/main/java/org/opensearch/tools/launchers/SystemJvmOptions.java b/distribution/tools/launchers/src/main/java/org/opensearch/tools/launchers/SystemJvmOptions.java index af7138569972a..5098d0e343f5f 100644 --- a/distribution/tools/launchers/src/main/java/org/opensearch/tools/launchers/SystemJvmOptions.java +++ b/distribution/tools/launchers/src/main/java/org/opensearch/tools/launchers/SystemJvmOptions.java @@ -32,6 +32,7 @@ package org.opensearch.tools.launchers; +import java.nio.file.Path; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -39,7 +40,7 @@ final class SystemJvmOptions { - static List systemJvmOptions() { + static List systemJvmOptions(final Path config) { return Collections.unmodifiableList( Arrays.asList( /* @@ -79,11 +80,17 @@ static List systemJvmOptions() { "-Dlog4j2.disable.jmx=true", // security manager allowSecurityManagerOption(), + loadJavaSecurityProperties(config), javaLocaleProviders() ) ).stream().filter(e -> e.isEmpty() == false).collect(Collectors.toList()); } + private static String loadJavaSecurityProperties(final Path config) { + var securityFile = config.resolve("fips_java.security").toFile(); + return "-Djava.security.properties=" + securityFile.getAbsolutePath(); + } + private static String allowSecurityManagerOption() { if (Runtime.version().feature() > 17) { return "-Djava.security.manager=allow"; diff --git a/distribution/tools/plugin-cli/licenses/bouncycastle-LICENSE.txt b/distribution/tools/plugin-cli/licenses/bouncycastle-LICENSE.txt index 1bd35a7a35c21..5c7c14696849d 100644 --- a/distribution/tools/plugin-cli/licenses/bouncycastle-LICENSE.txt +++ b/distribution/tools/plugin-cli/licenses/bouncycastle-LICENSE.txt @@ -1,17 +1,14 @@ -Copyright (c) 2000-2015 The Legion of the Bouncy Castle Inc. (http://www.bouncycastle.org) +Copyright (c) 2000 - 2023 The Legion of the Bouncy Castle Inc. (https://www.bouncycastle.org) -Permission is hereby granted, free of charge, to any person obtaining a copy of this software -and associated documentation files (the "Software"), to deal in the Software without restriction, -including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all copies or substantial -portions of the Software. +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the +Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/gradle/fips.gradle b/gradle/fips.gradle deleted file mode 100644 index 1ce2cb89176f6..0000000000000 --- a/gradle/fips.gradle +++ /dev/null @@ -1,96 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -import org.opensearch.gradle.ExportOpenSearchBuildResourcesTask -import org.opensearch.gradle.info.BuildParams -import org.opensearch.gradle.testclusters.OpenSearchCluster - -// Common config when running with a FIPS-140 runtime JVM -if (BuildParams.inFipsJvm) { - - allprojects { - File fipsResourcesDir = new File(project.buildDir, 'fips-resources') - boolean java8 = BuildParams.runtimeJavaVersion == JavaVersion.VERSION_1_8 - boolean zulu8 = java8 && BuildParams.runtimeJavaDetails.contains("Zulu") - File fipsSecurity; - File fipsPolicy; - if (java8) { - if (zulu8) { - //Azul brings many changes from JDK 11 to their Zulu8 so we can only use BCJSSE - fipsSecurity = new File(fipsResourcesDir, "fips_java_bcjsse_8.security") - fipsPolicy = new File(fipsResourcesDir, "fips_java_bcjsse_8.policy") - } else { - fipsSecurity = new File(fipsResourcesDir, "fips_java_sunjsse.security") - fipsPolicy = new File(fipsResourcesDir, "fips_java_sunjsse.policy") - } - } else { - fipsSecurity = new File(fipsResourcesDir, "fips_java_bcjsse_11.security") - fipsPolicy = new File(fipsResourcesDir, "fips_java_bcjsse_11.policy") - } - File fipsTrustStore = new File(fipsResourcesDir, 'cacerts.bcfks') - def bcFips = dependencies.create('org.bouncycastle:bc-fips:1.0.2.1') - def bcTlsFips = dependencies.create('org.bouncycastle:bctls-fips:1.0.12.2') - - pluginManager.withPlugin('java') { - TaskProvider fipsResourcesTask = project.tasks.register('fipsResources', ExportOpenSearchBuildResourcesTask) - fipsResourcesTask.configure { - outputDir = fipsResourcesDir - copy fipsSecurity.name - copy fipsPolicy.name - copy 'cacerts.bcfks' - } - - project.afterEvaluate { - def extraFipsJars = configurations.detachedConfiguration(bcFips, bcTlsFips) - // ensure that bouncycastle is on classpath for the all of test types, must happen in evaluateAfter since the rest tests explicitly - // set the class path to help maintain pure black box testing, and here we are adding to that classpath - tasks.withType(Test).configureEach { Test test -> - test.setClasspath(test.getClasspath().plus(extraFipsJars)) - } - } - - pluginManager.withPlugin("opensearch.testclusters") { - afterEvaluate { - // This afterEvaluate hooks is required to avoid deprecated configuration resolution - // This configuration can be removed once system modules are available - def extraFipsJars = configurations.detachedConfiguration(bcFips, bcTlsFips) - testClusters.all { - extraFipsJars.files.each { - extraJarFile it - } - } - } - testClusters.all { - extraConfigFile "fips_java.security", fipsSecurity - extraConfigFile "fips_java.policy", fipsPolicy - extraConfigFile "cacerts.bcfks", fipsTrustStore - systemProperty 'java.security.properties', '=${OPENSEARCH_PATH_CONF}/fips_java.security' - systemProperty 'java.security.policy', '=${OPENSEARCH_PATH_CONF}/fips_java.policy' - systemProperty 'javax.net.ssl.trustStore', '${OPENSEARCH_PATH_CONF}/cacerts.bcfks' - systemProperty 'javax.net.ssl.trustStorePassword', 'password' - systemProperty 'javax.net.ssl.keyStorePassword', 'password' - systemProperty 'javax.net.ssl.keyStoreType', 'BCFKS' - } - } - project.tasks.withType(Test).configureEach { Test task -> - task.dependsOn('fipsResources') - task.systemProperty('javax.net.ssl.trustStorePassword', 'password') - task.systemProperty('javax.net.ssl.keyStorePassword', 'password') - task.systemProperty('javax.net.ssl.trustStoreType', 'BCFKS') - // Using the key==value format to override default JVM security settings and policy - // see also: https://docs.oracle.com/javase/8/docs/technotes/guides/security/PolicyFiles.html - task.systemProperty('java.security.properties', String.format(Locale.ROOT, "=%s", fipsSecurity)) - task.systemProperty('java.security.policy', String.format(Locale.ROOT, "=%s", fipsPolicy)) - task.systemProperty('javax.net.ssl.trustStore', fipsTrustStore) - } - } - } -} diff --git a/libs/ssl-config/build.gradle b/libs/ssl-config/build.gradle index 3226ec12ff6f7..889c278ffbfcb 100644 --- a/libs/ssl-config/build.gradle +++ b/libs/ssl-config/build.gradle @@ -35,6 +35,10 @@ apply plugin: "opensearch.publish" dependencies { api project(':libs:opensearch-common') + // bouncyCastle + implementation 'org.bouncycastle:bcpkix-fips:1.0.7' + compileOnly 'org.bouncycastle:bc-fips:1.0.2.5' + testImplementation(project(":test:framework")) { exclude group: 'org.opensearch', module: 'opensearch-ssl-config' } @@ -49,6 +53,10 @@ tasks.named('forbiddenApisMain').configure { replaceSignatureFiles 'jdk-signatures' } +tasks.named("dependencyLicenses").configure { + mapping from: /bc.*/, to: 'bouncycastle' +} + forbiddenPatterns { exclude '**/*.key' exclude '**/*.pem' diff --git a/libs/ssl-config/licenses/bc-fips-1.0.2.5.jar.sha1 b/libs/ssl-config/licenses/bc-fips-1.0.2.5.jar.sha1 new file mode 100644 index 0000000000000..cc29a787cee56 --- /dev/null +++ b/libs/ssl-config/licenses/bc-fips-1.0.2.5.jar.sha1 @@ -0,0 +1 @@ +704e65f7e4fe679e5ab2aa8a840f27f8ced4c522 diff --git a/libs/ssl-config/licenses/bcpkix-fips-1.0.7.jar.sha1 b/libs/ssl-config/licenses/bcpkix-fips-1.0.7.jar.sha1 new file mode 100644 index 0000000000000..57cf4cfe10b5f --- /dev/null +++ b/libs/ssl-config/licenses/bcpkix-fips-1.0.7.jar.sha1 @@ -0,0 +1 @@ +fe07959721cfa2156be9722ba20fdfee2b5441b0 diff --git a/libs/ssl-config/src/main/java/org/opensearch/common/ssl/DefaultJdkTrustConfig.java b/libs/ssl-config/src/main/java/org/opensearch/common/ssl/DefaultJdkTrustConfig.java index 859b74b200dc6..6d5b1b02c67ca 100644 --- a/libs/ssl-config/src/main/java/org/opensearch/common/ssl/DefaultJdkTrustConfig.java +++ b/libs/ssl-config/src/main/java/org/opensearch/common/ssl/DefaultJdkTrustConfig.java @@ -65,7 +65,7 @@ final class DefaultJdkTrustConfig implements SslTrustConfig { * Create a trust config that uses supplied {@link BiFunction} to determine the TrustStore type, and the relevant password. */ DefaultJdkTrustConfig(BiFunction systemProperties) { - this(systemProperties, isPkcs11Truststore(systemProperties) ? getSystemTrustStorePassword(systemProperties) : null); + this(systemProperties, getSystemTrustStorePassword(systemProperties)); } /** @@ -93,11 +93,16 @@ public X509ExtendedTrustManager createTrustManager() { * @return the KeyStore used as truststore for PKCS#11 initialized with the password, null otherwise */ private KeyStore getSystemTrustStore() { - if (isPkcs11Truststore(systemProperties) && trustStorePassword != null) { + if (trustStorePassword != null) { try { - KeyStore keyStore = KeyStore.getInstance("PKCS11"); - keyStore.load(null, trustStorePassword); - return keyStore; + if (isBcfksTruststore(systemProperties)) { + var path = Path.of(System.getProperty("javax.net.ssl.trustStore", "")); + KeyStoreUtil.readKeyStore(path, "BCFKS", trustStorePassword); + } else if (isPkcs11Truststore(systemProperties)) { + KeyStore keyStore = KeyStore.getInstance("PKCS11"); + keyStore.load(null, trustStorePassword); + return keyStore; + } } catch (GeneralSecurityException | IOException e) { throw new SslConfigException("failed to load the system PKCS#11 truststore", e); } @@ -105,12 +110,17 @@ private KeyStore getSystemTrustStore() { return null; } + private static boolean isBcfksTruststore(BiFunction systemProperties) { + return systemProperties.apply("javax.net.ssl.trustStoreType", "").equalsIgnoreCase("BCFKS"); + } + private static boolean isPkcs11Truststore(BiFunction systemProperties) { return systemProperties.apply("javax.net.ssl.trustStoreType", "").equalsIgnoreCase("PKCS11"); } private static char[] getSystemTrustStorePassword(BiFunction systemProperties) { - return systemProperties.apply("javax.net.ssl.trustStorePassword", "").toCharArray(); + var password = systemProperties.apply("javax.net.ssl.trustStorePassword", ""); + return password.isEmpty() ? null : password.toCharArray(); } @Override diff --git a/libs/ssl-config/src/main/java/org/opensearch/common/ssl/KeyStoreUtil.java b/libs/ssl-config/src/main/java/org/opensearch/common/ssl/KeyStoreUtil.java index b6b6cdd90af14..3fca84e303e27 100644 --- a/libs/ssl-config/src/main/java/org/opensearch/common/ssl/KeyStoreUtil.java +++ b/libs/ssl-config/src/main/java/org/opensearch/common/ssl/KeyStoreUtil.java @@ -71,6 +71,8 @@ static String inferKeyStoreType(Path path) { String name = path == null ? "" : path.toString().toLowerCase(Locale.ROOT); if (name.endsWith(".p12") || name.endsWith(".pfx") || name.endsWith(".pkcs12")) { return "PKCS12"; + } else if (name.endsWith(".bks")) { + return "BCFKS"; } else { return "jks"; } diff --git a/libs/ssl-config/src/main/java/org/opensearch/common/ssl/PemKeyConfig.java b/libs/ssl-config/src/main/java/org/opensearch/common/ssl/PemKeyConfig.java index bfc29a5801b11..1865b13d644aa 100644 --- a/libs/ssl-config/src/main/java/org/opensearch/common/ssl/PemKeyConfig.java +++ b/libs/ssl-config/src/main/java/org/opensearch/common/ssl/PemKeyConfig.java @@ -32,6 +32,8 @@ package org.opensearch.common.ssl; +import org.bouncycastle.pkcs.PKCSException; + import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.X509ExtendedKeyManager; @@ -82,7 +84,12 @@ public X509ExtendedKeyManager createKeyManager() { private PrivateKey getPrivateKey() { try { - final PrivateKey privateKey = PemUtils.readPrivateKey(key, () -> keyPassword); + final PrivateKey privateKey = PemUtils.readPrivateKey(key, () -> { + if (keyPassword.length == 0) { + throw new SslConfigException("cannot read encrypted key [" + key.toAbsolutePath() + "] without a password"); + } + return keyPassword; + }); if (privateKey == null) { throw new SslConfigException("could not load ssl private key file [" + key + "]"); } @@ -91,7 +98,7 @@ private PrivateKey getPrivateKey() { throw new SslConfigException("the configured ssl private key file [" + key.toAbsolutePath() + "] does not exist", e); } catch (IOException e) { throw new SslConfigException("the configured ssl private key file [" + key.toAbsolutePath() + "] cannot be read", e); - } catch (GeneralSecurityException e) { + } catch (PKCSException e) { throw new SslConfigException("cannot load ssl private key file [" + key.toAbsolutePath() + "]", e); } } diff --git a/libs/ssl-config/src/main/java/org/opensearch/common/ssl/PemUtils.java b/libs/ssl-config/src/main/java/org/opensearch/common/ssl/PemUtils.java index 8a3730ee554f9..df842650d65ca 100644 --- a/libs/ssl-config/src/main/java/org/opensearch/common/ssl/PemUtils.java +++ b/libs/ssl-config/src/main/java/org/opensearch/common/ssl/PemUtils.java @@ -32,68 +32,32 @@ package org.opensearch.common.ssl; -import org.opensearch.common.CharArrays; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +import org.bouncycastle.openssl.PEMEncryptedKeyPair; +import org.bouncycastle.openssl.PEMKeyPair; +import org.bouncycastle.openssl.PEMParser; +import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; +import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder; +import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo; +import org.bouncycastle.pkcs.PKCSException; +import org.bouncycastle.pkcs.jcajce.JcePKCSPBEInputDecryptorProviderBuilder; -import javax.crypto.Cipher; -import javax.crypto.EncryptedPrivateKeyInfo; -import javax.crypto.SecretKey; -import javax.crypto.SecretKeyFactory; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.PBEKeySpec; -import javax.crypto.spec.SecretKeySpec; +import java.security.PrivateKey; -import java.io.BufferedReader; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.math.BigInteger; -import java.nio.charset.StandardCharsets; +import java.io.*; import java.nio.file.Files; -import java.nio.file.NoSuchFileException; import java.nio.file.Path; -import java.security.GeneralSecurityException; -import java.security.KeyFactory; -import java.security.KeyPairGenerator; -import java.security.MessageDigest; -import java.security.PrivateKey; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; -import java.security.interfaces.ECKey; -import java.security.spec.AlgorithmParameterSpec; -import java.security.spec.DSAPrivateKeySpec; -import java.security.spec.ECGenParameterSpec; -import java.security.spec.ECParameterSpec; -import java.security.spec.ECPrivateKeySpec; -import java.security.spec.PKCS8EncodedKeySpec; -import java.security.spec.RSAPrivateCrtKeySpec; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Base64; import java.util.Collection; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.function.Supplier; final class PemUtils { - private static final String PKCS1_HEADER = "-----BEGIN RSA PRIVATE KEY-----"; - private static final String PKCS1_FOOTER = "-----END RSA PRIVATE KEY-----"; - private static final String OPENSSL_DSA_HEADER = "-----BEGIN DSA PRIVATE KEY-----"; - private static final String OPENSSL_DSA_FOOTER = "-----END DSA PRIVATE KEY-----"; - private static final String OPENSSL_DSA_PARAMS_HEADER = "-----BEGIN DSA PARAMETERS-----"; - private static final String OPENSSL_DSA_PARAMS_FOOTER = "-----END DSA PARAMETERS-----"; - private static final String PKCS8_HEADER = "-----BEGIN PRIVATE KEY-----"; - private static final String PKCS8_FOOTER = "-----END PRIVATE KEY-----"; - private static final String PKCS8_ENCRYPTED_HEADER = "-----BEGIN ENCRYPTED PRIVATE KEY-----"; - private static final String PKCS8_ENCRYPTED_FOOTER = "-----END ENCRYPTED PRIVATE KEY-----"; - private static final String OPENSSL_EC_HEADER = "-----BEGIN EC PRIVATE KEY-----"; - private static final String OPENSSL_EC_FOOTER = "-----END EC PRIVATE KEY-----"; - private static final String OPENSSL_EC_PARAMS_HEADER = "-----BEGIN EC PARAMETERS-----"; - private static final String OPENSSL_EC_PARAMS_FOOTER = "-----END EC PARAMETERS-----"; - private static final String HEADER = "-----BEGIN"; - private PemUtils() { throw new IllegalStateException("Utility class should not be instantiated"); } @@ -106,555 +70,88 @@ private PemUtils() { * @param passwordSupplier A password supplier for the potentially encrypted (password protected) key * @return a private key from the contents of the file */ - public static PrivateKey readPrivateKey(Path keyPath, Supplier passwordSupplier) throws IOException, GeneralSecurityException { - try (BufferedReader bReader = Files.newBufferedReader(keyPath, StandardCharsets.UTF_8)) { - String line = bReader.readLine(); - while (null != line && line.startsWith(HEADER) == false) { - line = bReader.readLine(); - } - if (null == line) { - throw new SslConfigException("Error parsing Private Key [" + keyPath.toAbsolutePath() + "], file is empty"); - } - if (PKCS8_ENCRYPTED_HEADER.equals(line.trim())) { - char[] password = passwordSupplier.get(); - if (password == null) { - throw new SslConfigException("cannot read encrypted key [" + keyPath.toAbsolutePath() + "] without a password"); - } - return parsePKCS8Encrypted(bReader, password); - } else if (PKCS8_HEADER.equals(line.trim())) { - return parsePKCS8(bReader); - } else if (PKCS1_HEADER.equals(line.trim())) { - return parsePKCS1Rsa(bReader, passwordSupplier); - } else if (OPENSSL_DSA_HEADER.equals(line.trim())) { - return parseOpenSslDsa(bReader, passwordSupplier); - } else if (OPENSSL_DSA_PARAMS_HEADER.equals(line.trim())) { - return parseOpenSslDsa(removeDsaHeaders(bReader), passwordSupplier); - } else if (OPENSSL_EC_HEADER.equals(line.trim())) { - return parseOpenSslEC(bReader, passwordSupplier); - } else if (OPENSSL_EC_PARAMS_HEADER.equals(line.trim())) { - return parseOpenSslEC(removeECHeaders(bReader), passwordSupplier); - } else { - throw new SslConfigException( - "error parsing Private Key [" + keyPath.toAbsolutePath() + "], file does not contain a supported key format" - ); - } - } catch (FileNotFoundException | NoSuchFileException e) { - throw new SslConfigException("private key file [" + keyPath.toAbsolutePath() + "] does not exist", e); - } catch (IOException | GeneralSecurityException e) { - throw new SslConfigException("private key file [" + keyPath.toAbsolutePath() + "] cannot be parsed", e); - } - } - - /** - * Removes the EC Headers that OpenSSL adds to EC private keys as the information in them - * is redundant - * - * @throws IOException if the EC Parameter footer is missing - */ - private static BufferedReader removeECHeaders(BufferedReader bReader) throws IOException { - String line = bReader.readLine(); - while (line != null) { - if (OPENSSL_EC_PARAMS_FOOTER.equals(line.trim())) { - break; - } - line = bReader.readLine(); - } - if (null == line || OPENSSL_EC_PARAMS_FOOTER.equals(line.trim()) == false) { - throw new IOException("Malformed PEM file, EC Parameters footer is missing"); - } - // Verify that the key starts with the correct header before passing it to parseOpenSslEC - if (OPENSSL_EC_HEADER.equals(bReader.readLine()) == false) { - throw new IOException("Malformed PEM file, EC Key header is missing"); - } - return bReader; - } - - /** - * Removes the DSA Params Headers that OpenSSL adds to DSA private keys as the information in them - * is redundant - * - * @throws IOException if the EC Parameter footer is missing - */ - private static BufferedReader removeDsaHeaders(BufferedReader bReader) throws IOException { - String line = bReader.readLine(); - while (line != null) { - if (OPENSSL_DSA_PARAMS_FOOTER.equals(line.trim())) { - break; - } - line = bReader.readLine(); - } - if (null == line || OPENSSL_DSA_PARAMS_FOOTER.equals(line.trim()) == false) { - throw new IOException("Malformed PEM file, DSA Parameters footer is missing"); - } - // Verify that the key starts with the correct header before passing it to parseOpenSslDsa - if (OPENSSL_DSA_HEADER.equals(bReader.readLine()) == false) { - throw new IOException("Malformed PEM file, DSA Key header is missing"); - } - return bReader; - } - - /** - * Creates a {@link PrivateKey} from the contents of {@code bReader} that contains an plaintext private key encoded in - * PKCS#8 - * - * @param bReader the {@link BufferedReader} containing the key file contents - * @return {@link PrivateKey} - * @throws IOException if the file can't be read - * @throws GeneralSecurityException if the private key can't be generated from the {@link PKCS8EncodedKeySpec} - */ - private static PrivateKey parsePKCS8(BufferedReader bReader) throws IOException, GeneralSecurityException { - StringBuilder sb = new StringBuilder(); - String line = bReader.readLine(); - while (line != null) { - if (PKCS8_FOOTER.equals(line.trim())) { - break; - } - sb.append(line.trim()); - line = bReader.readLine(); - } - if (null == line || PKCS8_FOOTER.equals(line.trim()) == false) { - throw new IOException("Malformed PEM file, PEM footer is invalid or missing"); - } - byte[] keyBytes = Base64.getDecoder().decode(sb.toString()); - String keyAlgo = getKeyAlgorithmIdentifier(keyBytes); - KeyFactory keyFactory = KeyFactory.getInstance(keyAlgo); - return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(keyBytes)); - } - - /** - * Creates a {@link PrivateKey} from the contents of {@code bReader} that contains an EC private key encoded in - * OpenSSL traditional format. - * - * @param bReader the {@link BufferedReader} containing the key file contents - * @param passwordSupplier A password supplier for the potentially encrypted (password protected) key - * @return {@link PrivateKey} - * @throws IOException if the file can't be read - * @throws GeneralSecurityException if the private key can't be generated from the {@link ECPrivateKeySpec} - */ - private static PrivateKey parseOpenSslEC(BufferedReader bReader, Supplier passwordSupplier) throws IOException, - GeneralSecurityException { - StringBuilder sb = new StringBuilder(); - String line = bReader.readLine(); - Map pemHeaders = new HashMap<>(); - while (line != null) { - if (OPENSSL_EC_FOOTER.equals(line.trim())) { - break; - } - // Parse PEM headers according to https://www.ietf.org/rfc/rfc1421.txt - if (line.contains(":")) { - String[] header = line.split(":"); - pemHeaders.put(header[0].trim(), header[1].trim()); - } else { - sb.append(line.trim()); - } - line = bReader.readLine(); - } - if (null == line || OPENSSL_EC_FOOTER.equals(line.trim()) == false) { - throw new IOException("Malformed PEM file, PEM footer is invalid or missing"); - } - byte[] keyBytes = possiblyDecryptPKCS1Key(pemHeaders, sb.toString(), passwordSupplier); - KeyFactory keyFactory = KeyFactory.getInstance("EC"); - ECPrivateKeySpec ecSpec = parseEcDer(keyBytes); - return keyFactory.generatePrivate(ecSpec); + public static PrivateKey readPrivateKey(Path keyPath, Supplier passwordSupplier) throws IOException, PKCSException { + PrivateKeyInfo pki = loadPrivateKeyFromFile(keyPath, passwordSupplier); + JcaPEMKeyConverter converter = new JcaPEMKeyConverter(); + return converter.getPrivateKey(pki); } - /** - * Creates a {@link PrivateKey} from the contents of {@code bReader} that contains an RSA private key encoded in - * OpenSSL traditional format. - * - * @param bReader the {@link BufferedReader} containing the key file contents - * @param passwordSupplier A password supplier for the potentially encrypted (password protected) key - * @return {@link PrivateKey} - * @throws IOException if the file can't be read - * @throws GeneralSecurityException if the private key can't be generated from the {@link RSAPrivateCrtKeySpec} - */ - private static PrivateKey parsePKCS1Rsa(BufferedReader bReader, Supplier passwordSupplier) throws IOException, - GeneralSecurityException { - StringBuilder sb = new StringBuilder(); - String line = bReader.readLine(); - Map pemHeaders = new HashMap<>(); - - while (line != null) { - if (PKCS1_FOOTER.equals(line.trim())) { - // Unencrypted - break; - } - // Parse PEM headers according to https://www.ietf.org/rfc/rfc1421.txt - if (line.contains(":")) { - String[] header = line.split(":"); - pemHeaders.put(header[0].trim(), header[1].trim()); - } else { - sb.append(line.trim()); + static List readCertificates(Collection certPaths) throws CertificateException, IOException { + CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); + List certificates = new ArrayList<>(certPaths.size()); + for (Path path : certPaths) { + try (InputStream input = Files.newInputStream(path)) { + final Collection parsed = certFactory.generateCertificates(input); + if (parsed.isEmpty()) { + throw new SslConfigException("failed to parse any certificates from [" + path.toAbsolutePath() + "]"); + } + certificates.addAll(parsed); } - line = bReader.readLine(); } - if (null == line || PKCS1_FOOTER.equals(line.trim()) == false) { - throw new IOException("Malformed PEM file, PEM footer is invalid or missing"); - } - byte[] keyBytes = possiblyDecryptPKCS1Key(pemHeaders, sb.toString(), passwordSupplier); - RSAPrivateCrtKeySpec spec = parseRsaDer(keyBytes); - KeyFactory keyFactory = KeyFactory.getInstance("RSA"); - return keyFactory.generatePrivate(spec); + return certificates; } /** - * Creates a {@link PrivateKey} from the contents of {@code bReader} that contains an DSA private key encoded in - * OpenSSL traditional format. + * Creates a {@link PrivateKey} from the private key, with or without encryption. + * When enforcing the approved-only mode in Java security settings, some functionalities might be restricted due to the limited + * set of allowed algorithms. One such restriction includes Password Based Key Derivation Functions (PBKDF) like those used by OpenSSL + * and PKCS#12 formats. Since these formats rely on PBKDF algorithms, they cannot operate correctly within the approved-only mode. + * Consequently, attempting to utilize them could result in a {@link java.security.NoSuchAlgorithmException}. * - * @param bReader the {@link BufferedReader} containing the key file contents - * @param passwordSupplier A password supplier for the potentially encrypted (password protected) key + * @param passwordSupplier The password supplier for the encrypted (password protected) key * @return {@link PrivateKey} - * @throws IOException if the file can't be read - * @throws GeneralSecurityException if the private key can't be generated from the {@link DSAPrivateKeySpec} - */ - private static PrivateKey parseOpenSslDsa(BufferedReader bReader, Supplier passwordSupplier) throws IOException, - GeneralSecurityException { - StringBuilder sb = new StringBuilder(); - String line = bReader.readLine(); - Map pemHeaders = new HashMap<>(); - - while (line != null) { - if (OPENSSL_DSA_FOOTER.equals(line.trim())) { - // Unencrypted - break; - } - // Parse PEM headers according to https://www.ietf.org/rfc/rfc1421.txt - if (line.contains(":")) { - String[] header = line.split(":"); - pemHeaders.put(header[0].trim(), header[1].trim()); + * @throws IOException If the file can't be read + */ + private static PrivateKeyInfo loadPrivateKeyFromFile(Path keyPath, Supplier passwordSupplier) + throws IOException, PKCSException { + try (PEMParser pemParser = new PEMParser(new FileReader(keyPath.toFile()))) { + Object object = readObject(keyPath, pemParser); + + if (object instanceof PKCS8EncryptedPrivateKeyInfo) { // encrypted private key in pkcs8-format + var privateKeyInfo = (PKCS8EncryptedPrivateKeyInfo) object; + var inputDecryptorProvider = new JcePKCSPBEInputDecryptorProviderBuilder() + .setProvider("BCFIPS") + .build(passwordSupplier.get()); + return privateKeyInfo.decryptPrivateKeyInfo(inputDecryptorProvider); + } else if (object instanceof PEMEncryptedKeyPair) { // encrypted private key + var encryptedKeyPair = (PEMEncryptedKeyPair) object; + var decryptorProvider = new JcePEMDecryptorProviderBuilder() + .setProvider("BCFIPS") + .build(passwordSupplier.get()); + var keyPair = encryptedKeyPair.decryptKeyPair(decryptorProvider); + return keyPair.getPrivateKeyInfo(); + } else if (object instanceof PEMKeyPair) { // unencrypted private key + return ((PEMKeyPair) object).getPrivateKeyInfo(); + } else if (object instanceof PrivateKeyInfo) { // unencrypted private key in pkcs8-format + return (PrivateKeyInfo) object; } else { - sb.append(line.trim()); + throw new SslConfigException(String.format( + "error parsing private key [%s], invalid encrypted private key class: [%s]", + keyPath.toAbsolutePath(), + object.getClass().getName() + )); } - line = bReader.readLine(); } - if (null == line || OPENSSL_DSA_FOOTER.equals(line.trim()) == false) { - throw new IOException("Malformed PEM file, PEM footer is invalid or missing"); - } - byte[] keyBytes = possiblyDecryptPKCS1Key(pemHeaders, sb.toString(), passwordSupplier); - DSAPrivateKeySpec spec = parseDsaDer(keyBytes); - KeyFactory keyFactory = KeyFactory.getInstance("DSA"); - return keyFactory.generatePrivate(spec); - } - - /** - * Creates a {@link PrivateKey} from the contents of {@code bReader} that contains an encrypted private key encoded in - * PKCS#8 - * - * @param bReader the {@link BufferedReader} containing the key file contents - * @param keyPassword The password for the encrypted (password protected) key - * @return {@link PrivateKey} - * @throws IOException if the file can't be read - * @throws GeneralSecurityException if the private key can't be generated from the {@link PKCS8EncodedKeySpec} - */ - private static PrivateKey parsePKCS8Encrypted(BufferedReader bReader, char[] keyPassword) throws IOException, GeneralSecurityException { - StringBuilder sb = new StringBuilder(); - String line = bReader.readLine(); - while (line != null) { - if (PKCS8_ENCRYPTED_FOOTER.equals(line.trim())) { - break; - } - sb.append(line.trim()); - line = bReader.readLine(); - } - if (null == line || PKCS8_ENCRYPTED_FOOTER.equals(line.trim()) == false) { - throw new IOException("Malformed PEM file, PEM footer is invalid or missing"); - } - byte[] keyBytes = Base64.getDecoder().decode(sb.toString()); - - EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = new EncryptedPrivateKeyInfo(keyBytes); - SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(encryptedPrivateKeyInfo.getAlgName()); - SecretKey secretKey = secretKeyFactory.generateSecret(new PBEKeySpec(keyPassword)); - Cipher cipher = Cipher.getInstance(encryptedPrivateKeyInfo.getAlgName()); - cipher.init(Cipher.DECRYPT_MODE, secretKey, encryptedPrivateKeyInfo.getAlgParameters()); - PKCS8EncodedKeySpec keySpec = encryptedPrivateKeyInfo.getKeySpec(cipher); - String keyAlgo = getKeyAlgorithmIdentifier(keySpec.getEncoded()); - KeyFactory keyFactory = KeyFactory.getInstance(keyAlgo); - return keyFactory.generatePrivate(keySpec); - } - - /** - * Decrypts the password protected contents using the algorithm and IV that is specified in the PEM Headers of the file - * - * @param pemHeaders The Proc-Type and DEK-Info PEM headers that have been extracted from the key file - * @param keyContents The key as a base64 encoded String - * @param passwordSupplier A password supplier for the encrypted (password protected) key - * @return the decrypted key bytes - * @throws GeneralSecurityException if the key can't be decrypted - * @throws IOException if the PEM headers are missing or malformed - */ - private static byte[] possiblyDecryptPKCS1Key(Map pemHeaders, String keyContents, Supplier passwordSupplier) - throws GeneralSecurityException, IOException { - byte[] keyBytes = Base64.getDecoder().decode(keyContents); - String procType = pemHeaders.get("Proc-Type"); - if ("4,ENCRYPTED".equals(procType)) { - // We only handle PEM encryption - String encryptionParameters = pemHeaders.get("DEK-Info"); - if (null == encryptionParameters) { - // malformed pem - throw new IOException("Malformed PEM File, DEK-Info header is missing"); - } - char[] password = passwordSupplier.get(); - if (password == null) { - throw new IOException("cannot read encrypted key without a password"); - } - Cipher cipher = getCipherFromParameters(encryptionParameters, password); - byte[] decryptedKeyBytes = cipher.doFinal(keyBytes); - return decryptedKeyBytes; - } - return keyBytes; - } - - /** - * Creates a {@link Cipher} from the contents of the DEK-Info header of a PEM file. RFC 1421 indicates that supported algorithms are - * defined in RFC 1423. RFC 1423 only defines DES-CBS and triple DES (EDE) in CBC mode. AES in CBC mode is also widely used though ( 3 - * different variants of 128, 192, 256 bit keys ) - * - * @param dekHeaderValue The value of the DEK-Info PEM header - * @param password The password with which the key is encrypted - * @return a cipher of the appropriate algorithm and parameters to be used for decryption - * @throws GeneralSecurityException if the algorithm is not available in the used security provider, or if the key is inappropriate - * for the cipher - * @throws IOException if the DEK-Info PEM header is invalid - */ - private static Cipher getCipherFromParameters(String dekHeaderValue, char[] password) throws GeneralSecurityException, IOException { - final String padding = "PKCS5Padding"; - final SecretKey encryptionKey; - final String[] valueTokens = dekHeaderValue.split(","); - if (valueTokens.length != 2) { - throw new IOException("Malformed PEM file, DEK-Info PEM header is invalid"); - } - final String algorithm = valueTokens[0]; - final String ivString = valueTokens[1]; - final byte[] iv; - try { - iv = hexStringToByteArray(ivString); - } catch (IllegalArgumentException e) { - throw new IOException("Malformed PEM file, DEK-Info IV is invalid", e); - } - if ("DES-CBC".equals(algorithm)) { - byte[] key = generateOpenSslKey(password, iv, 8); - encryptionKey = new SecretKeySpec(key, "DES"); - } else if ("DES-EDE3-CBC".equals(algorithm)) { - byte[] key = generateOpenSslKey(password, iv, 24); - encryptionKey = new SecretKeySpec(key, "DESede"); - } else if ("AES-128-CBC".equals(algorithm)) { - byte[] key = generateOpenSslKey(password, iv, 16); - encryptionKey = new SecretKeySpec(key, "AES"); - } else if ("AES-192-CBC".equals(algorithm)) { - byte[] key = generateOpenSslKey(password, iv, 24); - encryptionKey = new SecretKeySpec(key, "AES"); - } else if ("AES-256-CBC".equals(algorithm)) { - byte[] key = generateOpenSslKey(password, iv, 32); - encryptionKey = new SecretKeySpec(key, "AES"); - } else { - throw new GeneralSecurityException("Private Key encrypted with unsupported algorithm [" + algorithm + "]"); - } - String transformation = encryptionKey.getAlgorithm() + "/" + "CBC" + "/" + padding; - Cipher cipher = Cipher.getInstance(transformation); - cipher.init(Cipher.DECRYPT_MODE, encryptionKey, new IvParameterSpec(iv)); - return cipher; - } - - /** - * Performs key stretching in the same manner that OpenSSL does. This is basically a KDF - * that uses n rounds of salted MD5 (as many times as needed to get the necessary number of key bytes) - *

- * https://www.openssl.org/docs/man1.1.0/crypto/PEM_write_bio_PrivateKey_traditional.html - */ - private static byte[] generateOpenSslKey(char[] password, byte[] salt, int keyLength) { - byte[] passwordBytes = CharArrays.toUtf8Bytes(password); - MessageDigest md5 = SslUtil.messageDigest("md5"); - byte[] key = new byte[keyLength]; - int copied = 0; - int remaining; - while (copied < keyLength) { - remaining = keyLength - copied; - md5.update(passwordBytes, 0, passwordBytes.length); - md5.update(salt, 0, 8);// AES IV (salt) is longer but we only need 8 bytes - byte[] tempDigest = md5.digest(); - int bytesToCopy = (remaining > 16) ? 16 : remaining; // MD5 digests are 16 bytes - System.arraycopy(tempDigest, 0, key, copied, bytesToCopy); - copied += bytesToCopy; - if (remaining == 0) { - break; - } - md5.update(tempDigest, 0, 16); // use previous round digest as IV - } - Arrays.fill(passwordBytes, (byte) 0); - return key; - } - - /** - * Converts a hexadecimal string to a byte array - */ - private static byte[] hexStringToByteArray(String hexString) { - int len = hexString.length(); - if (len % 2 == 0) { - byte[] data = new byte[len / 2]; - for (int i = 0; i < len; i += 2) { - final int k = Character.digit(hexString.charAt(i), 16); - final int l = Character.digit(hexString.charAt(i + 1), 16); - if (k == -1 || l == -1) { - throw new IllegalStateException("String [" + hexString + "] is not hexadecimal"); - } - data[i / 2] = (byte) ((k << 4) + l); - } - return data; - } else { - throw new IllegalStateException( - "Hexadecimal string [" + hexString + "] has odd length and cannot be converted to a byte array" - ); - } - } - - /** - * Parses a DER encoded EC key to an {@link ECPrivateKeySpec} using a minimal {@link DerParser} - * - * @param keyBytes the private key raw bytes - * @return {@link ECPrivateKeySpec} - * @throws IOException if the DER encoded key can't be parsed - */ - private static ECPrivateKeySpec parseEcDer(byte[] keyBytes) throws IOException, GeneralSecurityException { - DerParser parser = new DerParser(keyBytes); - DerParser.Asn1Object sequence = parser.readAsn1Object(); - parser = sequence.getParser(); - parser.readAsn1Object().getInteger(); // version - String keyHex = parser.readAsn1Object().getString(); - BigInteger privateKeyInt = new BigInteger(keyHex, 16); - DerParser.Asn1Object choice = parser.readAsn1Object(); - parser = choice.getParser(); - String namedCurve = getEcCurveNameFromOid(parser.readAsn1Object().getOid()); - KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC"); - AlgorithmParameterSpec algorithmParameterSpec = new ECGenParameterSpec(namedCurve); - keyPairGenerator.initialize(algorithmParameterSpec); - ECParameterSpec parameterSpec = ((ECKey) keyPairGenerator.generateKeyPair().getPrivate()).getParams(); - return new ECPrivateKeySpec(privateKeyInt, parameterSpec); - } - - /** - * Parses a DER encoded RSA key to a {@link RSAPrivateCrtKeySpec} using a minimal {@link DerParser} - * - * @param keyBytes the private key raw bytes - * @return {@link RSAPrivateCrtKeySpec} - * @throws IOException if the DER encoded key can't be parsed - */ - private static RSAPrivateCrtKeySpec parseRsaDer(byte[] keyBytes) throws IOException { - DerParser parser = new DerParser(keyBytes); - DerParser.Asn1Object sequence = parser.readAsn1Object(); - parser = sequence.getParser(); - parser.readAsn1Object().getInteger(); // (version) We don't need it but must read to get to modulus - BigInteger modulus = parser.readAsn1Object().getInteger(); - BigInteger publicExponent = parser.readAsn1Object().getInteger(); - BigInteger privateExponent = parser.readAsn1Object().getInteger(); - BigInteger prime1 = parser.readAsn1Object().getInteger(); - BigInteger prime2 = parser.readAsn1Object().getInteger(); - BigInteger exponent1 = parser.readAsn1Object().getInteger(); - BigInteger exponent2 = parser.readAsn1Object().getInteger(); - BigInteger coefficient = parser.readAsn1Object().getInteger(); - return new RSAPrivateCrtKeySpec(modulus, publicExponent, privateExponent, prime1, prime2, exponent1, exponent2, coefficient); - } - - /** - * Parses a DER encoded DSA key to a {@link DSAPrivateKeySpec} using a minimal {@link DerParser} - * - * @param keyBytes the private key raw bytes - * @return {@link DSAPrivateKeySpec} - * @throws IOException if the DER encoded key can't be parsed - */ - private static DSAPrivateKeySpec parseDsaDer(byte[] keyBytes) throws IOException { - DerParser parser = new DerParser(keyBytes); - DerParser.Asn1Object sequence = parser.readAsn1Object(); - parser = sequence.getParser(); - parser.readAsn1Object().getInteger(); // (version) We don't need it but must read to get to p - BigInteger p = parser.readAsn1Object().getInteger(); - BigInteger q = parser.readAsn1Object().getInteger(); - BigInteger g = parser.readAsn1Object().getInteger(); - parser.readAsn1Object().getInteger(); // we don't need x - BigInteger x = parser.readAsn1Object().getInteger(); - return new DSAPrivateKeySpec(x, p, q, g); } /** - * Parses a DER encoded private key and reads its algorithm identifier Object OID. + * Supports PEM files that includes parameters. * - * @param keyBytes the private key raw bytes - * @return A string identifier for the key algorithm (RSA, DSA, or EC) - * @throws GeneralSecurityException if the algorithm oid that is parsed from ASN.1 is unknown - * @throws IOException if the DER encoded key can't be parsed - */ - private static String getKeyAlgorithmIdentifier(byte[] keyBytes) throws IOException, GeneralSecurityException { - DerParser parser = new DerParser(keyBytes); - DerParser.Asn1Object sequence = parser.readAsn1Object(); - parser = sequence.getParser(); - parser.readAsn1Object().getInteger(); // version - DerParser.Asn1Object algSequence = parser.readAsn1Object(); - parser = algSequence.getParser(); - String oidString = parser.readAsn1Object().getOid(); - switch (oidString) { - case "1.2.840.10040.4.1": - return "DSA"; - case "1.2.840.113549.1.1.1": - return "RSA"; - case "1.2.840.10045.2.1": - return "EC"; - } - throw new GeneralSecurityException( - "Error parsing key algorithm identifier. Algorithm with OID [" + oidString + "] is not żsupported" - ); - } - - static List readCertificates(Collection certPaths) throws CertificateException, IOException { - CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); - List certificates = new ArrayList<>(certPaths.size()); - for (Path path : certPaths) { - try (InputStream input = Files.newInputStream(path)) { - final Collection parsed = certFactory.generateCertificates(input); - if (parsed.isEmpty()) { - throw new SslConfigException("failed to parse any certificates from [" + path.toAbsolutePath() + "]"); + * @return high-level Object from the content + */ + private static Object readObject(Path keyPath, PEMParser pemParser) throws IOException { + while (pemParser.ready()) { + try { + var object = pemParser.readObject(); + if (object instanceof ASN1ObjectIdentifier) { // handles -----BEGIN EC PARAMETERS----- + continue; } - certificates.addAll(parsed); + return object; + } catch (IOException e) { // handles -----BEGIN DSA PARAMETERS----- + // ignore } } - return certificates; - } - - private static String getEcCurveNameFromOid(String oidString) throws GeneralSecurityException { - switch (oidString) { - // see https://tools.ietf.org/html/rfc5480#section-2.1.1.1 - case "1.2.840.10045.3.1": - return "secp192r1"; - case "1.3.132.0.1": - return "sect163k1"; - case "1.3.132.0.15": - return "sect163r2"; - case "1.3.132.0.33": - return "secp224r1"; - case "1.3.132.0.26": - return "sect233k1"; - case "1.3.132.0.27": - return "sect233r1"; - case "1.2.840.10045.3.1.7": - return "secp256r1"; - case "1.3.132.0.16": - return "sect283k1"; - case "1.3.132.0.17": - return "sect283r1"; - case "1.3.132.0.34": - return "secp384r1"; - case "1.3.132.0.36": - return "sect409k1"; - case "1.3.132.0.37": - return "sect409r1"; - case "1.3.132.0.35": - return "secp521r1"; - case "1.3.132.0.38": - return "sect571k1"; - case "1.3.132.0.39": - return "sect571r1"; - } - throw new GeneralSecurityException( - "Error parsing EC named curve identifier. Named curve with OID: " + oidString + " is not supported" - ); + throw new SslConfigException("Error parsing Private Key [" + keyPath.toAbsolutePath() + "], file is empty"); } } diff --git a/libs/ssl-config/src/main/java/org/opensearch/common/ssl/SslConfiguration.java b/libs/ssl-config/src/main/java/org/opensearch/common/ssl/SslConfiguration.java index 23acb0ff269e2..d6aa106039ce3 100644 --- a/libs/ssl-config/src/main/java/org/opensearch/common/ssl/SslConfiguration.java +++ b/libs/ssl-config/src/main/java/org/opensearch/common/ssl/SslConfiguration.java @@ -32,6 +32,8 @@ package org.opensearch.common.ssl; +import org.bouncycastle.crypto.CryptoServicesRegistrar; + import javax.net.ssl.SSLContext; import javax.net.ssl.X509ExtendedKeyManager; import javax.net.ssl.X509ExtendedTrustManager; @@ -155,7 +157,12 @@ public SSLContext createSslContext() { final X509ExtendedKeyManager keyManager = keyConfig.createKeyManager(); final X509ExtendedTrustManager trustManager = trustConfig.createTrustManager(); try { - SSLContext sslContext = SSLContext.getInstance(contextProtocol()); + SSLContext sslContext; + if (CryptoServicesRegistrar.isInApprovedOnlyMode()) { + sslContext = SSLContext.getInstance("TLS"); + } else { + sslContext = SSLContext.getInstance(contextProtocol()); + } sslContext.init(new X509ExtendedKeyManager[] { keyManager }, new X509ExtendedTrustManager[] { trustManager }, null); return sslContext; } catch (GeneralSecurityException e) { diff --git a/plugins/identity-shiro/build.gradle b/plugins/identity-shiro/build.gradle index 222443efcb214..2d0d8f27f46b0 100644 --- a/plugins/identity-shiro/build.gradle +++ b/plugins/identity-shiro/build.gradle @@ -28,7 +28,8 @@ dependencies { implementation 'org.passay:passay:1.6.3' - implementation "org.bouncycastle:bcprov-jdk18on:${versions.bouncycastle}" + // Bcrypt hash matching + implementation 'com.password4j:password4j:1.8.2' testImplementation project(path: ':modules:transport-netty4') // for http testImplementation project(path: ':plugins:transport-nio') // for http diff --git a/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/realm/BCryptPasswordMatcher.java b/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/realm/BCryptPasswordMatcher.java index a2cb78425929e..e86430c59cb9b 100644 --- a/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/realm/BCryptPasswordMatcher.java +++ b/plugins/identity-shiro/src/main/java/org/opensearch/identity/shiro/realm/BCryptPasswordMatcher.java @@ -12,7 +12,16 @@ import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.authc.credential.CredentialsMatcher; -import org.bouncycastle.crypto.generators.OpenBSDBCrypt; +import com.password4j.BcryptFunction; +import com.password4j.Password; +import org.opensearch.SpecialPermission; + +import java.nio.CharBuffer; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Arrays; + +import static org.opensearch.core.common.Strings.isNullOrEmpty; /** * Password matcher for BCrypt @@ -30,7 +39,35 @@ public class BCryptPasswordMatcher implements CredentialsMatcher { @Override public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) { final UsernamePasswordToken userToken = (UsernamePasswordToken) token; - return OpenBSDBCrypt.checkPassword((String) info.getCredentials(), userToken.getPassword()); + return check(userToken.getPassword(), (String) info.getCredentials()); + + } + + private boolean check(char[] password, String hash) { + if (password == null || password.length == 0) { + throw new IllegalStateException("Password cannot be empty or null"); + } + if (isNullOrEmpty(hash)) { + throw new IllegalStateException("Hash cannot be empty or null"); + } + CharBuffer passwordBuffer = CharBuffer.wrap(password); + try { + SecurityManager securityManager = System.getSecurityManager(); + if (securityManager != null) { + securityManager.checkPermission(new SpecialPermission()); + } + return AccessController.doPrivileged((PrivilegedAction) () -> Password.check(passwordBuffer, hash) + .with(BcryptFunction.getInstanceFromHash(hash))); + } finally { + cleanup(passwordBuffer); + } + } + + private void cleanup(CharBuffer password) { + password.clear(); + char[] passwordOverwrite = new char[password.capacity()]; + Arrays.fill(passwordOverwrite, '\0'); + password.put(passwordOverwrite); } } diff --git a/plugins/ingest-attachment/build.gradle b/plugins/ingest-attachment/build.gradle index d631855013527..5fae4fb5f3da7 100644 --- a/plugins/ingest-attachment/build.gradle +++ b/plugins/ingest-attachment/build.gradle @@ -81,9 +81,6 @@ dependencies { api "org.apache.pdfbox:fontbox:${versions.pdfbox}" api "org.apache.pdfbox:jempbox:1.8.17" api "commons-logging:commons-logging:${versions.commonslogging}" - api "org.bouncycastle:bcmail-jdk18on:${versions.bouncycastle}" - api "org.bouncycastle:bcprov-jdk18on:${versions.bouncycastle}" - api "org.bouncycastle:bcpkix-jdk18on:${versions.bouncycastle}" // OpenOffice api "org.apache.poi:poi-ooxml:${versions.poi}" api "org.apache.poi:poi:${versions.poi}" diff --git a/plugins/ingest-attachment/licenses/bcmail-jdk18on-1.78.jar.sha1 b/plugins/ingest-attachment/licenses/bcmail-jdk18on-1.78.jar.sha1 deleted file mode 100644 index eb7e650306f73..0000000000000 --- a/plugins/ingest-attachment/licenses/bcmail-jdk18on-1.78.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -d26f5514b8c54f2878f8d49e0bc8e2acaab3c8bd \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/bcmail-jdk18on-LICENSE.txt b/plugins/ingest-attachment/licenses/bcmail-jdk18on-LICENSE.txt deleted file mode 100644 index dbba1dd7829c7..0000000000000 --- a/plugins/ingest-attachment/licenses/bcmail-jdk18on-LICENSE.txt +++ /dev/null @@ -1,23 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2000 - 2013 The Legion of the Bouncy Castle Inc. - (http://www.bouncycastle.org) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - diff --git a/plugins/ingest-attachment/licenses/bcmail-jdk18on-NOTICE.txt b/plugins/ingest-attachment/licenses/bcmail-jdk18on-NOTICE.txt deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/plugins/ingest-attachment/licenses/bcpkix-jdk18on-1.78.jar.sha1 b/plugins/ingest-attachment/licenses/bcpkix-jdk18on-1.78.jar.sha1 deleted file mode 100644 index 385a9d930eede..0000000000000 --- a/plugins/ingest-attachment/licenses/bcpkix-jdk18on-1.78.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -dd61bcdb87678451dd42d42e267979bd4b4451a1 \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/bcpkix-jdk18on-LICENSE.txt b/plugins/ingest-attachment/licenses/bcpkix-jdk18on-LICENSE.txt deleted file mode 100644 index e1fc4a1506db5..0000000000000 --- a/plugins/ingest-attachment/licenses/bcpkix-jdk18on-LICENSE.txt +++ /dev/null @@ -1,23 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2000 - 2013 The Legion of the Bouncy Castle Inc. - (http://www.bouncycastle.org) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - diff --git a/plugins/ingest-attachment/licenses/bcpkix-jdk18on-NOTICE.txt b/plugins/ingest-attachment/licenses/bcpkix-jdk18on-NOTICE.txt deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/plugins/ingest-attachment/licenses/bcprov-jdk18on-1.78.jar.sha1 b/plugins/ingest-attachment/licenses/bcprov-jdk18on-1.78.jar.sha1 deleted file mode 100644 index 47fb5fd5e5f5d..0000000000000 --- a/plugins/ingest-attachment/licenses/bcprov-jdk18on-1.78.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -619aafb92dc0b4c6cc4cf86c487ca48ee2d67a8e \ No newline at end of file diff --git a/plugins/ingest-attachment/licenses/bcprov-jdk18on-LICENSE.txt b/plugins/ingest-attachment/licenses/bcprov-jdk18on-LICENSE.txt deleted file mode 100644 index 9f27bafe96885..0000000000000 --- a/plugins/ingest-attachment/licenses/bcprov-jdk18on-LICENSE.txt +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2000 - 2013 The Legion of the Bouncy Castle Inc. - (http://www.bouncycastle.org) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/plugins/ingest-attachment/licenses/bcprov-jdk18on-NOTICE.txt b/plugins/ingest-attachment/licenses/bcprov-jdk18on-NOTICE.txt deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/server/build.gradle b/server/build.gradle index 429af5d0ac258..9a9b7a4175688 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -125,6 +125,10 @@ dependencies { api "com.google.protobuf:protobuf-java:${versions.protobuf}" api "jakarta.annotation:jakarta.annotation-api:${versions.jakarta_annotation}" + // bouncyCastle + api 'org.bouncycastle:bc-fips:1.0.2.5' + api 'org.bouncycastle:bctls-fips:1.0.19' + testImplementation(project(":test:framework")) { // tests use the locally compiled version of server exclude group: 'org.opensearch', module: 'server' diff --git a/server/src/main/java/org/opensearch/bootstrap/Bootstrap.java b/server/src/main/java/org/opensearch/bootstrap/Bootstrap.java index 4e167d10b99fa..1dca035bb9736 100644 --- a/server/src/main/java/org/opensearch/bootstrap/Bootstrap.java +++ b/server/src/main/java/org/opensearch/bootstrap/Bootstrap.java @@ -40,6 +40,7 @@ import org.apache.logging.log4j.core.config.Configurator; import org.apache.lucene.util.Constants; import org.apache.lucene.util.StringHelper; +import org.bouncycastle.crypto.CryptoServicesRegistrar; import org.opensearch.OpenSearchException; import org.opensearch.Version; import org.opensearch.cli.KeyStoreAwareCommand; @@ -51,6 +52,7 @@ import org.opensearch.common.logging.LogConfigurator; import org.opensearch.common.logging.Loggers; import org.opensearch.common.network.IfConfig; +import org.opensearch.common.settings.FipsSettings; import org.opensearch.common.settings.KeyStoreWrapper; import org.opensearch.common.settings.SecureSettings; import org.opensearch.common.settings.Settings; @@ -195,6 +197,17 @@ private void setup(boolean addShutdownHook, Environment environment) throws Boot BootstrapSettings.CTRLHANDLER_SETTING.get(settings) ); + var isFipsEnabled = FipsSettings.FIPS_ENABLED.get(settings); + var isRunningInFipsMode = CryptoServicesRegistrar.setApprovedOnlyMode(isFipsEnabled); + + if (!isRunningInFipsMode && isFipsEnabled){ + throw new BootstrapException(new RuntimeException("cannot enable FIPS mode")); + } + + if (isRunningInFipsMode) { + LogManager.getLogger(Bootstrap.class).info("running in FIPS mode"); + } + // initialize probes before the security manager is installed initializeProbes(); diff --git a/server/src/main/java/org/opensearch/common/settings/ClusterSettings.java b/server/src/main/java/org/opensearch/common/settings/ClusterSettings.java index 5dcf23ae52294..1faf4c0faf04e 100644 --- a/server/src/main/java/org/opensearch/common/settings/ClusterSettings.java +++ b/server/src/main/java/org/opensearch/common/settings/ClusterSettings.java @@ -657,6 +657,7 @@ public void apply(Settings value, Settings current, Settings previous) { ClusterManagerTaskThrottler.THRESHOLD_SETTINGS, ClusterManagerTaskThrottler.BASE_DELAY_SETTINGS, ClusterManagerTaskThrottler.MAX_DELAY_SETTINGS, + FipsSettings.FIPS_ENABLED, // Settings related to search backpressure SearchBackpressureSettings.SETTING_MODE, diff --git a/server/src/main/java/org/opensearch/common/settings/FipsSettings.java b/server/src/main/java/org/opensearch/common/settings/FipsSettings.java new file mode 100644 index 0000000000000..958a1440ec29f --- /dev/null +++ b/server/src/main/java/org/opensearch/common/settings/FipsSettings.java @@ -0,0 +1,23 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.common.settings; + +import org.opensearch.common.settings.Setting.Property; + +/** + * Settings used for NIST FIPS 140-2 compliance + */ +public class FipsSettings { + + public static final Setting FIPS_ENABLED = Setting.boolSetting( + "fips.approved", + false, + Property.NodeScope); + +} diff --git a/server/src/main/resources/org/opensearch/bootstrap/security.policy b/server/src/main/resources/org/opensearch/bootstrap/security.policy index 55e8db0d9c6a3..e64a7b6f05fd0 100644 --- a/server/src/main/resources/org/opensearch/bootstrap/security.policy +++ b/server/src/main/resources/org/opensearch/bootstrap/security.policy @@ -90,6 +90,28 @@ grant codeBase "${codebase.reactor-core}" { permission java.net.SocketPermission "*", "connect,resolve"; }; +// security +grant { + permission java.security.SecurityPermission "getProperty.jdk.tls.disabledAlgorithms"; + permission java.security.SecurityPermission "getProperty.jdk.certpath.disabledAlgorithms"; + permission java.security.SecurityPermission "getProperty.keystore.type.compat"; + permission java.security.SecurityPermission "getProperty.org.bouncycastle.*"; + permission java.security.SecurityPermission "putProviderProperty.BCFIPS"; + permission java.security.SecurityPermission "putProviderProperty.BCJSSE"; + permission java.lang.RuntimePermission "getProtectionDomain"; + permission java.util.PropertyPermission "java.runtime.name", "read"; + permission org.bouncycastle.crypto.CryptoServicesPermission "changeToApprovedModeEnabled"; + permission org.bouncycastle.crypto.CryptoServicesPermission "tlsAlgorithmsEnabled"; + //io.netty.handler.codec.DecoderException + permission java.lang.RuntimePermission "accessClassInPackage.sun.security.internal.spec"; + //java.security.InvalidAlgorithmParameterException: Cannot process GCMParameterSpec + permission java.lang.RuntimePermission "accessDeclaredMembers"; + permission org.bouncycastle.crypto.CryptoServicesPermission "exportSecretKey"; + permission org.bouncycastle.crypto.CryptoServicesPermission "exportPrivateKey"; + permission java.io.FilePermission "${javax.net.ssl.trustStore}", "read"; + permission java.io.FilePermission "${javaHome}/lib/security/jssecacerts", "read"; +}; + //// Everything else: grant {