From 9ccc5e6d844b2f5cb0b11e75157d74ea784b54b2 Mon Sep 17 00:00:00 2001 From: Marius Vikhammer Date: Thu, 26 Mar 2020 10:53:50 +0800 Subject: [PATCH] cert bundle: Fix memory leak during cert verification Also refactors the unit tests and fixes the test case, as it was giving false positives. Closes IDFGH-2950 Closes https://github.com/espressif/esp-idf/issues/4983 --- components/mbedtls/CMakeLists.txt | 1 + .../mbedtls/esp_crt_bundle/esp_crt_bundle.c | 31 +-- components/mbedtls/test/prvtkey.pem | 50 ++--- components/mbedtls/test/server_cert_bundle | Bin 31227 -> 400 bytes components/mbedtls/test/server_cert_chain.pem | 79 ++------ components/mbedtls/test/test_esp_crt_bundle.c | 178 ++++++++++-------- 6 files changed, 159 insertions(+), 180 deletions(-) diff --git a/components/mbedtls/CMakeLists.txt b/components/mbedtls/CMakeLists.txt index 1f6f391411..37abd64355 100644 --- a/components/mbedtls/CMakeLists.txt +++ b/components/mbedtls/CMakeLists.txt @@ -1,4 +1,5 @@ idf_build_get_property(idf_target IDF_TARGET) +idf_build_get_property(python PYTHON) idf_component_register(SRCS "esp_crt_bundle/esp_crt_bundle.c" INCLUDE_DIRS "port/include" "mbedtls/include" "esp_crt_bundle/include" diff --git a/components/mbedtls/esp_crt_bundle/esp_crt_bundle.c b/components/mbedtls/esp_crt_bundle/esp_crt_bundle.c index 6d99f781ce..c03da8d9a8 100644 --- a/components/mbedtls/esp_crt_bundle/esp_crt_bundle.c +++ b/components/mbedtls/esp_crt_bundle/esp_crt_bundle.c @@ -19,7 +19,6 @@ #include "esp_log.h" #include "esp_err.h" - #define BUNDLE_HEADER_OFFSET 2 #define CRT_HEADER_OFFSET 4 @@ -43,45 +42,48 @@ typedef struct crt_bundle_t { static crt_bundle_t s_crt_bundle; static int esp_crt_verify_callback(void *buf, mbedtls_x509_crt *crt, int data, uint32_t *flags); -static esp_err_t esp_crt_check_signature(mbedtls_x509_crt *child, const uint8_t *pub_key_buf, size_t pub_key_len); +static int esp_crt_check_signature(mbedtls_x509_crt *child, const uint8_t *pub_key_buf, size_t pub_key_len); -static esp_err_t esp_crt_check_signature(mbedtls_x509_crt *child, const uint8_t *pub_key_buf, size_t pub_key_len) +static int esp_crt_check_signature(mbedtls_x509_crt *child, const uint8_t *pub_key_buf, size_t pub_key_len) { - int ret = ESP_FAIL; + int ret = 0; mbedtls_x509_crt parent; const mbedtls_md_info_t *md_info; unsigned char hash[MBEDTLS_MD_MAX_SIZE]; mbedtls_x509_crt_init(&parent); - if ( (ret = mbedtls_pk_parse_public_key(&parent.pk , pub_key_buf, pub_key_len) ) != 0) { + if ( (ret = mbedtls_pk_parse_public_key(&parent.pk, pub_key_buf, pub_key_len) ) != 0) { ESP_LOGE(TAG, "PK parse failed with error %X", ret); - return ESP_FAIL; + goto cleanup; } // Fast check to avoid expensive computations when not necessary if (!mbedtls_pk_can_do(&parent.pk, child->sig_pk)) { ESP_LOGE(TAG, "Simple compare failed"); - return ESP_FAIL; + ret = -1; + goto cleanup; } md_info = mbedtls_md_info_from_type(child->sig_md); if ( (ret = mbedtls_md( md_info, child->tbs.p, child->tbs.len, hash )) != 0 ) { ESP_LOGE(TAG, "Internal mbedTLS error %X", ret); - return ESP_FAIL; + goto cleanup; } - ret = mbedtls_pk_verify_ext( child->sig_pk, child->sig_opts, &parent.pk, - child->sig_md, hash, mbedtls_md_get_size( md_info ), - child->sig.p, child->sig.len ) ; + if ( (ret = mbedtls_pk_verify_ext( child->sig_pk, child->sig_opts, &parent.pk, + child->sig_md, hash, mbedtls_md_get_size( md_info ), + child->sig.p, child->sig.len )) != 0 ) { - if (ret != 0) { ESP_LOGE(TAG, "PK verify failed with error %X", ret); - return ESP_FAIL; + goto cleanup; } - return ESP_OK; +cleanup: + mbedtls_x509_crt_free(&parent); + + return ret; } @@ -205,6 +207,7 @@ esp_err_t esp_crt_bundle_attach(void *conf) void esp_crt_bundle_detach(mbedtls_ssl_config *conf) { free(s_crt_bundle.crts); + s_crt_bundle.crts = NULL; if (conf) { mbedtls_ssl_conf_verify(conf, NULL, NULL); } diff --git a/components/mbedtls/test/prvtkey.pem b/components/mbedtls/test/prvtkey.pem index ac8ee3b17b..bb0a510a7c 100644 --- a/components/mbedtls/test/prvtkey.pem +++ b/components/mbedtls/test/prvtkey.pem @@ -1,27 +1,27 @@ -----BEGIN RSA PRIVATE KEY----- -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 -----END RSA PRIVATE KEY----- diff --git a/components/mbedtls/test/server_cert_bundle b/components/mbedtls/test/server_cert_bundle index 19123f6a93aac4adad599cb3f4d9344e3f0f52c0..0b7f09aa06da459957cb5c4df81df1876b9597b8 100644 GIT binary patch delta 349 zcmezUnQ;QMF#{t*3Zt4qk|DPNCmVAp3!5;Lv!9``fgp&(!NV1tpPQ;1T#{IlYA9&H z4-#abTq_|jEN&nQ;xO~@xfT}`r4|=wrcLf=RI6WpiTlipH|MuU<}idPH5_7HU2gDO zf92gRYA4O5ExCL9kM|n%u(PkaZC$caTIy#Hlid{~repluxa-4BA|4vn^sSuKzi9=} z;%O6iEW7YloQLHP?@xjIS&`fuo-^}*f8Uxm`&*j%pO(kQAwAa~Tj$7aW;ZTW_hv5= z-nhPR)uS2O^@qxA-zB8UzvXuSqI^^5u2*Woo|bBPVaFNk8P;CZZxQS7_+c`2`GLBu zGM#st+656_=3-yYKjN?SWYRrc>+Zv^rh1^5bHWyrXGMl51-+7XUad@a_Lw5QX@mB` x-W-E{p>8hQr?s(9k!(?Py3J+DRQK+-E4!=f?ETFwjo1D8TG%awnV1G literal 31227 zcmZSZX0T?6Hn240HsEAq4rO5zW(o~9kshGe!(amcI;Eo(p|cn zgkD_IyZ-Cs(HpCB<%=7wt!HFg))+56>etrSAfA!r8eZ!XA-mc9X*$R2w0jKJ3>D1|{Ys29`^!yUhI;2lM_r*j4SlqT0(DJm^4(NXZsOE#1@kOj#w z3yUFS6x?(2lM-{l;cC#tsARy)#;Mij(e|B}k&%&=fw_s1pTVGsk&CH`k&)rYi9LHo z0@8&4f4E%vM)}irspEaEABBpSNv;yOJ!esBNmRLAU~Sm9JIdRxyp508XzqK}lP~XU zR{OO}J(tvvJ^F7pe@5y8VZUQqDYLIm5IQfy_Qm?~6^(r__P|8y+>d|j*Ld zOfD9?pvmvK(&NMCRoiopE;q1rh+TK&QLI|DHAgz10#b2qnd#oQo0TBF_bot1cfY@uuy1TW>soQzCv(;ob-Mq%>E-*^yvKpWAp)@3Mc*S=DnAcK%0t@*A>>Pecd)n0@KaZibkR3;vWc zzia%^@lHAD57UU#m?1B5;xq4IP)+kt!~a|&bZLz zma8k?f8MBPkg@rQ&5bpAy)&aG_?g@>o7Lag5wJaXniJ>e>rlsEGaEYRR{s4ZUyIjJwtH=QIINTVLq4O5O8t^6%!H01cUni`j^uKIGV$^oJmUk zHlgXw@yj~d2B)Im+&o-++~J^vNcV#m#$}#bkL?vbC$I72TW{xaI`!S3%ZmJ|m$Ysg z_4hpeUcN!7dh65!or_oQdRwel#lvRQyr`~A#Ne#pf`?ibJ1e;i{|4-BNWH&3FuD1} zqmIN^-ieKGg0gomdgLvoG2dhHJf2&eeRkq{a(5N(M<3JbTCKOYVC_+EDb)!jMMtZ% zR-0Ti==}dQL%2GG`^2?%Z=&W*lCw?O+;i;y{*#UcZ^VkOs=cuYwRFDl=dVPJ!^i2b zug;p?E4%BK>ds>~?jICovRZvB@1vdG*BFjDHrJXnp9svB)Y_&Q{`jfw?D-#%0fs0$C(^(b>sN~0LG##8Ce#1jkc-zB$6MgTu@&rrFl2cja-~VAzEBEOyn)R2K zM7&e;J-KQ{^{kt}Ebh64i$49)bK<7QBVI9eS-BIr`3JF&O>S?Bt+ z<1hbfzqDC%>(b`K4>RU;{-|OIyZ0q@YR>OnDR+~)?{k+My-qr#Hf#3oRY}c0TiB`$ zPH66b%jP?Mr_2#U?W^&7f`!EA%na8|D^FB3WY<&moGbC^-DmB>o~^qq**(kuyw{sF zfwfmxzr6ar^M2Favu^w>Uas@$LijrkKehP&2^pOFJAT|@d#N|eE^zrpr6XL+Ej)5I z|NLLOx-IfD$cehI%GsP0D zXVqPL_SWLn8%8-Bk1$Wp<38pL^E&tr6hF*Qds*aeU?=dDoB8A{Cx)D(Q|c0rbltCS zlH!g$5@}Svc~3%SnC0IsY}Go~Qp|4d^?2JNU$<1LUu%ccm!|Dkf+tk?=}P@=%y+qN z%~&$c=>fwdz3f*<-xaOdz5m~8rCl7*-Io?W?Gy;AaQ=)DtfF2z{{KaKJ4(ft>oZ1yMZ+3CI| zmXZwHr!rYi-OZ)-t7lqkRenLXqjZw|O1vRv>XKh&ffJY2DYdh!X z=B5@UXC~$lEK0vg`=_&OFBJXCrgU1Kans{sS2h<FQ(JxocTJ{JP40e%8|u0ZY2A-IppyS5^Hmw%_r7=Jdx32d`;#o}co2r|a9t4&s7W zNfC2Glj8#XsvcWi$vY$U`{yO|bt+n` z!3&L>Yv6aLrS*}{dAWK2%vKwho4VXE zohD^#_M(dK##Q%)S()88|LnF}R~dEn%eBJYvnD8K&)VKm(#|6nzjL1MuhqfYcXpTA zP4?m7=#g8bEORAmtwr_X*`S?*e_|E>+ z>G?N*Iu!QS?wxw&+`a{I>dN>2F1w({$DsMz#_fY`!hZXJOLx>91LujDJFzS-2wx%H z{#N%_^}8Of$1Br1B~Ls4yz63?c>Ynj+lkt5UnVu0{k?7Sv8e7dkJ#QVqLA=*XQ(i6 zB_@3<7|4Oj7-k-E7(hdtao+Vs%NYOpX9DD z-c%;L_QIx$bg3nlGJ)LbVaq3;Xf-6ycrvLOxTEG7TnP@AYlOwICO8Enf{nh-AwdUE z+I_ltXJfEbq~`ODhg~(EUcdPE=uz*5t4gZF*L$h`x$!k_d7k9$5*~lkXd`aM$G2Zs zm7Bg=74_iSi-qA+K1^6#5;n!ffLSiaPrFsEq^XLhY(c*7IY-?EqHH4Z+nvr<_eUg} z+|lCrsdt!hnUaf)4|~(I=yMmmYFI8y7OQ_)HKSf{z2ED0IhFFP*ljPOU3c9%ypT0N ztgl}{ZK>yBz3YzcN)P3( zk7qvc$NI%SH|hVk91aD2|M%GHdseyWgisNK2H6?WNj4IWLXux zSHDww|H3UV{@3^R+-Lrn;1?n2^LnwpfOhjN#^Re(a$6YG)7E?_aNu;BwAOzzZz5-) z%8f}^xu+y_lw(mvh=1; zU)dmu#GqeEa&$x({4&uQQf? zITq#4>nmXV*l(lo;u8`%-f#YJPPjieMN?Ioec_f%wt5K<&R^ne-+k8OgYTh3RUKWs zcb$)vytcl71N(+~h98>_9=NZ!EH_7zZ|jXZH7QqS+YOaVrf96?uUC7zg5%$`7d9DF zg5Nb;b!dxDN6P~Y;v z=j^1*3NMZK1kGG;v{iGns@4{dpG6N(rF?(ZB+&a}?xC)ZUlwUyf0BPP{;c?09aH|R zto_%U`y2Txr-pCYtM>jHtx(*ya^utuOLy{$3En!M<6-0QppG}{Y?#f(>8v|Xd%v~xo^!^+~*ME(UHyCEvTAf%|HN#wjbt=`99N|*#+`Yfce!qDmVq(M2 z7L^nEUDEXK_k(joS6x19y<~FHd#+!(bH9CPZHQjz5j5ZO==OISYu=wO%MjUle=TFq zO!*^9vh)8Hl|RslUjKw;zkksJDU%s5EHmCrRjn>t_$+g0tK{C!o_}lKn!PIC^S}Ag z>Dq&r&UtO_Wu4dUIr;v<=nHvqAM_0Ln*RLSvqhZC<7vn2qqDQ`Z8)`s`NOR*6LzGg ze2?*2`=Tx_ZszqJhwM&V5T3jC-4oa9&d~2&@<~B0LYogcOn=-Z?>=!^Nz|nu8M-z~wb!59V^+)78H=y}EM#p|-SRga|SjF_-3IAuGShZ!n zV4!8(&EM&3uSN^bHJT;8_v)_br6*6TW(J3<$2>S?cDf_+x7_v%r{9VFzP!xm-lU~h z9^L=S7IIH-uJ@M72CEv+-Icl%Sj_8R@bc+4F-6JBP0u>HOH)o=UA>3%pv1!eS3(xH z+WZw!Vcyh!dC7KYl4GbquUN?~<&1GPN9FyE+l@857Pj0weOxiZvGY~fTy5{xW3oQ# zd+OB!i}D}Mzqa_)8CMav?zQjh+Irq>tkeI$vT&|Miz|y`XrA^fwffx?7$18}?s^h3 z&6RWG)tregYg>f-X7%tk@ECo2pw8e)Jx|~q4gXwHygZ{jVV(01)1!?B)!v?Se;-Ud z_w1hA-t4Q15~k}M?iPRVoM;gB+U8wr%_4^~>y^iLxZluvK51fB@ym@xmh&~|FPd>V zJ}#mym49JND%-_7-6xM1pXkZbe|lu~McFO}FGe*352PlJv!9`$0Y9j|;o#v2&PdEl z&q&OK4bF1$aJm*36r~myXQsi#n0YvJ@{<#DGV+T{2zJHRuJ_-!*Yi=dTWxr{)j#IY zYr^{LMKYS!-QV@a=SVEe%Abu&%jQVF=a&;car{xnn-#AvFiTFpeYUA=eP86Pe&Yol zQx3@oMO3mji9G#foO$_ez)^vAd6lTiB?}d2Kdydma>U?)LHCbqaoa=`ZC+juyTr=G z{b_-VlKN_Eu3g(cruwexeRkrhQS*d{o}03yBEEfJoxefY+JD)PV^d?l2ZnyPSTC)1 z#fxEM`u)A%g?CtrXEm4^uk(KQIL7AHaXxqcCz8H~-!xw&{*$`;ed)YdSGPGwSMN^9 zO-yRO@ICRp;hZ?*KMU2riMi%%lHXE)Q&IWq=ND`4dNhj)K3UCM;x%6>ofne&eVEh? zypX~?G#D0`T*925DXDqjksU*I165EwFbm7U#S{?343J9R*-?RTHNQMS@}B)n+bhOy zAM*nfn>MZV?@#ZHZMhkImq+8~^$-8oO?57s`EzS*P1ixEhlT+q7aMtN&dyd`?fhuX z+PJp7&zxQIQAT?gy`J*(MGV)Q|1&PFFWkH(c)nlz`$>EDzVSCQ=S zXFi!e_4MR86?W40H@`$}xAj5cL%#hd{rSva$}i;0x}T(X+LSG@Qs;!K#g|{+%R6J{ zTzbrF+2&PJb2YO4ki8#IOwbqG??Fu;UoBqq*V9Bwd+bLQbxgGfy z{PXQd&8{@wavhTD{20{?d<>!e6mDU0$N(W|k^r8RbQOY(422B@LCKCwmKeM z*r9t`@zm9v=-P)1G+P$_ygbG8v`W%Mg*|tOleq$?cSW6BzxwL;Rh+95_TIS}f4^vDf^ZxI<4Lby z_Df!|Gb=LnHJxHUG`pXaa5shpF%`o7jB440O126mv9AZ3x3!$onD>{pOz`O1j-o$q zOwPwQb|oqrGnW^=%?;0+qY1g&WFy zI39Xl%1Y9gkNY((<9?_5REzm+*X>T8R*U2%dEtTd+s{&DOerY zvV7soP5u+~j1pdkpWewbDI?>lg<3-CrupUnCyKmZr{MW#t%In?MJB^U$+#WU->)s- zo-(UyfBZq=_p_6J^X5DV$rM}3d@Ju|^W?&^W0|c@;?QtsA|}0?krM6%292h&zbHPH z_VbO%scM_=yc{35xN_?>ZSgqvI@)Gk^A5*J#v)7d>tAY}IkurA`{=gLi^n$o7daOw zqWH$^u5{F~YwhoRtwh=0rlfICYB=R`uXu{z=jEOC{uRj_CxilWoznDucUEfn21#{E zHEfq`$Nqgy$pIHW zNOazR!e9CI)gpPVO9gt>&-YB;og4hfqozlI-Qs`Rrt-wP2d{c;R&81G?ekPGA@ej=h|!xd$#@3B^=7Tuo9c`w#Qcf!ZUv*D9|)$IvjNjABkJy~L3w%I1j zCu>y9wtslPO-49;^0s@MemrX3Uc6|B=_=Px59XRJ%n#}MSQM70*Kst^$whR^yTwN> zrp@;Ai{f_w%x30UZm_4&{OPhciX5eXo+XOemtVa(ae50^&J`x#k^>@d|62ZT{uC#& zkYxk+%YX0sALemy;@F&+#+kK|%_?&|FKFaiF{@N|o>dzw!@S!K z*HnMrVV$QQ=~HGI{p#n^g*Te{FaE!8Ao#QOys^jEQ@JT3I#m(=m%TYWk4{aIO6IGy z{kpm5RK(gjPOg=CeC?7kAsbV-?eCd=NBAXg{tmr6>3Ju+1-G_{bTLR4G*?cXKJki_ z`hIgcBla62$u=53>mljRpGnQYmvHT_WuO5nW|@T*5w&|jX;MyRvO;iba!Gy>yi_J! z!EaNV@@I+1`WNomT))-Jo_w|akozwJFeTqu)#~)Vbhw2*4~G7=RR3D z`;C;9+y{=$7oMn?vd^Eh;M zo3V84iCf9Xe9x{)Gdz3oo?W|u*~6Vns@^9AY&v~vS=a;f^{Y*UEmdDwrahTawMuBl zoXE5`yR{!ToU&u%ul^wDZN9)IxBB#op29Czw@x%W^Fj2O_Ps#yjnjPsY&TAgtYNLy zRlLfupD}$=T=lY;Pv4b3PT-wpODp?w%ok=<9g9;nLW9CQ{&%vm};fzzx%d1^@DM5%?!s(H-2ZC%wMy&UP#P3 zd&>K!&jh#CqLB;TLp1f8HR5FsEm~b9&cN2^`lkDlv2Fb=NtY){MThSz9s3k^%q&HyP{K+X3fgiu~N$nWSyyY=c-Ir;jvk@a&zz2 zQMf@;eI+N)vZ?#Mdw70@&al4mLTRCQp+)7I>60=ychBY^{{#2 z?+zRY`)hQ5$<4k)%GY~We7>Q&X8Mx?i!a-(w#>DjuI9XW;U3S(A|GXgcE*Tre*0uH z!qV2Cb&Nf+*;gynZMFO@xknDRx6jt@oYXMK{4US5l(~15zNd8GPrN5OB}jBZZO}>Y z6^bX1ZG3zB@YIBh{2x{q{J4MGx9-igU#oBKtq=LM^kwC*o7c)zG>V~N&k$`809%8> zEi8>_I)P?Pz<~)`XrZeRWI{rPgM0DTj$@UV`*T$J=IdEX$}lfkH|wb-JEz#G3VtKCf zq66)zCS3AWcVF8WFFZQecS*kEREzqPaxJef&hwnqKBb+FXUD+!5nal@7c`W zbBiDQch&v=*xjs}{mMiBqt=F*K9~#{qnf3 z(CO7OJ_YOxZMh82iuU^dxO=51Kvd>^z`G-Q-~YS}`ZTfjLGD#kgT6TD;!ln*uVy{o z)M*uFX_3!n(~%ji<$H8_$c~)OGqZo%oO=4V^Q(}}q>5?R?v&(Bc1gV0d)#ePDPP+Q z$*!xpIae#hrY?GUg3IFdClz7;;*iP0tya=|(1 zb{4o#R0>QMt6aBnoueAZ2}$jXZmT@5rD$DmHurl8@6@_;i3Yo?n&X-F zGNzq+C)%68&|uEqg{||ZSbYxbU@$y?<6YJ?rkih>dw!SZK7X|%RE$+@)|Ury8#@l9 zw8n_~b{yZn>p|(uwXC*tk9T@J*sXDb`Jut&l(?V+kp)Vo;T>Hcr*bu4-(y(JX!7>G zTj7IjrWK||+0$pIJvc2>8Gl2HB}2}g>Dk;AddCWubi z<76Um(DLU-b-TK2U8P4it)F&z(Y@JEX6&nd__A^Bw){WW`EETh*}yAR{`@h=jQ0`0 z=Co`#TYu>OnoC~<*a`|wA#o7HsAdp_6bGKJNCOK_i8&>ix%ow@u>8y|%mrTI7y?=y zW(XaBV-}W#D^x&8gG(@iv9OqBx6)Qw232>IUzX`VzH4X4)-2BF#V-UMmR~$` zDydiZX+QU}fJ-X^4r{#nx>~ipYt}TGmFYX|d9Hr{86>QKNcw6)!H50>LZ{eH&w6=dGTAn~;porK znWvtv&bqSomg5A0gV(M(bspOP#X@MRPYCOGvnQ_2)gr0a?(q0tyjRDw+WLs-KC7^0 zQTb(=K`X0vo_L)#D>mqYE?2gx7^gtXg{)InYz*e^PwK+`?Ym#6@90={bYF1jrin*t zcW!8vWUJ7-ohbFI^{dy!h0n#V_ARo!KV?UOyoP=EABVW)W}9X$shQd5??q-=|H&7- zF8|j{;I&_qnAI9}p5Tvbuc~awW)3*0Ya@2o#YA1I^fx3Tk{Q(u5|JVzG}utoKo}Ac zJfKyQpwb4kfC^Swa0?5RmzV3MWTs~(rxumyCFkeD227ZR#o;<26$&^@fY%ZbSP%Tj z`k`Ray%kF?FAiT7u&AHAe8R~^OWz5dn-&qzM+x)D_o$Bn9n=&S>^(y`Ewo8$VqxY+1$jRRww?D7@WYsy* zrs~;hHmU6MYjj`4ajLlNN}5%7ZSjh_2O*^jtZM?c&qMhlOvU0~-t1B~?Y*T$XJDVY4Qo?_KMNf(E=@V8bvd!uLwCm-T zmy?XLw|)+P5i5PRM|Sm8mZ_efO?9U|_RPIo{`j}2_m(xG>?`}HZr-B2+b7rc!?%^2 zrW$Pd>l50e?iDUl(=_*N@ubUQUTi!)0T=t_IQNM!e5QEnZ}U@uJ>b@O!Rzw1lp5r4y8@b!F)o5s5Ubn9J<^PCz)P=-#@HgCt5bzM_FVsHk8T zmVjq`a7~$-0?GOACIsi#_dlCZoN;gWlUOes10{n@<2Q$vtLmLpwEfg|Bl6Hg&E>Wc ztIKAcT~{sL!h6I({$EN4XuHIDn>6GB5C-K)pH% z$k3wo8IubRZQ4~jGppEW%0&Se0loW8pV?cUm00H%KGNlpey4Etq=Ia+rtY~naegNz z|4>YvZStD+SlpXvsXebxoMD+1!Z}(qra5L=Psp{Cr zYft*eJvDu9c>31lxI2CQ4Y#&UoZ&E`@^HoRZ5xkl&62QS7U;ck#e9h#Yi-BZCOd(N z1*=1w9F4BuW+~Rno5)zB=yPSyI%($8*ow59R)#LKj2-rKT#xcxa_HI01?hK}sP9?m zZ@E=4Lj1c*j9Y)o_k)=oWi!fbBV(P)+B>IbR(X7Qut-QcZC1&td){)-`I6T?yYhb0 zB&&J0cNm_ys&;Ohk@6Ez+rdDR^;r{Q6n>Gn<-(COdYEOZV zW5TM9$r3J8rOJ}(zBO$4{UT+V@x@N|hkLg_b6z5`V_7-lL;fi|in5zRG*vrup7I3+ zx|i%zTeHov^j_7P_+I;ydoDW)3PnE^2zYIH^i+|U66gIIpHlneH;VB`6ausJH?D5$ z_CIvZHGZzbpM`Qq5A-kESjVL{^XrpL{TZ^lUvIA5B>PkvlG#!jRSc3*QzJBOaS2O= zWTYy%=PS4*rleFVxEJM@7Jzr5=ovyA$=t#kaB1h9#NuKFBRq{{1*9 z_vT)I6)xeqF#7fNc#pPM`|kdK@;+VWOKQgE@WKUg<-6a`_Iz2;U>hcp%#nZV)0qqQ z^-Lj3Ap$oxHC{PTYU^Epy!^0T#cC<$tqR%tS=t+qcGMl&Ew|y|4OjL2Rpt@j+tse$ z(EYYje7k&+ncp&T`_vheZfoVmF8bH;fcswk(_YbsuTNWDeI&F094$_}A*FKMNg} zoRSp#x9P$8NsfwwKH78+>R+J;4H4-=wv8sAPvg?+`=Mo(U6b; z1?aj{Lud>#3roPIA<<(9>gR*{l>{4U)23Zf^h|h^{lbx*=iZSAcZ8MC9yC4v`<-NU++xu)r~rog&~9gTah&Aa?hH|(J3xyNdAMfC)J zv-KOW+*`gSdCUKebJp3cFW(k+bnEIxFEg!d`HU-#>rVVUz^3$kP4WGudcTA2Y!ppZ_+^@wdGEf?$GklTw{=v6Zof7)&6seS zVZ(Gq+Y&|PR1@vp6_JatJuI`FwZyZpr(}KJl39yy&c0+3GkcN5nNsDJeXk$+8W;9S zeK_>vxa^{}%+qy#oL4>eZHJ2C#^l&Z^~oPpPX_DB@Ark|4)h2itC0rX{K5>``~q&) zITja}7Nw>rc)CD}Ya$lHujLax_r$p&W@G#3?Fl~%g_f?nqc){u)t7T$6)ZI(5_oqd>1uC6=txp3Uun9Ans`^^Jdqky+W9dv|SCEP9{yTk%s+ z;{Evw*X18=y!lTe<&scz{Lcsp(>?BI7n=RduD`Ou!tS8-;U43gH}oa`Y4C4e-hAzF zp38wm(`jP=BIk6MZZF*9p%W!x_doN-x_l z7)34GCEO(V)>3@#Osk@W&weerc~#u7bX{w$(mH!ax3+H@l_xgddJ-;vq)vu!!SH2v+W{?6BzdL_zL`E-}+o8 zPbt(eb^6}R;+N!-KHq+o6VPMd!ZYps_S4J{PUS8X@saMl`<7utP|f;@IU6m6FTNDw zIKZ$$MqBV!d4bvNb>}ME?=%%W<18r%&0N1YWB&fT+bTDInp>b5`epV1sfnLYI^@e< z+&6jmz~H^#qVy;T+e*DKVkc)Neegb?d+5{ID2@RUXt-T zF{W~tsOODbHp^Y~DtFkk?1@>>{!n^-VBgs{I=_DW%l%+gv#R?^`c%VSXoN6SpcGlO z$|1NLY?9CStZ_+wdv=F?#P8mX;VWHqSw%m+=l(BHGJk7QP<&dQ{L?$qu9e(pjs6xj;&}!1izyMNAYX-xWK?-8> z>&fj#C;q)GT*lJ6mPat<{yE?GCzvJL?H8{8ZL@6thrOG*ef7NuS|3iV57wV|ij{G)Tz{I3<#OL&QQL}HRMTFU*FTRu<~37QB;>W) z@z3>=R}BjK*Kcss`oH$I=G*J@SFyA&zVfqksY(28=ZQ^bH`8o3?%3J%caD{vzzOU3 zY^iBgvn3^L{}%?@80f|Cvn?;)6w8)#SUGH#cB|SXndMBIpV;%R=csJjVP^Yk&Slfh zdooXbJu~;k-;`eQ2IIUquh^L<<=VH*l{#=WPuNsB?*8m}+xOSLmmPX#x4VK@#wk6U z<;IU|xAMC`xh!B@4XP(|nbZujkz&L%#E{Q`2NcxoJgmN%If;3O$_9!cJ{OOaV{%Di zPG+$}uwH?lqn^Hjv9YOzsgZ%DnK^7ogPBJTrU*2smzq}s8{-32mEc}9fyve7$Br!3 zT%z;-sl?F@0gLkkt><$^ojl}W!F5Pxfkgept?fG=w8%OhP@8XGNH|IE=bE(Q*$;I^OnKOEQceUPbuTA5PPYhHFm458@__6H; z)1+Cq*NdEpUU;Tc`k{f^p9Qn!8<)Gwv%FqxIf-rl%**>Pzw!^5Bl&MyyPQqxx5W}F z&20kDL+n;qm8tkQDjt2z&-7#R6nBN)|E};|&e(bPNL5SIKcr;3wiA>H3W) zP9uK93wzh|=hx59|J`!AJZftGnkg;w-t9Ty)$cX=hl}DPL)XLrKY<&MO>2B4x7=gh zznE?QiLGa!$X^#{3ZX=CJX|_D%g4 zW-M9h7-@C+&Xj95w@WORc%AW8ZTjxDG+4;u-Dl6-!ijas+g2}&KfI*nhksJwz8^>5 z+_sqXnC&UYM4lrWj-kI#M|FzU6}~Xt?_0!ayU)rZAiCU8x_9$&71vqI*W@yYM6qn& zCHy*&Ylfqvl3!|Kd!u8(vdWmwdy-z^SM{F7$^`4w%{VK?CblG__<{+GaLmHO%TYC< zx8qVCnztK-u{;&rqS+wyvC^_*5tCqzdfN7m?G>L+ajmde&6RR!e_HI+mV*mb<=SJV z0=N}~&N%MQ;|{dg;eDD3yxTjUQOzJ1DPEjCV6!Y-!tCLm!KvP8{*q#yi~NVP2YIx zk=*ZEi}y3WZnB)=r@lilw)&Fll#V7*wJd4F-7BIM6}4~h`^}H~xH0NU)-zzuk-5dwSH0Zys-It9j2VvfZC2`q#0`W4pqag;mnwS7HPH?@GU+JX8LJ z{o{N8^P|f*X6NlVnezSNDMvRx&B6;imH(VsGw?@$Dt9! zP(jCtaRx<98TNx6d|n0|IL2QjvFFCeU7I@}ReCKJTb>=Fay#(Vb&sV#X7O&$rCPFUfI}Q5ic=~KS0{loImT>otwhG zOZq05KieB^IFX@{QOzJ9DGlQ6+Nl|+fYKPZuq?dq=2}sbT9lWVqk!DULrjTas&jS( zmk%LjhZZ{_#o@SolKOL^P5d$R?Nr!Ud;shvE3uh*W*Z<(bY z_n&{78xVDEy5^Lqh7F!oxAms}-EnHaxrMHr-9*Q=1!2cO_}jlQnpbk_#DtKS$Kv@b z=FW1k;aiiqDo&(-Ui-qVfIHV``K@Q&ysU7_oWjik30%j8=UTA0F|ByHB2!Ib!fKxl z{>rPZ{9A6^KR5qjX8!K$PS>+to*ZBMF4r->D5{Xdd~;>$lE?*9%)c|}^B$U1bnAje z(QT0xu18|EYPWB>cI4ffv%)UhPio(FW>Ec=xHB^=LRoP8a^wE6(#Wr8HLtsE=h9By zcG)RkbkmON-|t_>246Laf)4)HGO8I=BSnq76Ku+rgGb1{C^fMpwMfA?F)ukIwYVg; z2v&Bm^RNde=A`8pr5H*XNPyyui$~BoKQ})mAJohC$;{0xNlk(G#d#zg9UT=wy+6nl zVyXgcj|{c`9WKR#os(;dg2lG=2@W5bGxKNIJdDFm#Tcsyxv}iun%2Q z!mkV$%PpAs*W%kZb)$%U_OIDHev4?Y-@fY1wdDBXo?EOlS6Hk#EnZ<3xaQ@&FBMk3 z=lO3sJ(|I@T7Nzt$Kw)nGjeD04x6I*(GajBJE$Z z8T}sSIk4}_RgbOz9<94M>Ud!M>Lo_Y65F@s@wZ(vV4D8pdS>`S3Cq~5l*(g_k!x%W zy)0@jv0U=jc%cw)&A#Z2Z}IKV>->+0U5#2kt@GFC)frcm+alG^bE@xIlULL%V^DPe z!J?O!9+s3!?$w%CEgHfiecjn|ZK%f4M^d}L9C)(M!qfOnS?hdD*Go(0eG=*k{895n zxBlyz_}60dH|k#8b!MVdxsU3}2^RUcKb-m9CpSIP?rMVHGgghao|VGhyF6~||17#} zdFr414U?j;TO7_CG9G?&Pf%ApR(?mo#W>03nfKU@HBJRg{kc5kbVyr(>&l;=keF&_ zQZs05pkY)YMpSS{6%l(H_x`N-@$6~?Ps-dj>1iqH_DWWArHr-jf5)WvoIDegy>jBS zolZ{`yl3B}iFqbmXX};txk^o7wbtbe z7t?#C-S)W~T-7VOcr^Le^W;jGI`3qa&Ne+0drhC9Zms199!=Ef+<74Sr|iw=>TCbc zVw!&NSiq#&Ogej4?lg(y+TY^!|Ng(t^IzYb_h5$dn`PPMVp_M)thJU|(tgLSI8HJt z!!`WLgeIrmPS-vpJ~+78Hf?e27m0TrCws+RHD?-?G8w+pR6CK)^lWRAYrVN>lyXMr z6}x+W>Ki^d@a*vwKkd6{v)l!?sJq`LpARpc`Sn+uRC>kc{nP%L+}Zm)DD3Ixr=``` z9VfEhiOqaGmDhRapXO$|tqWp!6j$N&vI{X4fnqDR7X|x#5}zUzaO{e%xlv*75-W3 z>4{0*QccsU4(|yN%DllB<<@QWTJ4we$!!}N_c<{v`El)^f!^^;Zr`5vmHz#7X6J?W z=re2r3A>w3x5uU5bAL0_xMJoP< z@fH8V7{$M^ptHZPzl%R|@sHRi0h7aC{1B`JE;w{|7d@UR{XOuYbO_6g%PaREeAX3f z-Tr%b$2s1|`u1$oJ}lA<5^l5X zUmyJWaYFd(HC4Rbw~Gw2(yl&mTU>A_wZ12lsU9jJlMXMh&{z@|GO zU4M9Fr7FN{3I$ySB8CU|7bbCh{1s{PUHYWN;_nTf5igaRg_j*waAve_`S`5WbH4hk zZ!OY(3%1-Ym~q^QaU-i8yWhJnzx?$h4V0d*mJG{&)WH7dvsh_^WnlHwpQhVN{8sih z2|w9pzmmE6*P?x0qFMF#xDRiW6k$E}>hKDdDb{W^M!cUH(v(j0X6Bmp2uf_fyh$cu zi^VUdo^zM)y0PEmvU?p{bE4+=2JPS_laew6Raq0ITpD|HRyt2pzQ4w*aTcHEQJIx5 zW97EZn`gjfdG2FJ!*B1{Ge&P(lblsen8MTpk~+x);C5?}2M6$XthsPTnZ=1`K6SYDC}jROk@a43lfKp zkO;%k@?UgyPE+;NxgUcn+W&Ql?Pq;wk6f;Jz!q z|L0fDd$h4;*4pK5oW<=IpU-++wRBb9?_HY<4y1Q1GQF~R*2ceU54|`1wPNSz-yFv# zUi_^&m7xO^1g%IxK-~fcFx)Lf zmVX^GdG1SxqI}yOM1ynZS%#o$^{ICdxGyD`K2Y)?_Dntws(F((e}jplivK|%WWTT zU`c%UUvT>IFKu>QzwBauPJU)mbyFn!`JBzI3!19R))!8={^l?5uQ@rt?r&)NB2xEr z-;rhd{`E8KxBf|YF51h(aIN!ow$A0Z{WD}lR@Xg!ocHI(iYJp)ZaHTs?Fjw*>foN7 zZQY;wdS^YEos&89M}>!h>(#Xnd2L)1{uoUQRNs?+_>|tZ>{o8bk1H(LD4%-IgJahc zzm=bw-OO8Fzc|zA;hgSQq%}KcuaC4wSBz5imen(EzIzbZe3;YCFV}A7uc_jnJ@Z$Qa?^))-5PudexmSs+k$YwEOz~fUHor{RW}bb~T3 zeS?9LyxSm@DsIMi_%_Gp3v0r8%if+24f>r`tqA?DrV|tIL`5Uu>?# z-oQT7Wy{k}6s6x*Oxt*6b_Mf+0IL^gKHS+e&*$HI)@i2l;ZfWFpZjUMU1!_3kh>?I z3t9QD^Xb|iGi%Fs1=~~gN+#>-tTWOWU7j=gxdqQQPWV1$U$FUvV&~6BhlAhWUiGiU zFY|bIPr~GTM~?H{e`vJs%w+4pimB<{?`*^V@m$E{m*)Shs+Mh>`#sP7SF!WcI=-c8 z8#QZ;T9QLMkMJIx9AY3TyykMP`SRDXRyn3Iw;itw-CF6%eg8&77w6=pzO8BJRXfhM z?AU0(#FGCe)4bP0K2vKxXS1%16Kg)RBmL0};kjp`n=booejEM&-qYeW%GSCJ`%Obj zI6ZH8Mo869GJ6&{iSM&ot&Feuo|A8!)h_IJjCWY+;__wY^YHe00rKG?EA0N3B-TisdGb>xbQ~u2j?M=si7&U4-PxYB4sjMjK zdhhWGnZVHKb(3G1|JTlbdF9nUt-C7%wXdfgPK-%lI`Q>fQML7sPkyN!v(7Hbl03+` zyLj34Z-?)m-Ls^7>&(O*jEfdu@x8)oJ8|ugEgXd@>kB^0WY>K=Ts~K&xcUiC&%>K$ z4`+6>&g6dUYRGx&5X0^7MGK~_n%aJ$@A$UFV&1Z}o;%W8k{8XdpDb+faiRL{oBwq} zJ0>o8;LpB6BdkY4db&$r=iLY5dC#@p_giuqwlv*v5b@Nja(uG<-{X`s2O|r;brhFJ z*FKQFsMk3A@QsY`d#6dSDcC3`!hJhory3(GB-$o{qOBh(+K6x4BQ0TuPqZQr1wosb z(D_(qVWjz3NE?y-cK+VC2buqT>;J>FF+X~~$IZEwj}~obP7ISc?7r>#DO*#$PUr6n zGiTrHlKLyGFlUQrxshhr-<@xI?kfE%x}%;r{oS{ymN$8lJ3Dpmv_xMm`JEkqv~#II zP^VkymmQ@t&+4Cqtn>a6S`^MC`9r)g^0k}>d&$wc9{X~(d|9&LbBcN8qyul<;_4sf z2KywfKP%6jyxVH-??06cGZIVAt2H?!f8JD-CCNMU)D^zcGv>d$I}b*FOsbFH9G`0N zWBGR9vM9N-IFkb}riIPy+Wet@$=SVkj0|fgo_`Q8_#=CjXUxT!Q!XxgoE*(rwlys2 zp?8Vwv@2>?I9MVt%;4jDcJI&OH}VrcLgH>ZsJfhj8h6k|N?gJs!Iim*c_pdI3eNdO z1^GqbSrtR*QH9*XVsL5jh#S0)bTx2>)R8uD1(3;eLj~wwL|8i?N398J_nY8q^IlKe zDt&w1=7e`{uR4-Tt+l4)evTLV_LzU(HZGB1kDwEGtTvzaP*4-{`_g{?hznX7R7=jb;yKl`n8O)Y!{-{7lc@{@U-A zkE?u`eQ*B$+iszub9YvE2*>U8-_kw*G$!fq+rG(TdE>$xaUF-xSUNaHUA*)3mcV@d zTa3Pc_1tQH>Zx;On!lU4SUT&B%YNzOhpz~33P_mFxmQo=t7A*r{Id?rRx&gkl%G0j z{=Ls0?{CYNC;q9w|8Clnq6^#X{#U54KRrn_ljC->z@ul=*W}$<^KH>NIro`6JGTox zTX^Mcq+t5t{MH*zej58CLnB$HuI@K)*w6dnkGxfiVMJra^+%@P6zny3^o!-$dED9R z!~XD-)T=tK%4;k3zK(6vhA#f1YnnEqMw-5O)2q*ix5X=PSIzs&XMS1D+PXYh*p~5u zr`@lE?k6X0wz~c=f6n(=R{UT2xVR46pLi}L(s%W0?hKvHg+^Sjwf26yo~6K@?mFkv zrv1T6X`HdQ1wVgeHI7*PXvg`QvrV)2(lC-Sjq|ajn#@o6RPUcs&N2+m*DkE`Ty*93vm}dM0V2-1KMj9` ziQTkdnt1+o&fVRXels5$q`Q8+@}c+e{ePb<*cEFT;=)`uKS|g-QG3qnXqF=WO&T|g zR&QcIKWB5V)mZ{E#-?OIseQ5I5z7cP>m|J&!GOw&^{ldJMq=mg2b_@T= zDEwg5-xWGbaD(Kf69JXkd|9m5SFxCFTKwr@eEIV7IZx&8yu6=vFco@6^lVU}Gy}Dq z7J%$HBs6|TWF2d5U|U^89dSxKb936clZuJTwN(faB4=<7bo>G6FX!5ii6 zPoJN!Ae^gdb%MXv=6iOTMe>rMbK7UBuyxwoNr7m> z?D@S`3D24|zr|L$E4RKg(c8t#yuSi>pp#2^bpQm%{ww&VK zDX$V8*POf(a=%^wxY8!IGxH-uGUor;r=@=+RHoD+LA;eI)UvxC+EAJes%+6B5!ZG` zO9OLIbZ`qBK-;$F3h;f6o_Wc7c!t%?3`{^;nT2)XI*6!njc`@Cq8nfQWfpaQy5@De zxnR=!OU-6MwXWgZUcJ>J)>e7(qD%(@mYrACuRZ0l<9I-_jFuut%`CGU-2X1B9gQ$F z&`|vx8@qVNwxwGpz0J{?|N5?T=OP<#kBPDYZ`AHzOVHb^$bcGq!GSG?}Bb-YUHGn}#1VuIX^^LokJAKdo1Z0cNiK!eA#Kym8) zlmp8q3o)gtwH;g9`Qg{~mIVdt{iUYcv$`qM1i59QB?=VGfHSZxs zje?H<*VJ-y5?ECILhEYZ@1NpvZ@cW>f?n46j-|6d9eVfbqJv{@l7(y-+fiosKn2M^ z)dJjnN$bi&_s5s-v-4yOww z=Gkjs&gC+fcAT7U#w($(gKM=pzx2$VC_gs@nrxuG-~kX8yMHRFmUe+EDGF z`}&1?>&btKf9syrOqabn7t%f02#VEpw2oB^Vq+CCe~dj=K^+Ie8Ftftb)Db0Z$#g% zsFt2rp4NEUZcoj+&&$+jnJmsQEVM9Adh)q2t#aea)Wu7?ul6VC+B=~gq+Pwky_ zPLi$Y8BdHw=5dy5dveZ2fBT*>=Z>nVN5{gKD}T(G^WgJULybhiob+kEmNQQua#ewD zDBcW;+6|~tizszn^AMNU=oum^BhXIPREQkt#1DPvfMA8pVuj4SWM~gDHANw*QlTg{ zEwv~$FF6&dw+OAr%oI?~)-}+E)K)4`arDLp+AIKp1$x`2ZgY0}bu3pa-{bxiAIoso zcZVwl9W%_{zWFzcv45e3#-Hy3ZfSbHa#9}_3H&@#!w8XvL z6sEEK$Ag^?h2}?3yYo%g$~d4jHA(cg`^~QxwEs7GpImEq&*%1ph5&oct`>$j23)oh zXMg|Iikzi9Rb0n)o_wBjWiC(5jDH@>k1D&z#Qn{CF1O`dhU>+)=IBLTy&E5EHvCg9 z>{z>RPyd>rY2K>>d!Gm!O1|*(oqv1H=B%rGEJF%URaMnbxLYgRaI*tBDdo6QXLot4v%#G)ZSd@NVzI6k-a&kK;a<{<7 zxVeP6pOrGYPeCIGv?{7tAty62Nlz2DjGkLq z1{A4=mX?;VNCjD-09!(3Y+wkoh*?+*$$F$_BudOGXn<_iBv>%7_4QsU)VAr>Vg+_l zu@o(B?~D7ny?p!LHk2(*-(KNxpxEg|#(~?x+IQo2l+7@m+$}Y?M%ao^K6<;{`KKx( zavK|0_SWt=lkLs?k%{Nd>ZJ}U4D}^b7S83++}y$awY_|9=-DTS>#k>|tZo%)n;Jdc zC?MbV#I7`+$A9+j@H|-7=_&d5z>2+{i_`Uw&EHcfnXp6mrOwF-Wogw@9<8sqmKq#u zb8XJ1^P0>*^_K)}y5ez;)hM*<@1_(Vz9%!-OK;cT-^mf0&}yLhv)W@xX4_*^d%3pQ zsw&aF?-}|;l|sZBCa6u`!{?bXasLnZ%X6nX9pv-9pRacQu!Le#nsdRms|VO0LJH;s zpa|ZJntP$w@Nfz9g{2l{24|+{L5I(vl_9sV7)%0kW)FPC#>&6~6j9v5hM@Q~GBB`E zfT@HSfv2?zt)rQR^3(p@6cw~$p>_v&l1Laa=rgNqg#r^S^gG zWT*6m`~DFYtZaE37xK+yJ_qAx0aZ3}uUj8wtJ1P(B&035`Tmk#)5j0*IbXS)6wP|X zoM(J_&HPY?Pp0UapV6nHF8=S$ zk{7ZX8W>#VHXMIGYrjyqy3FDXeXjPsS=oGXsh?%auU={GEl}LD+30!qqN{C|OdEGx zazFRvq~{Uq<@bJv)Q0c-@FMR}H zRhAS&GW8))|8PI8quS62R#+2^YK#J$P*l4*8{+Clwg{iRuq)TG`cck>*P-!gOj0Ue zqB}F)AMZF+`|%Ug(fJF$MPKb^lWg-37RY5+QD3;=$5nU9M4#GIlht39@Vw8T#`!P# zO+l>L+Vt{uC+F6Lx>zkr4wV1;IO)ntcXqoo3JhmK8RZme=uxXk1Xppe@(mtrxYo5G z2Ebuj6i6QcC)|#`+j3>+mY>USd}gVbbIfh*=@a)><%#-*sJ}mwEA@u^G2aK5rQydNBIFjk%NM>$_?A26goNeWe@+r%_n-K9 z3RL(pmYF6CncYp>!^mQ`I5gwxA~Vi4s}j8(aUfe-hmX3w8icF`7+X@)?wlQJMmINH~9@gBOWb31*f?$asicbD#a zu)()~b7GjywVK0U&f2w|UexjE;f*DkN4GxOe{h+5K%ARp_tj~;w&siM7dWRbJdf3V zwqcd<4f&gwnasP_>bQUCYh+u_YrymRyA8jB?42UFcT1U0L?1pGmg#d;;^4$;?yJHK zv-i9|>|y7YpwOyyb;7o@-3!hb-QE7zA#_uKOxU_Jz2&~g6?~T)mc7rI)HOklqcLpL z+RVj47f#C@QMQ$z`OoKtSaRPnj=pb(-!A=nT6TA<%%sJhXU{U8%{aR&gcb2DwV$NEsX8U{AMx5@*EwXO-I#=_ z0m9LKde@wwKNa#)a{1Ec#e098%$_1-;QztQ{p*E^TwldcEKNKp=HUCgD{;cpGKGVJ zY_+R(@|JxSbnN@3yH+bPa!I1Q~{;*?fV^{*e-o-pi;ReGVdYMImK{vSI-w{P6;z4>2%9eenj_s+M| zl0*~FC-5%0aqq{2!$};rA;PacPeq2bsy)^2m!ALn>%xhLj6H8SCuaqDU+RBR5a@fc zGqd`5!MDwKgi3B#$9jidp0;1KeBp*Ki&Q_Ymzk6<@$?HD%lipCpD;YRYqeGKKhv%+ zHt!yEzc%lF^X2-QPv1V+uD@G4(O6Pe#^H2{ilpM?n^6hd*!0@f%M|XA{=#3?dKQoFy=ZRYj4s%t?OOz3svYnV`o7vBO2B1 z;1#Q|ikkeoos?+DIk~+&tg0sHR?2KX-IyD3YbM$LV|*s*Wh=t${VPFT;bwDZ=C6=r zvn1j|wNmb}ee3nIUMpaeYCEBWrSpM7;^uX+jLWO<)oj{#Dm9UzZQZT@>Iconf ); @@ -87,52 +93,49 @@ esp_err_t server_setup(mbedtls_endpoint_t * server) ret = mbedtls_x509_crt_parse( &server->cert, server_cert_chain_pem_start, server_cert_chain_pem_end - server_cert_chain_pem_start); - if( ret != 0 ) { + if ( ret != 0 ) { ESP_LOGE(TAG, "mbedtls_x509_crt_parse returned %d", ret ); return ESP_FAIL; } - ret = mbedtls_pk_parse_key( &server->pkey, (const unsigned char *)server_pk_start , + ret = mbedtls_pk_parse_key( &server->pkey, (const unsigned char *)server_pk_start, server_pk_end - server_pk_start, NULL, 0 ); - if( ret != 0 ) { + if ( ret != 0 ) { ESP_LOGE(TAG, "mbedtls_pk_parse_key returned %d", ret ); return ESP_FAIL; } ESP_LOGI(TAG, "Bind on https://%s:%s/", SERVER_ADDRESS, SERVER_PORT ); - if( ( ret = mbedtls_net_bind( &server->listen_fd, NULL, SERVER_PORT, MBEDTLS_NET_PROTO_TCP ) ) != 0 ) { + if ( ( ret = mbedtls_net_bind( &server->listen_fd, NULL, SERVER_PORT, MBEDTLS_NET_PROTO_TCP ) ) != 0 ) { ESP_LOGE(TAG, "mbedtls_net_bind returned %d", ret ); return ESP_FAIL; } + mbedtls_net_set_nonblock(&server->listen_fd); ESP_LOGI(TAG, "Seeding the random number generator"); - if( ( ret = mbedtls_ctr_drbg_seed( &server->ctr_drbg, mbedtls_entropy_func, &server->entropy, - NULL, 0) ) != 0 ) { + if ( ( ret = mbedtls_ctr_drbg_seed( &server->ctr_drbg, mbedtls_entropy_func, &server->entropy, + NULL, 0) ) != 0 ) { ESP_LOGE(TAG, "mbedtls_ctr_drbg_seed returned %d", ret ); return ESP_FAIL; } ESP_LOGI(TAG, "Setting up the SSL data"); - if( ( ret = mbedtls_ssl_config_defaults( &server->conf, - MBEDTLS_SSL_IS_SERVER, - MBEDTLS_SSL_TRANSPORT_STREAM, - MBEDTLS_SSL_PRESET_DEFAULT ) ) != 0 ) - { + if ( ( ret = mbedtls_ssl_config_defaults( &server->conf, + MBEDTLS_SSL_IS_SERVER, + MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT ) ) != 0 ) { ESP_LOGE(TAG, "mbedtls_ssl_config_defaults returned %d", ret ); return ESP_FAIL; } mbedtls_ssl_conf_rng( &server->conf, mbedtls_ctr_drbg_random, &server->ctr_drbg ); - mbedtls_ssl_conf_ca_chain( &server->conf, server->cert.next, NULL ); - if (( ret = mbedtls_ssl_conf_own_cert( &server->conf, &server->cert, &server->pkey ) ) != 0 ) - { + if (( ret = mbedtls_ssl_conf_own_cert( &server->conf, &server->cert, &server->pkey ) ) != 0 ) { ESP_LOGE(TAG, "mbedtls_ssl_conf_own_cert returned %d", ret ); return ESP_FAIL; } - if (( ret = mbedtls_ssl_setup( &server->ssl, &server->conf ) ) != 0 ) - { + if (( ret = mbedtls_ssl_setup( &server->ssl, &server->conf ) ) != 0 ) { ESP_LOGE(TAG, "mbedtls_ssl_setup returned %d", ret ); return ESP_FAIL; } @@ -151,27 +154,33 @@ void server_task(void *pvParameters) goto exit; } - ESP_LOGI(TAG, "Waiting for a remote connection" ); - if( ( ret = mbedtls_net_accept( &server.listen_fd, &server.client_fd, - NULL, 0, NULL ) ) != 0 ) { - ESP_LOGE(TAG, "mbedtls_net_accept returned %d", ret ); - goto exit; - } + bool connected = false; + while (!exit_flag) { - mbedtls_ssl_set_bio( &server.ssl, &server.client_fd, mbedtls_net_send, mbedtls_net_recv, NULL ); + ret = mbedtls_net_accept( &server.listen_fd, &server.client_fd, NULL, 0, NULL ); - while(exit_flag == false) { - mbedtls_ssl_handshake( &server.ssl ); + if (ret == 0) { + connected = true; + } + + if (connected) { + mbedtls_ssl_set_bio( &server.ssl, &server.client_fd, mbedtls_net_send, mbedtls_net_recv, NULL ); + ret = mbedtls_ssl_handshake( &server.ssl ); + mbedtls_ssl_session_reset(&server.ssl); + connected = false; + } + + vTaskDelay(20 / portTICK_PERIOD_MS); } ESP_LOGE(TAG, "Server shutdown"); - exit: - endpoint_teardown(&server); - xSemaphoreGive(*sema); - vTaskDelete(NULL); +exit: + endpoint_teardown(&server); + xSemaphoreGive(*sema); + vTaskDelete(NULL); } -esp_err_t endpoint_teardown(mbedtls_endpoint_t* endpoint) +esp_err_t endpoint_teardown(mbedtls_endpoint_t *endpoint) { mbedtls_net_free( &endpoint->client_fd ); mbedtls_net_free( &endpoint->listen_fd ); @@ -187,7 +196,7 @@ esp_err_t endpoint_teardown(mbedtls_endpoint_t* endpoint) return ESP_OK; } -esp_err_t client_setup(mbedtls_endpoint_t* client) +esp_err_t client_setup(mbedtls_endpoint_t *client) { int ret; mbedtls_ssl_config_init( &client->conf ); @@ -199,24 +208,24 @@ esp_err_t client_setup(mbedtls_endpoint_t* client) mbedtls_ctr_drbg_init( &client->ctr_drbg ); ESP_LOGI(TAG, "Seeding the random number generator"); - if((ret = mbedtls_ctr_drbg_seed(&client->ctr_drbg, mbedtls_entropy_func, &client->entropy, - NULL, 0)) != 0) { + if ((ret = mbedtls_ctr_drbg_seed(&client->ctr_drbg, mbedtls_entropy_func, &client->entropy, + NULL, 0)) != 0) { ESP_LOGE(TAG, "mbedtls_ctr_drbg_seed returned %d", ret); return ESP_FAIL; } ESP_LOGI(TAG, "Setting hostname for TLS session..."); - /* Hostname set here should match CN in server certificate */ - if((ret = mbedtls_ssl_set_hostname(&client->ssl, SERVER_ADDRESS)) != 0) { + /* Hostname set here should match CN in server certificate */ + if ((ret = mbedtls_ssl_set_hostname(&client->ssl, SERVER_ADDRESS)) != 0) { ESP_LOGE(TAG, "mbedtls_ssl_set_hostname returned -0x%x", -ret); return ESP_FAIL; } ESP_LOGI(TAG, "Setting up the SSL/TLS structure..."); - if((ret = mbedtls_ssl_config_defaults(&client->conf, - MBEDTLS_SSL_IS_CLIENT, - MBEDTLS_SSL_TRANSPORT_STREAM, - MBEDTLS_SSL_PRESET_DEFAULT)) != 0) { + if ((ret = mbedtls_ssl_config_defaults(&client->conf, + MBEDTLS_SSL_IS_CLIENT, + MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT)) != 0) { ESP_LOGE(TAG, "mbedtls_ssl_config_defaults returned %d", ret); return ESP_FAIL; } @@ -230,21 +239,26 @@ esp_err_t client_setup(mbedtls_endpoint_t* client) return ESP_OK; } -void client_task(void *pvParameters) +int client_task(const uint8_t *bundle, esp_crt_validate_res_t *res) { - int ret; - - xSemaphoreHandle *sema = (xSemaphoreHandle *) pvParameters; + int ret = ESP_FAIL; mbedtls_endpoint_t client; - if(client_setup(&client) != ESP_OK) { + *res = ESP_CRT_VALIDATE_UNKNOWN; + + if (client_setup(&client) != ESP_OK) { ESP_LOGE(TAG, "SSL client setup failed"); goto exit; } - // Set the custom bundle which DOESN'T includes the server's root certificate (default bundle) esp_crt_bundle_attach(&client.conf); + if (bundle) { + /* Set a bundle different from the menuconfig bundle */ + esp_crt_bundle_set(bundle); + } + + ESP_LOGI(TAG, "Connecting to %s:%s...", SERVER_ADDRESS, SERVER_PORT); if ((ret = mbedtls_net_connect(&client.client_fd, SERVER_ADDRESS, SERVER_PORT, MBEDTLS_NET_PROTO_TCP)) != 0) { @@ -256,57 +270,61 @@ void client_task(void *pvParameters) mbedtls_ssl_set_bio(&client.ssl, &client.client_fd, mbedtls_net_send, mbedtls_net_recv, NULL); ESP_LOGI(TAG, "Performing the SSL/TLS handshake with bundle that is missing the server root certificate"); - ret = mbedtls_ssl_handshake(&client.ssl); + while ( ( ret = mbedtls_ssl_handshake( &client.ssl ) ) != 0 ) { + if ( ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE ) { + printf( "mbedtls_ssl_handshake failed with -0x%x\n", -ret ); + break; + } + } ESP_LOGI(TAG, "Verifying peer X.509 certificate for bundle ..."); - TEST_ASSERT(mbedtls_ssl_get_verify_result(&client.ssl) != 0); + ret = mbedtls_ssl_get_verify_result(&client.ssl); + + *res = (ret == 0) ? ESP_CRT_VALIDATE_OK : ESP_CRT_VALIDATE_FAIL; + // Reset session before new connection mbedtls_ssl_close_notify(&client.ssl); mbedtls_ssl_session_reset(&client.ssl); - - // Set the custom bundle which includes the server's root certificate - esp_crt_bundle_set(server_cert_bundle_start); - - ESP_LOGI(TAG, "Performing the SSL/TLS handshake with bundle containing the server root certificate"); - ret = mbedtls_ssl_handshake(&client.ssl); - - ESP_LOGI(TAG, "Verifying peer X.509 certificate ..."); - ret = mbedtls_ssl_get_verify_result(&client.ssl); - TEST_ASSERT(ret == 0); - - if(ret == 0) { - ESP_LOGI(TAG, "Certificate validated"); - } + mbedtls_net_free( &client.client_fd); - exit_flag = true; +exit: + mbedtls_ssl_close_notify(&client.ssl); + mbedtls_ssl_session_reset(&client.ssl); + esp_crt_bundle_detach(&client.conf); + endpoint_teardown(&client); - exit: - mbedtls_ssl_close_notify(&client.ssl); - mbedtls_ssl_session_reset(&client.ssl); - esp_crt_bundle_detach(&client.conf); - endpoint_teardown(&client); - xSemaphoreGive(*sema); - vTaskDelete(NULL); + return ret; } TEST_CASE("custom certificate bundle", "[mbedtls]") { + esp_crt_validate_res_t validate_res; + test_case_uses_tcpip(); - xSemaphoreHandle exit_sema = xSemaphoreCreateCounting(2, 0); + xSemaphoreHandle exit_sema = xSemaphoreCreateBinary(); - xTaskCreate(server_task, "server task", 8192, &exit_sema, 5, NULL); + exit_flag = false; + xTaskCreate(server_task, "server task", 8192, &exit_sema, 10, NULL); // Wait for the server to start up vTaskDelay(100 / portTICK_PERIOD_MS); - xTaskCreate(client_task, "https_get_task", 8192, &exit_sema, 5, NULL); - for(int i = 0; i < 2; i++) { - if(!xSemaphoreTake(exit_sema, 10000/portTICK_PERIOD_MS)) { - TEST_FAIL_MESSAGE("exit_sem not released by test task"); - } + /* Test with default crt bundle that doesnt contain the ca crt */ + client_task(NULL, &validate_res); + TEST_ASSERT(validate_res == ESP_CRT_VALIDATE_FAIL); + + /* Test with bundle that does contain the CA crt */ + client_task(server_cert_bundle_start, &validate_res); + TEST_ASSERT(validate_res == ESP_CRT_VALIDATE_OK); + + exit_flag = true; + + if (!xSemaphoreTake(exit_sema, 10000 / portTICK_PERIOD_MS)) { + TEST_FAIL_MESSAGE("exit_sem not released by server task"); } + vSemaphoreDelete(exit_sema); -} \ No newline at end of file +}