From f6b8408b3a7d487804733ab690d25cdca61652f5 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Thu, 19 Feb 2026 12:51:07 +0100 Subject: [PATCH] Implement OCSP responder OCSP Responder Core API: - Add new public API for creating and managing an OCSP responder - Add public wrappers for internal OCSP request/response functions - OcspRespCheck: fix check when authorized responder is loaded into CM Header Cleanup: - Remove circular dependency when including `#include ` from wolfssl/wolfcrypt/ecc.h and wolfssl/wolfcrypt/rsa.h OCSP Responder Example (examples/ocsp_responder/): - Add a command-line OCSP responder for interoperability testing with OpenSSL's `openssl ocsp` client Test Scripts (scripts/): - ocsp-responder-openssl-interop.test: Tests wolfSSL OCSP responder with `openssl ocsp` client - ocsp-stapling-with-wolfssl-responder.test: Tests wolfSSL OCSP responder when doing OCSP stapling Certificate Infrastructure (certs/ocsp/): - Add DER-format certificates and keys for OCSP testing - Update renewcerts.sh to generate DER versions Known Limitations (documented in src/ocsp.c header comment): - Single request/response per OCSP exchange only - Key-hash responder ID only (no name-based responder ID) - No singleExtensions support --- .github/workflows/os-check.yml | 1 + .gitignore | 1 + certs/ocsp/include.am | 20 + certs/ocsp/intermediate1-ca-cert.der | Bin 0 -> 1268 bytes certs/ocsp/intermediate1-ca-key.der | Bin 0 -> 1194 bytes certs/ocsp/intermediate2-ca-cert.der | Bin 0 -> 1268 bytes certs/ocsp/intermediate2-ca-key.der | Bin 0 -> 1190 bytes certs/ocsp/intermediate3-ca-cert.der | Bin 0 -> 1274 bytes certs/ocsp/intermediate3-ca-key.der | Bin 0 -> 1192 bytes certs/ocsp/ocsp-responder-cert.der | Bin 0 -> 1218 bytes certs/ocsp/ocsp-responder-key.der | Bin 0 -> 1191 bytes certs/ocsp/renewcerts-for-test.sh | 10 + certs/ocsp/renewcerts.sh | 11 + certs/ocsp/root-ca-cert.der | Bin 0 -> 1258 bytes certs/ocsp/root-ca-key.der | Bin 0 -> 1191 bytes certs/ocsp/server1-cert.der | Bin 0 -> 1266 bytes certs/ocsp/server1-key.der | Bin 0 -> 1193 bytes certs/ocsp/server2-cert.der | Bin 0 -> 1266 bytes certs/ocsp/server2-key.der | Bin 0 -> 1191 bytes certs/ocsp/server3-cert.der | Bin 0 -> 1266 bytes certs/ocsp/server3-key.der | Bin 0 -> 1191 bytes certs/ocsp/server4-cert.der | Bin 0 -> 1266 bytes certs/ocsp/server4-key.der | Bin 0 -> 1190 bytes certs/ocsp/server5-cert.der | Bin 0 -> 1272 bytes certs/ocsp/server5-key.der | Bin 0 -> 1191 bytes configure.ac | 55 +- examples/include.am | 1 + examples/ocsp_responder/include.am | 14 + examples/ocsp_responder/ocsp_responder.c | 1030 ++++++++++++++ examples/ocsp_responder/ocsp_responder.h | 39 + examples/server/server.c | 10 +- scripts/include.am | 14 + scripts/ocsp-responder-openssl-interop.test | 567 ++++++++ .../ocsp-stapling-with-wolfssl-responder.test | 764 +++++++++++ scripts/ocsp-stapling_tls13multi.test | 2 +- src/ocsp.c | 750 +++++++++- src/tls13.c | 7 +- tests/api.c | 29 +- tests/api/test_certman.c | 16 +- tests/api/test_ocsp.c | 292 ++++ tests/api/test_ocsp.h | 1 + tests/api/test_pkcs7.c | 2 +- wolfcrypt/src/asn.c | 1202 +++++++++++++++-- wolfcrypt/src/memory.c | 2 +- wolfcrypt/test/test.c | 2 +- wolfssl/internal.h | 1 + wolfssl/ocsp.h | 41 +- wolfssl/test.h | 12 +- wolfssl/wolfcrypt/asn.h | 133 +- wolfssl/wolfcrypt/asn_public.h | 6 + wolfssl/wolfcrypt/ecc.h | 3 - wolfssl/wolfcrypt/rsa.h | 3 - wolfssl/wolfcrypt/settings.h | 15 + wolfssl/wolfcrypt/types.h | 3 +- 54 files changed, 4840 insertions(+), 219 deletions(-) create mode 100644 certs/ocsp/intermediate1-ca-cert.der create mode 100644 certs/ocsp/intermediate1-ca-key.der create mode 100644 certs/ocsp/intermediate2-ca-cert.der create mode 100644 certs/ocsp/intermediate2-ca-key.der create mode 100644 certs/ocsp/intermediate3-ca-cert.der create mode 100644 certs/ocsp/intermediate3-ca-key.der create mode 100644 certs/ocsp/ocsp-responder-cert.der create mode 100644 certs/ocsp/ocsp-responder-key.der create mode 100644 certs/ocsp/root-ca-cert.der create mode 100644 certs/ocsp/root-ca-key.der create mode 100644 certs/ocsp/server1-cert.der create mode 100644 certs/ocsp/server1-key.der create mode 100644 certs/ocsp/server2-cert.der create mode 100644 certs/ocsp/server2-key.der create mode 100644 certs/ocsp/server3-cert.der create mode 100644 certs/ocsp/server3-key.der create mode 100644 certs/ocsp/server4-cert.der create mode 100644 certs/ocsp/server4-key.der create mode 100644 certs/ocsp/server5-cert.der create mode 100644 certs/ocsp/server5-key.der create mode 100644 examples/ocsp_responder/include.am create mode 100644 examples/ocsp_responder/ocsp_responder.c create mode 100644 examples/ocsp_responder/ocsp_responder.h create mode 100755 scripts/ocsp-responder-openssl-interop.test create mode 100755 scripts/ocsp-stapling-with-wolfssl-responder.test diff --git a/.github/workflows/os-check.yml b/.github/workflows/os-check.yml index 763716b027a..ac88dc11edd 100644 --- a/.github/workflows/os-check.yml +++ b/.github/workflows/os-check.yml @@ -86,6 +86,7 @@ jobs: '--enable-all CPPFLAGS=''-DNO_WOLFSSL_SERVER -DWOLFSSL_NO_CLIENT_AUTH''', '--enable-curve25519=nonblock --enable-ecc=nonblock --enable-sp=yes,nonblock CPPFLAGS="-DWOLFSSL_PUBLIC_MP -DWOLFSSL_DEBUG_NONBLOCK"', '--enable-certreq --enable-certext --enable-certgen --disable-secure-renegotiation-info CPPFLAGS="-DNO_TLS"', + '--enable-ocsp --enable-ocsp-responder', ] name: make check if: github.repository_owner == 'wolfssl' diff --git a/.gitignore b/.gitignore index 7174921634e..3c2071c9bbc 100644 --- a/.gitignore +++ b/.gitignore @@ -70,6 +70,7 @@ examples/sctp/sctp-client examples/sctp/sctp-client-dtls examples/asn1/asn1 examples/pem/pem +examples/ocsp_responder/ocsp_responder server_ready snifftest output diff --git a/certs/ocsp/include.am b/certs/ocsp/include.am index 3f79753d2b2..74d6d806847 100644 --- a/certs/ocsp/include.am +++ b/certs/ocsp/include.am @@ -14,25 +14,45 @@ EXTRA_DIST += \ certs/ocsp/openssl.cnf \ certs/ocsp/renewcerts-for-test.sh \ certs/ocsp/intermediate1-ca-key.pem \ + certs/ocsp/intermediate1-ca-key.der \ certs/ocsp/intermediate1-ca-cert.pem \ + certs/ocsp/intermediate1-ca-cert.der \ certs/ocsp/intermediate2-ca-key.pem \ + certs/ocsp/intermediate2-ca-key.der \ certs/ocsp/intermediate2-ca-cert.pem \ + certs/ocsp/intermediate2-ca-cert.der \ certs/ocsp/intermediate3-ca-key.pem \ + certs/ocsp/intermediate3-ca-key.der \ certs/ocsp/intermediate3-ca-cert.pem \ + certs/ocsp/intermediate3-ca-cert.der \ certs/ocsp/ocsp-responder-key.pem \ + certs/ocsp/ocsp-responder-key.der \ certs/ocsp/ocsp-responder-cert.pem \ + certs/ocsp/ocsp-responder-cert.der \ certs/ocsp/server1-key.pem \ + certs/ocsp/server1-key.der \ certs/ocsp/server1-cert.pem \ + certs/ocsp/server1-cert.der \ certs/ocsp/server2-key.pem \ + certs/ocsp/server2-key.der \ certs/ocsp/server2-cert.pem \ + certs/ocsp/server2-cert.der \ certs/ocsp/server3-key.pem \ + certs/ocsp/server3-key.der \ certs/ocsp/server3-cert.pem \ + certs/ocsp/server3-cert.der \ certs/ocsp/server4-key.pem \ + certs/ocsp/server4-key.der \ certs/ocsp/server4-cert.pem \ + certs/ocsp/server4-cert.der \ certs/ocsp/server5-key.pem \ + certs/ocsp/server5-key.der \ certs/ocsp/server5-cert.pem \ + certs/ocsp/server5-cert.der \ certs/ocsp/root-ca-key.pem \ + certs/ocsp/root-ca-key.der \ certs/ocsp/root-ca-cert.pem \ + certs/ocsp/root-ca-cert.der \ certs/ocsp/test-response.der \ certs/ocsp/test-response-rsapss.der \ certs/ocsp/test-response-nointern.der \ diff --git a/certs/ocsp/intermediate1-ca-cert.der b/certs/ocsp/intermediate1-ca-cert.der new file mode 100644 index 0000000000000000000000000000000000000000..f4c9e0891db46a4bd990300677907f9ad03518d8 GIT binary patch literal 1268 zcmXqLV)4w|}oNUaYENsF|p}~g2 z27(|C2Mo}qvNA4rg$hdnqov7{s?6(+*P!(N`BlNKE8V<=)E1X9Dz z!|j@vo|%`LS_IT=C}AK55@qJ$hpJO3%Fi!RaCS75H;{$9gp*NBATuv5-vOkkxHv~I zIX~AxPMp`s)X>n-*vP=d(AXpj$h9!AFtCJj4H_5H)a6PB3J{k|!d#x2SCU$ko0^iD zSdt3#fr24nKQu8aAqO}kD+6;ABR_*d6C)Q>6C)$Ty)7qV${*-%{JL|kn7io$)6~>s z4;@y^ZP_Wj{dCXn@}*_Ba@*U#dHD)IQL|HTe;0i`%ih%>F*N3z>BZDvGXxhe552l? zVw3lueG{$)1SikZQF>@9c&CDCde;-TNppfZ%I?VrZ`*OI+^#Zj&xOal2GKs>rtlhQ zrp#w~di!^0Li+CYYR!S=D*D>o3#T*+%y{uyZG~LTlOs9lY*RzM?goANl#t7)EjiIN zcL|3(TcHZW&3nhfK6eM}JeoCa>F1=UM;Y8!Yr%?%#4ixfyo2N<6{wH5otbV)v43?h3WcSpGR5;3Y$_~Zt)p39s$WK zvo!88Xx!N#QoKQCiGI`R^X^M6u5VyzvZ^Z*Rb1FOcS+-{!JEXJ7?TaSLB8N;VF6}O zHUlFz4sA9@R#tXKMiw;#6$2$0-+-}AETg2Pz)D}=(8yfR00<4OjDWxZIe7u|Gcb8E zGL*mRG{0i1r}FpGy_+-I_XVxndgYt2iIC2RiW%(lzdtFvpZ9wDapuL(MDrd^5zBrX zxzbBu!I?ve6~}+euy`pP{4?d)%nepO`r5Uk2EoT?7l-8giY;5M+Z9+KFWr~G9oGG6 z3gh3#_~O%V$9$YDP{kV2i8dLc#ffue$3u@k1{J+cbtwrDQtHjAg9v5S`E%7y4 z-yFL$<&DG3B#Zab7yC}W_`CVV!-coRkWAso1j3 zT)q6C(C6EK7j$jqJve8=_EUAazyE6;{#P^K@x`Hf|6ksoM;2Vz^Vh;e>HpQ{%7XeF RgWk21fA1(~G+BR!4*-jdyY2u0 literal 0 HcmV?d00001 diff --git a/certs/ocsp/intermediate1-ca-key.der b/certs/ocsp/intermediate1-ca-key.der new file mode 100644 index 0000000000000000000000000000000000000000..3be14d31272b472ab75e026f0d01c021ddc835ef GIT binary patch literal 1194 zcmV;b1XcSmf&``l0RRGm0RaHsw8&g{;4QKBxvdsQHJ~+RWyaw^s~)tu6SvEZyLY8_ z+HHr2_DW3?D8ku`0k2uB8SBmmjo##Zx-Q!L_|mZkGz z<;3G7^3wbv>JMUejn$b*&fHkUyMNl0EP!-_#o(|NenI^X^7Qa{c6+TR4XQ|%7cU{> zl`AGU`Ko{~{@(UpyqA9h0|5X50)hbn0Fbz{D@4mJW~1a05|tas3~J;ArGz}MD~ZX% zdPnWs25*7t1612P)pEn6250f{cRJHlVny0Idb*URin^Kae1+9TogF!<--m|Zeminf zQM4%CFUm1Oo$u%W?T{;Gv@!3k0Gh*y<`F-!jOu!J|jxKr!ybqUhwarK41*+ax zwLZ(jhn}U#{ChuuTA1AKC(cftVFH1H0P_^E=Z+{L?vc#QVdq*{FDLbBPcwJiG$AP$ zopd-y7Y(_##T>i8OZ(E5u{o-wpVw~(-FW~%q0{L?UL`NqsFu`m)uhcre#&|WL{f(Q6r3j9n4mEG@EWuHAT>Y`%OX$CaZ#Tz_us{_Hx zqx3f-lC<-8Ti*x3SYoKDl*`O1zm{H6^#Xx`0LM^T@v_}>T{F$HbU=s_u&>$)e0ZKZ zKfpx#X%EM#dfc@?i3U?=y1fKp+l+=lFcd7MO;Zm79tJH)PW!y;4Ht)?!)7}yGJQZ^ z4psBCnFn{`a0^3dN$3KEI%Yk!r03+ZIl-Yh6aMoM4b{VMmfA+v8seILPrwnN! z^8$f^0ED$v2>Nz(?CO2N!+f+`a;TtNBsVPTIBnE~jAbwB%Ji;ii-L-oSJP&>g#h6CYy8mm6sT8l0?sG$K^K<8`O I?S`CNXCL!S`~Uy| literal 0 HcmV?d00001 diff --git a/certs/ocsp/intermediate2-ca-cert.der b/certs/ocsp/intermediate2-ca-cert.der new file mode 100644 index 0000000000000000000000000000000000000000..6afb9e6d42cda7b1c9a195de6e2ae8fff59291f6 GIT binary patch literal 1268 zcmXqLV)ImE4cLGmY$;wY%7S{B=|9^JC?pFd2-DOS7^}IEX2D>S1H~o#e z{6+CL--AzO#_pRL_E~LIo)P@|)q;D{XITB-C9lw#ta5EeMG5nh6P?b9A60jpwQfIX zsQP!8&mYYxuJbalr*n1nT@9LSu!!$&mdu7J<+p=^ws$J-n^vlF`laXn#<_EUgw`@@ zy{=#udZdtdJK6N?!f79V^eC8SwlkjE5@NPM^nr5i&foiHvIczE|E6B(p>3(C--$y8 z&$|CT?GF9n68H3R*n>3Pe5WYa>}jix$2xMvpSyl%zQPl?ivKrfGBGnUFfMLlv;>BT zsR0i#z-5J5SPhsN8UF*52aw0dBE}-ZdU019!=iUutSZ|D*XkyGoFLLKZqRrHB(KcU zxW}MzXM;%b2AL)LO{dShFSWS7fu+f+u1HjIVdLB-jk5-C5^rKmHsA*Nf}e#2m_6AH zjMzA|*%(<_*%=vG)C^P%lwf=V#x}8xl9B=|eSJeCb3FqfG_*1T0t4ja1-c9-L}a;IKP&4zg%nGxw-a8yTGh}2WD`k z|9knaIo|bJn25u>NjV)RRa`U0k9vO=`n-IDak@}g`b9V2VyhS1J^YSZzBZq8^=@g- z##873ed_)DLYsIl(PW!CK5cD)OD^8du*aI?g*k`cZk~5hwnB0NU#{(W Nj$0DRNAd-gGyoLuu6_Uj literal 0 HcmV?d00001 diff --git a/certs/ocsp/intermediate2-ca-key.der b/certs/ocsp/intermediate2-ca-key.der new file mode 100644 index 0000000000000000000000000000000000000000..e67d9677116750d90b31d9c96804bc427de9e1e0 GIT binary patch literal 1190 zcmV;X1X=qqf&`)h0RRGm0RaHeAUri0Z!AQ#enQ7}v}!A8+OCK@U5dxvB zSUvmPQOY_UAC;z7g7=UAYf!uD5aCB1HFqvcD8*AoBP)XaTGR9)+YaFJb~8t_0KPi0 zBbZa`>Y&~m%mq&FW2h{XB-WUCbOYqbi9=!WCAiHyhrux={kly2DU?N?Y1d~8ijLJ% zlQ5zV-D(`Lly}=wQn!gAzLs?)%jrqqft{WBReb>}>v#ha;vjC@V>Qj8mhkwDAT?=+ z0m`&gHlP*YBYnC1zL^D3@W1ST65%~{6;8;)Fy@Q?<%?DLL|*0NR^VnWZ$er{YnH0V zT|o$6&ez@HU$>+Y=I5PEh%J(kYZMC5}U_z?JjxkM^2q~qK>=Lm}#2A8rn{)`F z079$>)f1Utu{t>Tq{rPTH2vlsfk1RpSHkBlRQ z9`d8v*$${dW+RCg{LmRNq>rL_=Tu|kyExhM(_#g3Tafh(?{Eh65R0%dz&oOuT8cDf zyC?b)ju?Rep=;QKhHwE%C9^k40)c@5^(7O%x@S#(sQ2A0((vdEHfkhHxP!8+7G1{N6uqXTa@iQH)>zE@?Mfsc z1ss=`dL5~*%^}egE0aqvHm~fC2mxs(;)_1?av+3(faC(b>}Ut|pMELNktvAyw3A4? zv=m~*H(|Ok&b!>YpR+jt{$7>_A_QfTBM~ru3oO*PH)b2#N^TdI+DH)9fp@a1+Je@` z$@Xy7NWca;>O{|123F`r5KweO_o%+`gq^RUp5LvO2NT#_!g2T)s=<;k zPaUxuQ_Tf^H7S&;{;0xQz7#w=T*ifJ`z0ZI2P%_5HWnkmSKSVS0k|DO=Yl9&V&iUl E0FU)H)&Kwi literal 0 HcmV?d00001 diff --git a/certs/ocsp/intermediate3-ca-cert.der b/certs/ocsp/intermediate3-ca-cert.der new file mode 100644 index 0000000000000000000000000000000000000000..fe6b3b5d1bbde47006eb5e0ac248d0de7f9e09da GIT binary patch literal 1274 zcmXqLV)4w|}oNUaYENsF|p}~g2 z27(|C2Mo}qvNA4rg$hdnqov7{s?6(+*P!(N`BlNKE8V<=)E1X9Dz z!|j@vo|%`LS_IT=C}AK55@qJ$hpJO3%Fi!RaCS75H;{$9gp*NBATuv5-vOkkxHv~I zIX~AxPMp`s)X>n-*vP=d(AYEz$h9!AFtCJj4H}oz)a4omY7m#p!(1NZ8s_iq>Y|XD zSCU$ko0^iDSdvPVPnsB&kb|9(m4Ug5k)Oe!iIIz`iII`v-cc5T+Cz#jgu!Xdzp+SKWM2@h(HY%)8@_2s^uNsiP@?TA(7ysP@##XU^d-_3lq@WbN;YctcXSert%lc@W^nY<3fvnR$ZJFiD=@a-N%eO;IoQau{fpKvY zqa`pzObvK|!7eMz!fL?G$oL{N+)wMy7M_fC~vBaCr)u8bR zNM4zxagRac&IXa<4Khpgn@*p1UutoE151-tU6H8b!p6Bv8fOjOgxC4{#X&zD!lJ{t8 z>M`HvpZxm9!Vhc3^Gpp{zOJ#Xh|fEGtHnjFK5BWCZDh0lQc30*`48$FtJ?%$Ud#V| z$DqWw-($+;NR{+dmY&$E`PPq=-iKWIBUIeD)7x(U@;u#lth?_d797>sbw&H7 z>Pp>MK2I|gW?5-^s*vD(#IiQQ3iRJTG;A8_EAge0eyM11x}<1`^Xw2nWTj+%*&b0z zoqx#8D{XNq2LI+CgeZ!^?Wj`piKW1I*b`>LWu;>Sn6OI)PYi^h^eu98Yx)i0&qXKg zql^OA?wR7D@Z+GZY3*6$Cqa+z`h#bI4S}*#y!X^;4{kDo_41dFEc{^M=#hyGz$Oe_K3f{$7Yt z1(x!(nnejrQSfR_h$fQhvEaPCILX)>p*GKJDHj54$2qTNG z5s(K1y6!4uDbzN^`~JP^MmQNrc1d;+#pkunzw%%^!0J(;B^YCHqp@tHCyG zP6o1Qix2|h90x|c>f~pW6p|s;PIyMow8tw$jF0{uCR0Mf>oPmzx#61jER;5`a4l44 zZ7St#g8}eB6X{ph$v!~W6FFB=0)c@5_wr0shodyJMTySb*Dt6G4+kS$^0dS+I95zWqlovywUcKg@GR{FXHAt z4x`A4`_}+!n8*7ArI1yNhVAqu+=5NF9(Dw$5`W6}w#QFj$A1(X0-t%mXBLFX0)c@5 z<^f)D#Wu+)*P7;+$KP;H3Z92fq?m#J82^!kJ4Oqc3ISXKn_ukQ0qB}K-d4P{Aj7iX zlR4tF`Ed$LN3YdffmW-ONY@>%@=4-ficVZ0R?KGA>S+(dAxq9q3ETd73&clj&3*|| zd2jzcK@t_O5r&7KMywLYIL)@)DuD;z0)c@5(Td6&aPg){IlWF|uBh_^hapG&hsWFJ z=m@?ms6=%*?>HN{w70lVM_OezEbPhgILSrtW${`0#L@kHBjzwdtXPtiKVk)s%VRN!n_ZeoRJtOkv22(A0$NeiGa)(Yeihw~{wI8*&!TQo z0)c=t(r+Ud8Yb-Ig(Mqdk2g?$7zN05QD8`o{Z}3V^x!-d0|jOn_ucp#{rYkB5_#q0 z)AR#px{VjrbKt9|$2dJ@ZqKdLQiwRr6;5iJq%{CAAkj>hEzfq?+Sz;!DmY;UflMjSOAj{D?&U%+Hq z7LyLC0J|tG7?9|ghzIlB5jojYp2)c^cfYTDf}zAh_{~Cv73S@ltn*YORoQIlby>Tb z#`9#nirSrAbC(II+tyn?l-REC|7|>#W~GPYelYm`0oBs+e-@`-wh|6ecaM&+3UR_i Gh>|2d?M@^B literal 0 HcmV?d00001 diff --git a/certs/ocsp/ocsp-responder-cert.der b/certs/ocsp/ocsp-responder-cert.der new file mode 100644 index 0000000000000000000000000000000000000000..84165c262affda8f0cf969bebbad00df3cc37959 GIT binary patch literal 1218 zcmXqLV%cZV#Jp?)GZP~d6C;ZOFB_*;n@8JsUPeZ4RtAH{>4w|}oNUaYENsF|p}~g2 z27(|C2Mo}qvNA4rg$hdnqov7{s?6(+*P!(N`BlNKE8V<=)E1X9Dz z!|j@vo|%`LS_IT=C}AK55@qJ$hpJO3%Fi!RaCS75H;{$9gp*NBATuv5-vOkkxHv~I zIX~AxPMp`s)X>n-*vP=d(AYEz$h9!AFtCJj4I1as)a4-efn6>JbGg5BaDYNkYH>k+ zUP@{aVGlGhDj|n9BP#=Q6C*!^K@%evQxhX2!;W3bTfQBx7CF4+t8C)JuX4#dTa`@$ za(~n;>SF1>S{kGKJ-Oo|d(|s$S5xiR@*9=5OmVb$G`Hkhw}FyC;L+Qs?A^5gJ&m$= zEJ-h&x^l|Z!|$Aa^uC@n_2JFb?aoIe?uj)_IHzK&cqDX9RNw+Ic?8gJIH+v?3N%PStym%vxdCLTY-QpYmP5&zQ z;&|-rZb3;Qo7GK)75~@S8*Sn!+$7zjzD97z4QJ2u8wxgl=vjA5rCr?m21{xVLs`Ik zQI6|diZj$i+NU0l&r96V-twzKimQ9Z=g0s3Tx3#9Y6?SApIS08GcqtP28ISOMA*P- zP*#|Q$$$YI@Una?Vk{y?=?^znUTXX^Uzw-CAz-Up*bW0TgT^Dkh>=xhY20JbxU)f| zc!SIm{if6B-IrQi-@wvjRaYdcxUg~VlEztsH*q&HCL0KYe4)z1W5C76q0Pp~%F52n ziJV@5xf7UP7#Wn#KRRN=ci#DscKOZV-TU35($(az^*ma4Ir~=4(k+#m{5w8~6i!gA zu~F8G-nK1m>!rWb6n$#u&Tq}^xp8Q}wS4t@^_>RMfnH+l3q%^+4P8GNn(mxzS#A`# zpnLMfY2}Vm2BJHjz4q+0St{t3A^bIweT|b{=I8m@DOaC3$-d3mZoRSM*sKi}kDmRD zKHM(oDd$lf)luI0`*{2N_J`u#X<{xldGWeY`WN<zWl`dwx&Y^*_!|epPMa?j=g5 XVkv)|)VWVS-le#h>c5c)y}u literal 0 HcmV?d00001 diff --git a/certs/ocsp/ocsp-responder-key.der b/certs/ocsp/ocsp-responder-key.der new file mode 100644 index 0000000000000000000000000000000000000000..5bd513820441ec4163cf712d250cdda4fa70d490 GIT binary patch literal 1191 zcmV;Y1X%kpf&`-i0RRGm0RaHGx+Apq!+R9Nr1c$Pq4gePxrHM%P;K~pqKX8I)pcAg z_hX3B2YTuYMKvqyAF(2|ltDP+opjcVFd`69#oNk1Ml1g1T0cQ_XLXgSl-0xTLimmA zl9l1vWw%4b7~U3ukj^AEA;eXzT2bIGEwc(*PfBw!Q0__=y*Z1m1xSannc@(q26AW0 zT+x@k$nkdg;7aVglk{dxC~?u)W&^a4FuND9{+IP0=*L}~ixC+TJgb6nc>k_HGO`G9 zvKx#itP!}_LrKrDaIx@=uG%Dr7dzMlWo!U;Q12B8*R*qJCKQL2!(VP;xQB%LfEo&m znDgWRPDC7KbbN7CW#u^n0|5X50)hbmVm7rMRmFh*{Wc2>U8 z^n$x~?yLI6qPF^}NB%G25~XLI>ABpx5-S`#ko@{7TTWkFTKd;AHuJ0sbhL76)GU@W zYCiQ{62;(nS0)c@5 z*3aEm+!v@9_o&QYi|34MzTc}FZ2(cNWYUe1j2Gi$u;_Q8kHIIP`j`-3y)!bW{1IV9 z>Co?{u_zOKnnYBNoBMZfvocWU8nv#M%DPs!#)(aj?l*WZp_u($OkzuCAR8Sm-{V`u z^b}Xf*%A>D^_{E)@dgwrOqbVp{_8Gx0)c>a!n^VWBos=xKPZ8(05;>n{XxTR<9Nno z*#%Vf4<76(MEl5rGW4V4wEe4Z5`hMZjYW`jfIpy9^#cRx`-${$u50QB#qVU`)l2@m zzM-1yFcw^@CqukWvfaET2o;+2ER$2?JTPx-V*%X!dK#Bd4$Ar2Lt@vOe7Yrja&13)^;n70*w}^H%`IHAq3#D)HWZbp@K|s7I zdnrvRScxi_gDXN1f%;|N*Ex^eiXV=(_RNn6T=$N!u`fyqsZ_(-+3Re#{E5*m z+2)o1dN!Fsg^u;9Pb?r8z}~_d#)qu?6=CV&!z;#`wT4Gx>QA`yRP`93N!Kt^Pdg+` FbOGNnKkWbj literal 0 HcmV?d00001 diff --git a/certs/ocsp/renewcerts-for-test.sh b/certs/ocsp/renewcerts-for-test.sh index 7bae1005d24..91b761376af 100755 --- a/certs/ocsp/renewcerts-for-test.sh +++ b/certs/ocsp/renewcerts-for-test.sh @@ -1,10 +1,19 @@ #!/bin/sh +# Create a minimal openssl config for `openssl req`. All DN fields come from +# -subj, but openssl req still requires a [distinguished_name] section to +# exist in the config. Using this temp file avoids relying on the system +# openssl.cnf (which may not exist when testing with a custom OpenSSL build). +WOLF_REQ_CONF=$(mktemp) +printf '[req]\ndistinguished_name=req_dn\n[req_dn]\n' > "$WOLF_REQ_CONF" +trap 'rm -f "$WOLF_REQ_CONF"' EXIT + # $1 cert, $2 name, $3 ca, $4 extensions, $5 serial update_cert(){ openssl req \ -new \ + -config "$WOLF_REQ_CONF" \ -key $1-key.pem \ -out $1-cert.csr \ -subj "/C=US/ST=Washington/L=Seattle/O=wolfSSL/OU=Engineering/CN=$2/emailAddress=info@wolfssl.com" @@ -32,6 +41,7 @@ printf '%s\n' "Using CNF: $1" openssl req \ -new \ + -config "$WOLF_REQ_CONF" \ -key root-ca-key.pem \ -out root-ca-cert.csr \ -subj "/C=US/ST=Washington/L=Seattle/O=wolfSSL/OU=Engineering/CN=wolfSSL root CA/emailAddress=info@wolfssl.com" diff --git a/certs/ocsp/renewcerts.sh b/certs/ocsp/renewcerts.sh index 003aa125399..8a33c95e4da 100755 --- a/certs/ocsp/renewcerts.sh +++ b/certs/ocsp/renewcerts.sh @@ -48,6 +48,12 @@ openssl x509 -in root-ca-cert.pem -text > tmp.pem check_result $? "" mv tmp.pem root-ca-cert.pem +echo "OCSP renew certs Step 4" +openssl x509 -in root-ca-cert.pem -outform DER -out root-ca-cert.der +check_result $? "" +openssl rsa -in root-ca-key.pem -outform DER -out root-ca-key.der +check_result $? "" + # $1 cert, $2 name, $3 ca, $4 extensions, $5 serial update_cert() { echo "Updating certificate \"$1-cert.pem\"" @@ -75,6 +81,11 @@ update_cert() { check_result $? "Step 3" mv "$1"_tmp.pem "$1"-cert.pem cat "$3"-cert.pem >> "$1"-cert.pem + + openssl x509 -in "$1"-cert.pem -outform DER -out "$1"-cert.der + check_result $? "Step 4" + openssl rsa -in "$1"-key.pem -outform DER -out "$1"-key.der + check_result $? "Step 5" } update_cert intermediate1-ca "wolfSSL intermediate CA 1" root-ca v3_ca 01 diff --git a/certs/ocsp/root-ca-cert.der b/certs/ocsp/root-ca-cert.der new file mode 100644 index 0000000000000000000000000000000000000000..9c62211790dad7578cbfab43b25ca5fd85b40379 GIT binary patch literal 1258 zcmXqLVtHoJ#C&c6GZP~d6JxRgFB_*;n@8JsUPeZ4RtAH{>4w|}oNUaYENsF|p}~g2 z27(|C2Mo}qvNA4rg$hdnqov7{s?6(+*P!(N`BlNKE8V<=)E1X9Dz z!|j@vo|%`LS_IT=C}AK55@qJ$hpJO3%Fi!RaCS75H;{$9gp*NBATuv5-vOkkxHv~I zIX~AxPMp`s)X>n-*vP=d(AXpj$h9!AFtCJj2eHeW7?qHNnUR%&xrvdV!Jvtei>Zl` zkzuvY7JZ=S-}`B{wbY&2zpS$6!-0b-sy(IK1z1mcsPpg{$!z#Hv(<9j&PCtV|L5BD zh!txp)^{%pDV(tO4C~JMqQ|-H<1@c-#rG6$)!Co+h_^(Tvk((F!u~6v-3y(375D3%b9=k zY=yVzx}NBtGaH$h85tNCH!)fQL&Vg82be%)g;`h)m>C)W0}}<1$HyYZB2v6TW{G~& z>GSSOEv|21X|k#-5>+&4JOYweW@+4G(73Y!tD1$4bC)#E8obG)3784FLB8N;VF6}J zHUlFz4sA9@R#tXKMiw;#6$2$0-+-}AETg2Pz)D}=(8yfR00<4OjDWxZIe7tdF)(>C zGDJ8sK9xSVCy+;ZQ`&vYn{pfX&bs~UmRPe|UY*|0Shrb03pn#X9hkPubFF3w+e^i3 zW^q@Y*>z(5w(qZbIZ&=OZzfCvv3V!`rZDw|LSMpV-TZ&r(X0>(QS5#wuaVr0K zU_j0phg*LAY@!Ed@m4kHOkEYV_EFjfqiZGYuc|h=UDY}NOQK8vfz>+ktWW1kd!P80 z^glnQsF@n;$o^aN=Dt^(9yhS}i1aS5y%@M&ZSGmds}1WjgdDYBPdVdy@p#(jpQ1(k zraV^6{m4KPicN+paQwHQ|(tUt;;~EmPdekM$UHZfmje PTeEiV1AV2aeX5cG52~j@ literal 0 HcmV?d00001 diff --git a/certs/ocsp/root-ca-key.der b/certs/ocsp/root-ca-key.der new file mode 100644 index 0000000000000000000000000000000000000000..0ce09e132f23775cea8cbe9df5e48b4b593354cf GIT binary patch literal 1191 zcmV;Y1X%kpf&`-i0RRGm0RaH3EVM5j1_|#@DTai8%)h32eDJ`*WF?Gsw-5!&NGA*q zG90k}nT0vFxuW+c|7|>s7IP^fe~YG6ageRd1-YLU#|l4RY4i$TjB&Lrzh>eCnlkj# z>?(;%`89|XGe=G?1$hDOWHxrW_=U{@h^)o3v01pyTJ#ACIGfs9&(3(6A4xqy)&_i1 znaG#Xvw%9HPd3EGmN0iQILL6!;b#W3D=(9^Y0raxh*O&nMj=er1vW&PU}#6>G653Q z5Oc1YG?e;oxL8knTZ)pGt^Jp|b;`ojMST}*%`fv~dJZtP3#r+rd~{%)%n1WS@eh#G zxBhIO+0A%M6|Rh1`I&(N0|5X50)hbmcSIwbd=7jUm=8$d!gHv}Hg>*DpR&f1(q<*B z(;sNfc8aC}5VR=fXpwoL+2xnm3kQu~$SzetBrqtAU(HctEy_$Xk*R`LL-E)=Qyg7$ z_Ux9rjK}V8QCBBiR4&qN|CQpETrp|gS%7xIyJFsUwI%Be-%yhtM5TkeC2|WoN+)lH z)+I;#2tgzFjq<7~9L@=O9TxUZ>lBuP!*TfgH z0Is@JeH&-1OGpye%}>+a7;%@b&K_8P@F&iG=I|8Tpqe9#9%+&EEr8BM?SS~5g*8{d zdOv3$0iy|_41!yOQlIHCfVBCo0)c@5*PMIo{rNtF5n=}4)U_AX`Xwlc${(%38M6Zc zyyjKQjS+6#ZZ&)-8jpSkN;_Rro{>|!Ol2}XDsR)aP7 zb)UuU3i_Uc9E+@^l_oZ^-K3L&Oz#G4b8)$#|By0o2CtcsaSBpzTI3g$H&{gE>eZqq8(w)0>2a88#8Co zP{?3ZVgbJZ%)duIXJK3GGPJLGk96YL80z0@`G0@B;dg}N%=rKpSKDod+=x^NS#AIR zuMb_0ym+hdt`VwF;DW%npS57O#=1*;0)c@5yB^-!ZVX!aOC!Y%iofH({hTxsi$HY! ztPm9f^cw!inp98URB~*dPf5!-&_TBwRqT*DwZwUrW%mlP;Mt1gyS3_7D)LAXX{O9Z zEW0HMBz;$ zj&w(E+~o;vkn}+J*P(lt;rjW1A3VBXwc+yufq+HMf&P_=T16Ez&dzLUtA-#sU>vdw zq-yYdg%RcAOK0Lh_s|zb0`&}P_wa#{?A!AHm~ZtH;wOvSr#A0PIVMb)sLBB@2@WW3 zr#vwDhZ+&vhXt4|J<$_TIvg+_$szct)tUX={%>$J6rCM9ZBZM(#o3!`f|?)KNF>Dy FYR7C;G#&r| literal 0 HcmV?d00001 diff --git a/certs/ocsp/server1-cert.der b/certs/ocsp/server1-cert.der new file mode 100644 index 0000000000000000000000000000000000000000..74d1ce65e9d95029a216437fc66b0a2d1eb4879b GIT binary patch literal 1266 zcmXqLVtHrK#C&Z5GZP~d6Co}qvNA4rg$hdnqov7{s?6(+*P!(N`BlNKE8V<=)E1X9Dz z!|j@vo|%`LS_IT=sAQl35@qI*gsM}>%qvMP%1uqlOe{%NaCTHMG?X`xg}aH9QA{8+ zFD>5zq`bH|M=v=)*Fa93*T~e+(9qb(z{JqlEDFfAFt9MNgmMiUXVBCSk_O@sKM0hU zmmBIKxs0&Wn;4alL!6P7fw_s1pTVGsk&CH`k&)rqw9wM?UDGpqwpo0uV%=xFEzsS( z>-+7A`zB>?XXyEKR3%U18TsxL2#jaTyk=}9eYU2C7f~@LO+;2v*&44 zv4=y?@`J)3k^@4VzZD8S%wJj+q4xc6w{?NU0_R zjXN7envYp^>NI{~x<1$Ek=B92rWBW3dg$_wvPd_Abuh!deS2<9-dVffvrthOELT`>(Nif_< z3*Ph7l5dwlw+_#s}5b;J`08rB4N=&+ysYvnl`haoHW-4cTqtdCz}o e9$L`9?eM1Mf4JU07RhgL44Eu?v!BaHP80xkYO0n1 literal 0 HcmV?d00001 diff --git a/certs/ocsp/server1-key.der b/certs/ocsp/server1-key.der new file mode 100644 index 0000000000000000000000000000000000000000..de3f3db41c1a7341bcd999644aece12bbf2dc591 GIT binary patch literal 1193 zcmV;a1XlYnf&`@k0RRGm0RaH!mQ{7nikE1Nwm9~B1->)3QAan5_uG-al54jBjPk`K zZW!s30D%GAvrH(dw}A1M&m(#-uK42*Ez@2j=i5xOU5OSKzP^Fm`x0#^OAw$E1Si4C z(0P^b=#7k7n@)0lyh$q}PWVsa4Dspdm-*j}S_-N-9YX}8r-`BBgQQEfPDO~OB3-Ph zSZ9dA)nV_w5d|ldN4qnnipuUEjlgasug%K8fJbqatf}!`BW(3cw5j35X8YyDp`WKw z(Rj@+v_CH5g7bgGr}Qz;b|*jEF4AtpM4Mx;w7H+m;2yAWuDBDK3Tis36U0Rn-60QthzW*#b$8SISpT>ozB3-wERYBoRu`%PfG zVhI?~$PKlX(U6v1DCu)yYCnjXuEcozc|l8(f$2Qec3-82_#uZ6TB`gSi$K{)DKE^l zjg>buqXeJH-6<_NYBvW0{zIFRMA7b%2&vJD!*kJsQ-vXX5$pX!Vzj_V=-ygBr2>J0 z0PI-fOll@p$A#wuTYn9)E-+?B1grU{Nr?!M<(gC;*uIjxsN5H4S8A z7Rq3iOGQU;eB(S6(Kv!OMUp*%A-;~qodSV@0Gj33y=Hb5*^?M_&%tLyW;rq*(cNWZ zv;lBErgmrAj7Qv8S4t_z3n_)NW7m~iV#Qq+@k6Sg=}ZE-H3JwbbgU)pcndnA1DarPotNn{FTzSb`(@||z|F5us5!{5vlh^;h+*bz zH3ETvUoT}#Z!E>4QGq&6?HYWO7nb=_3z}RnS(h=fjm?I&ytxwE_V*mWeM&1z1G(lK zv~1^G^}PWIzelzQh%YFn<)}8d;DGR`{V+c!O%4;du1Tv)bth)?23Uw{@8Yc86~z|E z3jehbhOFZ=k5M=>bLq^Geruwm-b1?Y_9)fa0)c@5$%xs2CjH%(C$`7to}qvNA4rg$hdnqov7{s?6(+*P!(N`BlNKE8V<=)E1X9Dz z!|j@vo|%`LS_IT=sAQl35@qI*gsM}>%qvMP%1uqlOe{%NaCTHMG?X`xg}aH9QA{8+ zFD>5zq`bH|M=v=)*Fa93*T~e+(9qb(z{JqlEDFfAFt9MNgmMiUXVBCSk_O@sKM0hU zmmBFJxs0&Wn;4alL!6P7fw_s1pTVGsk&CH`k&)q;Y1fNYdyP-VwK%6crE1wc?%c90 zeWCTc+}4YiwevP6UHPcwyx{8grL|kX=5Vsr^t^9h?{9KW{q<)r^YFvhZ@hc5pgdJ) z`5}SS>}roAH#GkF`1#lAD;)8RF0^pqHCr6M+2z*0vmqj)Y5bR2IX>wzr|ZSUKHTu^ zfNFn4dGzJ$E0kV^UFKuVwJ-^DUnO*8m)83?5?`;}-uWy4PQdGT0*gep&Jud~xRt+B zWJf_RuZ*lkcxzS@&$Yce`xQQF=zTM3Sk7zIWa@u#@BE#AU28?YpF3sU+P*xcWKOE4#OALX*G@{ZWMXDyU|ih9Xa)=s zBXByD6=q>FU;riw=1^Ha7BLo)+T6e2xc%+Dk|uurESwP7b~tMOHG{?@AbDk$#ytj& zI~zork6CraKG)}w)`7yN6qj3k3mfMyX`E%yIGv`6L&87|k~sKb3A`vjzXX`D z38oET^c!%4e8JDc!py|_z`%%&Lz|6}m6e^5kwwiw#Xt$hH(+cN%P1)+u+rBzG&0vS z07646BOowDPFuj-3`|>$4BO{u+=?&yu(owa^bSe8*BZ&1b6i%uHuKvp(02djRS$+L zea@u4Evs^`uaNp)vvBvm+SgAb%$~fuVqYiGzjH_Pgx{A|Fz`A~-fG+K>iaM6OG?)M z>aYdMIik*vjP|#E_gMCLm47_#_+o?fjlg+Rd)$t7rWV-*N~J%Fn$Gn#@}Z$TyJ6kK zOET4S-fY+Vc29j;!!194CHLa4u*E+%tU4pB`TL`vL+LYz1NS{Pv+`F~_0HvIzy0sR zJjarK0Z$Cyz0-Nd!0@?OAgn8vQ;dD>{LYWriKjT&Cf+>y=G?BFA2a_o_{zL{KXq4& fqe50|;_G|j7Fx}#bq>oGUr9VBvna9c#C0D4a9p+I literal 0 HcmV?d00001 diff --git a/certs/ocsp/server2-key.der b/certs/ocsp/server2-key.der new file mode 100644 index 0000000000000000000000000000000000000000..695720e4c31eaf663da33ca4c8c9b67834618c12 GIT binary patch literal 1191 zcmV;Y1X%kpf&`-i0RRGm0RaHUHHzq}y)(&PghOXSWhy-5iL|C?p*!wvh0)V1Zn0w2 z@ghT@)wiX6we@TX27HX~hp$gG&L`{hN;g--*Vyjlpm${~r@|0rYkNq<*eL!?PEUR> zAjC;qaX3H?HltUwMB2X1R1_6v57Pw*@-72sE?iyVu;#!ek63qG)7PjX>Q>VZ0c|)m zR!6E5#JVc)>=^ab+qwF0+)(T85TX>dniApTg%5cYxNvO^9341Ug=&Hf*1ashAn_&B#A7i5)FKL3D4HiCdIEE#$4ghS(G*QieVl30`2{>Ex+1_`xSvB!91F_F%ybIF|QJnz~D__-M%7q zt->>jf13SX!9GqaNPfM)g&gGw9?HrIpImnsi z@ibG7`h>?3($&b5V}NzT$=_Ml82=cMTW#zJ_I$RAtf&izCNxBt$0PcOtIxw7*49mH zOQR1h9*rt53VO3xh`3T{H%fP5L^cXF5Cs_XP?>~G_(g=N3!gEi&w>#C9p?E(L(-7d zz3``F3}U!iA|9w-Ikmr-Lx3-V0)c@5?b&~YFmb=Lu|NA;8{ttOP34wk)=qR zI;4JV<`|L@62OBX`@BqXP0JGYOf9=qppCI9nYt~lyA(<5^Ry1S26p#n6u&)50)c@5 z)m5frhz>%pSGf|7s@>B~c*KEyJBar}T3b`5R*UJ|C76xzMNMsi_V)NhHj%uGvXE#; zfCD@E`RdP~MN%9;F&a%-@e5cs)8)r=jaPQ5$T`N8jZFKZ zpt^&KtwhEFQR&cj>qUm!Nhcj=D|>r00*Km8Na2J9)ZHPk6w z$-8hMagkaaLjL@}GEd?>otNX7fyUY_5(Ovu2{c+^Ij#CJEP9TobIO z5|C9c?6kt{IH9GCwlb`SgUm+69waML5&08=;{Dv^83?JNg_Sg^Tv#6Oy-kcLrn$ln Fm-Z-pN)7-3 literal 0 HcmV?d00001 diff --git a/certs/ocsp/server3-cert.der b/certs/ocsp/server3-cert.der new file mode 100644 index 0000000000000000000000000000000000000000..9543d802acd57d07ec97b488bc9ff3ca29fb46a9 GIT binary patch literal 1266 zcmXqLVtHrK#C&Z5GZP~d6C=9;FB_*;n@8JsUPeZ4RtAH{g@)V)oNUaYENsF|p}~g2 z27(|C2Mo}qvNA4rg$hdnqov7{s?6(+*P!(N`BlNKE8V<=)E1X9Dz z!|j@vo|%`LS_IT=sAQl35@qI*gsM}>%qvMP%1uqlOe{%NaCTHMGL$!vg}aH9QA{8+ zFD>5zq`bH|M=v=)*Fa93*T~e+(9qb(z{JqlEDFfAFt9MNgmMiUXVBCSk_O@sKM0hU zmmBLLxs0&Wn;4alL!6P7fw_s1pTVGsk&CH`k&$7aWU8FF*Zs~ntIUW=dr2k8`uv9PFv9= zt#sb;{=F?zw)5^v{QbqDX5pVjnv30iw@jH^9r)&n@b9xP)lMy+%*4#dz__@H(F_`|+t6rnOn3##3ShtqdBEfaH}~8uu79 z?racYy|}B5VbMD+R+a67YjqPoP7vuAU)VTzN#iVo#_2Ro91;d%ki@|cOW;NM`6a-F zO)zaVF)|r&gM7iy!otkN`oO@5jYFG_k(HI5ksK*c}_#y4PW6U!(mDX`MlH#9QW zGXO$ED$49xpvmM{CeR^^9#@1$@W)}JeNB}`|U?wEXWcGE6DpPti` zB->Wos`GC$b-jE-!fS*0>Lat2Z~S}bpVx4w&(`gCL4f8>liAaj{rSr09OJogx#=|9 zdQtAloJP57k8b&IN(`5&y??jYY0{U9C4K%cIFp5D9ouFq-TUo;S<3&lDbskGryN^u zaN768&y$I*5}#|k874n@m3RKU-kH_g9!k2;aM`KJVtMCdbE9POsk^_#tggr`|Gwjf z&rS#b-Deife9!f4xyn1|zh{y^XWJ%7oYyO?{AbuDZ`9W!PGOr z|5G8lEfl7?d}hi;dmMd_{B`r1Y4L0&Mxc^i0F&b)reL|31vh`8s?8#Sa_(tMPiQ!I%fxSlF(R=rDEh zrEz!iKRJS)$QE3fpF%nV)LXybgF)W!cAdG0RlR0HME!DrXKG22Yoemndwv@cQ9w09 zvX?Yzl@eVGiV8_glS<~|IKr9v0)c@5-_mI}K8bR^$HduzoA>bSQ4%p`4S1u|^0J-s z4_ogcgLCO-op#b%kCTk-+|BaJZr=h@q2*vNqL?G5p=B}{t1cdd;g2X4OYJMm7w+o) z08CBS<(8L9>|)clFlW!*lV)FAOZvfc*Fv6fLJ&C*KDRfWxjFm*)_3B#MDe#D0)c@5 z*=^X62&Oi=T4_bhBX9u-jvB?KC{^22`Xth=*RjI}r3)Yl{u|W)8V~x&b)I8T)PYcL z)vMd!z2kOs4|`+2);F3DGf|_Z3&Z#6JAVHAN4G@{0)c@5p3x`OifeCQPE#=7Rr9tXiCH#sDyHh- z&ic(ZV2z}H3{R+2Ci0X@tV~as7YUE}@_7xV;<)DOh8`SM^?Bd*KPm@*)TxIMt%8ax zpFHWqLVe;=ir2GT_oWGsj~(}1!jsNfG4Mg>iSkMfvLN|jHIu57 z0)c>2zh0^1+WEVGwTi#pYIn5C|JHab!N9r4vgm`9_4CVfUEvHy*YzcmA+3KiW~R%2&s>472ozUb&^&s9vI?wV2~ z45_(+8KlQ^eFaoYKtP0Q*e?_gu?_3AvE1+Hf?WZxxlOJuy)49nd&lvA&x)%_KI+#j FhTbzYO*;Sp literal 0 HcmV?d00001 diff --git a/certs/ocsp/server4-cert.der b/certs/ocsp/server4-cert.der new file mode 100644 index 0000000000000000000000000000000000000000..324d19a7de8be3be1202131317e2162335285069 GIT binary patch literal 1266 zcmXqLVtHrK#C&Z5GZP~d6C;NKFB_*;n@8JsUPeZ4RtAH{g@)V)oNUaYENsF|p}~g2 z27(|C2Mo}qvNA4rg$hdnqov7{s?6(+*P!(N`BlNKE8V<=)E1X9Dz z!|j@vo|%`LS_IT=sAQl35@qI*gsM}>%qvMP%1uqlOe{%NaCTHMGL$!vg}aH9QA{8+ zFD>5zq`bH|M=v=)*Fa93*T~e+(9qb(z{JqlEDFfAFt9MNgmMiUXVBCSk_O@sKM0hU zmz(Gzxs0&Wn;4alL!6P7fw_s1pTVGsk&CH`k&$7}`>whc--xP>6HlDZD7*iUOY>iS ziRsnDf1UST@B9-P7-jG>K%_Ua!APab#bJnd|X-#@{=g{HsbX*E35;rhaXEcr$qQli<~@HkIA3meqe>E)v-J zYf>R!hFMx(&+kWFvGGM|8{Bn|Zkc>8v3`P73e(;2xgS5iC{=v4-sSPMuQHE|+wW{= z6Vyj9urQy6c(nab+ga&zVF<4nwq42+AL7|nno zVgycyvcfD(1`NO?!5k{f$0EieGV9uVKd$QTl4l}sOlPrbP08R)j4)_C0+Lr|Y20Jb zxU)fo_2RBJhDGnRSXH(QuGLNWI6&f#K>g84e|v)3kx$7>jMKLHV$nzMpjmKMn)Dj0~G@$7~g=gO)R6Nq`*pF-_Xch z&j1Jwt&BhbIc))RGcav2GHfmB2x+(3AMGdm{p;0d-+EHtUe)@o7I=H@Kefv}hb|PF z&3+d0ZQ68O8EyAz($W)loNw`CEq0sp;C;%Qj+Z|a**Fh*no53tHKoSn&$1OIFR${Y z#oc|>_i@|9omwKF)MozmOI;DLp?s_PlH2btiSN)A*}Q()agimTr~7gwSHCZaxb%IJ zjLz#R;=bEvYYCr-DZg5m(W}m2e)DX~+^XNUM8V~W gev+w-{O9`TJ@U9R~O;h`i9{Z04X!IPyhe` literal 0 HcmV?d00001 diff --git a/certs/ocsp/server4-key.der b/certs/ocsp/server4-key.der new file mode 100644 index 0000000000000000000000000000000000000000..6cb78449e134f5e349782ea851dacb179649a55a GIT binary patch literal 1190 zcmV;X1X=qqf&`)h0RRGm0RaG?2 zFzHYfge1_&a(&iyLK2G>ty<{Qgr^59o)YhB%|3$^wzMuA zi4UrD(|-dSS!MNx;n`EGw2-se=M&-OUQC-Li3kUt zs%xKQ0ov@91x19}soTc_0|5X50)hbmGHFm3tMD9$?HXfY)4;piBJi?n#;mkzETrOG z;#x^&w2m31JF@2xniCBXBEf>IV=;7&ZNs9|&eIG^6bP~x@k5jec)yX)o7jp+P|IPj zHrDIv>OwL&umE{pb5`ej)7{Z)^3m!s!XVr9)JI*xv&-q)hHS|!-Y`0rmW|!AZS$2< z%XoUMEvX@z#&yzh4PIkMXeQv^Bk*EqJ!)7WW?KF)hJ;zM(BfZE1(OEBkbYZu9~*I@ z9S)ELH8OBp1iG4a1R^JUZR5Mc53;R z*^|U$(s3e?cEp{Y`Zn(?fqf*Y*$a}XivXynRdr9r`{=-U(nYVCVEKLXqwe4{0)c@5 z!8veuaPF6my>HG;UAG!rDTLKvu2&dZa+F}n^#-gA!1{@Q1hG8n)BVCm(i5Hb1qL8P zT_Holg(mGfqjpf?^{7R1`73+<_vtB@sCyxw^g);6LI9>Hmk!(}YnA|7$u*PPowwq; zpv{BKBoA};RW)QOXzUK0K!5LjUTv}+0)c=KWm@_R#n|mU%s`gkSpBUW@X)-`K;2}{ zEvY4Isit5V6MMgZ9hCBPg&iB%xO;&u!LshY2Fa{lT>42sJa2nyn_u%g5#|~7@2G1E zFN((|K`_g1T-}W3(7Ihd{q_DxN5%|Z!8+fA#+il~hZnO|8Wodqfo(^74l$5)?D1g& zfq;M8XhuNZKtEls>v1N}P2XzY*am?@Vab14FWfEzg2(kNZL2!hfKqTZz1v3lfEoTie2 zINZCyc?+giQI9?n!1pf>l`CcE`18+6Extbv-GH}P$-IJq^NU?yy}|9>(Cn@t_=|}e zUz~MQUDgl<&gyH)A;VpZW||$5do}qvNA4rg$hdnqov7{s?6(+*P!(N`BlNKE8V<=)E1X9Dz z!|j@vo|%`LS_IT=s9~T65@qI*hpJNuat-tMc6Cw6%qvMP%1uqlOe{%NaCS75H;{$9 zjFVAJATuv5-vOklxHv~IIX~AxPMp`s)X>n-*vP=d(AYc*$h9!AFtCJj4H{?A)F+Y# z;t-z*l$V#A>LIy|u+y6um5{@ok(GhDiIJbdpox)-sfm%1VNG%F%U?ZaMRuLmL7IC- z3KlVJ;fUCF?7;qpEcvA$ME`t|T$KJZD^@8}Z;ocLT$I;E^{YFqQ_Z^;y_UWbJ3-J? zaQ)+pJu1;#Cmh&+?{pPwq4fK*>vNsmTkojs*b(w4c>m$Q4~35Xkiq6LrS;8w z{j$^k)z!_&-?;P7&#o?sNwa*^V=J;>UJ#Lao0+TW;}`U7l1lIfy~9hU^ef0{#!N4e zGHRVr`2G6Qnf@NfZU5LVfB19StAp9E3?Iz?onhy_c8NCIghN}@UwvL|muFPEsvzsJ zvF2|l&MB2kU)%U~GL>`uy|wF)$g#eIakq0Xu=_JrCOn^d_{5ixW<{Y&=K6ZaB3YG#&xT zE3-82F=*V`AhKI@o{{f^AFH}H=<$eaM>&>wv$-y8oV%oPmO{<0&cE++R9nROTE^>R-=Mr$H+6oj(ekIO^AE+hN}u_dd$C|vl;wfi;uQ~%$T0smu?$iy)#GvR-|JO5 z|NpkR&U3Szw(WiOeZO>?-y_ND-?N>|92KP=yUm`(srOmUmcv kf6=PDMK(tjQXa@0&!5<~_Q@ z8KP(TYF#2}E}SV-9$HG#C)Kz+WjBhV>l@TvkP$TzujA2-BwMwRz`x$hdIfPC?{?Rn zLq~<&B)GU#{8PWf{oxYE{2OnTs?WQQBCmtJPHSfVetwv5vAO*Dii#MLnoK8Mcx&m< z6ddhoZ7EDnQs$B*Q?M?>q?C^!94TCva2hg&ka73drI}Ai$36T#r{VdQ>cMO3G2ol~ zXg*7=q$>uH!n7yq^P@g)GIgqOYU490`$7qnd8O+-PKg3{2>sf+{1nEH!Cu>K&<9Tf zd0^+2!^rehgCP=m1ApNH0|5X50)hbm2R?ohgB;qpr`g%<6s5(p35y^E?Y$S$M)@54 zAzN928@uc8lrfy4@@p&7ER?L&QNHVw{vVtWs0)c@5;Jyd{rPn&E*uXE<5V1BI`XWgnS0g~6J>{U0 zp+}UBYvTj$kve7|EyE)2oL@H~Z(>G{UNBQH=Bh|Vh`|M0oMO0Yp|$G`sQmw%Sw^j- z!xlBXEM#CLUY8(txIRg!LR0vgR zQMMj((q6)Y+Fh#7k8|SrZ!3E)6a9U4SrRL~^Zc#$p`_giKI$#ep1;$RlEoro>VDS~ zmfOZwL?TPTI+0WF&35S{*e0Y9kSKY3$8B7P&>hWcbKsuu7ZezU* zIs!My_ea9H!kfkxtLkYhBta71oVeILv-JXjfdH3sczndza5(MYf6h(@+9EQGJzWd9 z6(7g*(*sH(^IUavU0*ZCzh&U`LPf)lI^$f;_nEm7(p<$7UfDP);js|pkk1Uf>nh-* z2AmSm1lJ +#endif + +#ifndef WOLFSSL_USER_SETTINGS + #include +#endif +#include + +#undef TEST_OPENSSL_COEXIST /* can't use this option with this example */ +#undef OPENSSL_COEXIST /* can't use this option with this example */ + +#include +#include +#include +#include +#include + +#include + +/* Check if we have the required features */ +#if defined(HAVE_OCSP) && defined(HAVE_OCSP_RESPONDER) && !defined(NO_FILESYSTEM) + +#include +#include +#include + +/* Define mygetopt variables (used by mygetopt_long in test.h) */ +int myoptind = 0; +char* myoptarg = NULL; + +#ifdef _WIN32 + #include + #include + #define SOCKET_T SOCKET + #define close(s) closesocket(s) +#else + #include + #include + #include + #include + #include + #include + #include + #define SOCKET_T int +#ifndef INVALID_SOCKET + #define INVALID_SOCKET (-1) +#endif + #define SOCKET_ERROR (-1) +#endif + +/* Default values */ +#define DEFAULT_PORT 8888 +#define MAX_REQUEST_SIZE 65536 +#define MAX_RESPONSE_SIZE 65536 +#define MAX_HTTP_HEADER 4096 +#define MAX_PATH_LEN 256 +#define MAX_CERTS 16 + +/* Simple logging macro */ +#define LOG_ERROR(...) \ + do { \ + if (got_signal) \ + fprintf(stderr, "Shutdown requested, exiting loop\n"); \ + else \ + fprintf(stderr, __VA_ARGS__); \ + } while (0) + + +#define LOG_MSG(...) \ + do { \ + printf(__VA_ARGS__); \ + fflush(stdout); \ + } while(0) + +#ifndef _WIN32 +/* Signal handler flag */ +static volatile int got_signal = 0; + +static void sig_handler(int sig) +{ + (void)sig; + got_signal = 1; +} +#endif + +/* Index file entry structure */ +typedef struct IndexEntry { + char status; /* V=valid, R=revoked, E=expired */ + time_t expirationTime; + time_t revocationTime; + char serial[64]; + char filename[256]; + char subject[512]; + struct IndexEntry* next; +} IndexEntry; + +/* Program options */ +typedef struct { + word16 port; + const char* certFile; + const char* responderCertFile; + const char* keyFile; + const char* indexFile; + const char* readyFile; + int nrequests; + int verbose; + int sendCerts; +} OcspResponderOptions; + +/* Usage help */ + +/* Usage help */ +static void Usage(void) +{ + LOG_MSG("OCSP Responder Example\n\n"); + LOG_MSG("Usage: ocsp_responder [options]\n\n"); + LOG_MSG("Options:\n"); + LOG_MSG(" -? Help\n"); + LOG_MSG(" -p Port (default %d)\n", DEFAULT_PORT); + LOG_MSG(" -c CA certificate (issuer)\n"); + LOG_MSG(" -r Responder certificate (for authorized responder)\n"); + LOG_MSG(" -k Signing private key\n"); + LOG_MSG(" -i Index file for cert status\n"); + LOG_MSG(" -R Ready file for external monitor\n"); + LOG_MSG(" -n Exit after n requests\n"); + LOG_MSG(" -v Verbose\n"); + LOG_MSG(" -x Exclude certs from response\n"); +} + +/* Load file into buffer, auto-detect PEM vs DER */ +static int LoadFile(const char* filename, byte** buf, word32* bufSz, int* isPem) +{ + int ret; + size_t sz = 0; + + ret = load_file(filename, buf, &sz); + if (ret != 0) { + LOG_ERROR("Error opening file: %s\n", filename); + return ret; + } + + /* Check if PEM format by looking for -----BEGIN */ + if (isPem) { + *isPem = (XSTRSTR((char*)*buf, "-----BEGIN") != NULL) ? 1 : 0; + } + + *bufSz = (word32)sz; + return 0; +} + +/* Convert PEM to DER */ +static int ConvertPemToDer(const byte* pem, word32 pemSz, byte** der, word32* derSz, int type) +{ + int ret; + DerBuffer* derBuf = NULL; + + ret = wc_PemToDer(pem, pemSz, type, &derBuf, NULL, NULL, NULL); + if (ret != 0 || derBuf == NULL) { + return ret; + } + + *der = (byte*)XMALLOC(derBuf->length, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (*der == NULL) { + wc_FreeDer(&derBuf); + return MEMORY_E; + } + + XMEMCPY(*der, derBuf->buffer, derBuf->length); + *derSz = derBuf->length; + wc_FreeDer(&derBuf); + + return 0; +} + +/* Load certificate in DER format */ +static int LoadCertDer(const char* filename, byte** der, word32* derSz) +{ + byte* buf = NULL; + word32 bufSz = 0; + int isPem = 0; + int ret; + + ret = LoadFile(filename, &buf, &bufSz, &isPem); + if (ret != 0) { + return ret; + } + + if (isPem) { + ret = ConvertPemToDer(buf, bufSz, der, derSz, CERT_TYPE); + XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); + return ret; + } + else { + *der = buf; + *derSz = bufSz; + return 0; + } +} + +/* Load private key in DER format */ +static int LoadKeyDer(const char* filename, byte** der, word32* derSz) +{ + byte* buf = NULL; + word32 bufSz = 0; + int isPem = 0; + int ret; + + ret = LoadFile(filename, &buf, &bufSz, &isPem); + if (ret != 0) { + return ret; + } + + if (isPem) { + ret = ConvertPemToDer(buf, bufSz, der, derSz, PRIVATEKEY_TYPE); + XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); + return ret; + } + else { + *der = buf; + *derSz = bufSz; + return 0; + } +} + +/* Free index entries */ +static void FreeIndexEntries(IndexEntry* head) +{ + while (head) { + IndexEntry* next = head->next; + XFREE(head, NULL, DYNAMIC_TYPE_TMP_BUFFER); + head = next; + } +} + +/* Parse OpenSSL index.txt file + * Format: status\texpiration\trevocation\tserial\tfilename\tsubject + * V = valid, R = revoked, E = expired + */ +static IndexEntry* ParseIndexFile(const char* filename) +{ + XFILE f = XBADFILE; + char line[1024]; + IndexEntry* head = NULL; + IndexEntry* tail = NULL; + IndexEntry* entry = NULL; + IndexEntry* ret = NULL; + + if (filename == NULL) { + LOG_ERROR("Invalid filename parameter\n"); + goto cleanup; + } + + f = XFOPEN(filename, "r"); + if (f == XBADFILE) { + LOG_ERROR("Error opening index file: %s\n", filename); + goto cleanup; + } + + while (XFGETS(line, sizeof(line), f) != NULL) { + char* p = line; + char* field; + int fieldNum = 0; + + /* Skip empty lines */ + if (line[0] == '\n' || line[0] == '\r' || line[0] == '\0') + continue; + + entry = (IndexEntry*)XMALLOC(sizeof(IndexEntry), NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (entry == NULL) { + LOG_ERROR("Memory allocation failed for index entry\n"); + goto cleanup; + } + XMEMSET(entry, 0, sizeof(IndexEntry)); + + /* Parse tab-separated fields */ + while ((field = XSTRSEP(&p, "\t")) != NULL && fieldNum < 6) { + switch (fieldNum) { + case 0: /* Status */ + entry->status = field[0]; + break; + case 1: /* Expiration time (YYMMDDHHMMSSZ format) */ + /* Parse if needed */ + break; + case 2: /* Revocation time */ + /* Parse if needed, empty for valid certs */ + if (field[0] != '\0') { + struct tm tm; + XMEMSET(&tm, 0, sizeof(tm)); + /* Format: YYMMDDHHMMSSZ */ + if (XSTRLEN(field) >= 12) { + int year = (field[0] - '0') * 10 + (field[1] - '0'); + tm.tm_year = (year < 50) ? (100 + year) : year; + tm.tm_mon = (field[2] - '0') * 10 + (field[3] - '0') - 1; + tm.tm_mday = (field[4] - '0') * 10 + (field[5] - '0'); + tm.tm_hour = (field[6] - '0') * 10 + (field[7] - '0'); + tm.tm_min = (field[8] - '0') * 10 + (field[9] - '0'); + tm.tm_sec = (field[10] - '0') * 10 + (field[11] - '0'); + entry->revocationTime = XMKTIME(&tm); + } + } + break; + case 3: /* Serial (hex) */ + XSTRNCPY(entry->serial, field, sizeof(entry->serial) - 1); + break; + case 4: /* Filename */ + XSTRNCPY(entry->filename, field, sizeof(entry->filename) - 1); + break; + case 5: /* Subject */ + /* Remove trailing newline */ + { + size_t len = XSTRLEN(field); + if (len > 0 && (field[len-1] == '\n' || field[len-1] == '\r')) + field[len-1] = '\0'; + if (len > 1 && (field[len-2] == '\n' || field[len-2] == '\r')) + field[len-2] = '\0'; + } + XSTRNCPY(entry->subject, field, sizeof(entry->subject) - 1); + break; + } + fieldNum++; + } + + /* Validate that we got at least status and serial */ + if (fieldNum < 4 || entry->serial[0] == '\0' || + entry->revocationTime == (time_t)-1) { + LOG_ERROR("Invalid index entry - missing required fields\n"); + XFREE(entry, NULL, DYNAMIC_TYPE_TMP_BUFFER); + entry = NULL; + goto cleanup; + } + + /* Add to list */ + entry->next = NULL; + if (tail) { + tail->next = entry; + tail = entry; + } + else { + head = tail = entry; + } + entry = NULL; + } + + /* Success */ + ret = head; + head = NULL; + +cleanup: + if (f != XBADFILE) + XFCLOSE(f); + if (entry != NULL) + XFREE(entry, NULL, DYNAMIC_TYPE_TMP_BUFFER); + FreeIndexEntries(head); + return ret; +} + +/* Lookup certificate status by serial number */ +static int PopulateResponderFromIndex(OcspResponder* responder, IndexEntry* index, + DecodedCert* caCert) +{ + IndexEntry* entry; + const char* caSubject; + word32 caSubjSz; + int count = 0; + int ret; + + if (responder == NULL || index == NULL || caCert == NULL) { + return BAD_FUNC_ARG; + } + + caSubject = wc_GetDecodedCertSubject(caCert, &caSubjSz); + if (caSubject == NULL || caSubjSz == 0) { + LOG_ERROR("Could not get CA subject\n"); + return BAD_FUNC_ARG; + } + + for (entry = index; entry != NULL; entry = entry->next) { + byte serial[64]; + word32 serialLen = 0; + enum Ocsp_Cert_Status status; + time_t revTime = 0; + enum WC_CRL_Reason revReason = CRL_REASON_UNSPECIFIED; + word32 validity = 86400; + char* p = entry->serial; + word32 i; + + /* Convert hex string to bytes */ + serialLen = (word32)XSTRLEN(entry->serial) / 2; + if (serialLen == 0 || serialLen > sizeof(serial)) { + continue; + } + + for (i = 0; i < serialLen; i++) { + int high = (p[i*2] >= 'A') ? (p[i*2] - 'A' + 10) : + (p[i*2] >= 'a') ? (p[i*2] - 'a' + 10) : (p[i*2] - '0'); + int low = (p[i*2+1] >= 'A') ? (p[i*2+1] - 'A' + 10) : + (p[i*2+1] >= 'a') ? (p[i*2+1] - 'a' + 10) : (p[i*2+1] - '0'); + serial[i] = (byte)((high << 4) | low); + } + + /* Determine status */ + if (entry->status == 'V') { + status = CERT_GOOD; + } + else if (entry->status == 'R') { + status = CERT_REVOKED; + revTime = entry->revocationTime; + revReason = CRL_REASON_UNSPECIFIED; + validity = 0; + } + else { + status = CERT_UNKNOWN; + validity = 0; + } + + ret = wc_OcspResponder_SetCertStatus(responder, + caSubject, caSubjSz, + serial, serialLen, + status, revTime, revReason, validity); + if (ret == 0) { + count++; + } + } + + return count; +} + +/* Receive a complete HTTP request, looping until the full body arrives */ +static int RecvHttpRequest(SOCKET_T fd, byte* buf, int bufSz) +{ + int totalLen = 0; + int contentLen = 0; + int headerSz = 0; + + while (totalLen < bufSz - 1) { + int n = (int)recv(fd, (char*)buf + totalLen, + (size_t)(bufSz - 1 - totalLen), 0); + if (n <= 0) + break; + totalLen += n; + buf[totalLen] = '\0'; + + /* Once we find end-of-headers, parse Content-Length */ + if (headerSz == 0) { + const char* hdrEnd = XSTRSTR((char*)buf, "\r\n\r\n"); + if (hdrEnd != NULL) { + const char* cl; + headerSz = (int)(hdrEnd + 4 - (char*)buf); + cl = XSTRSTR((char*)buf, "Content-Length:"); + if (cl == NULL) + cl = XSTRSTR((char*)buf, "content-length:"); + if (cl != NULL) + contentLen = atoi(cl + 15); + } + } + /* Check if we have the full body */ + if (headerSz > 0 && totalLen >= headerSz + contentLen) + break; + } + return totalLen; +} + +/* Parse HTTP request and extract OCSP request body */ +static int ParseHttpRequest(const byte* httpReq, int httpReqSz, + const byte** body, int* bodySz, + char* path, int pathSz) +{ + /* Initialize outputs */ + *body = NULL; + *bodySz = 0; + if (path && pathSz > 0) + path[0] = '\0'; + + /* Check for POST method */ + if (XSTRNCMP((char*)httpReq, "POST ", 5) == 0) { + const char* contentLen; + /* Extract path */ + const char* pathStart = (const char*)httpReq + 5; + const char* pathEnd = XSTRSTR(pathStart, " "); + if (pathEnd && path && pathSz > 0) { + int len = (int)(pathEnd - pathStart); + if (len >= pathSz) len = pathSz - 1; + XMEMCPY(path, pathStart, (size_t)len); + path[len] = '\0'; + } + + /* Find Content-Length */ + contentLen = XSTRSTR((char*)httpReq, "Content-Length:"); + if (contentLen == NULL) { + contentLen = XSTRSTR((char*)httpReq, "content-length:"); + } + if (contentLen) { + *bodySz = atoi(contentLen + 15); + } + + /* Find body (after \r\n\r\n) */ + *body = (const byte*)XSTRSTR((char*)httpReq, "\r\n\r\n"); + if (*body) { + *body += 4; + /* Use Content-Length if available, otherwise use remaining data */ + if (*bodySz == 0) { + *bodySz = httpReqSz - (int)(*body - httpReq); + } + return 0; + } + } + /* Check for GET method with URL-encoded request */ + else if (XSTRNCMP((char*)httpReq, "GET ", 4) == 0) { + /* GET requests have base64-encoded OCSP request in URL */ + /* For simplicity, we'll require POST for now */ + LOG_ERROR("GET method not fully supported, use POST\n"); + return -1; + } + + return -1; +} + +/* Send HTTP response with OCSP response body */ +static int SendHttpResponse(SOCKET_T clientfd, const byte* ocspResp, int ocspRespSz) +{ + char header[MAX_HTTP_HEADER]; + int headerLen; + int sent; + + headerLen = snprintf(header, sizeof(header), + "HTTP/1.0 200 OK\r\n" + "Content-Type: application/ocsp-response\r\n" + "Content-Length: %d\r\n" + "Connection: close\r\n" + "\r\n", ocspRespSz); + + /* Send header */ + sent = (int)send(clientfd, header, (size_t)headerLen, 0); + if (sent != headerLen) { + LOG_ERROR("Failed to send HTTP header\n"); + return -1; + } + + /* Send body */ + sent = (int)send(clientfd, (const char*)ocspResp, (size_t)ocspRespSz, 0); + if (sent != ocspRespSz) { + LOG_ERROR("Failed to send OCSP response\n"); + return -1; + } + + return 0; +} + +/* Send HTTP error response */ +static int SendHttpError(SOCKET_T clientfd, int statusCode, const char* statusMsg) +{ + char response[512]; + int len; + int sent; + + len = snprintf(response, sizeof(response), + "HTTP/1.0 %d %s\r\n" + "Content-Type: text/plain\r\n" + "Content-Length: %d\r\n" + "Connection: close\r\n" + "\r\n" + "%s", statusCode, statusMsg, (int)XSTRLEN(statusMsg), statusMsg); + + sent = (int)send(clientfd, response, (size_t)len, 0); + return (sent == len) ? 0 : -1; +} + +/* Map error codes to OCSP response status */ +static enum Ocsp_Response_Status MapErrorToOcspStatus(int err) +{ + switch (err) { + case ASN_PARSE_E: + return OCSP_MALFORMED_REQUEST; + case ASN_SIG_HASH_E: + /* Unsupported hash algorithm */ + return OCSP_INTERNAL_ERROR; + case ASN_NO_SIGNER_E: + return OCSP_UNAUTHORIZED; + case OCSP_CERT_UNKNOWN: + return OCSP_UNAUTHORIZED; + default: + return OCSP_INTERNAL_ERROR; + } +} + +/* Main OCSP responder function */ +THREAD_RETURN WOLFSSL_THREAD ocsp_responder_test(void* args) +{ + func_args* myargs = (func_args*)args; + int argc = myargs->argc; + char** argv = myargs->argv; + int ret; + int ch; + + OcspResponderOptions opts; + OcspResponder* responder = NULL; + IndexEntry* indexEntries = NULL; + DecodedCert caCert; + SOCKET_T sockfd = INVALID_SOCKET; + SOCKET_T clientfd = INVALID_SOCKET; + + byte* caCertDer = NULL; + word32 caCertDerSz = 0; + byte* responderCertDer = NULL; + word32 responderCertDerSz = 0; + byte* caKeyDer = NULL; + word32 caKeyDerSz = 0; + + byte httpBuf[MAX_REQUEST_SIZE]; + byte respBuf[MAX_RESPONSE_SIZE]; + word32 respSz; + + int requestsProcessed = 0; + word32 caSubjectSz = 0; + + static const struct mygetopt_long_config long_options[] = { + { "help", 0, '?' }, + { NULL, 0, 0 } + }; + + /* Initialize options */ + XMEMSET(&opts, 0, sizeof(opts)); + opts.port = DEFAULT_PORT; + opts.nrequests = 0; + opts.verbose = 0; + opts.sendCerts = 1; + opts.readyFile = NULL; + + /* Parse command line arguments */ + while ((ch = mygetopt_long(argc, argv, "?p:c:r:k:i:R:n:vx", long_options, 0)) != -1) { + switch (ch) { + case '?': + Usage(); + ret = 0; + goto cleanup; + case 'p': + opts.port = (word16)atoi(myoptarg); + break; + case 'c': + opts.certFile = myoptarg; + break; + case 'r': + opts.responderCertFile = myoptarg; + break; + case 'k': + opts.keyFile = myoptarg; + break; + case 'i': + opts.indexFile = myoptarg; + break; + case 'R': + opts.readyFile = myoptarg; + break; + case 'n': + opts.nrequests = atoi(myoptarg); + break; + case 'v': + opts.verbose = 1; + break; + case 'x': + opts.sendCerts = 0; + break; + default: + Usage(); + ret = MY_EX_USAGE; + goto cleanup; + } + } + + /* Validate required options */ + if (opts.certFile == NULL) { + LOG_ERROR("Error: CA certificate required (-c)\n"); + Usage(); + ret = MY_EX_USAGE; + goto cleanup; + } + if (opts.keyFile == NULL) { + LOG_ERROR("Error: CA key required (-k)\n"); + Usage(); + ret = MY_EX_USAGE; + goto cleanup; + } + + /* Load CA certificate */ + ret = LoadCertDer(opts.certFile, &caCertDer, &caCertDerSz); + if (ret != 0) { + LOG_ERROR("Error loading CA certificate: %s\n", opts.certFile); + ret = -1; + goto cleanup; + } + if (opts.verbose) { + LOG_MSG("Loaded CA certificate: %s (%d bytes)\n", opts.certFile, caCertDerSz); + } + + /* Load responder certificate if provided */ + if (opts.responderCertFile != NULL) { + ret = LoadCertDer(opts.responderCertFile, &responderCertDer, &responderCertDerSz); + if (ret != 0) { + LOG_ERROR("Error loading responder certificate: %s\n", opts.responderCertFile); + ret = -1; + goto cleanup; + } + if (opts.verbose) { + LOG_MSG("Loaded responder certificate: %s (%d bytes)\n", + opts.responderCertFile, responderCertDerSz); + } + } + + /* Load CA key */ + ret = LoadKeyDer(opts.keyFile, &caKeyDer, &caKeyDerSz); + if (ret != 0) { + LOG_ERROR("Error loading signing key: %s\n", opts.keyFile); + ret = -1; + goto cleanup; + } + if (opts.verbose) { + LOG_MSG("Loaded signing key: %s (%d bytes)\n", opts.keyFile, caKeyDerSz); + } + + /* Parse CA certificate to get subject */ + XMEMSET(&caCert, 0, sizeof(caCert)); + wc_InitDecodedCert(&caCert, caCertDer, caCertDerSz, NULL); + ret = wc_ParseCert(&caCert, CERT_TYPE, 0, NULL); + if (ret != 0) { + LOG_ERROR("Error parsing CA certificate: %d\n", ret); + ret = -1; + goto cleanup; + } + (void)wc_GetDecodedCertSubject(&caCert, &caSubjectSz); + (void)caSubjectSz; /* Not used in current implementation */ + + /* Load index file if provided */ + if (opts.indexFile) { + indexEntries = ParseIndexFile(opts.indexFile); + if (indexEntries == NULL) { + LOG_ERROR("Warning: Could not parse index file: %s\n", opts.indexFile); + } + else if (opts.verbose) { + LOG_MSG("Loaded index file: %s\n", opts.indexFile); + } + } + + /* Create OCSP responder */ + responder = wc_OcspResponder_new(NULL, opts.sendCerts); + if (responder == NULL) { + LOG_ERROR("Error creating OCSP responder\n"); + ret = -1; + goto cleanup; + } + + /* Add signer to responder */ + if (opts.responderCertFile != NULL) { + /* Authorized responder: use responder cert as signer, CA cert as issuer */ + ret = wc_OcspResponder_AddSigner(responder, responderCertDer, responderCertDerSz, + caKeyDer, caKeyDerSz, caCertDer, caCertDerSz); + if (ret != 0) { + LOG_ERROR("Error adding authorized responder to responder: %d\n", ret); + goto cleanup; + } + if (opts.verbose) { + LOG_MSG("Added authorized responder with CA issuer\n"); + } + } + else { + /* CA as signer (self-signed) */ + ret = wc_OcspResponder_AddSigner(responder, caCertDer, caCertDerSz, + caKeyDer, caKeyDerSz, NULL, 0); + if (ret != 0) { + LOG_ERROR("Error adding CA to responder: %d\n", ret); + goto cleanup; + } + if (opts.verbose) { + LOG_MSG("Added CA to responder\n"); + } + } + + /* Populate responder with certificate statuses from index */ + if (indexEntries != NULL) { + int statusCount = PopulateResponderFromIndex(responder, indexEntries, &caCert); + if (statusCount < 0) { + LOG_ERROR("Error populating responder from index: %d\n", statusCount); + } + else if (opts.verbose) { + LOG_MSG("Populated responder with %d certificate statuses\n", statusCount); + } + } + + /* Create and listen on server socket */ + tcp_listen(&sockfd, &opts.port, 1, 0, 0); + + /* Write ready file if requested */ + if (opts.readyFile != NULL) { + XFILE rf = XFOPEN(opts.readyFile, "w"); + if (rf != NULL) { + fprintf(rf, "%d\n", (int)opts.port); + fclose(rf); + if (opts.verbose) { + LOG_MSG("Ready file created: %s\n", opts.readyFile); + } + } + else { + LOG_ERROR("Warning: Failed to create ready file: %s\n", opts.readyFile); + } + } + +#ifndef _WIN32 + /* Install signal handlers for clean shutdown */ + { + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = sig_handler; + /* Do NOT set SA_RESTART so accept() is interrupted */ + sigaction(SIGTERM, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + } + if (opts.verbose) { + LOG_MSG("Signal handlers installed\n"); + } +#endif + + /* Main loop */ + while ((opts.nrequests == 0 || requestsProcessed < opts.nrequests) +#ifndef _WIN32 + && !got_signal +#endif + ) { + struct sockaddr_in clientAddr; + socklen_t clientAddrLen = sizeof(clientAddr); + int recvLen; + const byte* ocspReq; + int ocspReqSz; + char path[MAX_PATH_LEN]; + + /* Accept connection */ + clientfd = accept(sockfd, (struct sockaddr*)&clientAddr, &clientAddrLen); + if (clientfd == INVALID_SOCKET) { + LOG_ERROR("accept() failed\n"); + continue; + } + + if (opts.verbose) { + LOG_MSG("Connection from %s:%d\n", + inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port)); + } + + /* Receive HTTP request */ + recvLen = RecvHttpRequest(clientfd, httpBuf, (int)sizeof(httpBuf)); + if (recvLen <= 0) { + LOG_ERROR("recv() failed\n"); + close(clientfd); + continue; + } + httpBuf[recvLen] = '\0'; + + if (opts.verbose) { + LOG_MSG("Received %d bytes\n", recvLen); + } + + /* Parse HTTP request */ + ret = ParseHttpRequest(httpBuf, recvLen, &ocspReq, &ocspReqSz, path, sizeof(path)); + if (ret != 0 || ocspReq == NULL || ocspReqSz <= 0) { + LOG_ERROR("Invalid HTTP request\n"); + SendHttpError(clientfd, 400, "Bad Request"); + close(clientfd); + continue; + } + + if (opts.verbose) { + LOG_MSG("OCSP request: %d bytes, path: %s\n", ocspReqSz, path); + } + + /* Process OCSP request and generate response */ + respSz = sizeof(respBuf); + ret = wc_OcspResponder_WriteResponse(responder, ocspReq, (word32)ocspReqSz, + respBuf, &respSz); + + if (ret != 0) { + enum Ocsp_Response_Status errStatus; + LOG_ERROR("Error generating OCSP response: %d\n", ret); + + /* Generate appropriate OCSP error response */ + errStatus = MapErrorToOcspStatus(ret); + respSz = sizeof(respBuf); + ret = wc_OcspResponder_WriteErrorResponse(errStatus, respBuf, &respSz); + + if (ret != 0) { + /* If we can't even encode an error response, send HTTP error */ + LOG_ERROR("Error encoding OCSP error response: %d\n", ret); + SendHttpError(clientfd, 500, "Internal Server Error"); + close(clientfd); + continue; + } + + if (opts.verbose) { + LOG_MSG("Generated OCSP error response (status=%d): %d bytes\n", + errStatus, respSz); + } + } + + if (opts.verbose) { + LOG_MSG("Generated OCSP response: %d bytes\n", respSz); + } + + /* Send HTTP response */ + ret = SendHttpResponse(clientfd, respBuf, (int)respSz); + if (ret != 0) { + LOG_ERROR("Error sending response\n"); + } + + close(clientfd); + clientfd = INVALID_SOCKET; + requestsProcessed++; + + if (opts.verbose) { + LOG_MSG("Processed request %d\n", requestsProcessed); + } + } + + ret = 0; + +cleanup: + if (clientfd != INVALID_SOCKET) + close(clientfd); + if (sockfd != INVALID_SOCKET) + close(sockfd); + + wc_FreeDecodedCert(&caCert); + + if (responder) + wc_OcspResponder_free(responder); + if (indexEntries) + FreeIndexEntries(indexEntries); + if (caCertDer) + XFREE(caCertDer, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (responderCertDer) + XFREE(responderCertDer, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (caKeyDer) + XFREE(caKeyDer, NULL, DYNAMIC_TYPE_TMP_BUFFER); + +#ifdef _WIN32 + WSACleanup(); +#endif + + myargs->return_code = ret; +#ifndef WOLFSSL_THREAD_VOID_RETURN + return (THREAD_RETURN)0; +#else + return; +#endif +} + +#ifndef NO_MAIN_DRIVER +int main(int argc, char** argv) +{ + func_args args; + int ret; + +#ifdef HAVE_WNR + if (wc_InitNetRandom(wnrConfigFile, NULL, 5000) != 0) { + err_sys("Whitewood netRandom global config failed"); + } +#endif + + ret = wolfSSL_Init(); + if (ret != WOLFSSL_SUCCESS) { + err_sys("wolfSSL_Init failed"); + } + + args.argc = argc; + args.argv = argv; + args.return_code = 0; + + ocsp_responder_test(&args); + + wolfSSL_Cleanup(); + +#ifdef HAVE_WNR + if (wc_FreeNetRandom() < 0) + err_sys("Failed to free netRandom context"); +#endif + + return args.return_code; +} +#endif /* !NO_MAIN_DRIVER */ + +#else /* HAVE_OCSP && HAVE_OCSP_RESPONDER && !NO_FILESYSTEM */ + +#ifndef NO_MAIN_DRIVER +int main(int argc, char** argv) +{ + (void)argc; + (void)argv; + printf("OCSP Responder requires HAVE_OCSP, HAVE_OCSP_RESPONDER, and filesystem support\n"); + return 0; +} +#endif + +THREAD_RETURN WOLFSSL_THREAD ocsp_responder_test(void* args) +{ + func_args* myargs = (func_args*)args; + printf("OCSP Responder requires HAVE_OCSP, HAVE_OCSP_RESPONDER, and filesystem support\n"); + myargs->return_code = 0; + WOLFSSL_RETURN_FROM_THREAD(0); +} + +#endif /* HAVE_OCSP && HAVE_OCSP_RESPONDER && !NO_FILESYSTEM */ diff --git a/examples/ocsp_responder/ocsp_responder.h b/examples/ocsp_responder/ocsp_responder.h new file mode 100644 index 00000000000..60acc44d430 --- /dev/null +++ b/examples/ocsp_responder/ocsp_responder.h @@ -0,0 +1,39 @@ +/* ocsp_responder.h + * + * Copyright (C) 2006-2025 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#ifndef WOLFSSL_OCSP_RESPONDER_EXAMPLE_H +#define WOLFSSL_OCSP_RESPONDER_EXAMPLE_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +THREAD_RETURN WOLFSSL_THREAD ocsp_responder_test(void* args); + +#ifdef __cplusplus +} +#endif + +#endif /* WOLFSSL_OCSP_RESPONDER_EXAMPLE_H */ diff --git a/examples/server/server.c b/examples/server/server.c index 21f5380e73a..2f4a573a8f4 100644 --- a/examples/server/server.c +++ b/examples/server/server.c @@ -3621,14 +3621,16 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args) err = SSL_get_error(ssl, 0); LOG_ERROR("SSL_accept error %d, %s\n", err, ERR_error_string((unsigned long)err, buffer)); - if (!exitWithRet) { - err_sys_ex(runWithErrors, "SSL_accept failed"); - } else { - /* cleanup */ + if (exitWithRet || !runWithErrors) { + /* cleanup before exit */ SSL_free(ssl); ssl = NULL; SSL_CTX_free(ctx); ctx = NULL; CloseSocket(clientfd); CloseSocket(sockfd); + } + if (!exitWithRet) { + err_sys_ex(runWithErrors, "SSL_accept failed"); + } else { ((func_args*)args)->return_code = err; goto exit; } diff --git a/scripts/include.am b/scripts/include.am index b2dd157ea84..f7a0bb37c8b 100644 --- a/scripts/include.am +++ b/scripts/include.am @@ -58,6 +58,20 @@ endif endif +if BUILD_OCSP_RESPONDER +if BUILD_OCSP_STAPLING +dist_noinst_SCRIPTS+= scripts/ocsp-stapling-with-wolfssl-responder.test +scripts/ocsp-stapling-with-wolfssl-responder.log: scripts/ocsp-stapling-with-ca-as-responder.log +dist_noinst_SCRIPTS+= scripts/ocsp-responder-openssl-interop.test +scripts/ocsp-responder-openssl-interop.log: scripts/ocsp-stapling-with-wolfssl-responder.log +testsuite/testsuite.log: scripts/ocsp-responder-openssl-interop.log +else +dist_noinst_SCRIPTS+= scripts/ocsp-responder-openssl-interop.test +scripts/ocsp-responder-openssl-interop.log: scripts/ocsp.log +testsuite/testsuite.log: scripts/ocsp-responder-openssl-interop.log +endif +endif + endif if BUILD_PSK diff --git a/scripts/ocsp-responder-openssl-interop.test b/scripts/ocsp-responder-openssl-interop.test new file mode 100755 index 00000000000..ed62c199512 --- /dev/null +++ b/scripts/ocsp-responder-openssl-interop.test @@ -0,0 +1,567 @@ +#!/usr/bin/env bash + +# ocsp-responder-openssl-interop.test +# +# Interop test: wolfSSL ocsp_responder serving OCSP responses to openssl ocsp +# client queries. Tests every certificate entry in each index file. +# +# Requires: WOLFSSL_OPENSSL_TEST, HAVE_OCSP, HAVE_OCSP_RESPONDER +# Uses: examples/ocsp_responder/ocsp_responder as the OCSP responder +# openssl ocsp as the client + +if ! test -n "$WOLFSSL_OPENSSL_TEST"; then + echo "WOLFSSL_OPENSSL_TEST NOT set, won't run" + exit 77 +fi + +# if we can, isolate the network namespace to eliminate port collisions. +if [[ -n "$NETWORK_UNSHARE_HELPER" ]]; then + if [[ -z "$NETWORK_UNSHARE_HELPER_CALLED" ]]; then + export NETWORK_UNSHARE_HELPER_CALLED=yes + exec "$NETWORK_UNSHARE_HELPER" "$0" "$@" || exit $? + fi +elif [ "${AM_BWRAPPED-}" != "yes" ]; then + bwrap_path="$(command -v bwrap)" + if [ -n "$bwrap_path" ]; then + export AM_BWRAPPED=yes + exec "$bwrap_path" --unshare-net --dev-bind / / "$0" "$@" + fi + unset AM_BWRAPPED +fi + +if [[ -z "${RETRIES_REMAINING-}" ]]; then + export RETRIES_REMAINING=2 +fi + +SCRIPT_DIR="$(dirname "$0")" +WOLFSSL_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +cd "$WOLFSSL_DIR" || exit 1 + +OCSP_RESPONDER="./examples/ocsp_responder/ocsp_responder" +OCSP_DIR="certs/ocsp" + +if [ "$OPENSSL" = "" ]; then + OPENSSL=openssl +fi + +# Check prerequisites +[ ! -x "$OCSP_RESPONDER" ] && \ + echo 'skipping: ocsp_responder not built.' 1>&2 && exit 77 + +if $OCSP_RESPONDER -? 2>&1 | grep -q "OCSP Responder requires"; then + echo 'skipping: ocsp_responder not compiled with required features.' 1>&2 + exit 77 +fi + +command -v $OPENSSL >/dev/null 2>&1 || \ + { echo "Requires openssl command, skipping." 1>&2; exit 77; } + +echo "wolfSSL OCSP Responder / OpenSSL OCSP client interop test" +echo "OpenSSL: $($OPENSSL version)" +echo + +# IPv6 detection +if nc -h 2>&1 | fgrep -q -i ipv6; then + IPV6_SUPPORTED=yes +else + IPV6_SUPPORTED=no +fi + +if ./examples/client/client '-#' | fgrep -q -e ' -DTEST_IPV6 '; then + if [[ "$IPV6_SUPPORTED" == "no" ]]; then + echo 'Skipping IPV6 test in environment lacking IPV6 support.' + exit 77 + fi + LOCALHOST='[::1]' + LOCALHOST_FOR_NC='-6 ::1' +else + LOCALHOST='127.0.0.1' + LOCALHOST_FOR_NC='127.0.0.1' +fi + +######################################################################## +# Helpers +######################################################################## + +resp_pids="" +resp_logs="" +ready_files="" + +cleanup() { + exit_status=$? + for p in $resp_pids; do + kill $p 2>/dev/null + wait $p 2>/dev/null + done + # Clean up log files + for log in $resp_logs; do + rm -f "$log" 2>/dev/null + done + # Clean up ready files + for rf in $ready_files; do + rm -f "$rf" 2>/dev/null + done + if [[ ("$exit_status" == 1) && ($RETRIES_REMAINING -gt 0) ]]; then + echo "retrying..." + RETRIES_REMAINING=$((RETRIES_REMAINING - 1)) + exec $0 "$@" + fi +} +trap cleanup EXIT INT TERM + +tests_run=0 +tests_passed=0 +tests_failed=0 + +print_responder_logs() { + echo "--- Responder Logs ---" + for log in $resp_logs; do + if [ -f "$log" ]; then + echo "=== $(basename $log) ===" + cat "$log" + echo + fi + done + echo "----------------------" +} + +# choose consecutive ports based on the PID, skipping any that are +# already bound, to avoid the birthday problem in case other +# instances are sharing this host. + +get_first_free_port() { + local ret="$1" + while :; do + if [[ "$ret" -ge 65536 ]]; then + ret=1024 + fi + if ! nc -z ${LOCALHOST_FOR_NC} "$ret"; then + break + fi + ret=$((ret+1)) + done + echo "$ret" + return 0 +} + +# wait_for_ready_file READY_FILE PID PORT +wait_for_ready_file() { + local ready_file="$1" + local pid="$2" + local port="$3" + local counter=0 + + while [ ! -s "$ready_file" ] && [ "$counter" -lt 20 ]; do + if ! kill -0 "$pid" 2>/dev/null; then + echo "ERROR: Responder pid $pid for port $port exited before creating ready file" 1>&2 + print_responder_logs + exit 1 + fi + sleep 0.1 + counter=$((counter + 1)) + done + + if [ ! -s "$ready_file" ]; then + echo "ERROR: Ready file $ready_file not created after 2 seconds" 1>&2 + print_responder_logs + exit 1 + fi +} + +# query_ocsp ISSUER_CERT CERT_TO_CHECK PORT EXPECTED_STATUS DESCRIPTION +query_ocsp() { + local issuer="$1" + local cert="$2" + local _port="$3" + local expected="$4" + local desc="$5" + + tests_run=$((tests_run+1)) + printf " TEST %2d: %-55s " "$tests_run" "$desc" + + local output + output=$($OPENSSL ocsp \ + -issuer "$issuer" \ + -cert "$cert" \ + -url "http://127.0.0.1:$_port/" \ + -resp_text 2>&1) + local rc=$? + + # Extract the cert status line + local status + status=$(echo "$output" | grep "Cert Status:" | head -1 | \ + sed 's/.*Cert Status: *//') + + if [ "$status" = "$expected" ]; then + printf "PASSED (status: %s)\n" "$status" + tests_passed=$((tests_passed+1)) + else + printf "FAILED (expected: %s, got: %s, rc: %d)\n" \ + "$expected" "$status" "$rc" + tests_failed=$((tests_failed+1)) + echo "--- openssl output ---" + echo "$output" + echo "----------------------" + print_responder_logs + fi +} + +######################################################################## +# Start responders - one per CA/index-file pair +######################################################################## + + +base_port=$((((($$ + $RETRIES_REMAINING) * 5) % (65536 - 2048)) + 1024)) +port1=$(get_first_free_port $base_port) # OCSP responder: intermediate1-ca +port2=$(get_first_free_port $((port1 + 1))) # OCSP responder: intermediate2-ca +port3=$(get_first_free_port $((port2 + 1))) # OCSP responder: intermediate3-ca +port4=$(get_first_free_port $((port3 + 1))) # OCSP responder: root-ca +port5=$(get_first_free_port $((port4 + 1))) # TLS server + +# Responder 1: intermediate1-ca (server1=valid, server2=revoked) +log1=$(mktemp /tmp/ocsp_resp1.XXXXXX) +resp_logs="$resp_logs $log1" +ready1=$(mktemp /tmp/ocsp_ready1.XXXXXX) +ready_files="$ready_files $ready1" +$OCSP_RESPONDER -p $port1 -v -R "$ready1" \ + -c $OCSP_DIR/intermediate1-ca-cert.pem \ + -k $OCSP_DIR/intermediate1-ca-key.pem \ + -i $OCSP_DIR/index-intermediate1-ca-issued-certs.txt \ + > "$log1" 2>&1 & +pid1=$! +resp_pids="$resp_pids $pid1" + +# Responder 2: intermediate2-ca (server3=valid, server4=revoked) +log2=$(mktemp /tmp/ocsp_resp2.XXXXXX) +resp_logs="$resp_logs $log2" +ready2=$(mktemp /tmp/ocsp_ready2.XXXXXX) +ready_files="$ready_files $ready2" +$OCSP_RESPONDER -p $port2 -v -R "$ready2" \ + -c $OCSP_DIR/intermediate2-ca-cert.pem \ + -k $OCSP_DIR/intermediate2-ca-key.pem \ + -i $OCSP_DIR/index-intermediate2-ca-issued-certs.txt \ + > "$log2" 2>&1 & +pid2=$! +resp_pids="$resp_pids $pid2" + +# Responder 3: intermediate3-ca (server5=valid) +log3=$(mktemp /tmp/ocsp_resp3.XXXXXX) +resp_logs="$resp_logs $log3" +ready3=$(mktemp /tmp/ocsp_ready3.XXXXXX) +ready_files="$ready_files $ready3" +$OCSP_RESPONDER -p $port3 -v -R "$ready3" \ + -c $OCSP_DIR/intermediate3-ca-cert.pem \ + -k $OCSP_DIR/intermediate3-ca-key.pem \ + -i $OCSP_DIR/index-intermediate3-ca-issued-certs.txt \ + > "$log3" 2>&1 & +pid3=$! +resp_pids="$resp_pids $pid3" + +# Responder 4: root-ca (intermediate CAs: 1=valid, 2=valid, 3=revoked) +log4=$(mktemp /tmp/ocsp_resp4.XXXXXX) +resp_logs="$resp_logs $log4" +ready4=$(mktemp /tmp/ocsp_ready4.XXXXXX) +ready_files="$ready_files $ready4" +$OCSP_RESPONDER -p $port4 -v -R "$ready4" \ + -c $OCSP_DIR/root-ca-cert.pem \ + -k $OCSP_DIR/root-ca-key.pem \ + -i $OCSP_DIR/index-ca-and-intermediate-cas.txt \ + > "$log4" 2>&1 & +pid4=$! +resp_pids="$resp_pids $pid4" + +# Responder 5: authorized responder (delegated OCSP signer with id-kp-OCSPSigning) +log5=$(mktemp /tmp/ocsp_resp5.XXXXXX) +resp_logs="$resp_logs $log5" +ready5=$(mktemp /tmp/ocsp_ready5.XXXXXX) +ready_files="$ready_files $ready5" +$OCSP_RESPONDER -p $port5 -v -R "$ready5" \ + -c $OCSP_DIR/root-ca-cert.pem \ + -r $OCSP_DIR/ocsp-responder-cert.pem \ + -k $OCSP_DIR/ocsp-responder-key.pem \ + -i $OCSP_DIR/index-ca-and-intermediate-cas.txt \ + > "$log5" 2>&1 & +pid5=$! +resp_pids="$resp_pids $pid5" + +echo "Starting wolfSSL OCSP responders..." +echo " Responder 1 (intermediate1-ca): port $port1, pid $pid1" +echo " Responder 2 (intermediate2-ca): port $port2, pid $pid2" +echo " Responder 3 (intermediate3-ca): port $port3, pid $pid3" +echo " Responder 4 (root-ca): port $port4, pid $pid4" +echo " Responder 5 (authorized resp): port $port5, pid $pid5" + +# Wait for all responders to be ready +wait_for_ready_file "$ready1" "$pid1" "$port1" +wait_for_ready_file "$ready2" "$pid2" "$port2" +wait_for_ready_file "$ready3" "$pid3" "$port3" +wait_for_ready_file "$ready4" "$pid4" "$port4" +wait_for_ready_file "$ready5" "$pid5" "$port5" + +echo "All responders ready" + +echo "All responders running." +echo + +######################################################################## +# Test index-intermediate1-ca-issued-certs.txt +# serial 05 = server1 (V = valid) +# serial 06 = server2 (R = revoked) +######################################################################## + +echo "=== index-intermediate1-ca-issued-certs.txt (intermediate1-ca) ===" + +query_ocsp "$OCSP_DIR/intermediate1-ca-cert.pem" \ + "$OCSP_DIR/server1-cert.pem" \ + $port1 "good" \ + "server1 (serial 05) -> good" + +query_ocsp "$OCSP_DIR/intermediate1-ca-cert.pem" \ + "$OCSP_DIR/server2-cert.pem" \ + $port1 "revoked" \ + "server2 (serial 06) -> revoked" + +echo + +######################################################################## +# Test index-intermediate2-ca-issued-certs.txt +# serial 07 = server3 (V = valid) +# serial 08 = server4 (R = revoked) +######################################################################## + +echo "=== index-intermediate2-ca-issued-certs.txt (intermediate2-ca) ===" + +query_ocsp "$OCSP_DIR/intermediate2-ca-cert.pem" \ + "$OCSP_DIR/server3-cert.pem" \ + $port2 "good" \ + "server3 (serial 07) -> good" + +query_ocsp "$OCSP_DIR/intermediate2-ca-cert.pem" \ + "$OCSP_DIR/server4-cert.pem" \ + $port2 "revoked" \ + "server4 (serial 08) -> revoked" + +echo + +######################################################################## +# Test index-intermediate3-ca-issued-certs.txt +# serial 09 = server5 (V = valid) +######################################################################## + +echo "=== index-intermediate3-ca-issued-certs.txt (intermediate3-ca) ===" + +query_ocsp "$OCSP_DIR/intermediate3-ca-cert.pem" \ + "$OCSP_DIR/server5-cert.pem" \ + $port3 "good" \ + "server5 (serial 09) -> good" + +echo + +######################################################################## +# Test index-ca-and-intermediate-cas.txt +# serial 01 = intermediate1-ca (V = valid) +# serial 02 = intermediate2-ca (V = valid) +# serial 03 = intermediate3-ca (R = revoked) +# serial 63 = root-ca (V = valid, self-signed) +######################################################################## + +echo "=== index-ca-and-intermediate-cas.txt (root-ca) ===" + +query_ocsp "$OCSP_DIR/root-ca-cert.pem" \ + "$OCSP_DIR/intermediate1-ca-cert.pem" \ + $port4 "good" \ + "intermediate1-ca (serial 01) -> good" + +query_ocsp "$OCSP_DIR/root-ca-cert.pem" \ + "$OCSP_DIR/intermediate2-ca-cert.pem" \ + $port4 "good" \ + "intermediate2-ca (serial 02) -> good" + +query_ocsp "$OCSP_DIR/root-ca-cert.pem" \ + "$OCSP_DIR/intermediate3-ca-cert.pem" \ + $port4 "revoked" \ + "intermediate3-ca (serial 03) -> revoked" + +query_ocsp "$OCSP_DIR/root-ca-cert.pem" \ + "$OCSP_DIR/root-ca-cert.pem" \ + $port4 "good" \ + "root-ca (serial 63) -> good" + +echo + +echo "=== Negative tests: unsupported features ===" + +# Test non-SHA-1 hash algorithms +tests_run=$((tests_run+1)) +printf " TEST %2d: %-55s " "$tests_run" "SHA-384 hash -> good" + +output=$($OPENSSL ocsp \ + -sha384 \ + -issuer "$OCSP_DIR/intermediate1-ca-cert.pem" \ + -cert "$OCSP_DIR/server1-cert.pem" \ + -url "http://127.0.0.1:$port1/" \ + -resp_text 2>&1) +rc=$? + +# Extract the cert status line +status=$(echo "$output" | grep "Cert Status:" | head -1 | \ + sed 's/.*Cert Status: *//') + +# Expect "good" status (feature should be supported) +if [ "$status" = "good" ]; then + printf "PASSED (status: %s)\n" "$status" + tests_passed=$((tests_passed+1)) +else + printf "FAILED (expected: good, got: %s, rc: %d)\n" "$status" "$rc" + tests_failed=$((tests_failed+1)) + echo "--- openssl output ---" + echo "$output" + echo "----------------------" + print_responder_logs +fi + +######################################################################## +# Test: Authorized Responder (RFC 6960 Section 4.2.2.2) +######################################################################## + +# Test: Authorized Responder (RFC 6960 Section 4.2.2.2) +# An OCSP response can be signed by a delegated signer with id-kp-OCSPSigning +# extended key usage instead of the CA itself. +# Uses Responder 5 which has ocsp-responder-cert.pem with id-kp-OCSPSigning EKU. +tests_run=$((tests_run+1)) +printf " TEST %2d: %-55s " "$tests_run" "Authorized responder" + +# Query using OpenSSL - asks about intermediate1-ca which was issued by root-ca +# Response will be signed by ocsp-responder-cert (authorized responder) +output=$($OPENSSL ocsp \ + -issuer "$OCSP_DIR/root-ca-cert.pem" \ + -cert "$OCSP_DIR/intermediate1-ca-cert.pem" \ + -url "http://127.0.0.1:$port5/" \ + -resp_text 2>&1) +rc=$? + +# Extract the cert status line +status=$(echo "$output" | grep "Cert Status:" | head -1 | \ + sed 's/.*Cert Status: *//') + +# Expect "good" status +if [ "$status" = "good" ]; then + printf "PASSED (authorized responder works)\n" + tests_passed=$((tests_passed+1)) +else + printf "FAILED (expected good status, got: %s)\n" "$status" + tests_failed=$((tests_failed+1)) + echo "--- openssl output ---" + echo "$output" + echo "----------------------" + print_responder_logs +fi + +######################################################################## +# Test: Unknown certificates (not in responder's database) +######################################################################## + +echo "=== Unknown certificate tests ===" + +# Test 1: Query Responder 1 (intermediate1-ca) about server3 (issued by intermediate2-ca) +# Note: This tests wrong issuer - should get OCSP error (unauthorized) +tests_run=$((tests_run+1)) +printf " TEST %2d: %-55s " "$tests_run" "Wrong issuer: server3 to responder 1 (wrong issuer)" + +output=$($OPENSSL ocsp \ + -issuer "$OCSP_DIR/intermediate1-ca-cert.pem" \ + -cert "$OCSP_DIR/server3-cert.pem" \ + -url "http://127.0.0.1:$port1/" \ + -noverify \ + -resp_text 2>&1) +rc=$? + +# server3 is actually issued by intermediate2-ca, not intermediate1-ca +# Expect OCSP error response: unauthorized (6) +if echo "$output" | grep -q "Responder Error: unauthorized"; then + printf "PASSED (OCSP unauthorized - wrong issuer)\n" + tests_passed=$((tests_passed+1)) +else + printf "FAILED (expected OCSP unauthorized, got rc: %d)\n" "$rc" + tests_failed=$((tests_failed+1)) + echo "--- openssl output ---" + echo "$output" + echo "----------------------" + print_responder_logs +fi + +# Test 2: Query Responder 2 (intermediate2-ca) about server1 (issued by intermediate1-ca) +# Note: This tests wrong issuer - should get OCSP error (unauthorized) +tests_run=$((tests_run+1)) +printf " TEST %2d: %-55s " "$tests_run" "Wrong issuer: server1 to responder 2 (wrong issuer)" + +output=$($OPENSSL ocsp \ + -issuer "$OCSP_DIR/intermediate2-ca-cert.pem" \ + -cert "$OCSP_DIR/server1-cert.pem" \ + -url "http://127.0.0.1:$port2/" \ + -noverify \ + -resp_text 2>&1) +rc=$? + +# server1 is actually issued by intermediate1-ca, not intermediate2-ca +# Expect OCSP error response: unauthorized (6) +if echo "$output" | grep -q "Responder Error: unauthorized"; then + printf "PASSED (OCSP unauthorized - wrong issuer)\n" + tests_passed=$((tests_passed+1)) +else + printf "FAILED (expected OCSP unauthorized, got rc: %d)\n" "$rc" + tests_failed=$((tests_failed+1)) + echo "--- openssl output ---" + echo "$output" + echo "----------------------" + print_responder_logs +fi + +echo + +######################################################################## +# Negative tests: unsupported functionality +######################################################################## + +# Test: Multiple requests in one OCSP request (should fail with OCSP error) +tests_run=$((tests_run+1)) +printf " TEST %2d: %-55s " "$tests_run" "Multiple requests (should return OCSP error)" + +# Using multiple -cert options creates one OCSP request with multiple certificate IDs +output=$($OPENSSL ocsp \ + -issuer "$OCSP_DIR/intermediate1-ca-cert.pem" \ + -cert "$OCSP_DIR/server1-cert.pem" \ + -cert "$OCSP_DIR/server2-cert.pem" \ + -url "http://127.0.0.1:$port1/" \ + -resp_text 2>&1) +rc=$? + +# Expect OCSP error response: malformedrequest (1) +# OpenSSL shows "Responder Error: malformedrequest (1)" +if echo "$output" | grep -q "Responder Error: malformedrequest"; then + printf "PASSED (OCSP malformedrequest)\n" + tests_passed=$((tests_passed+1)) +else + printf "FAILED (expected OCSP malformedrequest, got rc: %d)\n" "$rc" + tests_failed=$((tests_failed+1)) + echo "--- openssl output ---" + echo "$output" + echo "----------------------" + print_responder_logs +fi + +echo + +######################################################################## +# Results +######################################################################## + +echo "============================================================" +echo "Results: $tests_passed/$tests_run passed, $tests_failed failed" +echo "============================================================" + +if [ $tests_failed -ne 0 ]; then + exit 1 +fi + +exit 0 diff --git a/scripts/ocsp-stapling-with-wolfssl-responder.test b/scripts/ocsp-stapling-with-wolfssl-responder.test new file mode 100755 index 00000000000..1af5c450a99 --- /dev/null +++ b/scripts/ocsp-stapling-with-wolfssl-responder.test @@ -0,0 +1,764 @@ +#!/usr/bin/env bash + +# ocsp-stapling-with-wolfssl-responder.test +# Tests OCSP stapling using wolfSSL's own ocsp_responder example +# instead of the OpenSSL ocsp utility. +# +# Covers scenarios from: +# ocsp-stapling.test (v1 stapling with single responder) +# ocsp-stapling2.test (v2 stapling with multiple responders) +# ocsp-stapling-with-ca-as-responder.test (CA signs OCSP responses directly) +# ocsp-stapling_tls13multi.test (TLS 1.3 multi-stapling) +# +# Requires: HAVE_OCSP, HAVE_OCSP_RESPONDER, HAVE_CERTIFICATE_STATUS_REQUEST + +SCRIPT_DIR="$(dirname "$0")" + +# if we can, isolate the network namespace to eliminate port collisions. +if [[ -n "$NETWORK_UNSHARE_HELPER" ]]; then + if [[ -z "$NETWORK_UNSHARE_HELPER_CALLED" ]]; then + export NETWORK_UNSHARE_HELPER_CALLED=yes + exec "$NETWORK_UNSHARE_HELPER" "$0" "$@" || exit $? + fi +elif [ "${AM_BWRAPPED-}" != "yes" ]; then + bwrap_path="$(command -v bwrap)" + if [ -n "$bwrap_path" ]; then + export AM_BWRAPPED=yes + exec "$bwrap_path" --unshare-net --dev-bind / / "$0" "$@" + fi + unset AM_BWRAPPED +fi + +if [[ -z "${RETRIES_REMAINING-}" ]]; then + export RETRIES_REMAINING=2 +fi + +# Check prerequisites +[ ! -x ./examples/client/client ] && printf '\n\n%s\n' "Client doesn't exist" \ + && exit 1 + +[ ! -x ./examples/server/server ] && printf '\n\n%s\n' "Server doesn't exist" \ + && exit 1 + +[ ! -x ./examples/ocsp_responder/ocsp_responder ] && \ + printf '\n\n%s\n' "OCSP Responder doesn't exist" && exit 1 + +./examples/client/client '-?' 2>&1 | grep -q 'Client not compiled in!' +if [ $? -eq 0 ]; then + echo 'skipping ocsp-stapling-with-wolfssl-responder.test because client not compiled in.' 1>&2 + exit 77 +fi + +./examples/server/server '-?' 2>&1 | grep -q 'Server not compiled in!' +if [ $? -eq 0 ]; then + echo 'skipping ocsp-stapling-with-wolfssl-responder.test because server not compiled in.' 1>&2 + exit 77 +fi + +# Check that OCSP responder is compiled with required features +if ./examples/ocsp_responder/ocsp_responder -? 2>&1 | grep -q "OCSP Responder requires"; then + echo 'skipping ocsp-stapling-with-wolfssl-responder.test because ocsp_responder not compiled with required features.' 1>&2 + exit 77 +fi + +if ! ./examples/client/client -V | grep -q 3; then + echo 'skipping ocsp-stapling-with-wolfssl-responder.test because TLS1.2 is not available.' 1>&2 + exit 77 +fi + +if ./examples/client/client '-#' | fgrep -q -e ' -DWOLFSSL_SNIFFER '; then + echo 'skipping ocsp-stapling-with-wolfssl-responder.test because WOLFSSL_SNIFFER defined.' + exit 77 +fi + +# Detect TLS/DTLS version support +tls13=no +if ./examples/client/client -V | grep -q 4; then + tls13=yes +fi +dtls13=no +if ./examples/client/client -? 2>&1 | grep -q 'DTLSv1.3'; then + dtls13=yes +fi +dtls12=no +if ./examples/client/client -? 2>&1 | grep -q 'DTLSv1.2'; then + dtls12=yes +fi + +# Detect OCSP stapling version support (check independently) +stapling_v1=no +stapling_v2=no +if ./examples/client/client '-#' | grep -q 'HAVE_CERTIFICATE_STATUS_REQUEST[^_]'; then + stapling_v1=yes +fi +if ./examples/client/client '-#' | grep -q 'HAVE_CERTIFICATE_STATUS_REQUEST_V2'; then + stapling_v2=yes +fi + +# Skip entire test if no stapling support at all +if [ "$stapling_v1" == "no" ] && [ "$stapling_v2" == "no" ]; then + echo 'skipping ocsp-stapling-with-wolfssl-responder.test because OCSP stapling not compiled in.' 1>&2 + exit 77 +fi + +printf '%s\n' "OCSP Stapling v1 support: $stapling_v1" +printf '%s\n' "OCSP Stapling v2 support: $stapling_v2" + +# IPv6 detection +if nc -h 2>&1 | fgrep -q -i ipv6; then + IPV6_SUPPORTED=yes +else + IPV6_SUPPORTED=no +fi + +if ./examples/client/client '-#' | fgrep -q -e ' -DTEST_IPV6 '; then + if [[ "$IPV6_SUPPORTED" == "no" ]]; then + echo 'Skipping IPV6 test in environment lacking IPV6 support.' + exit 77 + fi + LOCALHOST='[::1]' + LOCALHOST_FOR_NC='-6 ::1' +else + LOCALHOST='127.0.0.1' + LOCALHOST_FOR_NC='127.0.0.1' +fi + +PARENTDIR="$PWD" +OCSP_RESPONDER="./examples/ocsp_responder/ocsp_responder" + +# Create a unique workspace +WORKSPACE="${PARENTDIR}/workspace.pid$$" + +mkdir "${WORKSPACE}" || exit $? +cp -pR ${SCRIPT_DIR}/../certs "${WORKSPACE}"/ || exit $? +cd "$WORKSPACE" || exit $? +ln -s ../examples + +CERT_DIR="certs/ocsp" + +ready_file1="$WORKSPACE"/wolf_ocsp_wr_readyF1$$ +ready_file2="$WORKSPACE"/wolf_ocsp_wr_readyF2$$ +ready_file3="$WORKSPACE"/wolf_ocsp_wr_readyF3$$ +ready_file4="$WORKSPACE"/wolf_ocsp_wr_readyF4$$ +ready_file5="$WORKSPACE"/wolf_ocsp_wr_readyF5$$ +ready_file_resp1="$WORKSPACE"/wolf_ocsp_resp_readyF1$$ +ready_file_resp2="$WORKSPACE"/wolf_ocsp_resp_readyF2$$ +ready_file_resp3="$WORKSPACE"/wolf_ocsp_resp_readyF3$$ +ready_file_resp4="$WORKSPACE"/wolf_ocsp_resp_readyF4$$ +printf '%s\n' "ready file 1: $ready_file1" +printf '%s\n' "ready file 2: $ready_file2" +printf '%s\n' "ready file 3: $ready_file3" +printf '%s\n' "ready file 4: $ready_file4" +printf '%s\n' "ready file 5: $ready_file5" +printf '%s\n' "ready file resp 1: $ready_file_resp1" +printf '%s\n' "ready file resp 2: $ready_file_resp2" +printf '%s\n' "ready file resp 3: $ready_file_resp3" +printf '%s\n' "ready file resp 4: $ready_file_resp4" + +# Create temporary log files for responder output +responder_log1=$(mktemp) +responder_log2=$(mktemp) +responder_log3=$(mktemp) +responder_log4=$(mktemp) +printf '%s\n' "responder log 1: $responder_log1" +printf '%s\n' "responder log 2: $responder_log2" +printf '%s\n' "responder log 3: $responder_log3" +printf '%s\n' "responder log 4: $responder_log4" + +test_cnf="ocsp_wolf_resp.cnf" + +wait_for_readyFile(){ + + counter=0 + + while [ ! -s $1 -a "$counter" -lt 20 ]; do + if [[ -n "${2-}" ]]; then + if ! kill -0 $2 2>&-; then + echo "pid $2 for port ${3-} exited before creating ready file. bailing..." + exit 1 + fi + fi + echo -e "waiting for ready file..." + sleep 0.1 + counter=$((counter+ 1)) + done + + if test -e $1; then + echo -e "found ready file, starting client..." + else + echo -e "NO ready file at $1 -- ending test..." + exit 1 + fi + +} + +remove_single_rF(){ + if test -e $1; then + printf '%s\n' "removing ready file: $1" + rm $1 + fi +} + +#create a configure file for cert generation with the port 0 solution +create_new_cnf() { + printf '%s\n' "Random Ports Selected: $1 $2 $3 $4" + + cat <<- EOF > $test_cnf + # + # openssl configuration file for OCSP certificates + # + + # Extensions to add to a certificate request (intermediate1-ca) + [ v3_req1 ] + basicConstraints = CA:false + subjectKeyIdentifier = hash + authorityKeyIdentifier = keyid:always,issuer:always + keyUsage = nonRepudiation, digitalSignature, keyEncipherment + authorityInfoAccess = OCSP;URI:http://127.0.0.1:$1 + + # Extensions to add to a certificate request (intermediate2-ca) + [ v3_req2 ] + basicConstraints = CA:false + subjectKeyIdentifier = hash + authorityKeyIdentifier = keyid:always,issuer:always + keyUsage = nonRepudiation, digitalSignature, keyEncipherment + authorityInfoAccess = OCSP;URI:http://127.0.0.1:$2 + + # Extensions to add to a certificate request (intermediate3-ca) + [ v3_req3 ] + basicConstraints = CA:false + subjectKeyIdentifier = hash + authorityKeyIdentifier = keyid:always,issuer:always + keyUsage = nonRepudiation, digitalSignature, keyEncipherment + authorityInfoAccess = OCSP;URI:http://127.0.0.1:$3 + + # Extensions for a typical CA + [ v3_ca ] + basicConstraints = CA:true + subjectKeyIdentifier = hash + authorityKeyIdentifier = keyid:always,issuer:always + keyUsage = keyCertSign, cRLSign + authorityInfoAccess = OCSP;URI:http://127.0.0.1:$4 + + # OCSP extensions. + [ v3_ocsp ] + basicConstraints = CA:false + subjectKeyIdentifier = hash + authorityKeyIdentifier = keyid:always,issuer:always + extendedKeyUsage = OCSPSigning +EOF + + mv $test_cnf $CERT_DIR/$test_cnf + cd $CERT_DIR + CURR_LOC="$PWD" + printf '%s\n' "echo now in $CURR_LOC" + ./renewcerts-for-test.sh $test_cnf + cd $WORKSPACE +} + +remove_ready_file(){ + if test -e $ready_file1; then + printf '%s\n' "removing ready file: $ready_file1" + rm $ready_file1 + fi + if test -e $ready_file2; then + printf '%s\n' "removing ready file: $ready_file2" + rm $ready_file2 + fi + if test -e $ready_file3; then + printf '%s\n' "removing ready file: $ready_file3" + rm $ready_file3 + fi + if test -e $ready_file4; then + printf '%s\n' "removing ready file: $ready_file4" + rm $ready_file4 + fi + if test -e $ready_file5; then + printf '%s\n' "removing ready file: $ready_file5" + rm $ready_file5 + fi +} + +cleanup() +{ + exit_status=$? + for i in $(jobs -pr) + do + kill -s KILL "$i" + done + remove_ready_file + rm -f $CERT_DIR/$test_cnf + + # Print responder logs on failure + if [ "$exit_status" -ne 0 ]; then + printf '\n\n%s\n' "Test failed, printing responder logs..." + if [ -f "$responder_log1" ]; then + echo "=============== Responder 1 log ===============" + cat "$responder_log1" + fi + if [ -f "$responder_log2" ]; then + echo "=============== Responder 2 log ===============" + cat "$responder_log2" + fi + if [ -f "$responder_log3" ]; then + echo "=============== Responder 3 log ===============" + cat "$responder_log3" + fi + if [ -f "$responder_log4" ]; then + echo "=============== Responder 4 log ===============" + cat "$responder_log4" + fi + fi + + rm -f "$responder_log1" "$responder_log2" "$responder_log3" "$responder_log4" + cd "$PARENTDIR" || return 1 + rm -r "$WORKSPACE" || return 1 + + if [[ ("$exit_status" == 1) && ($RETRIES_REMAINING -gt 0) ]]; then + echo "retrying..." + RETRIES_REMAINING=$((RETRIES_REMAINING - 1)) + exec $0 "$@" + fi +} +trap cleanup EXIT INT TERM HUP + +[ ! -x ./examples/client/client ] && echo -e "\n\nClient doesn't exist" && exit 1 + +# check if supported key size is large enough to handle 4096 bit RSA +size="$(./examples/client/client '-?' | grep "Max RSA key")" +size="${size//[^0-9]/}" +if [ ! -z "$size" ]; then + printf 'check on max key size of %d ...' $size + if [ $size -lt 4096 ]; then + printf '%s\n' "4096 bit RSA keys not supported" + exit 0 + fi + printf 'OK\n' +fi + +# choose consecutive ports based on the PID, skipping any that are +# already bound, to avoid the birthday problem in case other +# instances are sharing this host. + +get_first_free_port() { + local ret="$1" + while :; do + if [[ "$ret" -ge 65536 ]]; then + ret=1024 + fi + if ! nc -z ${LOCALHOST_FOR_NC} "$ret"; then + break + fi + ret=$((ret+1)) + done + echo "$ret" + return 0 +} + +base_port=$((((($$ + $RETRIES_REMAINING) * 5) % (65536 - 2048)) + 1024)) +port1=$(get_first_free_port $base_port) # OCSP responder: intermediate1-ca +port2=$(get_first_free_port $((port1 + 1))) # OCSP responder: intermediate2-ca +port3=$(get_first_free_port $((port2 + 1))) # OCSP responder: intermediate3-ca +port4=$(get_first_free_port $((port3 + 1))) # OCSP responder: root-ca +port5=$(get_first_free_port $((port4 + 1))) # TLS server + +# Verify ports by starting and stopping dummy servers +# 1: +./examples/server/server -R $ready_file1 -p $port1 & +server_pid1=$! +wait_for_readyFile $ready_file1 $server_pid1 $port1 +if [ ! -f $ready_file1 ]; then + printf '%s\n' "Failed to create ready file1: \"$ready_file1\"" + exit 1 +fi +# 2: +./examples/server/server -R $ready_file2 -p $port2 & +server_pid2=$! +wait_for_readyFile $ready_file2 $server_pid2 $port2 +if [ ! -f $ready_file2 ]; then + printf '%s\n' "Failed to create ready file2: \"$ready_file2\"" + exit 1 +fi +# 3: +./examples/server/server -R $ready_file3 -p $port3 & +server_pid3=$! +wait_for_readyFile $ready_file3 $server_pid3 $port3 +if [ ! -f $ready_file3 ]; then + printf '%s\n' "Failed to create ready file3: \"$ready_file3\"" + exit 1 +fi +# 4: +./examples/server/server -R $ready_file4 -p $port4 & +server_pid4=$! +wait_for_readyFile $ready_file4 $server_pid4 $port4 +if [ ! -f $ready_file4 ]; then + printf '%s\n' "Failed to create ready file4: \"$ready_file4\"" + exit 1 +fi + +printf '%s\n' "------------- PORTS ---------------" +printf '%s\n' "OCSP responder ports: $port1 $port2 $port3 $port4" +printf '%s\n' "TLS server port: $port5" +printf '%s\n' "-----------------------------------" +# Use client connections to cleanly shutdown the servers +./examples/client/client -p $port1 +./examples/client/client -p $port2 +./examples/client/client -p $port3 +./examples/client/client -p $port4 +create_new_cnf $port1 $port2 $port3 $port4 + +# Start wolfSSL OCSP responders (CA signs responses directly) +printf '%s\n' "Starting wolfSSL OCSP responders..." + +# Responder 1: intermediate1-ca (for server1, server2) +$OCSP_RESPONDER -v -p $port1 -R "$ready_file_resp1" \ + -i certs/ocsp/index-intermediate1-ca-issued-certs.txt \ + -c certs/ocsp/intermediate1-ca-cert.pem \ + -k certs/ocsp/intermediate1-ca-key.pem > "$responder_log1" 2>&1 & +resp_pid1=$! + +# Responder 2: intermediate2-ca (for server3, server4) +$OCSP_RESPONDER -v -p $port2 -R "$ready_file_resp2" \ + -i certs/ocsp/index-intermediate2-ca-issued-certs.txt \ + -c certs/ocsp/intermediate2-ca-cert.pem \ + -k certs/ocsp/intermediate2-ca-key.pem > "$responder_log2" 2>&1 & +resp_pid2=$! + +# Responder 3: intermediate3-ca (for server5) +$OCSP_RESPONDER -v -p $port3 -R "$ready_file_resp3" \ + -i certs/ocsp/index-intermediate3-ca-issued-certs.txt \ + -c certs/ocsp/intermediate3-ca-cert.pem \ + -k certs/ocsp/intermediate3-ca-key.pem > "$responder_log3" 2>&1 & +resp_pid3=$! + +# Responder 4: root-ca (for intermediate CA certs) +$OCSP_RESPONDER -v -p $port4 -R "$ready_file_resp4" \ + -i certs/ocsp/index-ca-and-intermediate-cas.txt \ + -c certs/ocsp/root-ca-cert.pem \ + -k certs/ocsp/root-ca-key.pem > "$responder_log4" 2>&1 & +resp_pid4=$! + +# Wait for all responders to be ready +wait_for_readyFile "$ready_file_resp1" "$resp_pid1" "$port1" +wait_for_readyFile "$ready_file_resp2" "$resp_pid2" "$port2" +wait_for_readyFile "$ready_file_resp3" "$resp_pid3" "$port3" +wait_for_readyFile "$ready_file_resp4" "$resp_pid4" "$port4" + +printf '\n\n%s\n\n' "All wolfSSL OCSP responders started successfully!" + +######################################################################## +# v1 STAPLING TESTS (server1/server2 issued by intermediate1-ca) +######################################################################## + +if [ "$stapling_v1" == "yes" ]; then + printf '%s\n\n' "============ v1 STAPLING TESTS ================================" + + printf '%s\n\n' "------------- TEST CASE 1 SHOULD PASS -------------------------" + # client test against our own server - GOOD CERT + ./examples/server/server -c certs/ocsp/server1-cert.pem \ + -k certs/ocsp/server1-key.pem -R $ready_file5 \ + -p $port5 & + server_pid5=$! + wait_for_readyFile $ready_file5 $server_pid5 $port5 + ./examples/client/client -C -A certs/ocsp/root-ca-cert.pem -W 1 -p $port5 + RESULT=$? + [ $RESULT -ne 0 ] && printf '\n\n%s\n' "Client connection 1 failed" && exit 1 + printf '%s\n\n' "Test PASSED!" + + printf '%s\n\n' "------------- TEST CASE 2 SHOULD REVOKE -----------------------" + # client test against our own server - REVOKED CERT + remove_single_rF $ready_file5 + ./examples/server/server -c certs/ocsp/server2-cert.pem \ + -k certs/ocsp/server2-key.pem -R $ready_file5 \ + -p $port5 & + server_pid5=$! + wait_for_readyFile $ready_file5 $server_pid5 $port5 + sleep 0.1 + ./examples/client/client -C -A certs/ocsp/root-ca-cert.pem -W 1 -p $port5 + RESULT=$? + [ $RESULT -ne 1 ] && printf '\n\n%s\n' "Client connection 2 succeeded $RESULT" \ + && exit 1 + printf '%s\n\n' "Test successfully REVOKED!" + + wait $server_pid5 2>/dev/null + + if [ "$tls13" == "yes" ]; then + printf '%s\n\n' "------------- TEST CASE 3 TLS13 SHOULD PASS -----------------" + # client test against our own server - GOOD CERT + remove_single_rF $ready_file5 + ./examples/server/server -c certs/ocsp/server1-cert.pem \ + -k certs/ocsp/server1-key.pem -v 4 \ + -R $ready_file5 -p $port5 & + server_pid5=$! + wait_for_readyFile $ready_file5 $server_pid5 $port5 + ./examples/client/client -C -A certs/ocsp/root-ca-cert.pem -W 1 -v 4 -F 1 \ + -p $port5 + RESULT=$? + [ $RESULT -ne 0 ] && printf '\n\n%s\n' "Client connection 3 failed" && exit 1 + printf '%s\n\n' "Test PASSED!" + + printf '%s\n\n' "------------- TEST CASE 4 TLS13 MUST-STAPLE SHOULD PASS -----" + # client test against our own server, must staple - GOOD CERT + remove_single_rF $ready_file5 + ./examples/server/server -c certs/ocsp/server1-cert.pem \ + -k certs/ocsp/server1-key.pem -v 4 \ + -R $ready_file5 -p $port5 & + server_pid5=$! + wait_for_readyFile $ready_file5 $server_pid5 $port5 + ./examples/client/client -C -A certs/ocsp/root-ca-cert.pem -W 1m -v 4 -F 1 \ + -p $port5 + RESULT=$? + [ $RESULT -ne 0 ] && printf '\n\n%s\n' "Client connection 4 failed" && exit 1 + printf '%s\n\n' "Test PASSED!" + + printf '%s\n\n' "------------- TEST CASE 5 TLS13 SHOULD REVOKE ---------------" + # client test against our own server - REVOKED CERT + remove_single_rF $ready_file5 + ./examples/server/server -c certs/ocsp/server2-cert.pem \ + -k certs/ocsp/server2-key.pem -v 4 \ + -R $ready_file5 -p $port5 & + server_pid5=$! + wait_for_readyFile $ready_file5 $server_pid5 $port5 + ./examples/client/client -C -A certs/ocsp/root-ca-cert.pem -W 1 -v 4 -F 1 \ + -p $port5 + RESULT=$? + [ $RESULT -ne 1 ] && \ + printf '\n\n%s\n' "Client connection 5 succeeded $RESULT" \ + && exit 1 + printf '%s\n\n' "Test successfully REVOKED!" + wait $server_pid5 2>/dev/null + fi + + # DTLS 1.2 v1 test + if [[ "$dtls12" == "yes" ]]; then + printf '%s\n\n' "------------- TEST CASE DTLS12-1 SHOULD PASS ----------------" + remove_single_rF $ready_file5 + ./examples/server/server -c certs/ocsp/server1-cert.pem -R $ready_file5 \ + -k certs/ocsp/server1-key.pem -u -v 3 \ + -p $port5 & + server_pid5=$! + sleep 0.2 + ./examples/client/client -C -A certs/ocsp/root-ca-cert.pem -u -v 3 \ + -W 1 -p $port5 + RESULT=$? + [ $RESULT -ne 0 ] && printf '\n\n%s\n' "Client DTLS12 connection failed" && exit 1 + printf '%s\n\n' "Test PASSED!" + fi + + # DTLS 1.3 v1 test + if [ "$dtls13" == "yes" ]; then + printf '%s\n\n' "------------- TEST CASE DTLS13-1 SHOULD PASS ----------------" + remove_single_rF $ready_file5 + ./examples/server/server -c certs/ocsp/server1-cert.pem -R $ready_file5 \ + -k certs/ocsp/server1-key.pem -u -v 4 \ + -p $port5 & + server_pid5=$! + sleep 0.2 + ./examples/client/client -C -A certs/ocsp/root-ca-cert.pem -u -v 4 \ + -W 1 -p $port5 + RESULT=$? + [ $RESULT -ne 0 ] && printf '\n\n%s\n' "Client DTLS13 connection failed" && exit 1 + printf '%s\n\n' "Test PASSED!" +fi +else + printf '%s\n\n' "============ v1 STAPLING TESTS ================================" + printf '%s\n\n' "Skipping v1 stapling tests - HAVE_CERTIFICATE_STATUS_REQUEST not compiled in" +fi + +######################################################################## +# v2 STAPLING TESTS (server3/server4/server5 with multiple CAs) +######################################################################## + +if [ "$stapling_v2" == "yes" ]; then + printf '%s\n\n' "============ v2 STAPLING TESTS ================================" + + printf '%s\n\n' "------------- TEST CASE V2-1 SHOULD PASS ----------------------" + # client test against our own server - GOOD CERTS + remove_single_rF $ready_file5 + ./examples/server/server -c certs/ocsp/server3-cert.pem \ + -k certs/ocsp/server3-key.pem -R $ready_file5 \ + -p $port5 & + server_pid5=$! + wait_for_readyFile $ready_file5 $server_pid5 $port5 + ./examples/client/client -C -A certs/ocsp/root-ca-cert.pem -W 2 -v 3 \ + -p $port5 + RESULT=$? + [ $RESULT -ne 0 ] && printf '\n\n%s\n' "Client connection V2-1 failed" && exit 1 + printf '%s\n\n' "Test PASSED!" + + printf '%s\n\n' "------------- TEST CASE V2-2 SHOULD PASS ----------------------" + remove_single_rF $ready_file5 + ./examples/server/server -c certs/ocsp/server3-cert.pem \ + -k certs/ocsp/server3-key.pem -R $ready_file5 \ + -p $port5 & + server_pid5=$! + wait_for_readyFile $ready_file5 $server_pid5 $port5 + ./examples/client/client -C -A certs/ocsp/root-ca-cert.pem -W 3 -v 3 \ + -p $port5 + RESULT=$? + [ $RESULT -ne 0 ] && printf '\n\n%s\n' "Client connection V2-2 failed" && exit 1 + printf '%s\n\n' "Test PASSED!" + + printf '%s\n\n' "------------- TEST CASE V2-3 SHOULD REVOKE --------------------" + # client test against our own server - REVOKED SERVER CERT + remove_single_rF $ready_file5 + ./examples/server/server -c certs/ocsp/server4-cert.pem \ + -k certs/ocsp/server4-key.pem -R $ready_file5 \ + -p $port5 & + server_pid5=$! + wait_for_readyFile $ready_file5 $server_pid5 $port5 + ./examples/client/client -C -A certs/ocsp/root-ca-cert.pem -W 2 -v 3 \ + -p $port5 + RESULT=$? + [ $RESULT -ne 1 ] && printf '\n\n%s\n' "Client connection V2-3 succeeded $RESULT" && exit 1 + printf '%s\n\n' "Test successfully REVOKED!" + wait $server_pid5 2>/dev/null + + printf '%s\n\n' "------------- TEST CASE V2-4 SHOULD REVOKE --------------------" + remove_single_rF $ready_file5 + ./examples/server/server -c certs/ocsp/server4-cert.pem \ + -k certs/ocsp/server4-key.pem -R $ready_file5 \ + -p $port5 & + sleep 0.1 + ./examples/client/client -C -A certs/ocsp/root-ca-cert.pem -W 3 -v 3 \ + -p $port5 + RESULT=$? + [ $RESULT -ne 1 ] && printf '\n\n%s\n' "Client connection V2-4 succeeded $RESULT" && exit 1 + printf '%s\n\n' "Test successfully REVOKED!" + wait $server_pid5 2>/dev/null + + printf '%s\n\n' "------------- TEST CASE V2-5 SHOULD PASS ----------------------" + # client test against our own server - REVOKED INTERMEDIATE CERT + remove_single_rF $ready_file5 + ./examples/server/server -c certs/ocsp/server5-cert.pem \ + -k certs/ocsp/server5-key.pem -R $ready_file5 \ + -p $port5 & + server_pid5=$! + wait_for_readyFile $ready_file5 $server_pid5 $port5 + ./examples/client/client -C -A certs/ocsp/root-ca-cert.pem -W 2 -v 3 \ + -p $port5 + RESULT=$? + [ $RESULT -ne 0 ] && printf '\n\n%s\n' "Client connection V2-5 failed $RESULT" && exit 1 + printf '%s\n\n' "Test PASSED!" + + printf '%s\n\n' "------------- TEST CASE V2-6 SHOULD REVOKE --------------------" + remove_single_rF $ready_file5 + ./examples/server/server -c certs/ocsp/server5-cert.pem \ + -k certs/ocsp/server5-key.pem -R $ready_file5 \ + -p $port5 & + server_pid5=$! + wait_for_readyFile $ready_file5 $server_pid5 $port5 + ./examples/client/client -C -A certs/ocsp/root-ca-cert.pem -W 3 -v 3 \ + -p $port5 + RESULT=$? + [ $RESULT -ne 1 ] && printf '\n\n%s\n' "Client connection V2-6 succeeded $RESULT" && exit 1 + printf '%s\n\n' "Test successfully REVOKED!" + wait $server_pid5 2>/dev/null + + # DTLS 1.2 v2 test + if [[ "$dtls12" == "yes" ]]; then + printf '%s\n\n' "------------- TEST CASE DTLS12-V2 SHOULD PASS ----------------" + remove_single_rF $ready_file5 + ./examples/server/server -c certs/ocsp/server3-cert.pem \ + -k certs/ocsp/server3-key.pem -R $ready_file5 \ + -p $port5 -u -v 3 & + server_pid5=$! + sleep 0.2 + ./examples/client/client -C -A certs/ocsp/root-ca-cert.pem -W 2 -u -v 3 \ + -p $port5 + RESULT=$? + [ $RESULT -ne 0 ] && printf '\n\n%s\n' "Client DTLS12 v2 connection failed" && exit 1 + printf '%s\n\n' "Test PASSED!" + fi +else + printf '%s\n\n' "============ v2 STAPLING TESTS ================================" + printf '%s\n\n' "Skipping v2 stapling tests - HAVE_CERTIFICATE_STATUS_REQUEST_V2 not compiled in" +fi + +######################################################################## +# TLS 1.3 MULTI-STAPLING TESTS +######################################################################## + +if [ "$tls13" == "yes" ] && [ "$stapling_v1" == "yes" ]; then + printf '%s\n\n' "============ TLS 1.3 MULTI-STAPLING TESTS ====================" + + printf '%s\n\n' "------------- TEST CASE T13-1 SHOULD PASS --------------------" + # client test against our own server - GOOD CERTS + remove_single_rF $ready_file5 + ./examples/server/server -c certs/ocsp/server3-cert.pem \ + -k certs/ocsp/server3-key.pem -R $ready_file5 \ + -p $port5 -v 4 & + server_pid5=$! + wait_for_readyFile $ready_file5 $server_pid5 $port5 + ./examples/client/client -C -A certs/ocsp/root-ca-cert.pem -W 1 -v 4 \ + -p $port5 + RESULT=$? + [ $RESULT -ne 0 ] && printf '\n\n%s\n' "Client connection T13-1 failed" && exit 1 + printf '%s\n\n' "Test PASSED!" + + printf '%s\n\n' "------------- TEST CASE T13-2 SHOULD REVOKE ------------------" + # client test against our own server - REVOKED SERVER CERT + remove_single_rF $ready_file5 + ./examples/server/server -c certs/ocsp/server4-cert.pem \ + -k certs/ocsp/server4-key.pem -R $ready_file5 \ + -p $port5 -v 4 & + server_pid5=$! + wait_for_readyFile $ready_file5 $server_pid5 $port5 + ./examples/client/client -C -A certs/ocsp/root-ca-cert.pem -W 1 -v 4 \ + -p $port5 + RESULT=$? + [ $RESULT -ne 1 ] && printf '\n\n%s\n' "Client connection T13-2 succeeded $RESULT" && exit 1 + printf '%s\n\n' "Test successfully REVOKED!" + wait $server_pid5 2>/dev/null + + printf '%s\n\n' "------------- TEST CASE T13-3 SHOULD REVOKE ------------------" + # client test against our own server - REVOKED INTERMEDIATE CERT + remove_single_rF $ready_file5 + ./examples/server/server -c certs/ocsp/server5-cert.pem \ + -k certs/ocsp/server5-key.pem -R $ready_file5 \ + -p $port5 -v 4 & + server_pid5=$! + wait_for_readyFile $ready_file5 $server_pid5 $port5 + ./examples/client/client -C -A certs/ocsp/root-ca-cert.pem -W 1 -v 4 \ + -p $port5 + RESULT=$? + [ $RESULT -ne 1 ] && printf '\n\n%s\n' "Client connection T13-3 succeeded $RESULT" && exit 1 + printf '%s\n\n' "Test successfully REVOKED!" + wait $server_pid5 2>/dev/null + + # DTLS 1.3 multi-stapling tests + if [ "$dtls13" == "yes" ]; then + printf '%s\n\n' "------------- TEST CASE DTLS13-V2 SHOULD PASS ----------------" + remove_single_rF $ready_file5 + ./examples/server/server -c certs/ocsp/server3-cert.pem \ + -k certs/ocsp/server3-key.pem -R $ready_file5 \ + -p $port5 -u -v 4 & + server_pid5=$! + sleep 0.2 + ./examples/client/client -C -A certs/ocsp/root-ca-cert.pem -W 1 -u -v 4 \ + -p $port5 + RESULT=$? + [ $RESULT -ne 0 ] && printf '\n\n%s\n' "Client DTLS13 connection failed" && exit 1 + printf '%s\n\n' "Test PASSED!" + + printf '%s\n\n' "------------- TEST CASE DTLS13-V2-REVOKE SHOULD REVOKE -------" + remove_single_rF $ready_file5 + ./examples/server/server -c certs/ocsp/server4-cert.pem \ + -k certs/ocsp/server4-key.pem -R $ready_file5 \ + -p $port5 -v 4 & + server_pid5=$! + sleep 0.2 + ./examples/client/client -C -A certs/ocsp/root-ca-cert.pem -W 1 -v 4 \ + -p $port5 + RESULT=$? + [ $RESULT -ne 1 ] && printf '\n\n%s\n' "Client DTLS13 connection succeeded $RESULT" && exit 1 + printf '%s\n\n' "Test successfully REVOKED!" + fi +elif [ "$tls13" == "yes" ] || [ "$dtls13" == "yes" ]; then + printf '%s\n\n' "============ TLS 1.3 MULTI-STAPLING TESTS ====================" + printf '%s\n\n' "Skipping TLS 1.3 multi-stapling tests - HAVE_CERTIFICATE_STATUS_REQUEST not compiled in" +fi + +printf '%s\n\n' "------------------- TESTS COMPLETE ---------------------------" + +exit 0 diff --git a/scripts/ocsp-stapling_tls13multi.test b/scripts/ocsp-stapling_tls13multi.test index dbdcb53f7c8..287ec3c7172 100755 --- a/scripts/ocsp-stapling_tls13multi.test +++ b/scripts/ocsp-stapling_tls13multi.test @@ -324,7 +324,7 @@ fi printf '%s\n' "------------- PORTS ---------------" printf '%s' "Random ports selected: $port1 $port2" -printf '%s\n' " $port3 $port4" +printf '%s\n' " $port3 $port4 $port5" printf '%s\n' "-----------------------------------" # Use client connections to cleanly shutdown the servers ./examples/client/client -p $port1 diff --git a/src/ocsp.c b/src/ocsp.c index ce42b4d65a8..fd1b4ce8872 100644 --- a/src/ocsp.c +++ b/src/ocsp.c @@ -30,6 +30,13 @@ * OCSP response signer and direct issuer. */ +/* + * OCSP responder missing features: + * - Support for multiple requests and responses in a single OCSP exchange + * - Support name-based responder ID + * - Support for singleExtensions + */ + #ifndef WOLFCRYPT_ONLY #ifdef HAVE_OCSP @@ -576,7 +583,7 @@ int CheckOcspRequest(WOLFSSL_OCSP* ocsp, OcspRequest* ocspRequest, } #ifndef WOLFSSL_NO_OCSP_ISSUER_CHAIN_CHECK -static int CheckOcspResponderChain(OcspEntry* single, DecodedCert *cert, +static int CheckOcspResponderChain(OcspEntry* single, byte* issuerHash, void* vp, Signer* pendingCAs) { /* Attempt to build a chain up to cert's issuer */ WOLFSSL_CERT_MANAGER* cm = (WOLFSSL_CERT_MANAGER*)vp; @@ -606,8 +613,7 @@ static int CheckOcspResponderChain(OcspEntry* single, DecodedCert *cert, #endif for (; ca != NULL && ca != prev; prev = ca) { - if (XMEMCMP(cert->issuerHash, ca->issuerNameHash, - OCSP_DIGEST_SIZE) == 0) { + if (XMEMCMP(issuerHash, ca->issuerNameHash, OCSP_DIGEST_SIZE) == 0) { WOLFSSL_MSG("\tOCSP Response signed by authorized " "responder delegated by issuer " "(found in chain)"); @@ -631,7 +637,8 @@ static int CheckOcspResponderChain(OcspEntry* single, DecodedCert *cert, * @param cert The decoded bs->cert * @return */ -int CheckOcspResponder(OcspResponse *bs, DecodedCert *cert, void* vp) +int CheckOcspResponder(OcspResponse *bs, byte* subjectHash, + byte extExtKeyUsage, byte* issuerHash, void* vp) { int ret = 0; OcspEntry* single; @@ -646,7 +653,7 @@ int CheckOcspResponder(OcspResponse *bs, DecodedCert *cert, void* vp) /* In the future if this API is used more then it could be beneficial to * implement calling InitDecodedCert and ParseCertRelative here * automatically when cert == NULL. */ - if (bs == NULL || cert == NULL) + if (bs == NULL || subjectHash == NULL || issuerHash == NULL) return BAD_FUNC_ARG; /* Traverse the list and check that the cert has the authority to provide @@ -654,21 +661,21 @@ int CheckOcspResponder(OcspResponse *bs, DecodedCert *cert, void* vp) for (single = bs->single; single != NULL; single = single->next) { int passed = 0; - if (XMEMCMP(cert->subjectHash, single->issuerHash, OCSP_DIGEST_SIZE) - == 0) { + if (XMEMCMP(subjectHash, single->issuerHash, OCSP_DIGEST_SIZE) == 0) { WOLFSSL_MSG("\tOCSP Response signed by issuer"); passed = 1; } - else if ((cert->extExtKeyUsage & EXTKEYUSE_OCSP_SIGN) != 0) { - if (XMEMCMP(cert->issuerHash, single->issuerHash, - OCSP_DIGEST_SIZE) == 0) { + else if ((extExtKeyUsage & EXTKEYUSE_OCSP_SIGN) != 0) { + if (XMEMCMP(issuerHash, single->issuerHash, OCSP_DIGEST_SIZE) + == 0) { WOLFSSL_MSG("\tOCSP Response signed by authorized responder " "delegated by issuer"); passed = 1; } #ifndef WOLFSSL_NO_OCSP_ISSUER_CHAIN_CHECK else if (vp != NULL) { - passed = CheckOcspResponderChain(single, cert, vp, bs->pendingCAs); + passed = CheckOcspResponderChain(single, issuerHash, vp, + bs->pendingCAs); } #endif } @@ -683,6 +690,47 @@ int CheckOcspResponder(OcspResponse *bs, DecodedCert *cert, void* vp) } +OcspRequest* wc_OcspRequest_new(void* heap) +{ + OcspRequest* request = (OcspRequest*)XMALLOC(sizeof(OcspRequest), heap, + DYNAMIC_TYPE_OCSP_REQUEST); + if (request != NULL) { + XMEMSET(request, 0, sizeof(OcspRequest)); + request->heap = heap; + } + + return request; +} + +void wc_OcspRequest_free(OcspRequest* request) +{ + if (request != NULL) { + FreeOcspRequest(request); + XFREE(request, request->heap, DYNAMIC_TYPE_OCSP_REQUEST); + } +} + +OcspResponse* wc_OcspResponse_new(void* heap) +{ + OcspResponse* response = (OcspResponse*)XMALLOC(sizeof(OcspResponse), + heap, + DYNAMIC_TYPE_OCSP_RESPONSE); + if (response != NULL) { + XMEMSET(response, 0, sizeof(OcspResponse)); + response->heap = heap; + } + + return response; +} + +void wc_OcspResponse_free(OcspResponse* response) +{ + if (response != NULL) { + FreeOcspResponse(response); + XFREE(response, response->heap, DYNAMIC_TYPE_OCSP_RESPONSE); + } +} + /* compatibility layer OCSP functions */ #ifdef OPENSSL_EXTRA int wolfSSL_OCSP_resp_find_status(WOLFSSL_OCSP_BASICRESP *bs, @@ -1029,7 +1077,8 @@ static int OcspVerifySigner(WOLFSSL_OCSP_BASICRESP *resp, DecodedCert *cert, } #ifndef WOLFSSL_NO_OCSP_ISSUER_CHECK if ((flags & WOLFSSL_OCSP_NOCHECKS) == 0) { - ret = CheckOcspResponder(resp, c, st->cm); + ret = CheckOcspResponder(resp, c->subjectHash, c->extExtKeyUsage, + c->issuerHash, st->cm); } else { ret = 0; @@ -1328,8 +1377,9 @@ WOLFSSL_OCSP_ONEREQ* wolfSSL_OCSP_request_add0_id(OcspRequest *req, /* Keep to free */ req->cid = (void*)cid; - XMEMCPY(req->issuerHash, cid->issuerHash, KEYID_SIZE); - XMEMCPY(req->issuerKeyHash, cid->issuerKeyHash, KEYID_SIZE); + XMEMCPY(req->issuerHash, cid->issuerHash, WC_MAX_DIGEST_SIZE); + XMEMCPY(req->issuerKeyHash, cid->issuerKeyHash, WC_MAX_DIGEST_SIZE); + req->hashAlg = (int)cid->hashAlgoOID; if (cid->status->serialSz > req->serialSz) { XFREE(req->serial, req->heap, DYNAMIC_TYPE_OCSP); req->serial = (byte*)XMALLOC((size_t)cid->status->serialSz, @@ -2120,6 +2170,678 @@ int wolfSSL_OCSP_check_nonce(OcspRequest* req, WOLFSSL_OCSP_BASICRESP* bs) #endif /* OPENSSL_ALL */ +#ifdef HAVE_OCSP_RESPONDER + +/* Free a CA entry and all its resources */ +static void FreeOcspResponderCa(OcspResponderCa* ca, void* heap) +{ + OcspResponderCertStatus* status; + OcspResponderCertStatus* nextStatus; + + if (ca == NULL) + return; + + /* Free private key */ +#ifndef NO_RSA + if (ca->keyType == RSAk) { + wc_FreeRsaKey(&ca->key.rsa); + } +#endif +#ifdef HAVE_ECC + if (ca->keyType == ECDSAk) { + wc_ecc_free(&ca->key.ecc); + } +#endif + + /* Free certificate DER if allocated */ + if (ca->certDer != NULL) { + XFREE(ca->certDer, heap, DYNAMIC_TYPE_OCSP); + } + + /* Free certificate status list */ + status = ca->statuses; + while (status != NULL) { + nextStatus = status->next; + XFREE(status, heap, DYNAMIC_TYPE_OCSP); + status = nextStatus; + } + + XFREE(ca, heap, DYNAMIC_TYPE_OCSP); +} + +/* Allocate and initialize an OCSP Responder */ +OcspResponder* wc_OcspResponder_new(void* heap, int sendCerts) +{ + OcspResponder* responder; + + WOLFSSL_ENTER("wc_OcspResponder_new"); + + responder = (OcspResponder*)XMALLOC(sizeof(OcspResponder), heap, + DYNAMIC_TYPE_OCSP); + if (responder != NULL) { + XMEMSET(responder, 0, sizeof(OcspResponder)); + responder->heap = heap; + responder->sendCerts = sendCerts ? 1 : 0; + if (wc_InitRng(&responder->rng) != 0) { + XFREE(responder, heap, DYNAMIC_TYPE_OCSP); + responder = NULL; + } + } + return responder; +} + +/* Free an OCSP Responder */ +void wc_OcspResponder_free(OcspResponder* responder) +{ + WOLFSSL_ENTER("wc_OcspResponder_free"); + if (responder != NULL) { + OcspResponderCa* ca; + OcspResponderCa* nextCa; + /* Free all CAs */ + ca = responder->caList; + while (ca != NULL) { + nextCa = ca->next; + FreeOcspResponderCa(ca, responder->heap); + ca = nextCa; + } + wc_FreeRng(&responder->rng); + XFREE(responder, responder->heap, DYNAMIC_TYPE_OCSP); + } +} + +int wc_OcspResponder_AddSigner(OcspResponder* responder, + const byte* signerDer, word32 signerDerSz, + const byte* keyDer, word32 keyDerSz, + /* Necessary when adding an authorized responder */ + const byte* issuerCertDer, word32 issuerCertDerSz) +{ + int ret = -1; + OcspResponderCa* ca = NULL; + DecodedCert* decoded = NULL; + word32 keyOID = 0; + + WOLFSSL_ENTER("wc_OcspResponder_AddResponder"); + + if (responder == NULL || signerDer == NULL || signerDerSz == 0 || + keyDer == NULL || keyDerSz == 0) + return BAD_FUNC_ARG; + + /* Allocate CA structure */ + ca = (OcspResponderCa*)XMALLOC(sizeof(OcspResponderCa), responder->heap, + DYNAMIC_TYPE_OCSP); + if (ca == NULL) { + ret = MEMORY_E; + goto out; + } + XMEMSET(ca, 0, sizeof(OcspResponderCa)); + + /* Parse certificate */ + decoded = (DecodedCert*)XMALLOC(sizeof(DecodedCert), responder->heap, + DYNAMIC_TYPE_OCSP); + if (decoded == NULL) { + ret = MEMORY_E; + goto out; + } + + wc_InitDecodedCert(decoded, signerDer, signerDerSz, responder->heap); + ret = wc_ParseCert(decoded, CERT_TYPE, 0, NULL); + if (ret != 0) + goto out; + + ret = wc_CheckPrivateKeyCert(keyDer, keyDerSz, decoded, 1, responder->heap); + if (ret != 1) { + ret = (ret == 0) ? BAD_FUNC_ARG : ret; + goto out; + } + keyOID = decoded->keyOID; + + ret = wc_ShaHash(decoded->publicKeyForHash, decoded->pubKeyForHashSize, + ca->responderKeyHash); + if (ret != 0) + goto out; + + /* Now place the real issuer into decoded and perform cert can actually + * sign for issuer. */ + if (issuerCertDer != NULL && issuerCertDerSz > 0) { + char issuer[WC_ASN_NAME_MAX]; + + if (signerDerSz == issuerCertDerSz && + XMEMCMP(signerDer, issuerCertDer, signerDerSz) == 0) { + /* API misuse */ + ret = BAD_FUNC_ARG; + goto out; + } + if ((decoded->extExtKeyUsage & EXTKEYUSE_OCSP_SIGN) == 0) { + /* Cert is not authorized for OCSP signing */ + ret = BAD_FUNC_ARG; + goto out; + } + XMEMCPY(issuer, decoded->issuer, WC_ASN_NAME_MAX); + + /* Use allocated DecodedCert */ + wc_FreeDecodedCert(decoded); + wc_InitDecodedCert(decoded, issuerCertDer, issuerCertDerSz, + responder->heap); + ret = wc_ParseCert(decoded, CERT_TYPE, 0, NULL); + if (ret != 0) + goto out; + + if (XMEMCMP(issuer, decoded->subject, WC_ASN_NAME_MAX) != 0) { + /* Issuer name in responder cert does not match subject of issuer cert */ + ret = BAD_FUNC_ARG; + goto out; + } + + ca->authResp = 1; + } + + /* Use responder cert's subject and public key for hashes */ + if (decoded->subjectRawForHash == NULL || + decoded->subjectRawForHashLen <= 0) { + ret = BAD_FUNC_ARG; + goto out; + } + ret = AsnHashesHash(&ca->issuerHashes, decoded->subjectRawForHash, + (word32)decoded->subjectRawForHashLen); + if (ret != 0) + goto out; + if (decoded->publicKeyForHash == NULL || decoded->pubKeyForHashSize == 0) { + ret = BAD_FUNC_ARG; + goto out; + } + ret = AsnHashesHash(&ca->issuerKeyHashes, decoded->publicKeyForHash, + decoded->pubKeyForHashSize); + if (ret != 0) + goto out; + + /* Extract necessary info from decoded cert */ + XMEMCPY(ca->subject, decoded->subject, WC_ASN_NAME_MAX); + + /* Store raw certificate DER if sendCerts is enabled */ + if (responder->sendCerts) { + ca->certDer = (byte*)XMALLOC(signerDerSz, responder->heap, DYNAMIC_TYPE_OCSP); + if (ca->certDer == NULL) { + ret = MEMORY_E; + goto out; + } + XMEMCPY(ca->certDer, signerDer, signerDerSz); + ca->certDerSz = signerDerSz; + } + + /* Load the private key */ +#ifndef NO_RSA + if (keyOID == RSAk) { + ret = wc_InitRsaKey(&ca->key.rsa, responder->heap); + if (ret == 0) { + word32 idx = 0; + ca->keyType = RSAk; + ret = wc_RsaPrivateKeyDecode(keyDer, &idx, &ca->key.rsa, keyDerSz); + } + if (ret != 0) + goto out; + } + else +#endif +#ifdef HAVE_ECC + if (keyOID == ECDSAk) { + ret = wc_ecc_init_ex(&ca->key.ecc, responder->heap, INVALID_DEVID); + if (ret == 0) { + word32 idx = 0; + ca->keyType = ECDSAk; + ret = wc_EccPrivateKeyDecode(keyDer, &idx, &ca->key.ecc, keyDerSz); + } + if (ret != 0) + goto out; + } + else +#endif + { + ret = NOT_COMPILED_IN; + goto out; + } + + /* Check for duplicates before adding */ + { + OcspResponderCa* existing = responder->caList; + while (existing != NULL) { + if (XMEMCMP(&existing->issuerHashes, &ca->issuerHashes, + sizeof(AsnHashes)) == 0 && + XMEMCMP(&existing->issuerKeyHashes, &ca->issuerKeyHashes, + sizeof(AsnHashes)) == 0) { + ret = DUPE_ENTRY_E; + goto out; + } + existing = existing->next; + } + } + + /* Add CA to list */ + ca->next = responder->caList; + responder->caList = ca; + ca = NULL; /* Ownership transferred */ + ret = 0; + +out: + if (decoded != NULL) { + wc_FreeDecodedCert(decoded); + XFREE(decoded, responder->heap, DYNAMIC_TYPE_OCSP); + } + if (ca != NULL) + FreeOcspResponderCa(ca, responder->heap); + return ret; +} + +/* Find Auth CA by comparing cert DER */ +/* Find Auth CA by issuer hashes from request */ +static OcspResponderCa* FindCaByHashes(OcspResponder* responder, + const byte* issuerHash, const byte* issuerKeyHash, int hashAlg) +{ + OcspResponderCa* ca = responder->caList; + + while (ca != NULL) { + int hashSz = 0; + const byte* caIssuerHash = AsnHashesGetHash(&ca->issuerHashes, + hashAlg, &hashSz); + const byte* caKeyHash = AsnHashesGetHash(&ca->issuerKeyHashes, + hashAlg, &hashSz); + + if (caIssuerHash != NULL && caKeyHash != NULL && hashSz > 0 && + XMEMCMP(caIssuerHash, issuerHash, (size_t)hashSz) == 0 && + XMEMCMP(caKeyHash, issuerKeyHash, (size_t)hashSz) == 0) { + return ca; + } + ca = ca->next; + } + + return NULL; +} + +/* Find certificate status in a CA */ +static OcspResponderCertStatus* FindCertStatus(OcspResponderCa* ca, + const byte* serial, word32 serialSz) +{ + OcspResponderCertStatus* status = ca->statuses; + + while (status != NULL) { + if (status->serialSz == (int)serialSz && + XMEMCMP(status->serial, serial, serialSz) == 0) { + return status; + } + status = status->next; + } + + return NULL; +} + +/* Find Auth CA by subject string */ +static OcspResponderCa* FindCaBySubject(OcspResponder* responder, + const char* caSubject, word32 caSubjectSz) +{ + OcspResponderCa* ca = responder->caList; + + while (ca != NULL) { + word32 subjectLen = (word32)XSTRLEN(ca->subject); + if (subjectLen == caSubjectSz && + XMEMCMP(ca->subject, caSubject, caSubjectSz) == 0) { + return ca; + } + ca = ca->next; + } + + return NULL; +} + +/* Add a certificate status for a specific CA */ +int wc_OcspResponder_SetCertStatus(OcspResponder* responder, + const char* caSubject, word32 caSubjectSz, + const byte* serial, word32 serialSz, enum Ocsp_Cert_Status status, + time_t revocationTime, enum WC_CRL_Reason revocationReason, + word32 validityPeriod) +{ + OcspResponderCa* ca = NULL; + OcspResponderCertStatus* certStatus = NULL; + int ret = WC_NO_ERR_TRACE(BAD_FUNC_ARG); + int isNew = 0; + + WOLFSSL_ENTER("wc_OcspResponder_SetCertStatus"); + + if (responder == NULL || caSubject == NULL || caSubjectSz == 0 || + serial == NULL || serialSz == 0 || serialSz > EXTERNAL_SERIAL_SIZE) + goto out; + + if (status != CERT_GOOD && status != CERT_REVOKED && + status != CERT_UNKNOWN) + goto out; + + /* Validate revocation parameters when status is REVOKED */ + if (status == CERT_REVOKED) { + if (revocationTime <= 0) + goto out; + if (revocationReason < CRL_REASON_UNSPECIFIED || + revocationReason > CRL_REASON_AA_COMPROMISE) + goto out; + /* Skip value 7 which is not used */ + if (revocationReason == 7) + goto out; + } + + if (status == CERT_GOOD && validityPeriod == 0) + goto out; + if (status != CERT_GOOD && validityPeriod != 0) + goto out; + + /* Find the CA */ + ca = FindCaBySubject(responder, caSubject, caSubjectSz); + if (ca == NULL) { + ret = ASN_NO_SIGNER_E; + goto out; + } + + /* Check if status already exists for this serial */ + certStatus = FindCertStatus(ca, serial, serialSz); + if (certStatus == NULL) { + /* Allocate new status entry */ + certStatus = (OcspResponderCertStatus*)XMALLOC( + sizeof(OcspResponderCertStatus), responder->heap, DYNAMIC_TYPE_OCSP); + if (certStatus == NULL) { + ret = MEMORY_E; + goto out; + } + XMEMSET(certStatus, 0, sizeof(OcspResponderCertStatus)); + XMEMCPY(certStatus->serial, serial, serialSz); + certStatus->serialSz = serialSz; + isNew = 1; + } + + certStatus->status = status; + certStatus->validityPeriod = 0; + if (status == CERT_REVOKED) { + ret = GetFormattedTime_ex(&revocationTime, certStatus->revocationDate, + sizeof(certStatus->revocationDate), ASN_GENERALIZED_TIME); + if (ret <= 0) { + WOLFSSL_MSG("Failed to format revocation time"); + goto out; + } + certStatus->revocationDateSz = (word32)ret; + certStatus->revocationReason = revocationReason; + } + else if (status == CERT_GOOD) { + certStatus->validityPeriod = validityPeriod; + } + + if (isNew) { + /* Add to CA's status list */ + certStatus->next = ca->statuses; + ca->statuses = certStatus; + certStatus = NULL; /* Ownership transferred */ + } + ret = 0; + +out: + if (isNew && certStatus != NULL) + XFREE(certStatus, responder->heap, DYNAMIC_TYPE_OCSP); + + return ret; +} + +static int OcspResponse_WriteResponse(OcspResponder* responder, byte* response, + word32* responseSz, OcspResponderCa* ca, + OcspResponderCertStatus* certStatus, OcspRequest* req) +{ + OcspResponse resp; + CertStatus status; + OcspEntry entry; + int ret = 0; + time_t now; + RsaKey* rsaKey = NULL; + ecc_key* eccKey = NULL; + int respInited = 0; + int hashSz = 0; + const byte* caIssuerHash = NULL; + const byte* caKeyHash = NULL; + int hashAlg = -1; + + WOLFSSL_ASSERT_SIZEOF_EQ(resp.responderId.keyHash, ca->responderKeyHash); + + WOLFSSL_ENTER("OcspResponse_WriteResponse"); + + if (responseSz == NULL || ca == NULL || certStatus == NULL || + certStatus->serialSz > EXTERNAL_SERIAL_SIZE) { + ret = BAD_FUNC_ARG; + goto out; + } + + InitOcspResponse(&resp, &entry, &status, NULL, 0, responder->heap); + respInited = 1; + + resp.responseStatus = OCSP_SUCCESSFUL; + + status.status = certStatus->status; + + XMEMCPY(status.serial, certStatus->serial, certStatus->serialSz); + status.serialSz = (byte)certStatus->serialSz; + + /* Copy revocation information if status is REVOKED */ + if (certStatus->status == CERT_REVOKED) { + XMEMCPY(status.revocationDate, certStatus->revocationDate, + certStatus->revocationDateSz); + status.revocationDateSz = certStatus->revocationDateSz; + status.revocationReason = (byte)certStatus->revocationReason; + } + + now = wc_Time(NULL); + ret = GetFormattedTime_ex(&now, status.thisDate, sizeof(status.thisDate), + ASN_GENERALIZED_TIME); + if (ret <= 0) { + WOLFSSL_MSG("Failed to format thisUpdate time"); + goto out; + } + XMEMCPY(resp.producedDate, status.thisDate, ret); + resp.producedDateSz = status.thisDateSz = (byte)(ret); + resp.producedDateFormat = status.thisDateFormat = ASN_GENERALIZED_TIME; + + /* Set nextUpdate if validity period is specified (for CERT_GOOD) */ + if (certStatus->status == CERT_GOOD && certStatus->validityPeriod > 0) { + time_t nextTime = now + (time_t)certStatus->validityPeriod; + ret = GetFormattedTime_ex(&nextTime, status.nextDate, + sizeof(status.nextDate), ASN_GENERALIZED_TIME); + if (ret <= 0) { + WOLFSSL_MSG("Failed to format nextUpdate time"); + goto out; + } + status.nextDateSz = (byte)ret; + status.nextDateFormat = ASN_GENERALIZED_TIME; + } + + /* Set certificate if sendCerts is enabled */ + if (responder->sendCerts && ca->certDer != NULL) { + resp.cert = ca->certDer; + resp.certSz = ca->certDerSz; + } + + /* Copy nonce from request to response if present */ + if (req != NULL && req->nonceSz > 0) { + resp.nonce = req->nonce; + resp.nonceSz = req->nonceSz; + } + + /* Echo the hash algorithm from the request */ + if (req == NULL) { + hashAlg = SHAh; /* Fall back to SHA-1 as its required for OCSP */ + } + else { + hashAlg = req->hashAlg; + } + caIssuerHash = AsnHashesGetHash(&ca->issuerHashes, hashAlg, &hashSz); + caKeyHash = AsnHashesGetHash(&ca->issuerKeyHashes, hashAlg, &hashSz); + if (caIssuerHash == NULL || caKeyHash == NULL || hashSz <= 0) { + ret = ASN_SIG_HASH_E; + goto out; + } + entry.hashAlgoOID = (word32)hashAlg; + XMEMCPY(entry.issuerHash, caIssuerHash, (size_t)hashSz); + XMEMCPY(entry.issuerKeyHash, caKeyHash, (size_t)hashSz); + + resp.responderIdType = OCSP_RESPONDER_ID_KEY; + XMEMCPY(resp.responderId.keyHash, ca->responderKeyHash, WC_SHA_DIGEST_SIZE); + + /* TODO allow user to set algo */ + if (ca->keyType == RSAk) { + rsaKey = &ca->key.rsa; + resp.sigOID = CTC_SHA256wRSA; + } + else if (ca->keyType == ECDSAk) { + eccKey = &ca->key.ecc; + resp.sigOID = CTC_SHA256wECDSA; + } + else { + ret = NOT_COMPILED_IN; + goto out; + } + + ret = OcspResponseEncode(&resp, response, responseSz, rsaKey, eccKey, + &responder->rng); + if (ret != 0) { + WOLFSSL_MSG("Failed to encode OCSP response"); + goto out; + } + + ret = 0; +out: + if (respInited) + FreeOcspResponse(&resp); + return ret; +} + +/* Generate OCSP response for a request */ +int wc_OcspResponder_WriteResponse(OcspResponder* responder, + const byte* request, word32 requestSz, + byte* response, word32* responseSz) +{ + int ret = 0; + OcspRequest req; + OcspResponderCa* ca = NULL; + OcspResponderCertStatus* certStatus = NULL; + int reqInited = 0; + + WOLFSSL_ENTER("wc_OcspResponder_WriteResponse"); + + if (responder == NULL || request == NULL || requestSz == 0) { + ret = BAD_FUNC_ARG; + goto out; + } + + XMEMSET(&req, 0, sizeof(OcspRequest)); + + /* Decode the OCSP request */ + ret = DecodeOcspRequest(&req, request, requestSz); + if (ret != 0) { + WOLFSSL_MSG("Failed to decode OCSP request"); + goto out; + } + reqInited = 1; + + /* Find the CA by issuer hashes */ + ca = FindCaByHashes(responder, req.issuerHash, req.issuerKeyHash, + req.hashAlg); + if (ca == NULL) { + WOLFSSL_MSG("No matching CA found for request"); + ret = ASN_NO_SIGNER_E; + goto out; + } + + /* Find the certificate status */ + certStatus = FindCertStatus(ca, req.serial, req.serialSz); + if (certStatus == NULL) { + WOLFSSL_MSG("No status configured for requested certificate"); + ret = OCSP_CERT_UNKNOWN; + goto out; + } + + WOLFSSL_MSG("Found CA and certificate status"); + + ret = OcspResponse_WriteResponse(responder, response, responseSz, ca, + certStatus, &req); + if (ret != 0) { + WOLFSSL_MSG("Failed to write OCSP response"); + goto out; + } + + ret = 0; +out: + if (reqInited) + FreeOcspRequest(&req); + return ret; +} + +int wc_OcspResponder_WriteErrorResponse(enum Ocsp_Response_Status status, + byte* response, word32* responseSz) +{ + int ret = 0; + OcspResponse resp; + int respInited = 0; + + WOLFSSL_ENTER("wc_OcspResponder_WriteErrorResponse"); + + if (responseSz == NULL) { + ret = BAD_FUNC_ARG; + goto out; + } + + /* Validate status - OCSP_SUCCESSFUL is not allowed for error responses */ + if (status == OCSP_SUCCESSFUL) { + WOLFSSL_MSG("OCSP_SUCCESSFUL is not a valid error status"); + ret = BAD_FUNC_ARG; + goto out; + } + + /* Validate that status is a known enumeration value */ + if (status != OCSP_MALFORMED_REQUEST && + status != OCSP_INTERNAL_ERROR && + status != OCSP_TRY_LATER && + status != OCSP_SIG_REQUIRED && + status != OCSP_UNAUTHORIZED) { + WOLFSSL_MSG("Invalid OCSP response status"); + ret = BAD_FUNC_ARG; + goto out; + } + + /* Initialize a minimal OCSP response structure */ + InitOcspResponse(&resp, NULL, NULL, NULL, 0, NULL); + respInited = 1; + + /* Set the error status */ + resp.responseStatus = (byte)status; + + /* Encode the error response (no responseBytes, just status) */ + ret = OcspResponseEncode(&resp, response, responseSz, NULL, NULL, NULL); + if (ret != 0) { + WOLFSSL_MSG("Failed to encode OCSP error response"); + goto out; + } + + ret = 0; +out: + if (respInited) + FreeOcspResponse(&resp); + return ret; +} + +/* Helper functions for testing */ +int wc_InitOcspRequest(OcspRequest* req, DecodedCert* cert, + byte useNonce, void* heap) +{ + return InitOcspRequest(req, cert, useNonce, heap); +} + +int wc_EncodeOcspRequest(OcspRequest* req, byte* output, + word32 size) +{ + return EncodeOcspRequest(req, output, size); +} + +#endif /* HAVE_OCSP_RESPONDER */ + #else /* HAVE_OCSP */ diff --git a/src/tls13.c b/src/tls13.c index 101b31541ad..447f57bdc51 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -9069,10 +9069,11 @@ static int SendTls13Certificate(WOLFSSL* ssl) if (extIdx != 0 && extIdx < MAX_CERT_EXTENSIONS && ssl->buffers.certExts[extIdx] != NULL && - offset == len + extSz[extIdx]) + offset == len + extSz[extIdx]) { FreeDer(&ssl->buffers.certExts[extIdx]); - /* for next chain cert */ - len += extSz[extIdx] - OPAQUE16_LEN; + /* for next chain cert */ + len += extSz[extIdx] - OPAQUE16_LEN; + } } } diff --git a/tests/api.c b/tests/api.c index 3af30a55422..6a13cd79720 100644 --- a/tests/api.c +++ b/tests/api.c @@ -3588,7 +3588,7 @@ static int test_wolfSSL_CTX_use_certificate_chain_buffer_format(void) wolfSSL_CTX_free(ctx); #ifndef NO_FILESYSTEM if (buf != NULL) { - free(buf); + XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); } #endif #endif @@ -11400,7 +11400,7 @@ static int test_wc_PemToDer(void) pDer = NULL; if (cert_buf != NULL) { - free(cert_buf); + XFREE(cert_buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); cert_buf = NULL; } @@ -11412,7 +11412,7 @@ static int test_wc_PemToDer(void) pDer = NULL; if (cert_buf != NULL) { - free(cert_buf); + XFREE(cert_buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); cert_buf = NULL; } @@ -11434,7 +11434,7 @@ static int test_wc_PemToDer(void) #endif wc_FreeDer(&pDer); if (cert_buf != NULL) - free(cert_buf); + XFREE(cert_buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); } #endif #endif @@ -11491,7 +11491,7 @@ static int test_wc_CertPemToDer(void) if (cert_der != NULL) free(cert_der); if (cert_buf != NULL) - free(cert_buf); + XFREE(cert_buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); #endif return EXPECT_RESULT(); } @@ -11605,7 +11605,7 @@ static int test_wc_PubKeyPemToDer(void) } if (cert_buf != NULL) { - free(cert_buf); + XFREE(cert_buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); } #endif return EXPECT_RESULT(); @@ -11994,7 +11994,7 @@ static int test_wc_CheckCertSigPubKey(void) if (cert_der != NULL) free(cert_der); if (cert_buf != NULL) - free(cert_buf); + XFREE(cert_buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); #endif return EXPECT_RESULT(); } @@ -18596,7 +18596,7 @@ static int test_wolfSSL_d2i_and_i2d_PublicKey_ecc(void) const unsigned char* p; unsigned char *der = NULL; unsigned char *tmp = NULL; - int derLen; + int derLen = 0; unsigned char pub_buf[65]; unsigned char pub_spki_buf[91]; const int pub_len = 65; @@ -18706,7 +18706,7 @@ static int test_wolfSSL_d2i_and_i2d_DSAparams(void) }; int derInLen = sizeof(derIn); byte* derOut = NULL; - int derOutLen; + int derOutLen = 0; byte* p = derIn; /* Check that params can be successfully decoded. */ @@ -21578,9 +21578,9 @@ static int test_RsaSigFailure_cm(void) #endif } - /* load_file() uses malloc. */ + /* load_file() uses XMALLOC. */ if (cert_buf != NULL) { - free(cert_buf); + XFREE(cert_buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); } #endif /* !NO_RSA */ return EXPECT_RESULT(); @@ -21619,9 +21619,9 @@ static int test_EccSigFailure_cm(void) #endif } - /* load_file() uses malloc. */ + /* load_file() uses XMALLOC. */ if (cert_buf != NULL) { - free(cert_buf); + XFREE(cert_buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); } #ifdef FP_ECC wc_ecc_fp_free(); @@ -26049,7 +26049,7 @@ static int load_pem_key_file_as_der(const char* privKeyFile, DerBuffer** pDer, } if (key_buf != NULL) { - free(key_buf); key_buf = NULL; + XFREE(key_buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); key_buf = NULL; } (void)encInfo; /* not used in this test */ @@ -32993,6 +32993,7 @@ TEST_CASE testCases[] = { TEST_DECL(test_ocsp_response_parsing), TEST_DECL(test_ocsp_certid_enc_dec), TEST_DECL(test_ocsp_tls_cert_cb), + TEST_DECL(test_ocsp_responder), TEST_TLS_DECLS, TEST_DECL(test_wc_DhSetNamedKey), /* This test needs to stay at the end to clean up any caches allocated. */ diff --git a/tests/api/test_certman.c b/tests/api/test_certman.c index 1e72ee44362..0d811873703 100644 --- a/tests/api/test_certman.c +++ b/tests/api/test_certman.c @@ -269,7 +269,7 @@ static int test_cm_load_ca_file(const char* ca_cert_file) if (ret == WOLFSSL_SUCCESS) { /* test including null terminator in length */ - byte* tmp = (byte*)realloc(cert_buf, cert_sz+1); + byte* tmp = (byte*)XREALLOC(cert_buf, cert_sz+1, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (tmp == NULL) { ret = MEMORY_E; } @@ -297,7 +297,7 @@ static int test_cm_load_ca_file(const char* ca_cert_file) #endif } - free(cert_buf); + XFREE(cert_buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); return ret; } @@ -339,7 +339,7 @@ static int test_cm_load_ca_file_ex(const char* ca_cert_file, word32 flags) if (ret == WOLFSSL_SUCCESS) { /* test including null terminator in length */ - byte* tmp = (byte*)realloc(cert_buf, cert_sz+1); + byte* tmp = (byte*)XREALLOC(cert_buf, cert_sz+1, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (tmp == NULL) { ret = MEMORY_E; } @@ -367,7 +367,7 @@ static int test_cm_load_ca_file_ex(const char* ca_cert_file, word32 flags) #endif } - free(cert_buf); + XFREE(cert_buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); return ret; } @@ -614,13 +614,13 @@ int test_wolfSSL_CertManagerLoadCABufferType(void) if (cm) wolfSSL_CertManagerFree(cm); if (ca_cert_buf) - free(ca_cert_buf); + XFREE(ca_cert_buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (int1_cert_buf) - free(int1_cert_buf); + XFREE(int1_cert_buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (int2_cert_buf) - free(int2_cert_buf); + XFREE(int2_cert_buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (client_cert_buf) - free(client_cert_buf); + XFREE(client_cert_buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); #endif return EXPECT_RESULT(); diff --git a/tests/api/test_ocsp.c b/tests/api/test_ocsp.c index 5fc26252086..36afe4c3aff 100644 --- a/tests/api/test_ocsp.c +++ b/tests/api/test_ocsp.c @@ -26,6 +26,7 @@ #include #include #include +#include #if defined(HAVE_OCSP) && !defined(NO_SHA) && !defined(NO_RSA) struct ocsp_cb_ctx { @@ -1025,3 +1026,294 @@ int test_ocsp_tls_cert_cb(void) return TEST_SKIPPED; } #endif + +#if defined(HAVE_OCSP_RESPONDER) && defined(WOLFSSL_ASN_TEMPLATE) && \ + !defined(NO_SHA) && !defined(NO_RSA) +/* Structure to hold test configuration */ +typedef struct { + const char* caCertPath; + const char* responderCertPath; + const char* responderKeyPath; + const char* targetCertPath; + enum Ocsp_Cert_Status certStatus; + time_t revocationTime; /* Used when status is CERT_REVOKED */ + enum WC_CRL_Reason revocationReason; /* Used when status is CERT_REVOKED */ + word32 validityPeriod; /* Used when status is CERT_GOOD */ + int expectedResult; + const char* testName; +} OcspResponderTestConfig; + +/* Helper function to run a single OCSP responder test configuration */ +static int ocspResponderTest_Run(OcspResponderTestConfig* config, int sendCerts) +{ + EXPECT_DECLS; + OcspResponder* responder = NULL; + OcspRequest* clientReq = NULL; + DecodedCert targetCert; + DecodedCert decodedCaCert; + WOLFSSL_CERT_MANAGER* cm = NULL; + byte caCertDer[4096]; + byte responderCertDer[4096]; + byte responderKeyDer[4096]; + byte targetCertDer[4096]; + byte* respBuf = NULL; + word32 caCertSz = sizeof(caCertDer); + word32 responderCertSz = sizeof(responderCertDer); + word32 responderKeyDerSz = sizeof(responderKeyDer); + word32 targetCertSz = sizeof(targetCertDer); + word32 respSz = 0; + byte reqBuf[1024]; + int reqSz = 0; + const char* caSubject = NULL; + word32 caSubjectSz = 0; + const byte* serial = NULL; + word32 serialSz = 0; + XFILE f = XBADFILE; + byte usingAuthCa = XSTRCMP(config->caCertPath, config->responderCertPath) != 0; + + printf("\nRunning OCSP Responder Test: %s (sendCerts=%d)\n", + config->testName, sendCerts); + + XMEMSET(&targetCert, 0, sizeof(targetCert)); + XMEMSET(&decodedCaCert, 0, sizeof(decodedCaCert)); + + /* Create certificate manager */ + ExpectNotNull(cm = wolfSSL_CertManagerNew()); + + /* Load CA certificate */ + ExpectTrue((f = XFOPEN(config->caCertPath, "rb")) != XBADFILE); + ExpectIntGT(caCertSz = (word32)XFREAD(caCertDer, 1, + caCertSz, f), 0); + if (f != XBADFILE) { + XFCLOSE(f); + f = XBADFILE; + } + ExpectIntEQ(wolfSSL_CertManagerLoadCABuffer(cm, caCertDer, caCertSz, + WOLFSSL_FILETYPE_ASN1), + WOLFSSL_SUCCESS); + + wc_InitDecodedCert(&decodedCaCert, caCertDer, caCertSz, NULL); + ExpectIntEQ(wc_ParseCert(&decodedCaCert, CERT_TYPE, 0, NULL), 0); + + /* Load responder certificate */ + ExpectTrue((f = XFOPEN(config->responderCertPath, "rb")) != XBADFILE); + ExpectIntGT(responderCertSz = (word32)XFREAD(responderCertDer, 1, + responderCertSz, f), 0); + if (f != XBADFILE) { + XFCLOSE(f); + f = XBADFILE; + } + + if (usingAuthCa && !sendCerts) { + /* If responder is not sending certs then it must be loaded into cm */ + ExpectIntEQ(wolfSSL_CertManagerLoadCABuffer(cm, responderCertDer, + responderCertSz, WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS); + } + + /* Load responder private key */ + ExpectTrue((f = XFOPEN(config->responderKeyPath, "rb")) != XBADFILE); + ExpectIntGT(responderKeyDerSz = (word32)XFREAD(responderKeyDer, 1, + responderKeyDerSz, f), 0); + if (f != XBADFILE) { + XFCLOSE(f); + f = XBADFILE; + } + + /* Load target certificate */ + ExpectTrue((f = XFOPEN(config->targetCertPath, "rb")) != XBADFILE); + ExpectIntGT(targetCertSz = (word32)XFREAD(targetCertDer, 1, + targetCertSz, f), 0); + if (f != XBADFILE) { + XFCLOSE(f); + f = XBADFILE; + } + + /* Parse target certificate */ + wc_InitDecodedCert(&targetCert, targetCertDer, targetCertSz, NULL); + ExpectIntEQ(wc_ParseCert(&targetCert, CERT_TYPE, 0, cm), 0); + + /* Create OCSP request from target certificate */ + ExpectNotNull(clientReq = wc_OcspRequest_new(NULL)); + ExpectIntEQ(wc_InitOcspRequest(clientReq, &targetCert, 1, NULL), 0); + ExpectIntGT(reqSz = wc_EncodeOcspRequest(clientReq, reqBuf, + sizeof(reqBuf)), 0); + + /* Create OCSP Responder */ + ExpectNotNull(responder = wc_OcspResponder_new(NULL, sendCerts)); + + /* Add responder (authorized) to responder */ + ExpectIntEQ(wc_OcspResponder_AddSigner(responder, + responderCertDer, responderCertSz, + responderKeyDer, responderKeyDerSz, + usingAuthCa ? caCertDer : NULL, usingAuthCa ? caCertSz : 0), 0); + + /* Set certificate status */ + ExpectNotNull(caSubject = wc_GetDecodedCertSubject(&decodedCaCert, &caSubjectSz)); + ExpectIntGT(caSubjectSz, 0); + ExpectNotNull(serial = wc_GetDecodedCertSerial(&targetCert, &serialSz)); + ExpectIntGT(serialSz, 0); + + ExpectIntEQ(wc_OcspResponder_SetCertStatus(responder, + caSubject, caSubjectSz, + serial, serialSz, + config->certStatus, + config->revocationTime, + config->revocationReason, + config->validityPeriod), 0); + + /* Get required response size */ + ExpectIntEQ(wc_OcspResponder_WriteResponse(responder, reqBuf, reqSz, + NULL, &respSz), 0); + + /* Allocate response buffer */ + ExpectNotNull(respBuf = (byte*)XMALLOC(respSz, NULL, + DYNAMIC_TYPE_TMP_BUFFER)); + + /* Generate OCSP response */ + ExpectIntEQ(wc_OcspResponder_WriteResponse(responder, reqBuf, reqSz, + respBuf, &respSz), 0); + + /* Verify response matches expected result */ + { + WOLFSSL_OCSP* ocsp = NULL; + ExpectNotNull(ocsp = wc_NewOCSP(cm)); + ExpectIntEQ(wc_CheckCertOcspResponse(ocsp, &targetCert, respBuf, + respSz, NULL), config->expectedResult); + wc_FreeOCSP(ocsp); + } + + /* Cleanup */ + XFREE(respBuf, NULL, DYNAMIC_TYPE_TMP_BUFFER); + wc_OcspRequest_free(clientReq); + wc_OcspResponder_free(responder); + wc_FreeDecodedCert(&targetCert); + wc_FreeDecodedCert(&decodedCaCert); + wolfSSL_CertManagerFree(cm); + + return EXPECT_RESULT(); +} + +int test_ocsp_responder(void) +{ + EXPECT_DECLS; + time_t now = wc_Time(NULL); + OcspResponderTestConfig configs[] = { + { + "./certs/ca-cert.der", + "./certs/ca-cert.der", + "./certs/ca-key.der", + "./certs/server-cert.der", + CERT_GOOD, + 0, CRL_REASON_UNSPECIFIED, + 86400, /* validityPeriod - 24 hours */ + 0, + "RSA server cert - GOOD status" + }, + { + "./certs/ca-cert.der", + "./certs/ca-cert.der", + "./certs/ca-key.der", + "./certs/server-cert.der", + CERT_REVOKED, + now, CRL_REASON_KEY_COMPROMISE, /* Revoked due to key compromise */ + 0, /* validityPeriod (not used for REVOKED) */ + OCSP_CERT_REVOKED, + "RSA server cert - REVOKED status" + }, + { + "./certs/ca-cert.der", + "./certs/ca-cert.der", + "./certs/ca-key.der", + "./certs/server-cert.der", + CERT_UNKNOWN, + 0, CRL_REASON_UNSPECIFIED, + 0, /* validityPeriod (not used for UNKNOWN) */ + OCSP_CERT_UNKNOWN, + "RSA server cert - UNKNOWN status" + }, + { + "./certs/ocsp/root-ca-cert.der", + "./certs/ocsp/ocsp-responder-cert.der", + "./certs/ocsp/ocsp-responder-key.der", + "./certs/ocsp/intermediate1-ca-cert.der", + CERT_GOOD, + 0, CRL_REASON_UNSPECIFIED, + 86400, /* validityPeriod - 24 hours */ + 0, + "RSA int1 cert with responder - GOOD status" + }, + { + "./certs/ocsp/root-ca-cert.der", + "./certs/ocsp/ocsp-responder-cert.der", + "./certs/ocsp/ocsp-responder-key.der", + "./certs/ocsp/intermediate1-ca-cert.der", + CERT_REVOKED, + now, CRL_REASON_KEY_COMPROMISE, /* Revoked due to key compromise */ + 0, /* validityPeriod (not used for REVOKED) */ + OCSP_CERT_REVOKED, + "RSA int1 cert with responder - REVOKED status" + }, + { + "./certs/ocsp/root-ca-cert.der", + "./certs/ocsp/ocsp-responder-cert.der", + "./certs/ocsp/ocsp-responder-key.der", + "./certs/ocsp/intermediate1-ca-cert.der", + CERT_UNKNOWN, + 0, CRL_REASON_UNSPECIFIED, + 0, /* validityPeriod (not used for UNKNOWN) */ + OCSP_CERT_UNKNOWN, + "RSA int1 cert with responder - UNKNOWN status" + }, +#ifdef HAVE_ECC + { + "./certs/ca-ecc-cert.der", + "./certs/ca-ecc-cert.der", + "./certs/ca-ecc-key.der", + "./certs/server-ecc.der", + CERT_GOOD, + 0, CRL_REASON_UNSPECIFIED, + 86400, /* validityPeriod - 24 hours */ + 0, + "ECC server cert - GOOD status" + }, + { + "./certs/ca-ecc-cert.der", + "./certs/ca-ecc-cert.der", + "./certs/ca-ecc-key.der", + "./certs/server-ecc.der", + CERT_REVOKED, + now, CRL_REASON_AFFILIATION_CHANGED, + 0, /* validityPeriod (not used for REVOKED) */ + OCSP_CERT_REVOKED, + "ECC server cert - REVOKED status" + }, + { + "./certs/ca-ecc-cert.der", + "./certs/ca-ecc-cert.der", + "./certs/ca-ecc-key.der", + "./certs/server-ecc.der", + CERT_UNKNOWN, + 0, CRL_REASON_UNSPECIFIED, + 0, /* validityPeriod (not used for UNKNOWN) */ + OCSP_CERT_UNKNOWN, + "ECC server cert - UNKNOWN status" + } +#endif + }; + int i; + int numTests = (int)(sizeof(configs) / sizeof(configs[0])); + + /* Run each test configuration twice: once without certs, once with certs */ + for (i = 0; i < numTests; i++) { + ExpectIntEQ(ocspResponderTest_Run(&configs[i], 0), TEST_SUCCESS); + ExpectIntEQ(ocspResponderTest_Run(&configs[i], 1), TEST_SUCCESS); + } + + return EXPECT_RESULT(); +} +#else +int test_ocsp_responder(void) +{ + return TEST_SKIPPED; +} +#endif /* HAVE_OCSP_RESPONDER && !NO_SHA && !NO_RSA */ diff --git a/tests/api/test_ocsp.h b/tests/api/test_ocsp.h index 1760727eb72..9c5592a23da 100644 --- a/tests/api/test_ocsp.h +++ b/tests/api/test_ocsp.h @@ -27,5 +27,6 @@ int test_ocsp_status_callback(void); int test_ocsp_basic_verify(void); int test_ocsp_response_parsing(void); int test_ocsp_tls_cert_cb(void); +int test_ocsp_responder(void); #endif /* WOLFSSL_TEST_OCSP_H */ diff --git a/tests/api/test_pkcs7.c b/tests/api/test_pkcs7.c index edbbb2b4efa..4fd6e663168 100644 --- a/tests/api/test_pkcs7.c +++ b/tests/api/test_pkcs7.c @@ -3051,7 +3051,7 @@ int test_wc_PKCS7_GetEnvelopedDataKariRid(void) byte rid[256]; byte cms[1024]; XFILE cmsFile = XBADFILE; - int ret; + int ret = 0; word32 ridSz = sizeof(rid); XFILE skiHexFile = XBADFILE; byte skiHex[256]; diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index 658d5b53a94..e1a4e9cbfb7 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -540,7 +540,8 @@ static word32 SizeASNLength(word32 length) * @param [in, out] err Error variable. * @param [in] heap Dynamic memory allocation hint. */ - #define ALLOC_ASNGETDATA(name, cnt, err, heap) WC_DO_NOTHING + #define ALLOC_ASNGETDATA(name, cnt, err, heap) \ + (void)(cnt); (void)(err); (void)(heap); WC_DO_NOTHING /* Clears the memory of the dynamic BER encoding data. * @@ -550,14 +551,15 @@ static word32 SizeASNLength(word32 length) * @param [in] heap Dynamic memory allocation hint. */ #define CALLOC_ASNGETDATA(name, cnt, err, heap) \ - XMEMSET(name, 0, sizeof(name)) + ((void)(cnt), (void)(err), (void)(heap), XMEMSET(name, 0, sizeof(name))) /* No implementation as declaration is static. * * @param [in] name Variable name to declare. * @param [in] heap Dynamic memory allocation hint. */ - #define FREE_ASNGETDATA(name, heap) WC_DO_NOTHING + #define FREE_ASNGETDATA(name, heap) \ + (void)(name); (void)(heap); WC_DO_NOTHING /* Declare the variable that is the dynamic data for encoding DER data. * @@ -574,7 +576,8 @@ static word32 SizeASNLength(word32 length) * @param [in, out] err Error variable. * @param [in] heap Dynamic memory allocation hint. */ - #define ALLOC_ASNSETDATA(name, cnt, err, heap) WC_DO_NOTHING + #define ALLOC_ASNSETDATA(name, cnt, err, heap) \ + (void)(cnt); (void)(err); (void)(heap); WC_DO_NOTHING /* Clears the memory of the dynamic BER encoding data. * @@ -584,14 +587,15 @@ static word32 SizeASNLength(word32 length) * @param [in] heap Dynamic memory allocation hint. */ #define CALLOC_ASNSETDATA(name, cnt, err, heap) \ - XMEMSET(name, 0, sizeof(name)) + ((void)(cnt), (void)(err), (void)(heap), XMEMSET(name, 0, sizeof(name))) /* No implementation as declaration is static. * * @param [in] name Variable name to declare. * @param [in] heap Dynamic memory allocation hint. */ - #define FREE_ASNSETDATA(name, heap) WC_DO_NOTHING + #define FREE_ASNSETDATA(name, heap) \ + (void)(name); (void)(heap); WC_DO_NOTHING #endif @@ -993,8 +997,9 @@ int SetASN_Items(const ASNItem* asn, ASNSetData *data, int count, byte* output) } else { /* Dump in the DER encoded data. */ - XMEMCPY(out + idx, data[i].data.buffer.data, - data[i].data.buffer.length); + /* Allow data to come from output buffer. */ + XMEMMOVE(out + idx, data[i].data.buffer.data, + data[i].data.buffer.length); } break; @@ -1059,7 +1064,7 @@ static int GetOID(const byte* input, word32* inOutIdx, word32* oid, word32 oidType, int length); /* Maximum supported depth in ASN.1 description. */ -#define GET_ASN_MAX_DEPTH 7 +#define GET_ASN_MAX_DEPTH 8 /* Maximum number of checked numbered choices. Only one of the items with the * number is allowed. */ @@ -1532,7 +1537,7 @@ static int GetASN_StoreData(const ASNItem* asn, ASNGetData* data, * @return ASN_UNKNOWN_OID_E when the OID cannot be verified. */ int GetASN_Items(const ASNItem* asn, ASNGetData *data, int count, int complete, - const byte* input, word32* inOutIdx, word32 length) + const byte* input, word32* inOutIdx, const word32 length) { int i; int j; @@ -1574,12 +1579,12 @@ int GetASN_Items(const ASNItem* asn, ASNGetData *data, int count, int complete, data[i].length = 0; /* Get current item depth. */ depth = asn[i].depth; + if (depth >= GET_ASN_MAX_DEPTH) { #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE - if (depth > GET_ASN_MAX_DEPTH) { WOLFSSL_MSG("Depth in template too large"); + #endif return ASN_PARSE_E; } - #endif /* Keep track of minimum depth. */ if (depth < minDepth) { minDepth = depth; @@ -1705,6 +1710,12 @@ int GetASN_Items(const ASNItem* asn, ASNGetData *data, int count, int complete, } /* Store length of data. */ data[i].length = (word32)len; + if (depth + 1 >= GET_ASN_MAX_DEPTH) { + #ifdef WOLFSSL_DEBUG_ASN_TEMPLATE + WOLFSSL_MSG("Depth in template too large"); + #endif + return ASN_PARSE_E; + } /* Note the max length of items under this one. */ endIdx[depth + 1] = idx + (word32)len; if (choice > 1) { @@ -8484,7 +8495,8 @@ int wc_CreatePKCS8Key(byte* out, word32* outSz, byte* key, word32 keySz, (void)tmpAlgId; #endif - CALLOC_ASNSETDATA(dataASN, pkcs8KeyASN_Length-1, ret, NULL); + if (ret == 0) + CALLOC_ASNSETDATA(dataASN, pkcs8KeyASN_Length-1, ret, NULL); if (ret == 0) { /* Only support default PKCS #8 format - v0. */ @@ -10376,7 +10388,8 @@ static int EncryptContentPBES2(byte* input, word32 inputSz, byte* out, &blockSz) < 0) { ret = ASN_INPUT_E; } - CALLOC_ASNSETDATA(dataASN, p8EncPbes2ASN_Length, ret, heap); + if (ret == 0) + CALLOC_ASNSETDATA(dataASN, p8EncPbes2ASN_Length, ret, heap); if (ret == 0) { /* Setup data to go into encoding including PBE algorithm, salt, @@ -10741,8 +10754,8 @@ int EncryptContent(byte* input, word32 inputSz, byte* out, word32* outSz, return EncryptContentPBES2(input, inputSz, out, outSz, password, passwordSz, encAlgId, salt, saltSz, itt, hmacOid, rng, heap); } - - CALLOC_ASNSETDATA(dataASN, p8EncPbes1ASN_Length, ret, heap); + if (ret == 0) + CALLOC_ASNSETDATA(dataASN, p8EncPbes1ASN_Length, ret, heap); if (ret == 0) { /* Setup data to go into encoding including PBE algorithm, salt, @@ -11424,6 +11437,9 @@ int wc_DhKeyDecode(const byte* input, word32* inOutIdx, DhKey* key, word32 inSz) if (inOutIdx == NULL) return BAD_FUNC_ARG; + if (key == NULL) + return BAD_FUNC_ARG; + if (GetSequence(input, inOutIdx, &length, inSz) < 0) return ASN_PARSE_E; @@ -11519,11 +11535,13 @@ int wc_DhKeyDecode(const byte* input, word32* inOutIdx, DhKey* key, word32 inSz) ret = BAD_FUNC_ARG; } + if (ret == 0) { #ifdef WOLFSSL_DH_EXTRA - ALLOC_ASNGETDATA(dataASN, dhKeyPkcs8ASN_Length, ret, key->heap); + ALLOC_ASNGETDATA(dataASN, dhKeyPkcs8ASN_Length, ret, key->heap); #else - ALLOC_ASNGETDATA(dataASN, dhParamASN_Length, ret, key->heap); + ALLOC_ASNGETDATA(dataASN, dhParamASN_Length, ret, key->heap); #endif + } if (ret == 0) { /* Initialize data and set mp_ints to hold p and g. */ @@ -11568,7 +11586,7 @@ int wc_DhKeyDecode(const byte* input, word32* inOutIdx, DhKey* key, word32 inSz) #endif } - FREE_ASNGETDATA(dataASN, key->heap); + FREE_ASNGETDATA(dataASN, key != NULL ? key->heap : NULL); return ret; #endif /* WOLFSSL_ASN_TEMPLATE */ } @@ -12282,7 +12300,8 @@ int wc_DsaPrivateKeyDecode(const byte* input, word32* inOutIdx, DsaKey* key, ret = BAD_FUNC_ARG; } - CALLOC_ASNGETDATA(dataASN, dsaKeyASN_Length, ret, key->heap); + if (ret == 0) + CALLOC_ASNGETDATA(dataASN, dsaKeyASN_Length, ret, key->heap); if (ret == 0) { int i; @@ -12319,7 +12338,7 @@ int wc_DsaPrivateKeyDecode(const byte* input, word32* inOutIdx, DsaKey* key, key->type = DSA_PRIVATE; } - FREE_ASNGETDATA(dataASN, key->heap); + FREE_ASNGETDATA(dataASN, key != NULL ? key->heap : NULL); return ret; #endif } @@ -12520,7 +12539,8 @@ int wc_SetDsaPublicKey(byte* output, DsaKey* key, int outLen, int with_header) ret = BAD_FUNC_ARG; } - CALLOC_ASNSETDATA(dataASN, dsaPubKeyASN_Length, ret, key->heap); + if (ret == 0) + CALLOC_ASNSETDATA(dataASN, dsaPubKeyASN_Length, ret, key->heap); if (ret == 0) { if (with_header) { @@ -12559,7 +12579,7 @@ int wc_SetDsaPublicKey(byte* output, DsaKey* key, int outLen, int with_header) ret = SetASN_Items(data, dataASN, count, output); } - FREE_ASNSETDATA(dataASN, key->heap); + FREE_ASNSETDATA(dataASN, key != NULL ? key->heap : NULL); return ret; #endif /* WOLFSSL_ASN_TEMPLATE */ } @@ -12672,8 +12692,8 @@ static int DsaKeyIntsToDer(DsaKey* key, byte* output, word32* inLen, if ((ret == 0) && (ints > DSA_INTS)) { ret = BAD_FUNC_ARG; } - - CALLOC_ASNSETDATA(dataASN, dsaKeyASN_Length, ret, key->heap); + if (ret == 0) + CALLOC_ASNSETDATA(dataASN, dsaKeyASN_Length, ret, key->heap); if (ret == 0) { int i; @@ -13052,6 +13072,11 @@ static int StoreKey(DecodedCert* cert, const byte* source, word32* srcIdx, cert->pubKeyStored = 1; cert->pubKeySize = (word32)length; +#ifdef HAVE_OCSP_RESPONDER + cert->publicKeyForHash = cert->publicKey; + cert->pubKeyForHashSize = cert->pubKeySize; +#endif + *srcIdx += (word32)length; } } @@ -13682,6 +13707,10 @@ static int StoreRsaKey(DecodedCert* cert, const byte* source, word32* srcIdx, cert->sigCtx.CertAtt.pubkey_n_start = cert->sigCtx.CertAtt.pubkey_e_start = dataASN[RSACERTKEYASN_IDX_SEQ].offset; #endif + #ifdef HAVE_OCSP_RESPONDER + cert->publicKeyForHash = cert->publicKey; + cert->pubKeyForHashSize = cert->pubKeySize; + #endif #ifdef HAVE_OCSP /* Calculate the hash of the public key for OCSP. */ ret = CalcHashId_ex(cert->publicKey, cert->pubKeySize, @@ -13861,6 +13890,13 @@ static int StoreEccKey(DecodedCert* cert, const byte* source, word32* srcIdx, + 1; #endif + #ifdef HAVE_OCSP_RESPONDER + cert->publicKeyForHash = + dataASN[ECCCERTKEYASN_IDX_SUBJPUBKEY].data.ref.data; + cert->pubKeyForHashSize = + dataASN[ECCCERTKEYASN_IDX_SUBJPUBKEY].data.ref.length; + #endif + #ifdef HAVE_OCSP if (ret == 0) { /* Calculate the hash of the subject public key for OCSP. */ @@ -16026,6 +16062,13 @@ static int GetCertName(DecodedCert* cert, char* full, byte* hash, int nameType, ret = ASN_PARSE_E; } +#ifdef HAVE_OCSP_RESPONDER + if (ret == 0 && nameType == ASN_SUBJECT) { + cert->subjectRawForHash = input + srcIdx; + cert->subjectRawForHashLen = (int)(maxIdx - srcIdx); + } +#endif + CALLOC_ASNGETDATA(dataASN, rdnASN_Length, ret, cert->heap); #ifdef WOLFSSL_X509_NAME_AVAILABLE @@ -16485,7 +16528,8 @@ static int ValidateGmtime(struct tm* inTime) } #if !defined(NO_ASN_TIME) && !defined(USER_TIME) && \ - !defined(TIME_OVERRIDES) && (defined(OPENSSL_EXTRA) || defined(HAVE_PKCS7)) + !defined(TIME_OVERRIDES) && (defined(OPENSSL_EXTRA) || \ + defined(HAVE_PKCS7) || defined(HAVE_OCSP_RESPONDER)) /* Set current time string, either UTC or GeneralizedTime. * (void*) currTime should be a pointer to time_t, output is placed in buf. * @@ -16502,7 +16546,7 @@ int GetAsnTimeString(void* currTime, byte* buf, word32 len) return BAD_FUNC_ARG; XMEMSET(uf_time, 0, sizeof(uf_time)); - /* GetFormattedTime returns length with null terminator */ + /* GetFormattedTime returns length without null terminator */ data_len = GetFormattedTime(currTime, uf_time, (word32)sizeof(uf_time)); if (data_len <= 0) { return ASN_TIME_E; @@ -16541,6 +16585,13 @@ int GetAsnTimeString(void* currTime, byte* buf, word32 len) /* return just the time string as either UTC or Generalized Time*/ int GetFormattedTime(void* currTime, byte* buf, word32 len) +{ + WOLFSSL_ENTER("GetFormattedTime"); + + return GetFormattedTime_ex(currTime, buf, len, 0); +} + +int GetFormattedTime_ex(void* currTime, byte* buf, word32 len, byte format) { struct tm* ts = NULL; struct tm* tmpTime = NULL; @@ -16553,9 +16604,10 @@ int GetFormattedTime(void* currTime, byte* buf, word32 len) /* Needed in case XGMTIME does not use the tmpTime argument. */ (void)tmpTime; - WOLFSSL_ENTER("GetFormattedTime"); + WOLFSSL_ENTER("GetFormattedTime_ex"); - if (buf == NULL || len == 0) + if (buf == NULL || len == 0 || (format != 0 && format != ASN_UTC_TIME && + format != ASN_GENERALIZED_TIME)) return BAD_FUNC_ARG; ts = (struct tm *)XGMTIME((time_t*)currTime, tmpTime); @@ -16566,8 +16618,16 @@ int GetFormattedTime(void* currTime, byte* buf, word32 len) /* Note ASN_UTC_TIME_SIZE and ASN_GENERALIZED_TIME_SIZE include space for * the null terminator. ASN encoded values leave off the terminator. */ + if (format == 0) { + if (ts->tm_year >= 50 && ts->tm_year < 150) { + format = ASN_UTC_TIME; + } + else { + format = ASN_GENERALIZED_TIME; + } + } - if (ts->tm_year >= 50 && ts->tm_year < 150) { + if (format == ASN_UTC_TIME) { /* UTC Time */ if (ts->tm_year >= 50 && ts->tm_year < 100) { year = ts->tm_year; @@ -24629,6 +24689,33 @@ int wc_ParseCert(DecodedCert* cert, int type, int verify, void* cm) return ParseCert(cert, type, verify, cm); } +const char* wc_GetDecodedCertSubject(struct DecodedCert* cert, word32* subjectSz) +{ + if (cert == NULL || subjectSz == NULL) { + return NULL; + } + *subjectSz = (word32)XSTRLEN(cert->subject); + return cert->subject; +} + +const char* wc_GetDecodedCertIssuer(struct DecodedCert* cert, word32* issuerSz) +{ + if (cert == NULL || issuerSz == NULL) { + return NULL; + } + *issuerSz = (word32)XSTRLEN(cert->issuer); + return cert->issuer; +} + +const byte* wc_GetDecodedCertSerial(struct DecodedCert* cert, word32* serialSz) +{ + if (cert == NULL || serialSz == NULL) { + return NULL; + } + *serialSz = (word32)cert->serialSz; + return cert->serial; +} + #ifdef WOLFCRYPT_ONLY /* dummy functions, not using wolfSSL so don't need actual ones */ @@ -26313,6 +26400,7 @@ int FillSigner(Signer* signer, DecodedCert* cert, int type, DerBuffer *der) #endif signer->keyUsage = cert->extKeyUsageSet ? cert->extKeyUsage : 0xFFFF; + signer->extKeyUsage = cert->extExtKeyUsage; signer->next = NULL; /* If Key Usage not set, all uses valid. */ cert->publicKey = 0; /* in case lock fails don't free here. */ cert->subjectCN = 0; @@ -28391,7 +28479,8 @@ static int SetRsaPublicKey(byte* output, RsaKey* key, int outLen, ret = BAD_FUNC_ARG; } - CALLOC_ASNSETDATA(dataASN, rsaPublicKeyASN_Length, ret, key->heap); + if (ret == 0) + CALLOC_ASNSETDATA(dataASN, rsaPublicKeyASN_Length, ret, key->heap); if (ret == 0) { if (!with_header) { @@ -28424,7 +28513,7 @@ static int SetRsaPublicKey(byte* output, RsaKey* key, int outLen, ret = sz; } - FREE_ASNSETDATA(dataASN, key->heap); + FREE_ASNSETDATA(dataASN, key != NULL ? key->heap : NULL); return ret; #endif /* WOLFSSL_ASN_TEMPLATE */ } @@ -28611,7 +28700,8 @@ int wc_RsaKeyToDer(RsaKey* key, byte* output, word32 inLen) ret = BAD_FUNC_ARG; } - CALLOC_ASNSETDATA(dataASN, rsaKeyASN_Length, ret, key->heap); + if (ret == 0) + CALLOC_ASNSETDATA(dataASN, rsaKeyASN_Length, ret, key->heap); if (ret == 0) { /* Set the version. */ @@ -28638,7 +28728,7 @@ int wc_RsaKeyToDer(RsaKey* key, byte* output, word32 inLen) ret = sz; } - FREE_ASNSETDATA(dataASN, key->heap); + FREE_ASNSETDATA(dataASN, key != NULL ? key->heap : NULL); return ret; #endif } @@ -37338,8 +37428,8 @@ int wc_BuildEccKeyDer(ecc_key* key, byte* output, word32 *inLen, if ((ret == 0) && curveIn && (key->dp == NULL)) { ret = BAD_FUNC_ARG; } - - CALLOC_ASNSETDATA(dataASN, eccKeyASN_Length, ret, key->heap); + if (ret == 0) + CALLOC_ASNSETDATA(dataASN, eccKeyASN_Length, ret, key->heap); if (ret == 0) { /* Private key size is the curve size. */ @@ -38703,6 +38793,66 @@ enum { #define certidasn_Length (sizeof(certIDASNItems) / sizeof(ASNItem)) #endif +#ifdef HAVE_OCSP_RESPONDER + +WC_MAYBE_UNUSED static int EncodeCertID(OcspEntry* entry, byte* out, + word32* outSz) +{ +#ifndef WOLFSSL_ASN_TEMPLATE + (void)entry; + (void)out; + (void)outSz; + /* Encoding ocsp CertID not supported in legacy ASN parsing */ + return NOT_COMPILED_IN; +#else + DECL_ASNSETDATA(dataASN, certidasn_Length); + int ret = 0; + int sz = 0; + word32 digestSz; + + if (entry == NULL || entry->status == NULL || + entry->status->serialSz <= 0 || outSz == NULL) { + return BAD_FUNC_ARG; + } + + WOLFSSL_ENTER("EncodeCertID"); + + CALLOC_ASNSETDATA(dataASN, certidasn_Length, ret, NULL); + + if (ret == 0) { + digestSz = (word32)wc_HashGetDigestSize( + wc_OidGetHash((int)entry->hashAlgoOID)); + if (digestSz <= 0) + ret = ASN_SIG_HASH_E; + } + if (ret == 0) { + SetASN_OID(&dataASN[CERTIDASN_IDX_CID_HASHALGO_OID], + entry->hashAlgoOID, oidHashType); + SetASN_Buffer(&dataASN[CERTIDASN_IDX_CID_ISSUERHASH], + entry->issuerHash, digestSz); + SetASN_Buffer(&dataASN[CERTIDASN_IDX_CID_ISSUERKEYHASH], + entry->issuerKeyHash, digestSz); + SetASN_Buffer(&dataASN[CERTIDASN_IDX_CID_SERIAL], + entry->status->serial, entry->status->serialSz); + ret = SizeASN_Items(certIDASNItems, dataASN, certidasn_Length, &sz); + } + /* Check buffer big enough for encoding if supplied. */ + if (ret == 0 && out != NULL && sz > (int)*outSz) { + ret = BUFFER_E; + } + if (ret == 0 && out != NULL) + if (SetASN_Items(certIDASNItems, dataASN, certidasn_Length, out) != sz) + ret = ASN_PARSE_E; + if (ret == 0) + *outSz = sz; + + FREE_ASNSETDATA(dataASN, NULL); + return ret; +#endif +} + +#endif /* HAVE_OCSP_RESPONDER */ + #ifndef WOLFSSL_ASN_TEMPLATE static int OcspDecodeCertIDInt(const byte* input, word32* inOutIdx, word32 inSz, OcspEntry* entry) @@ -38710,16 +38860,24 @@ static int OcspDecodeCertIDInt(const byte* input, word32* inOutIdx, word32 inSz, int length; word32 oid; int ret; + int expectedDigestSz; + /* Hash algorithm */ ret = GetAlgoId(input, inOutIdx, &oid, oidHashType, inSz); if (ret < 0) return ret; entry->hashAlgoOID = oid; + + /* Validate hash algorithm and get expected digest size */ + expectedDigestSz = wc_HashGetDigestSize(wc_OidGetHash((int)oid)); + if (expectedDigestSz <= 0) + return ASN_SIG_HASH_E; + /* Save reference to the hash of CN */ ret = GetOctetString(input, inOutIdx, &length, inSz); if (ret < 0) return ret; - if (length != OCSP_DIGEST_SIZE) + if (length != expectedDigestSz || length > (int)sizeof(entry->issuerHash)) return ASN_PARSE_E; XMEMCPY(entry->issuerHash, input + *inOutIdx, length); *inOutIdx += length; @@ -38727,7 +38885,7 @@ static int OcspDecodeCertIDInt(const byte* input, word32* inOutIdx, word32 inSz, ret = GetOctetString(input, inOutIdx, &length, inSz); if (ret < 0) return ret; - if (length != OCSP_DIGEST_SIZE) + if (length != expectedDigestSz || length > (int)sizeof(entry->issuerKeyHash)) return ASN_PARSE_E; XMEMCPY(entry->issuerKeyHash, input + *inOutIdx, length); *inOutIdx += length; @@ -38743,8 +38901,8 @@ static int OcspDecodeCertIDInt(const byte* input, word32* inOutIdx, word32 inSz, OcspEntry* entry) { DECL_ASNGETDATA(dataASN, certidasn_Length); - word32 issuerKeyHashLen = OCSP_DIGEST_SIZE; - word32 issuerHashLen = OCSP_DIGEST_SIZE; + word32 issuerKeyHashLen = WC_MAX_DIGEST_SIZE; + word32 issuerHashLen = WC_MAX_DIGEST_SIZE; word32 serialSz = EXTERNAL_SERIAL_SIZE; word32 digestSz; int ret = 0; @@ -38804,6 +38962,134 @@ int OcspDecodeCertID(const byte *input, word32 *inOutIdx, word32 inSz, return 0; } +#ifdef HAVE_OCSP_RESPONDER + +WC_MAYBE_UNUSED static int EncodeSingleResponse(OcspEntry* single, byte* out, + word32* outSz, void* heap) +{ +#ifndef WOLFSSL_ASN_TEMPLATE + (void)single; + (void)out; + (void)outSz; + (void)heap; + /* Encoding ocsp responses not supported in legacy ASN parsing */ + return NOT_COMPILED_IN; +#else + DECL_ASNSETDATA(dataASN, singleResponseASN_Length); + int ret = 0; + int sz = 0; + word32 cidSz = 0; + + if (single == NULL || + /* Only one status is allowed per single response */ + single->status == NULL || single->status->next != NULL || + /* thisDate has to be set */ + single->status->thisDateSz == 0 || + single->status->thisDateSz > MAX_DATE_SIZE || + single->status->thisDateFormat != ASN_GENERALIZED_TIME || + /* nextDate is optional but if set, must be valid */ + single->status->nextDateSz > MAX_DATE_SIZE || + (single->status->nextDateSz > 0 && + single->status->nextDateFormat != ASN_GENERALIZED_TIME) || + outSz == NULL) { + return BAD_FUNC_ARG; + } + + WOLFSSL_ENTER("EncodeSingleResponse"); + + CALLOC_ASNSETDATA(dataASN, singleResponseASN_Length, ret, heap); + + if (ret == 0) + ret = EncodeCertID(single, NULL, &cidSz); + if (ret == 0) { + SetASN_Buffer(&dataASN[SINGLERESPONSEASN_IDX_CID_SEQ], NULL, cidSz); + + if (single->status->status == CERT_GOOD) { + SetASNItem_NoOutNode(dataASN, singleResponseASN, + SINGLERESPONSEASN_IDX_CS_REVOKED, + singleResponseASN_Length); + SetASNItem_NoOutNode(dataASN, singleResponseASN, + SINGLERESPONSEASN_IDX_UNKNOWN, + singleResponseASN_Length); + } + else if (single->status->status == CERT_REVOKED) { + SetASNItem_NoOutNode(dataASN, singleResponseASN, + SINGLERESPONSEASN_IDX_CS_GOOD, + singleResponseASN_Length); + SetASNItem_NoOutNode(dataASN, singleResponseASN, + SINGLERESPONSEASN_IDX_UNKNOWN, + singleResponseASN_Length); + /* Set revocation time - always GeneralizedTime format */ + if (single->status->revocationDateSz == 0 || + single->status->revocationDateSz > MAX_DATE_SIZE) { + ret = BAD_FUNC_ARG; + } + else { + SetASN_Buffer(&dataASN[SINGLERESPONSEASN_IDX_CS_REVOKED_TIME], + single->status->revocationDate, + single->status->revocationDateSz); + /* Set revocation reason - optional field */ + SetASN_Int8Bit(&dataASN[SINGLERESPONSEASN_IDX_CS_REVOKED_REASON_VAL], + single->status->revocationReason); + } + } + else if (single->status->status == CERT_UNKNOWN) { + SetASNItem_NoOutNode(dataASN, singleResponseASN, + SINGLERESPONSEASN_IDX_CS_GOOD, + singleResponseASN_Length); + SetASNItem_NoOutNode(dataASN, singleResponseASN, + SINGLERESPONSEASN_IDX_CS_REVOKED, + singleResponseASN_Length); + } + else { + ret = BAD_FUNC_ARG; + } + } + if (ret == 0) { + SetASN_Buffer(&dataASN[SINGLERESPONSEASN_IDX_THISUPDATE_GT], + single->status->thisDate, + single->status->thisDateSz); + /* Handle optional nextUpdate */ + if (single->status->nextDateSz > 0) { + SetASN_Buffer(&dataASN[SINGLERESPONSEASN_IDX_NEXTUPDATE_GT], + single->status->nextDate, + single->status->nextDateSz); + } + else { + SetASNItem_NoOutNode(dataASN, singleResponseASN, + SINGLERESPONSEASN_IDX_NEXTUPDATE, + singleResponseASN_Length); + } + /* TODO add singleExtensions support */ + SetASNItem_NoOutNode(dataASN, singleResponseASN, + SINGLERESPONSEASN_IDX_EXT, + singleResponseASN_Length); + /* Calculate size of encoding. */ + ret = SizeASN_Items(singleResponseASN, dataASN, + singleResponseASN_Length, &sz); + } + /* Check buffer big enough for encoding if supplied. */ + if (ret == 0 && out != NULL && sz > (int)*outSz) + ret = BUFFER_E; + if (ret == 0 && out != NULL) { + if (SetASN_Items(singleResponseASN, dataASN, singleResponseASN_Length, + out) != sz) + ret = ASN_PARSE_E; + if (ret == 0) { + ret = EncodeCertID(single, + (byte*)dataASN[SINGLERESPONSEASN_IDX_CID_SEQ].data.buffer.data, + &cidSz); + } + } + if (ret == 0) + *outSz = sz; + + FREE_ASNSETDATA(dataASN, heap); + return ret; +#endif +} + +#endif /* HAVE_OCSP_RESPONDER */ static int DecodeSingleResponse(byte* source, word32* ioIndex, word32 size, int wrapperSz, OcspEntry* single) @@ -38965,7 +39251,11 @@ static int DecodeSingleResponse(byte* source, word32* ioIndex, word32 size, cs->thisDate, &thisDateLen); GetASN_Buffer(&dataASN[SINGLERESPONSEASN_IDX_NEXTUPDATE_GT], cs->nextDate, &nextDateLen); - /* TODO: decode revoked time and reason. */ + cs->revocationDateSz = MAX_DATE_SIZE; + GetASN_Buffer(&dataASN[SINGLERESPONSEASN_IDX_CS_REVOKED_TIME], + cs->revocationDate, &cs->revocationDateSz); + GetASN_Int8Bit(&dataASN[SINGLERESPONSEASN_IDX_CS_REVOKED_REASON_VAL], + &cs->revocationReason); /* Decode OCSP single response. */ ret = GetASN_Items(singleResponseASN, dataASN, singleResponseASN_Length, 1, source, ioIndex, size); @@ -39169,10 +39459,11 @@ static int DecodeOcspRespExtensions(byte* source, word32* ioIndex, /* Step through all extensions. */ while ((ret == 0) && (idx < maxIdx)) { + byte isCrit = 0; /* Clear dynamic data, set OID type to expect. */ XMEMSET(dataASN, 0, sizeof(*dataASN) * certExtASN_Length); GetASN_OID(&dataASN[CERTEXTASN_IDX_OID], oidOcspType); - /* TODO: check criticality. */ + GetASN_Boolean(&dataASN[CERTEXTASN_IDX_CRIT], &isCrit); /* Decode OCSP response extension. */ ret = GetASN_Items(certExtASN, dataASN, certExtASN_Length, 0, source, &idx, sz); @@ -39190,6 +39481,9 @@ static int DecodeOcspRespExtensions(byte* source, word32* ioIndex, resp->nonceSz = length; } } + else if (isCrit) { + ret = ASN_PARSE_E; + } /* Ignore all other extension types. */ /* Skip over rest of extension. */ @@ -39205,6 +39499,82 @@ static int DecodeOcspRespExtensions(byte* source, word32* ioIndex, #endif } +#ifdef WOLFSSL_ASN_TEMPLATE +/* ASN.1 template for OCSP nonce extension. + * RFC 6960, 4.4.1 - Nonce + * X.509: RFC 5280, 4.1 - Basic Certificate Fields. (Extension) + */ +static const ASNItem ocspNonceExtASN[] = { +/* SEQ */ { 0, ASN_SEQUENCE, 1, 1, 0 }, + /* Extension */ +/* EXT */ { 1, ASN_SEQUENCE, 1, 1, 0 }, + /* extnId */ +/* EXT_OID */ {2, ASN_OBJECT_ID, 0, 0, 0 }, + /* critical not encoded. */ + /* extnValue */ +/* EXT_VAL */ {2, ASN_OCTET_STRING, 0, 1, 0 }, + /* nonce */ +/* EXT_NONCE */ {3, ASN_OCTET_STRING, 0, 0, 0 }, +}; +enum { + OCSPNONCEEXTASN_IDX_SEQ = 0, + OCSPNONCEEXTASN_IDX_EXT, + OCSPNONCEEXTASN_IDX_EXT_OID, + OCSPNONCEEXTASN_IDX_EXT_VAL, + OCSPNONCEEXTASN_IDX_EXT_NONCE, +}; + +/* Number of items in ASN.1 template for OCSP nonce extension. */ +#define ocspNonceExtASN_Length (sizeof(ocspNonceExtASN) / sizeof(ASNItem)) +#endif /* WOLFSSL_ASN_TEMPLATE */ + +/* Encode OCSP response extensions (currently only nonce) */ +WC_MAYBE_UNUSED static int EncodeOcspRespExtensions(OcspResponse* resp, + byte* out, word32* outSz) +{ +#ifndef WOLFSSL_ASN_TEMPLATE + (void)resp; + (void)out; + (void)outSz; + /* Encoding ocsp responses not supported in legacy ASN parsing */ + return NOT_COMPILED_IN; +#else + DECL_ASNSETDATA(dataASN, ocspNonceExtASN_Length); + int sz = 0; + int ret = 0; + + if (resp == NULL || outSz == NULL) { + return BAD_FUNC_ARG; + } + + CALLOC_ASNSETDATA(dataASN, ocspNonceExtASN_Length, ret, resp->heap); + + if (ret == 0) { + SetASN_OID(&dataASN[OCSPNONCEEXTASN_IDX_EXT_OID], OCSP_NONCE_OID, + oidOcspType); + SetASN_Buffer(&dataASN[OCSPNONCEEXTASN_IDX_EXT_NONCE], + resp->nonce, resp->nonceSz); + /* Calculate size of encoding. */ + ret = SizeASN_Items(ocspNonceExtASN, dataASN, + ocspNonceExtASN_Length, &sz); + } + /* Check buffer big enough for encoding if supplied. */ + if (ret == 0 && out != NULL && sz > (int)*outSz) { + ret = BUFFER_E; + } + if (ret == 0 && out != NULL) { + if (SetASN_Items(ocspNonceExtASN, dataASN, ocspNonceExtASN_Length, + out) != sz) + ret = ASN_PARSE_E; + } + if (ret == 0) + *outSz = sz; + + FREE_ASNSETDATA(dataASN, resp->heap); + return ret; +#endif +} + #ifdef WOLFSSL_ASN_TEMPLATE /* ASN.1 template for OCSP ResponseData. * RFC 6960, 4.2.1 - ASN.1 Specification of the OCSP Response @@ -39213,7 +39583,7 @@ static const ASNItem ocspRespDataASN[] = { /* SEQ */ { 0, ASN_SEQUENCE, 1, 1, 0 }, /* version DEFAULT v1 */ /* VER_PRESENT */ { 1, ASN_CONTEXT_SPECIFIC | 0, 1, 1, 1 }, -/* VER */ { 2, ASN_INTEGER, 1, 0, 0 }, +/* VER */ { 2, ASN_INTEGER, 0, 0, 0 }, /* byName */ /* BYNAME */ { 1, ASN_CONTEXT_SPECIFIC | 1, 1, 0, 2 }, /* byKey */ @@ -39242,6 +39612,130 @@ enum { #define ocspRespDataASN_Length (sizeof(ocspRespDataASN) / sizeof(ASNItem)) #endif +#ifdef HAVE_OCSP_RESPONDER + +WC_MAYBE_UNUSED static int EncodeResponseData(OcspResponse* resp, byte* out, + word32* outSz) +{ +#ifndef WOLFSSL_ASN_TEMPLATE + (void)resp; + (void)out; + (void)outSz; + /* Encoding ocsp responses not supported in legacy ASN parsing */ + return NOT_COMPILED_IN; +#else + DECL_ASNSETDATA(dataASN, ocspRespDataASN_Length); + int ret = 0; + int sz = 0; + word32 respListSz = 0; + word32 respExtSz = 0; + OcspEntry* single = NULL; + + if (resp == NULL || outSz == NULL) { + return BAD_FUNC_ARG; + } + + WOLFSSL_ENTER("EncodeResponseData"); + + CALLOC_ASNSETDATA(dataASN, ocspRespDataASN_Length, ret, resp->heap); + + if (ret == 0) { + if (resp->single == NULL) + ret = BAD_FUNC_ARG; + } + for (single = resp->single; ret == 0 && single != NULL; + single = single->next) { + word32 singleRespSz = 0; + singleRespSz = *outSz - respListSz; + ret = EncodeSingleResponse(single, NULL, &singleRespSz, resp->heap); + if (ret == 0) + respListSz += singleRespSz; + } + if (ret == 0) { + if (resp->responderIdType == OCSP_RESPONDER_ID_NAME) { + /* TODO support name-based responder ID */ + ret = NOT_COMPILED_IN; + } + else if (resp->responderIdType == OCSP_RESPONDER_ID_KEY) { + SetASNItem_NoOutNode(dataASN, ocspRespDataASN, + OCSPRESPDATAASN_IDX_BYNAME, + ocspRespDataASN_Length); + SetASN_Buffer(&dataASN[OCSPRESPDATAASN_IDX_BYKEY_OCT], + resp->responderId.keyHash, + OCSP_RESPONDER_ID_KEY_SZ); + } + else + ret = BAD_FUNC_ARG; + } + if (ret == 0) { + if (resp->producedDateSz > 0 && resp->producedDateSz < MAX_DATE_SIZE) { + SetASN_Buffer(&dataASN[OCSPRESPDATAASN_IDX_PA], + resp->producedDate, resp->producedDateSz); + } + else + ret = BAD_FUNC_ARG; + } + if (ret == 0) { + /* Encode responseExtensions if nonce is present */ + if (resp->nonceSz > 0) { + ret = EncodeOcspRespExtensions(resp, NULL, &respExtSz); + if (ret == 0 && respExtSz > 0) { + SetASN_Buffer(&dataASN[OCSPRESPDATAASN_IDX_RESPEXT], + NULL, respExtSz); + } + } + else { + SetASNItem_NoOutNode(dataASN, ocspRespDataASN, + OCSPRESPDATAASN_IDX_RESPEXT, + ocspRespDataASN_Length); + } + } + if (ret == 0) { + SetASN_Int8Bit(&dataASN[OCSPRESPDATAASN_IDX_VER], 0); + SetASN_Buffer(&dataASN[OCSPRESPDATAASN_IDX_RESP], NULL, + respListSz); + /* Calculate size of encoding. */ + ret = SizeASN_Items(ocspRespDataASN, dataASN, ocspRespDataASN_Length, + &sz); + } + /* Check buffer big enough for encoding if supplied. */ + if (ret == 0 && out != NULL && sz > (int)*outSz) + ret = BUFFER_E; + if (ret == 0 && out != NULL) { + byte* respList = NULL; + if (SetASN_Items(ocspRespDataASN, dataASN, ocspRespDataASN_Length, out) + != sz) + ret = ASN_PARSE_E; + respList = (byte*)dataASN[OCSPRESPDATAASN_IDX_RESP].data.buffer.data; + for (single = resp->single; ret == 0 && single != NULL; + single = single->next) { + word32 singleRespSz = respListSz; + ret = EncodeSingleResponse(single, respList, &singleRespSz, + resp->heap); + if (ret == 0) { + respList += singleRespSz; + respListSz -= singleRespSz; + } + } + if (ret == 0 && respListSz != 0) + ret = ASN_PARSE_E; + /* Encode response extensions if buffer was allocated */ + if (ret == 0 && respExtSz > 0) { + ret = EncodeOcspRespExtensions(resp, + (byte*)dataASN[OCSPRESPDATAASN_IDX_RESPEXT].data.buffer.data, + &respExtSz); + } + } + if (ret == 0) + *outSz = sz; + + FREE_ASNSETDATA(dataASN, resp->heap); + return ret; +#endif +} + +#endif /* HAVE_OCSP_RESPONDER */ + static int DecodeResponseData(byte* source, word32* ioIndex, OcspResponse* resp, word32 size) { @@ -39403,6 +39897,7 @@ static int DecodeResponseData(byte* source, word32* ioIndex, if (dateSz < MIN_DATE_SIZE) { ret = ASN_PARSE_E; } + resp->producedDateSz = (byte)dateSz; } if (ret == 0) { if (dataASN[OCSPRESPDATAASN_IDX_BYNAME].tag != 0) { @@ -39414,11 +39909,8 @@ static int DecodeResponseData(byte* source, word32* ioIndex, } else { resp->responderIdType = OCSP_RESPONDER_ID_KEY; if (dataASN[OCSPRESPDATAASN_IDX_BYKEY_OCT].length - != OCSP_RESPONDER_ID_KEY_SZ) { + != OCSP_RESPONDER_ID_KEY_SZ) ret = ASN_PARSE_E; - } else { - resp->responderIdType = OCSP_RESPONDER_ID_KEY; - } } } if (ret == 0) { @@ -39465,6 +39957,7 @@ static int DecodeResponseData(byte* source, word32* ioIndex, if (ret == 0) { /* Decode SingleResponse into OcspEntry. */ ret = DecodeSingleResponse(source, &idx, + /* max index is start of next item */ dataASN[OCSPRESPDATAASN_IDX_RESPEXT].offset, (int)dataASN[OCSPRESPDATAASN_IDX_RESP].length, single); /* single->used set on successful decode. */ @@ -39579,20 +40072,23 @@ static int OcspRespIdMatch(OcspResponse *resp, const byte *NameHash, } #ifndef WOLFSSL_NO_OCSP_ISSUER_CHECK -static int OcspRespCheck(OcspResponse *resp, Signer *responder) +static int OcspRespCheck(OcspResponse *resp, Signer *responder, void* vp) { OcspEntry *s; + int ret; s = resp->single; if (s == NULL) return -1; - /* singles responses must have the same issuer */ - for (; s != NULL; s = s->next) { - if (XMEMCMP(s->issuerKeyHash, responder->subjectKeyHash, - KEYID_SIZE) != 0) - return -1; - } + if (OcspRespIdMatch(resp, responder->subjectNameHash, + responder->subjectKeyHash) == 0) + return -1; + + ret = CheckOcspResponder(resp, responder->subjectNameHash, + responder->extKeyUsage, responder->issuerNameHash, vp); + if (ret != 0) + return -1; return 0; } @@ -39678,7 +40174,8 @@ static int OcspCheckCert(OcspResponse *resp, int noVerify, #ifndef WOLFSSL_NO_OCSP_ISSUER_CHECK if (ret == 0 && !noVerify) { - ret = CheckOcspResponder(resp, cert, cm); + ret = CheckOcspResponder(resp, cert->subjectHash, cert->extExtKeyUsage, + cert->issuerHash, cm); if (ret != 0) { WOLFSSL_MSG("\tOCSP Responder certificate issuer check failed"); goto err; @@ -39705,6 +40202,132 @@ static int OcspCheckCert(OcspResponse *resp, int noVerify, return ret; } +#ifdef HAVE_OCSP_RESPONDER + +WC_MAYBE_UNUSED static int EncodeBasicOcspResponse(OcspResponse* resp, + byte* out, word32* outSz, RsaKey* rsaKey, ecc_key* eccKey, WC_RNG* rng) +{ +#ifndef WOLFSSL_ASN_TEMPLATE + (void)resp; + (void)out; + (void)outSz; + (void)rsaKey; + (void)eccKey; + (void)rng; + /* Encoding ocsp responses not supported in legacy ASN parsing */ + return NOT_COMPILED_IN; +#else + DECL_ASNSETDATA(dataASN, ocspBasicRespASN_Length); + int ret = 0; + int sz = 0; + word32 respDataSz = 0; + word32 sigSz = 0; + + if (outSz == NULL) { + return BAD_FUNC_ARG; + } + + WOLFSSL_ENTER("EncodeBasicOcspResponse"); + + CALLOC_ASNSETDATA(dataASN, ocspBasicRespASN_Length, ret, resp->heap); + + if (ret == 0) { + respDataSz = *outSz; + ret = EncodeResponseData(resp, NULL, &respDataSz); + } + if (ret == 0) { + if (resp->sigOID == CTC_SHA256wRSA) + ret = wc_RsaEncryptSize(rsaKey); + else if (resp->sigOID == CTC_SHA256wECDSA) + ret = wc_ecc_sig_size(eccKey); + else + ret = BAD_FUNC_ARG; + if (ret > 0) { + sigSz = (word32)ret; + ret = 0; + } + else + ret = ret == 0 ? BAD_FUNC_ARG : ret; + } + if (ret == 0) { + SetASN_OID(&dataASN[OCSPBASICRESPASN_IDX_SIGALGO_OID], resp->sigOID, + oidSigType); + if (resp->sigParams != NULL && resp->sigParamsSz != 0) { + SetASN_Buffer(&dataASN[OCSPBASICRESPASN_IDX_SIGNATURE_PARAMS], + resp->sigParams, resp->sigParamsSz); + } + else { + SetASNItem_NoOutNode(dataASN, ocspBasicRespASN, + OCSPBASICRESPASN_IDX_SIGNATURE_PARAMS, + ocspBasicRespASN_Length); + } + if (resp->cert != NULL && resp->certSz > 0) { + SetASN_Buffer(&dataASN[OCSPBASICRESPASN_IDX_CERTS_SEQ], + resp->cert, resp->certSz); + } + else { + SetASNItem_NoOutNode(dataASN, ocspBasicRespASN, + OCSPBASICRESPASN_IDX_CERTS, + ocspBasicRespASN_Length); + } + if (out == NULL) { + /* Calculate size of encoding. */ + SetASN_ReplaceBuffer(&dataASN[OCSPBASICRESPASN_IDX_TBS_SEQ], NULL, + respDataSz); + SetASN_Buffer(&dataASN[OCSPBASICRESPASN_IDX_SIGNATURE], NULL, + sigSz); + ret = SizeASN_Items(ocspBasicRespASN, dataASN, + ocspBasicRespASN_Length, &sz); + } + else { + /* The real ECC signature might differ from the size returned by + * wc_ecc_sig_size. We handle this by placing the signature at + * the end of the buffer and then moving it into place. */ + byte* respData = out + (*outSz - respDataSz - sigSz); + byte* sigData = out + (*outSz - sigSz); + if (respDataSz + sigSz > *outSz) + ret = BUFFER_E; + if (ret == 0) + ret = EncodeResponseData(resp, respData, &respDataSz); + if (ret == 0) { + CertSignCtx certSignCtx; + XMEMSET(&certSignCtx, 0, sizeof(CertSignCtx)); + ret = MakeSignature(&certSignCtx, respData, respDataSz, + sigData, sigSz, rsaKey, eccKey, NULL, NULL, NULL, NULL, + NULL, rng, resp->sigOID, resp->heap); + if (ret > 0) { + sigSz = (word32)ret; + ret = 0; + } + else + ret = ret == 0 ? WC_FAILURE : ret; + } + if (ret == 0) { + SetASN_ReplaceBuffer(&dataASN[OCSPBASICRESPASN_IDX_TBS_SEQ], + respData, respDataSz); + SetASN_Buffer(&dataASN[OCSPBASICRESPASN_IDX_SIGNATURE], + sigData, sigSz); + /* Re-calculate size of encoding. */ + ret = SizeASN_Items(ocspBasicRespASN, dataASN, + ocspBasicRespASN_Length, &sz); + } + if (ret == 0) { + if (SetASN_Items(ocspBasicRespASN, dataASN, ocspBasicRespASN_Length, + out) != sz) + ret = ASN_PARSE_E; + } + } + } + + if (ret == 0) + *outSz = sz; + FREE_ASNSETDATA(dataASN, resp->heap); + return ret; +#endif +} + +#endif /* HAVE_OCSP_RESPONDER */ + static int DecodeBasicOcspResponse(byte* source, word32* ioIndex, OcspResponse* resp, word32 size, void* cm, void* heap, int noVerify, int noVerifySignature) @@ -39793,7 +40416,7 @@ static int DecodeBasicOcspResponse(byte* source, word32* ioIndex, return ASN_NO_SIGNER_E; #ifndef WOLFSSL_NO_OCSP_ISSUER_CHECK - if (OcspRespCheck(resp, ca) != 0) + if (OcspRespCheck(resp, ca, cm) != 0) return BAD_OCSP_RESPONDER; #endif InitSignatureCtx(&sigCtx, heap, INVALID_DEVID); @@ -39884,25 +40507,29 @@ static int DecodeBasicOcspResponse(byte* source, word32* ioIndex, } #ifndef WOLFSSL_NO_OCSP_ISSUER_CHECK if (ret == 0 && !noVerifySignature && !sigValid) { - if (OcspRespCheck(resp, ca) != 0) { + if (OcspRespCheck(resp, ca, cm) != 0) { ret = BAD_OCSP_RESPONDER; } } #endif if (ret == 0 && !noVerifySignature && !sigValid) { - SignatureCtx sigCtx; - /* Initialize the signature context. */ - InitSignatureCtx(&sigCtx, heap, INVALID_DEVID); - - /* TODO: ConfirmSignature is blocking here */ - /* Check the signature of the response CA public key. */ - sigValid = ConfirmSignature(&sigCtx, resp->response, - resp->responseSz, ca->publicKey, ca->pubKeySize, ca->keyOID, - resp->sig, resp->sigSz, resp->sigOID, resp->sigParams, - resp->sigParamsSz, NULL); - if (sigValid != 0) { - WOLFSSL_MSG("\tOCSP Confirm signature failed"); - ret = ASN_OCSP_CONFIRM_E; + /* Extra NULL check to satisfy compiler */ + if (ca == NULL) + ret = ASN_NO_SIGNER_E; + if (ret == 0) { + SignatureCtx sigCtx; + /* Initialize the signature context. */ + InitSignatureCtx(&sigCtx, heap, INVALID_DEVID); + /* TODO: ConfirmSignature is blocking here */ + /* Check the signature of the response CA public key. */ + sigValid = ConfirmSignature(&sigCtx, resp->response, + resp->responseSz, ca->publicKey, ca->pubKeySize, ca->keyOID, + resp->sig, resp->sigSz, resp->sigOID, resp->sigParams, + resp->sigParamsSz, NULL); + if (sigValid != 0) { + WOLFSSL_MSG("\tOCSP Confirm signature failed"); + ret = ASN_OCSP_CONFIRM_E; + } } } if (ret == 0) { @@ -39921,20 +40548,25 @@ void InitOcspResponse(OcspResponse* resp, OcspEntry* single, CertStatus* status, { WOLFSSL_ENTER("InitOcspResponse"); - XMEMSET(status, 0, sizeof(CertStatus)); - XMEMSET(single, 0, sizeof(OcspEntry)); - XMEMSET(resp, 0, sizeof(OcspResponse)); - - single->status = status; - resp->responseStatus = -1; - resp->single = single; - resp->source = source; - resp->maxIdx = inSz; - resp->heap = heap; - resp->pendingCAs = NULL; - resp->sigParams = NULL; - resp->sigParamsSz = 0; - resp->responderIdType = OCSP_RESPONDER_ID_INVALID; + if (status != NULL) { + XMEMSET(status, 0, sizeof(CertStatus)); + } + if (single != NULL) { + XMEMSET(single, 0, sizeof(OcspEntry)); + single->status = status; + } + if (resp != NULL) { + XMEMSET(resp, 0, sizeof(OcspResponse)); + resp->responseStatus = -1; + resp->single = single; + resp->source = source; + resp->maxIdx = inSz; + resp->heap = heap; + resp->pendingCAs = NULL; + resp->sigParams = NULL; + resp->sigParamsSz = 0; + resp->responderIdType = OCSP_RESPONDER_ID_INVALID; + } } void FreeOcspResponse(OcspResponse* resp) @@ -39988,6 +40620,99 @@ enum { #define ocspResponseASN_Length (sizeof(ocspResponseASN) / sizeof(ASNItem)) #endif /* WOLFSSL_ASN_TEMPLATE */ +#ifdef HAVE_OCSP_RESPONDER + +int OcspResponseEncode(OcspResponse* resp, byte* out, word32* outSz, + RsaKey* rsaKey, ecc_key* eccKey, WC_RNG* rng) +{ +#ifndef WOLFSSL_ASN_TEMPLATE + (void)resp; + (void)out; + (void)outSz; + (void)rsaKey; + (void)eccKey; + (void)rng; + /* Encoding ocsp responses not supported in legacy ASN parsing */ + return NOT_COMPILED_IN; +#else + DECL_ASNSETDATA(dataASN, ocspResponseASN_Length); + int ret = 0; + int sz = 0; + word32 basicRespSz = 0; + + WOLFSSL_ENTER("OcspResponseEncode"); + + if (resp == NULL || outSz == NULL) { + return BAD_FUNC_ARG; + } + + CALLOC_ASNSETDATA(dataASN, ocspResponseASN_Length, ret, resp->heap); + + if (ret == 0) { + SetASN_Int8Bit(&dataASN[OCSPRESPONSEASN_IDX_STATUS], + resp->responseStatus); + } + if (ret == 0 && resp->responseStatus == OCSP_SUCCESSFUL) { + SetASN_OID(&dataASN[OCSPRESPONSEASN_IDX_BYTES_TYPE], OCSP_BASIC_OID, + oidOcspType); + + basicRespSz = *outSz; + ret = EncodeBasicOcspResponse(resp, NULL, &basicRespSz, rsaKey, eccKey, + rng); + if (ret == 0 && out == NULL) { + SetASN_Buffer(&dataASN[OCSPRESPONSEASN_IDX_BYTES_VAL], NULL, + basicRespSz); + ret = SizeASN_Items(ocspResponseASN, dataASN, + ocspResponseASN_Length, &sz); + } + else if (ret == 0 && out != NULL) { + /* The size of the basic response may change because of the + * signature encoding. */ + byte* basicResp = out + (*outSz - basicRespSz); + if (basicRespSz > *outSz) + ret = BUFFER_E; + if (ret == 0) { + ret = EncodeBasicOcspResponse(resp, basicResp, + &basicRespSz, rsaKey, eccKey, rng); + } + if (ret == 0) { + SetASN_Buffer(&dataASN[OCSPRESPONSEASN_IDX_BYTES_VAL], + basicResp, basicRespSz); + ret = SizeASN_Items(ocspResponseASN, dataASN, + ocspResponseASN_Length, &sz); + } + if (ret == 0) { + if (SetASN_Items(ocspResponseASN, dataASN, + ocspResponseASN_Length, out) != sz) + ret = ASN_PARSE_E; + } + } + + } + else if (ret == 0 && resp->responseStatus != OCSP_SUCCESSFUL) { + SetASNItem_NoOutNode(dataASN, ocspResponseASN, + OCSPRESPONSEASN_IDX_BYTES, + ocspResponseASN_Length); + ret = SizeASN_Items(ocspResponseASN, dataASN, ocspResponseASN_Length, + &sz); + if (ret == 0 && out != NULL && sz > (int)*outSz) + ret = BUFFER_E; + if (ret == 0 && out != NULL) { + if (SetASN_Items(ocspResponseASN, dataASN, ocspResponseASN_Length, + out) != sz) + ret = ASN_PARSE_E; + } + } + + if (ret == 0) + *outSz = (word32)sz; + FREE_ASNSETDATA(dataASN, resp->heap); + return ret; +#endif +} + +#endif /* HAVE_OCSP_RESPONDER */ + int OcspResponseDecode(OcspResponse* resp, void* cm, void* heap, int noVerifyCert, int noVerifySignature) { @@ -40113,35 +40838,6 @@ int OcspResponseDecode(OcspResponse* resp, void* cm, void* heap, #endif /* WOLFSSL_ASN_TEMPLATE */ } -#ifdef WOLFSSL_ASN_TEMPLATE -/* ASN.1 template for OCSP nonce extension. - * RFC 6960, 4.4.1 - Nonce - * X.509: RFC 5280, 4.1 - Basic Certificate Fields. (Extension) - */ -static const ASNItem ocspNonceExtASN[] = { -/* SEQ */ { 0, ASN_SEQUENCE, 1, 1, 0 }, - /* Extension */ -/* EXT */ { 1, ASN_SEQUENCE, 1, 1, 0 }, - /* extnId */ -/* EXT_OID */ {2, ASN_OBJECT_ID, 0, 0, 0 }, - /* critical not encoded. */ - /* extnValue */ -/* EXT_VAL */ {2, ASN_OCTET_STRING, 0, 1, 0 }, - /* nonce */ -/* EXT_NONCE */ {3, ASN_OCTET_STRING, 0, 0, 0 }, -}; -enum { - OCSPNONCEEXTASN_IDX_SEQ = 0, - OCSPNONCEEXTASN_IDX_EXT, - OCSPNONCEEXTASN_IDX_EXT_OID, - OCSPNONCEEXTASN_IDX_EXT_VAL, - OCSPNONCEEXTASN_IDX_EXT_NONCE, -}; - -/* Number of items in ASN.1 template for OCSP nonce extension. */ -#define ocspNonceExtASN_Length (sizeof(ocspNonceExtASN) / sizeof(ASNItem)) -#endif /* WOLFSSL_ASN_TEMPLATE */ - word32 EncodeOcspRequestExtensions(OcspRequest* req, byte* output, word32 size) { const byte NonceObjId[] = { 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, @@ -40253,6 +40949,8 @@ static const ASNItem ocspRequestASN[] = { /* hashAlgorithm */ /* TBS_REQ_HASH */ { 5, ASN_SEQUENCE, 1, 1, 0 }, /* TBS_REQ_HASH_OID */ { 6, ASN_OBJECT_ID, 0, 0, 0 }, + /* optional NULL params */ +/* TBS_REQ_HASH_NULL */ { 6, ASN_TAG_NULL, 0, 0, 1 }, /* issuerNameHash */ /* TBS_REQ_ISSUER */ { 5, ASN_OCTET_STRING, 0, 0, 0 }, /* issuerKeyHash */ @@ -40260,8 +40958,9 @@ static const ASNItem ocspRequestASN[] = { /* serialNumber */ /* TBS_REQ_SERIAL */ { 5, ASN_INTEGER, 0, 0, 0 }, /* requestExtensions */ -/* TBS_REQEXT */ { 2, ASN_CONTEXT_SPECIFIC | 2, 1, 0, 0 }, - /* optionalSignature not written. */ +/* TBS_REQEXT */ { 2, ASN_CONTEXT_SPECIFIC | 2, 1, 0, 1 }, + /* optionalSignature */ +/* OPT_SIG */ { 1, ASN_CONTEXT_SPECIFIC | 0, 1, 1, 1 } }; enum { OCSPREQUESTASN_IDX_SEQ = 0, @@ -40271,10 +40970,12 @@ enum { OCSPREQUESTASN_IDX_TBS_REQ_CID, OCSPREQUESTASN_IDX_TBS_REQ_HASH, OCSPREQUESTASN_IDX_TBS_REQ_HASH_OID, + OCSPREQUESTASN_IDX_TBS_REQ_HASH_NULL, OCSPREQUESTASN_IDX_TBS_REQ_ISSUER, OCSPREQUESTASN_IDX_TBS_REQ_ISSUERKEY, OCSPREQUESTASN_IDX_TBS_REQ_SERIAL, OCSPREQUESTASN_IDX_TBS_REQEXT, + OCSPREQUESTASN_IDX_OPT_SIG, }; /* Number of items in ASN.1 template for OCSPRequest. */ @@ -40297,13 +40998,8 @@ int EncodeOcspRequest(OcspRequest* req, byte* output, word32 size) WOLFSSL_ENTER("EncodeOcspRequest"); -#ifdef NO_SHA - algoSz = SetAlgoID(SHA256h, algoArray, oidHashType, 0); - keyIdSz = WC_SHA256_DIGEST_SIZE; -#else - algoSz = SetAlgoID(SHAh, algoArray, oidHashType, 0); - keyIdSz = WC_SHA_DIGEST_SIZE; -#endif + algoSz = SetAlgoID(req->hashAlg, algoArray, oidHashType, 0); + keyIdSz = wc_HashGetDigestSize(wc_OidGetHash(req->hashAlg)); issuerSz = SetDigest(req->issuerHash, keyIdSz, issuerArray); issuerKeySz = SetDigest(req->issuerKeyHash, keyIdSz, issuerKeyArray); @@ -40372,15 +41068,9 @@ int EncodeOcspRequest(OcspRequest* req, byte* output, word32 size) if (ret == 0) { /* Set OID of hash algorithm use on issuer and key. */ - #ifdef NO_SHA - SetASN_OID(&dataASN[OCSPREQUESTASN_IDX_TBS_REQ_HASH_OID], SHA256h, + SetASN_OID(&dataASN[OCSPREQUESTASN_IDX_TBS_REQ_HASH_OID], req->hashAlg, oidHashType); - keyIdSz = WC_SHA256_DIGEST_SIZE; - #else - SetASN_OID(&dataASN[OCSPREQUESTASN_IDX_TBS_REQ_HASH_OID], SHAh, - oidHashType); - keyIdSz = WC_SHA_DIGEST_SIZE; - #endif + keyIdSz = (word32)wc_HashGetDigestSize(wc_OidGetHash(req->hashAlg)); /* Set issuer, issuer key hash and serial number of certificate being * checked. */ SetASN_Buffer(&dataASN[OCSPREQUESTASN_IDX_TBS_REQ_ISSUER], @@ -40402,6 +41092,8 @@ int EncodeOcspRequest(OcspRequest* req, byte* output, word32 size) /* Don't write out extensions. */ dataASN[OCSPREQUESTASN_IDX_TBS_REQEXT].noOut = 1; } + /* Don't write out signature. */ + dataASN[OCSPREQUESTASN_IDX_OPT_SIG].noOut = 1; } if (ret == 0) { /* Calculate size of encoding. */ @@ -40436,6 +41128,175 @@ int EncodeOcspRequest(OcspRequest* req, byte* output, word32 size) #endif /* WOLFSSL_ASN_TEMPLATE */ } +#ifdef HAVE_OCSP_RESPONDER + +#ifdef WOLFSSL_ASN_TEMPLATE +/* Decode OCSP request extensions. + * RFC 6960, 4.1.1 - ASN.1 Specification of the OCSP Request + * + * @param [in] source Buffer containing encoded extensions (inside [2]). + * @param [in] sz Length of buffer in bytes. + * @param [in, out] req OCSP request object to store nonce. + * @return 0 on success. + * @return ASN_PARSE_E when data is malformed. + */ +static int DecodeOcspReqExtensions(const byte* source, word32 sz, + OcspRequest* req) +{ + DECL_ASNGETDATA(dataASN, certExtASN_Length); + int ret = 0; + word32 idx = 0; + word32 maxIdx; + int seqLen; + + WOLFSSL_ENTER("DecodeOcspReqExtensions"); + + /* Skip the outer SEQUENCE that wraps all extensions */ + if (GetSequence(source, &idx, &seqLen, sz) < 0) { + return ASN_PARSE_E; + } + maxIdx = idx + (word32)seqLen; + + CALLOC_ASNGETDATA(dataASN, certExtASN_Length, ret, req->heap); + + /* Step through all extensions. */ + while ((ret == 0) && (idx < maxIdx)) { + byte isCrit = 0; + /* Clear dynamic data, set OID type to expect. */ + XMEMSET(dataASN, 0, sizeof(*dataASN) * certExtASN_Length); + GetASN_OID(&dataASN[CERTEXTASN_IDX_OID], oidOcspType); + GetASN_Boolean(&dataASN[CERTEXTASN_IDX_CRIT], &isCrit); + /* Decode extension. */ + ret = GetASN_Items(certExtASN, dataASN, certExtASN_Length, 0, + source, &idx, sz); + if (ret == 0) { + word32 oid = dataASN[CERTEXTASN_IDX_OID].data.oid.sum; + int length = (int)dataASN[CERTEXTASN_IDX_VAL].length; + + if (oid == OCSP_NONCE_OID) { + /* Extract nonce data - get data inside inner OCTET_STRING */ + ret = GetOctetString(source, &idx, &length, sz); + if (ret >= 0) { + ret = 0; + if (length <= (int)sizeof(req->nonce)) { + XMEMCPY(req->nonce, source + idx, (size_t)length); + req->nonceSz = length; + } + else { + /* Nonce too large */ + ret = BUFFER_E; + } + } + } + else if (isCrit) { + /* Unknown critical extension - fail */ + ret = ASN_PARSE_E; + } + /* Ignore all other extension types. */ + + /* Skip over rest of extension. */ + idx += (word32)length; + } + } + + /* Ensure all extension data was consumed */ + if (ret == 0 && idx != maxIdx) { + ret = ASN_PARSE_E; + } + + FREE_ASNGETDATA(dataASN, req->heap); + return ret; +} +#endif /* WOLFSSL_ASN_TEMPLATE */ + +int DecodeOcspRequest(OcspRequest* req, const byte* input, word32 size) +{ +#ifndef WOLFSSL_ASN_TEMPLATE + (void)req; + (void)input; + (void)size; + /* Decoding ocsp requests not supported in legacy ASN parsing */ + return NOT_COMPILED_IN; +#else + /* TODO: support multiple Requested in a requestList */ + DECL_ASNGETDATA(dataASN, ocspRequestASN_Length); + int ret = 0; + word32 idx = 0; + word32 issuerHashSz = sizeof(req->issuerHash); + word32 issuerKeyHashSz = sizeof(req->issuerKeyHash); + byte* serial = NULL; + word32 serialSz = 0; + + WOLFSSL_ENTER("DecodeOcspRequest"); + + CALLOC_ASNGETDATA(dataASN, ocspRequestASN_Length, ret, req->heap); + + if (ret == 0) { + GetASN_OID(&dataASN[OCSPREQUESTASN_IDX_TBS_REQ_HASH_OID], oidHashType); + GetASN_Buffer(&dataASN[OCSPREQUESTASN_IDX_TBS_REQ_ISSUER], + req->issuerHash, &issuerHashSz); + GetASN_Buffer(&dataASN[OCSPREQUESTASN_IDX_TBS_REQ_ISSUERKEY], + req->issuerKeyHash, &issuerKeyHashSz); + ret = GetASN_Items(ocspRequestASN, dataASN, ocspRequestASN_Length, 1, + input, &idx, size); + } + if (ret == 0) { + /* Make sure all input was consumed */ + if (idx != size) + ret = ASN_PARSE_E; + } + if (ret == 0) { + /* Store hash algorithm from the request */ + int digestSz; + req->hashAlg = dataASN[OCSPREQUESTASN_IDX_TBS_REQ_HASH_OID].data.oid.sum; + digestSz = wc_HashGetDigestSize(wc_OidGetHash(req->hashAlg)); + if (digestSz <= 0) + ret = ASN_SIG_HASH_E; + else if ((int)issuerHashSz != digestSz || + (int)issuerKeyHashSz != digestSz) + ret = ASN_PARSE_E; + } + if (ret == 0) { + /* Parse optional extensions */ + if (dataASN[OCSPREQUESTASN_IDX_TBS_REQEXT].data.ref.data != NULL) { + ret = DecodeOcspReqExtensions( + dataASN[OCSPREQUESTASN_IDX_TBS_REQEXT].data.ref.data, + dataASN[OCSPREQUESTASN_IDX_TBS_REQEXT].data.ref.length, + req); + } + } + if (ret == 0) { + /* Optional signature not supported yet */ + if (dataASN[OCSPREQUESTASN_IDX_OPT_SIG].length > 0 || + dataASN[OCSPREQUESTASN_IDX_OPT_SIG].data.ref.data != NULL) + ret = NOT_COMPILED_IN; + } + if (ret == 0) { + GetASN_GetRef(&dataASN[OCSPREQUESTASN_IDX_TBS_REQ_SERIAL], + &serial, &serialSz); + if (serialSz == 0 || serial == NULL) + ret = ASN_PARSE_E; + } + if (ret == 0) { + /* Copy serial number */ + req->serial = (byte*)XMALLOC((size_t)serialSz, req->heap, + DYNAMIC_TYPE_OCSP_REQUEST); + if (req->serial == NULL) { + ret = MEMORY_E; + } + else { + XMEMCPY(req->serial, serial, (size_t)serialSz); + req->serialSz = serialSz; + } + } + + FREE_ASNGETDATA(dataASN, req->heap); + return ret; +#endif +} + +#endif /* HAVE_OCSP_RESPONDER */ + int InitOcspRequest(OcspRequest* req, DecodedCert* cert, byte useNonce, void* heap) @@ -40450,6 +41311,12 @@ int InitOcspRequest(OcspRequest* req, DecodedCert* cert, byte useNonce, XMEMSET(req, 0, sizeof(OcspRequest)); req->heap = heap; +#ifdef NO_SHA + req->hashAlg = SHA256h; +#else + req->hashAlg = SHAh; +#endif + if (cert) { XMEMCPY(req->issuerHash, cert->issuerHash, KEYID_SIZE); XMEMCPY(req->issuerKeyHash, cert->issuerKeyHash, KEYID_SIZE); @@ -40569,12 +41436,8 @@ int CompareOcspReqResp(OcspRequest* req, OcspResponse* resp) /* match based on found status and return */ for (single = resp->single; single; single = next) { - #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3) ocspDigestSize = wc_HashGetDigestSize( wc_OidGetHash(single->hashAlgoOID)); - #else - ocspDigestSize = OCSP_DIGEST_SIZE; - #endif cmp = req->serialSz - single->status->serialSz; if (cmp == 0) { cmp = XMEMCMP(req->serial, single->status->serial, @@ -44785,6 +45648,77 @@ int wc_VerifyX509Acert(const byte* acert, word32 acertSz, #endif /* WOLFSSL_ACERT && WOLFSSL_ASN_TEMPLATE */ +int AsnHashesHash(AsnHashes* hashes, const byte* data, word32 dataSz) +{ + int ret = 0; + + if (hashes == NULL || data == NULL) + ret = BAD_FUNC_ARG; +#if !defined(NO_MD5) + if (ret == 0) + ret = wc_Md5Hash(data, dataSz, hashes->md5); +#endif +#if !defined(NO_SHA) + if (ret == 0) + ret = wc_ShaHash(data, dataSz, hashes->sha); +#endif +#ifndef NO_SHA256 + if (ret == 0) + ret = wc_Sha256Hash(data, dataSz, hashes->sha256); +#endif +#ifdef WOLFSSL_SHA384 + if (ret == 0) + ret = wc_Sha384Hash(data, dataSz, hashes->sha384); +#endif +#ifdef WOLFSSL_SHA512 + if (ret == 0) + ret = wc_Sha512Hash(data, dataSz, hashes->sha512); +#endif +#ifdef WOLFSSL_SM3 + if (ret == 0) + ret = wc_Sm3Hash(data, dataSz, hashes->sm3); +#endif + + return ret; +} + +const byte* AsnHashesGetHash(const AsnHashes* hashes, int hashAlg, int* size) +{ + if (hashes == NULL || size == NULL) + return NULL; + + switch (hashAlg) { +#if !defined(NO_SHA) + case SHAh: + *size = WC_SHA_DIGEST_SIZE; + return hashes->sha; +#endif +#ifndef NO_SHA256 + case SHA256h: + *size = WC_SHA256_DIGEST_SIZE; + return hashes->sha256; +#endif +#ifdef WOLFSSL_SHA384 + case SHA384h: + *size = WC_SHA384_DIGEST_SIZE; + return hashes->sha384; +#endif +#ifdef WOLFSSL_SHA512 + case SHA512h: + *size = WC_SHA512_DIGEST_SIZE; + return hashes->sha512; +#endif +#ifdef WOLFSSL_SM3 + case SM3h: + *size = WC_SM3_DIGEST_SIZE; + return hashes->sm3; +#endif + default: + *size = 0; + return NULL; + } +} + #ifdef WOLFSSL_SEP diff --git a/wolfcrypt/src/memory.c b/wolfcrypt/src/memory.c index baf9d203e89..9d1a40f1b07 100644 --- a/wolfcrypt/src/memory.c +++ b/wolfcrypt/src/memory.c @@ -313,7 +313,7 @@ void wc_MemZero_Check(void* addr, size_t len) nextIdx--; if (nextIdx > 0) { /* Remove entry. */ - XMEMCPY(memZero + i, memZero + i + 1, + XMEMMOVE(memZero + i, memZero + i + 1, sizeof(MemZero) * (nextIdx - i)); /* Clear out top to make it easier to see what is to be checked. */ XMEMSET(&memZero[nextIdx], 0, sizeof(MemZero)); diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index fa49d76bca2..0ccf4710f97 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -20574,7 +20574,7 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t random_bank_test(void) #ifdef WC_DRBG_BANKREF WC_ALLOC_VAR_EX(rng, WC_RNG, 1, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER, - return WC_TEST_RET_ENC_EC(MEMORY_E)); + ERROR_OUT(WC_TEST_RET_ENC_EC(MEMORY_E), out)); XMEMSET(rng, 0, sizeof(*rng)); #endif diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 294c69be411..6fde781c618 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -2048,6 +2048,7 @@ WOLFSSL_LOCAL int NamedGroupIsPqcHybrid(int group); #define WOLFSSL_ASSERT_EQ(x, y) wc_static_assert((x) == (y)) #define WOLFSSL_ASSERT_SIZEOF_GE(x, y) wc_static_assert(sizeof(x) >= sizeof(y)) +#define WOLFSSL_ASSERT_SIZEOF_EQ(x, y) wc_static_assert(sizeof(x) == sizeof(y)) /* states. Adding state before HANDSHAKE_DONE will break session importing */ enum states { diff --git a/wolfssl/ocsp.h b/wolfssl/ocsp.h index 5f7da34aaaf..525e0678837 100644 --- a/wolfssl/ocsp.h +++ b/wolfssl/ocsp.h @@ -74,8 +74,8 @@ WOLFSSL_LOCAL int CheckOcspResponse(WOLFSSL_OCSP *ocsp, byte *response, int resp OcspEntry *entry, OcspRequest *ocspRequest, void* heap); -WOLFSSL_LOCAL int CheckOcspResponder(OcspResponse *bs, DecodedCert *cert, - void* vp); +WOLFSSL_LOCAL int CheckOcspResponder(OcspResponse *bs, byte* subjectHash, + byte extExtKeyUsage, byte* issuerHash, void* vp); /* Allocates and initializes a WOLFSSL_OCSP object */ WOLFSSL_API WOLFSSL_OCSP* wc_NewOCSP(WOLFSSL_CERT_MANAGER* cm); @@ -84,6 +84,16 @@ WOLFSSL_API void wc_FreeOCSP(WOLFSSL_OCSP* ocsp); WOLFSSL_API int wc_CheckCertOcspResponse(WOLFSSL_OCSP *ocsp, DecodedCert *cert, byte *response, int responseSz, void* heap); +WOLFSSL_API OcspRequest* wc_OcspRequest_new(void* heap); +WOLFSSL_API void wc_OcspRequest_free(OcspRequest* request); + +WOLFSSL_API int wc_InitOcspRequest(OcspRequest* req, DecodedCert* cert, + byte useNonce, void* heap); +WOLFSSL_API int wc_EncodeOcspRequest(OcspRequest* req, byte* output, + word32 size); + +WOLFSSL_API OcspResponse* wc_OcspResponse_new(void* heap); +WOLFSSL_API void wc_OcspResponse_free(OcspResponse* response); #ifdef OPENSSL_EXTRA WOLFSSL_API int wolfSSL_OCSP_resp_find_status(WOLFSSL_OCSP_BASICRESP *bs, @@ -179,6 +189,33 @@ WOLFSSL_API int wolfSSL_OCSP_check_nonce(OcspRequest* req, WOLFSSL_OCSP_BASICRESP* bs); #endif /* OPENSSL_EXTRA */ +#ifdef HAVE_OCSP_RESPONDER +/* OCSP Responder API */ +WOLFSSL_API OcspResponder* wc_OcspResponder_new(void* heap, int sendCerts); +WOLFSSL_API void wc_OcspResponder_free(OcspResponder* responder); + +/* Add a cert that this responder can respond for (DER format only) */ +WOLFSSL_API int wc_OcspResponder_AddSigner(OcspResponder* responder, + const byte* signerDer, word32 signerDerSz, + const byte* keyDer, word32 keyDerSz, + const byte* issuerCertDer, word32 issuerCertDerSz); + +/* Add a certificate status for a specific CA */ +WOLFSSL_API int wc_OcspResponder_SetCertStatus(OcspResponder* responder, + const char* caSubject, word32 caSubjectSz, + const byte* serial, word32 serialSz, enum Ocsp_Cert_Status status, + time_t revocationTime, enum WC_CRL_Reason revocationReason, + word32 validityPeriod); + +/* Generate OCSP response for a request */ +WOLFSSL_API int wc_OcspResponder_WriteResponse(OcspResponder* responder, + const byte* request, word32 requestSz, + byte* response, word32* responseSz); +WOLFSSL_API int wc_OcspResponder_WriteErrorResponse( + enum Ocsp_Response_Status status, + byte* response, word32* responseSz); +#endif /* HAVE_OCSP_RESPONDER */ + #ifdef __cplusplus } /* extern "C" */ diff --git a/wolfssl/test.h b/wolfssl/test.h index 5a191aa27f5..7742eeb4da5 100644 --- a/wolfssl/test.h +++ b/wolfssl/test.h @@ -2334,7 +2334,7 @@ static WC_INLINE void OCSPRespFreeCb(void* ioCtx, unsigned char* response) LIBCALL_CHECK_RET(XFSEEK(lFile, 0, XSEEK_SET)); if (fileSz > 0) { *bufLen = (size_t)fileSz; - *buf = (byte*)malloc(*bufLen); + *buf = (byte*)XMALLOC(*bufLen, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (*buf == NULL) { ret = MEMORY_E; fprintf(stderr, @@ -2399,7 +2399,7 @@ static WC_INLINE void OCSPRespFreeCb(void* ioCtx, unsigned char* response) } if (buff) - free(buff); + XFREE(buff, NULL, DYNAMIC_TYPE_TMP_BUFFER); } static WC_INLINE void load_ssl_buffer(WOLFSSL* ssl, const char* fname, int type) @@ -2441,7 +2441,7 @@ static WC_INLINE void OCSPRespFreeCb(void* ioCtx, unsigned char* response) } if (buff) - free(buff); + XFREE(buff, NULL, DYNAMIC_TYPE_TMP_BUFFER); } #ifdef TEST_PK_PRIVKEY @@ -2457,18 +2457,18 @@ static WC_INLINE void OCSPRespFreeCb(void* ioCtx, unsigned char* response) *derBuf = (byte*)malloc(bufLen); if (*derBuf == NULL) { - free(buf); + XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); return MEMORY_E; } ret = wc_KeyPemToDer(buf, (word32)bufLen, *derBuf, (word32)bufLen, NULL); if (ret < 0) { - free(buf); + XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); free(*derBuf); return ret; } *derLen = ret; - free(buf); + XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); return 0; } diff --git a/wolfssl/wolfcrypt/asn.h b/wolfssl/wolfcrypt/asn.h index 4af64812056..24ccc8e86b5 100644 --- a/wolfssl/wolfcrypt/asn.h +++ b/wolfssl/wolfcrypt/asn.h @@ -356,7 +356,7 @@ WOLFSSL_LOCAL int SizeASN_Items(const ASNItem* asn, ASNSetData *data, WOLFSSL_LOCAL int SetASN_Items(const ASNItem* asn, ASNSetData *data, int count, byte* output); WOLFSSL_LOCAL int GetASN_Items(const ASNItem* asn, ASNGetData *data, int count, - int complete, const byte* input, word32* inOutIdx, word32 length); + int complete, const byte* input, word32* inOutIdx, const word32 length); #ifdef WOLFSSL_ASN_TEMPLATE_TYPE_CHECK WOLFSSL_LOCAL void GetASN_Int8Bit(ASNGetData *dataASN, byte* num); @@ -693,6 +693,7 @@ WOLFSSL_LOCAL void SetASN_OID(ASNSetData *dataASN, int oid, int oidType); /* Set the data items below node to not be encoded. * * @param [in] dataASN Dynamic ASN data item. + * @param [in] asn ASN template item. * @param [in] node Node who's children should not be encoded. * @param [in] dataASNLen Number of items in dataASN. */ @@ -710,6 +711,7 @@ WOLFSSL_LOCAL void SetASN_OID(ASNSetData *dataASN, int oid, int oidType); /* Set the node and all nodes below to not be encoded. * * @param [in] dataASN Dynamic ASN data item. + * @param [in] asn ASN template item. * @param [in] node Node which should not be encoded. Child nodes will * also not be encoded. * @param [in] dataASNLen Number of items in dataASN. @@ -1726,6 +1728,10 @@ typedef int (*wc_UnknownExtCallbackEx)(const word16* oid, word32 oidSz, struct DecodedCert { const byte* publicKey; word32 pubKeySize; +#ifdef HAVE_OCSP_RESPONDER + const byte* publicKeyForHash; + word32 pubKeyForHashSize; +#endif int pubKeyStored; word32 certBegin; /* offset to start of cert */ word32 sigIndex; /* offset to start of signature */ @@ -1846,6 +1852,10 @@ struct DecodedCert { const byte* issuerRaw; /* pointer to issuer inside source */ int issuerRawLen; #endif +#ifdef HAVE_OCSP_RESPONDER + const byte* subjectRawForHash; /* pointer to subject including tags */ + int subjectRawForHashLen; +#endif #if !defined(IGNORE_NAME_CONSTRAINTS) || defined(WOLFSSL_CERT_EXT) const byte* subjectRaw; /* pointer to subject inside source */ int subjectRawLen; @@ -2093,6 +2103,7 @@ struct Signer { word32 pubKeySize; word32 keyOID; /* key type */ word16 keyUsage; + byte extKeyUsage; word16 maxPathLen; WC_BITFIELD selfSigned:1; const byte* publicKey; @@ -2397,9 +2408,11 @@ WOLFSSL_LOCAL int GetTimeString(byte* date, int format, char* buf, int len, int dateLen); #endif #if !defined(NO_ASN_TIME) && !defined(USER_TIME) && \ - !defined(TIME_OVERRIDES) && (defined(OPENSSL_EXTRA) || defined(HAVE_PKCS7)) + !defined(TIME_OVERRIDES) && (defined(OPENSSL_EXTRA) || \ + defined(HAVE_PKCS7) || defined(HAVE_OCSP_RESPONDER)) WOLFSSL_LOCAL int GetFormattedTime(void* currTime, byte* buf, word32 len); WOLFSSL_LOCAL int GetAsnTimeString(void* currTime, byte* buf, word32 len); +WOLFSSL_LOCAL int GetFormattedTime_ex(void* currTime, byte* buf, word32 len, byte format); #endif WOLFSSL_LOCAL int ExtractDate(const unsigned char* date, unsigned char format, wolfssl_tm* certTime, int* idx, int len); @@ -2631,7 +2644,31 @@ enum cert_enums { #endif /* WOLFSSL_CERT_GEN */ +/* hashes type for asn */ +typedef struct AsnHashes { + #if !defined(NO_MD5) + byte md5[WC_MD5_DIGEST_SIZE]; + #endif + #if !defined(NO_SHA) + byte sha[WC_SHA_DIGEST_SIZE]; + #endif + #ifndef NO_SHA256 + byte sha256[WC_SHA256_DIGEST_SIZE]; + #endif + #ifdef WOLFSSL_SHA384 + byte sha384[WC_SHA384_DIGEST_SIZE]; + #endif + #ifdef WOLFSSL_SHA512 + byte sha512[WC_SHA512_DIGEST_SIZE]; + #endif + #ifdef WOLFSSL_SM3 + byte sm3[WC_SM3_DIGEST_SIZE]; + #endif +} AsnHashes; +WOLFSSL_LOCAL int AsnHashesHash(AsnHashes* hashes, const byte* data, word32 dataSz); +WOLFSSL_LOCAL const byte* AsnHashesGetHash(const AsnHashes* hashes, int hashAlg, + int* size); /* for pointer use */ typedef struct CertStatus CertStatus; @@ -2679,7 +2716,9 @@ struct CertStatus { int status; byte thisDate[MAX_DATE_SIZE]; + byte thisDateSz; byte nextDate[MAX_DATE_SIZE]; + byte nextDateSz; byte thisDateFormat; byte nextDateFormat; #ifdef WOLFSSL_OCSP_PARSE_STATUS @@ -2688,6 +2727,9 @@ struct CertStatus { byte* thisDateAsn; byte* nextDateAsn; #endif + byte revocationDate[MAX_DATE_SIZE]; /* ASN-formatted revocation time */ + word32 revocationDateSz; + byte revocationReason; /* CRL reason code */ byte* rawOcspResponse; word32 rawOcspResponseSz; @@ -2715,8 +2757,8 @@ struct OcspEntry { OcspEntry *next; /* next entry */ word32 hashAlgoOID; /* hash algo ID */ - byte issuerHash[OCSP_DIGEST_SIZE]; /* issuer hash */ - byte issuerKeyHash[OCSP_DIGEST_SIZE]; /* issuer public key hash */ + byte issuerHash[WC_MAX_DIGEST_SIZE]; /* issuer hash */ + byte issuerKeyHash[WC_MAX_DIGEST_SIZE]; /* issuer public key hash */ CertStatus *status; /* OCSP response list */ int totalStatus; /* number on list */ byte* rawCertId; /* raw bytes of the CertID */ @@ -2737,7 +2779,7 @@ struct OcspEntry enum responderIdType { OCSP_RESPONDER_ID_INVALID = 0, OCSP_RESPONDER_ID_NAME = 1, - OCSP_RESPONDER_ID_KEY = 2, + OCSP_RESPONDER_ID_KEY = 2 }; /* TODO: Long-term, it would be helpful if we made this struct and other OCSP structs conform to the ASN spec as described in RFC 6960. It will help @@ -2759,6 +2801,7 @@ struct OcspResponse { byte producedDate[MAX_DATE_SIZE]; /* Date at which this response was signed */ byte producedDateFormat; /* format of the producedDate */ + byte producedDateSz; byte* cert; word32 certSz; @@ -2783,11 +2826,9 @@ struct OcspResponse { struct OcspRequest { - byte issuerHash[KEYID_SIZE]; - byte issuerKeyHash[KEYID_SIZE]; -#if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3) - int hashSz; -#endif + byte issuerHash[WC_MAX_DIGEST_SIZE]; + byte issuerKeyHash[WC_MAX_DIGEST_SIZE]; + int hashAlg; /* Hash_Sum OID, e.g. SHAh, SHA256h */ byte* serial; /* copy of the serial number in source cert */ int serialSz; #ifdef OPENSSL_EXTRA @@ -2806,6 +2847,8 @@ struct OcspRequest { WOLFSSL_LOCAL void InitOcspResponse(OcspResponse* resp, OcspEntry* single, CertStatus* status, byte* source, word32 inSz, void* heap); WOLFSSL_LOCAL void FreeOcspResponse(OcspResponse* resp); +WOLFSSL_LOCAL int OcspResponseEncode(OcspResponse* resp, byte* out, word32* outSz, + RsaKey* rsaKey, ecc_key* eccKey, WC_RNG* rng); WOLFSSL_LOCAL int OcspResponseDecode(OcspResponse* resp, void* cm, void* heap, int noVerifyCert, int noVerifySignature); @@ -2814,6 +2857,8 @@ WOLFSSL_LOCAL int InitOcspRequest(OcspRequest* req, DecodedCert* cert, WOLFSSL_LOCAL void FreeOcspRequest(OcspRequest* req); WOLFSSL_LOCAL int EncodeOcspRequest(OcspRequest* req, byte* output, word32 size); +WOLFSSL_LOCAL int DecodeOcspRequest(OcspRequest* req, const byte* input, + word32 size); WOLFSSL_LOCAL word32 EncodeOcspRequestExtensions(OcspRequest* req, byte* output, word32 size); @@ -2822,6 +2867,74 @@ WOLFSSL_LOCAL int CompareOcspReqResp(OcspRequest* req, OcspResponse* resp); WOLFSSL_LOCAL int OcspDecodeCertID(const byte* input, word32* inOutIdx, word32 inSz, OcspEntry* entry); +#ifdef HAVE_OCSP_RESPONDER +/* Revocation reason codes from RFC 5280 */ +enum WC_CRL_Reason { + CRL_REASON_UNSPECIFIED = 0, + CRL_REASON_KEY_COMPROMISE = 1, + CRL_REASON_CA_COMPROMISE = 2, + CRL_REASON_AFFILIATION_CHANGED = 3, + CRL_REASON_SUPERSEDED = 4, + CRL_REASON_CESSATION_OF_OPERATION = 5, + CRL_REASON_CERTIFICATE_HOLD = 6, + /* value 7 is not used */ + CRL_REASON_REMOVE_FROM_CRL = 8, + CRL_REASON_PRIVILEGE_WITHDRAWN = 9, + CRL_REASON_AA_COMPROMISE = 10 +}; + +/* Certificate status entry for a single certificate */ +typedef struct OcspResponderCertStatus OcspResponderCertStatus; +struct OcspResponderCertStatus { + byte serial[EXTERNAL_SERIAL_SIZE]; + int serialSz; + enum Ocsp_Cert_Status status; /* CERT_GOOD, CERT_REVOKED, CERT_UNKNOWN */ + byte revocationDate[MAX_DATE_SIZE]; /* ASN-formatted revocation time (if REVOKED) */ + word32 revocationDateSz; /* Size of revocation date */ + enum WC_CRL_Reason revocationReason; /* Reason for revocation */ + word32 validityPeriod; /* Validity period in seconds (for CERT_GOOD) */ + OcspResponderCertStatus* next; +}; + +/* CA entry with its certificates and key */ +typedef struct OcspResponderCa OcspResponderCa; +struct OcspResponderCa { + char subject[WC_ASN_NAME_MAX]; /* CA subject name for lookup */ + + union { +#ifndef NO_RSA + struct RsaKey rsa; +#endif +#ifdef HAVE_ECC + struct ecc_key ecc; +#endif + } key; /* private key for signing */ + enum Key_Sum keyType; /* Type of key */ + + AsnHashes issuerHashes; /* Hashes of CA's subject DN */ + AsnHashes issuerKeyHashes; /* Hashes of CA's public key */ + + byte responderKeyHash[WC_SHA_DIGEST_SIZE]; /* Hash of the responder's public key */ + + byte* certDer; /* Raw DER certificate (if sendCerts enabled) */ + word32 certDerSz; /* Size of certificate DER */ + + OcspResponderCertStatus* statuses; /* List of certificate statuses for this CA */ + + OcspResponderCa* next; /* Next Auth CA in list */ + + WC_BITFIELD authResp:1; /* Is the cert an authorized responder */ +}; + +typedef struct OcspResponder OcspResponder; +struct OcspResponder { + OcspResponderCa* caList; /* List of CAs this responder handles */ + void* heap; + WC_RNG rng; /* RNG for signing responses */ + WC_BITFIELD sendCerts:1; /* Whether to include CA in responses */ +}; +#endif /* HAVE_OCSP_RESPONDER */ + #endif /* HAVE_OCSP */ diff --git a/wolfssl/wolfcrypt/asn_public.h b/wolfssl/wolfcrypt/asn_public.h index 91b3aa28d29..656253e18ec 100644 --- a/wolfssl/wolfcrypt/asn_public.h +++ b/wolfssl/wolfcrypt/asn_public.h @@ -930,6 +930,12 @@ WOLFSSL_API int wc_GetSubjectPubKeyInfoDerFromCert(const byte* certDer, word32 certDerSz, byte* pubKeyDer, word32* pubKeyDerSz); +WOLFSSL_API const char* wc_GetDecodedCertSubject(struct DecodedCert* cert, + word32* subjectSz); +WOLFSSL_API const char* wc_GetDecodedCertIssuer(struct DecodedCert* cert, + word32* issuerSz); +WOLFSSL_API const byte* wc_GetDecodedCertSerial(struct DecodedCert* cert, + word32* serialSz); #ifdef WOLFSSL_FPKI WOLFSSL_API int wc_GetUUIDFromCert(struct DecodedCert* cert, diff --git a/wolfssl/wolfcrypt/ecc.h b/wolfssl/wolfcrypt/ecc.h index 08067d23125..e5a6839d56e 100644 --- a/wolfssl/wolfcrypt/ecc.h +++ b/wolfssl/wolfcrypt/ecc.h @@ -44,9 +44,6 @@ #ifdef WOLFSSL_ASYNC_CRYPT #include - #ifdef WOLFSSL_CERT_GEN - #include - #endif #endif #if defined(WOLFSSL_ATECC508A) || defined(WOLFSSL_ATECC608A) diff --git a/wolfssl/wolfcrypt/rsa.h b/wolfssl/wolfcrypt/rsa.h index 19f963a1815..771b6a9d614 100644 --- a/wolfssl/wolfcrypt/rsa.h +++ b/wolfssl/wolfcrypt/rsa.h @@ -140,9 +140,6 @@ RSA keys can be used to encrypt, decrypt, sign and verify data. #ifdef WOLFSSL_ASYNC_CRYPT #include - #ifdef WOLFSSL_CERT_GEN - #include - #endif #endif #if FIPS_VERSION3_GE(6,0,0) diff --git a/wolfssl/wolfcrypt/settings.h b/wolfssl/wolfcrypt/settings.h index e9943fb270b..a80a7c25cbf 100644 --- a/wolfssl/wolfcrypt/settings.h +++ b/wolfssl/wolfcrypt/settings.h @@ -5005,6 +5005,21 @@ extern void uITRON4_free(void *p) ; #undef WC_RNG_BANK_SUPPORT #endif +#ifdef HAVE_OCSP_RESPONDER + #ifndef HAVE_OCSP + #error "HAVE_OCSP_RESPONDER requires HAVE_OCSP" + #endif + #ifndef WOLFSSL_ASN_TEMPLATE + #error "HAVE_OCSP_RESPONDER requires WOLFSSL_ASN_TEMPLATE" + #endif + #ifdef NO_CERTS + #error "HAVE_OCSP_RESPONDER incompatible with NO_CERTS" + #endif + #ifndef WOLFSSL_CERT_GEN + #error "HAVE_OCSP_RESPONDER requires WOLFSSL_CERT_GEN" + #endif +#endif + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/wolfssl/wolfcrypt/types.h b/wolfssl/wolfcrypt/types.h index c0cd8bd847d..d5863527742 100644 --- a/wolfssl/wolfcrypt/types.h +++ b/wolfssl/wolfcrypt/types.h @@ -1362,6 +1362,7 @@ enum { DYNAMIC_TYPE_OS_BUF = 104, DYNAMIC_TYPE_ASCON = 105, DYNAMIC_TYPE_SHA = 106, + DYNAMIC_TYPE_OCSP_RESPONSE = 107, DYNAMIC_TYPE_SNIFFER_SERVER = 1000, DYNAMIC_TYPE_SNIFFER_SESSION = 1001, DYNAMIC_TYPE_SNIFFER_PB = 1002, @@ -2371,7 +2372,7 @@ enum Max_ASN { #define MAX_SIG_SZ MAX_ENCODED_SIG_SZ -#ifdef WOLFSSL_CERT_GEN +#if defined(WOLFSSL_CERT_GEN) || defined(HAVE_OCSP_RESPONDER) /* Used in asn.c MakeSignature for ECC and RSA non-blocking/async */ enum CertSignState { CERTSIGN_STATE_BEGIN,