@@ -93,6 +93,37 @@ def _tmp(name):
9393URI.1 = https://www.wolfssl.com
9494"""
9595
96+ # A CSR built from this config carries no subjectAltName, so the input PEM
97+ # stays short. Applying v3_big_alt with -extfile then grows the signed
98+ # certificate well past the length of that PEM.
99+ BIG_EXT_CONF = """\
100+ [ req ]
101+ distinguished_name =req_distinguished_name
102+ attributes =req_attributes
103+ prompt =no
104+ [ req_distinguished_name ]
105+ countryName =US
106+ commonName = bigext
107+ [ req_attributes ]
108+ [ v3_big_alt ]
109+ basicConstraints = CA:TRUE
110+ keyUsage = digitalSignature
111+ subjectAltName = @big_alt_names
112+ [big_alt_names]
113+ DNS.1 = first-of-the-long-alternative-names.overflow.example.com
114+ DNS.2 = second-of-the-long-alternative-names.overflow.example.com
115+ DNS.3 = third-of-the-long-alternative-names.overflow.example.com
116+ DNS.4 = fourth-of-the-long-alternative-names.overflow.example.com
117+ DNS.5 = fifth-of-the-long-alternative-names.overflow.example.com
118+ DNS.6 = sixth-of-the-long-alternative-names.overflow.example.com
119+ DNS.7 = seventh-of-the-long-alternative-names.overflow.example.com
120+ DNS.8 = eighth-of-the-long-alternative-names.overflow.example.com
121+ DNS.9 = ninth-of-the-long-alternative-names.overflow.example.com
122+ DNS.10 = tenth-of-the-long-alternative-names.overflow.example.com
123+ DNS.11 = eleventh-of-the-long-alternative-names.overflow.example.com
124+ DNS.12 = twelfth-of-the-long-alternative-names.overflow.example.com
125+ """
126+
96127
97128def _cleanup (* files ):
98129 for f in files :
@@ -751,6 +782,97 @@ def test_extfile_v3_alt_ca(self):
751782 self .assertIn ("CA:TRUE" , r2 .stdout )
752783
753784
785+ class TestX509ReqLargeExtensions (unittest .TestCase ):
786+ """Test x509 -req when the added extensions outgrow the input PEM.
787+
788+ Sizing the output against the input used to write past the buffer
789+ holding it while still emitting correct bytes, so these cases do not
790+ reliably fail on such a build unless a sanitizer catches the write.
791+ The ci.yml matrix runs make check under -fsanitize=address."""
792+
793+ @classmethod
794+ def setUpClass (cls ):
795+ cls .conf_file = _tmp ("test_x509req_bigext.conf" )
796+ with open (cls .conf_file , "w" , encoding = "utf-8" , newline = "\n " ) as f :
797+ f .write (BIG_EXT_CONF )
798+ cls .csr = _tmp ("test_x509req_bigext.csr" )
799+ r = run_wolfssl ("req" , "-new" ,
800+ "-key" , os .path .join (CERTS_DIR , "server-key.pem" ),
801+ "-config" , cls .conf_file ,
802+ "-out" , cls .csr )
803+ assert r .returncode == 0 , "setup CSR creation failed: " + r .stderr
804+
805+ @classmethod
806+ def tearDownClass (cls ):
807+ _cleanup (cls .conf_file , cls .csr )
808+
809+ def _clean (self , * files ):
810+ for f in files :
811+ self .addCleanup (lambda p = f : _cleanup (p ))
812+
813+ def _sign_with_big_ext (self , out , * extra ):
814+ """Sign the CSR applying the oversized v3_big_alt extensions."""
815+ r = run_wolfssl ("x509" , "-req" , "-in" , self .csr , "-days" , "3650" ,
816+ "-extfile" , self .conf_file ,
817+ "-extensions" , "v3_big_alt" ,
818+ "-signkey" ,
819+ os .path .join (CERTS_DIR , "server-key.pem" ),
820+ "-out" , out , * extra )
821+ combined = r .stdout + r .stderr
822+ if "not compiled with cert extensions" in combined :
823+ self .skipTest ("cert extensions not compiled in" )
824+ if "WOLFSSL_ALT_NAMES" in combined :
825+ self .skipTest ("alt names not compiled in" )
826+ self .assertEqual (r .returncode , 0 , r .stderr )
827+
828+ def _assert_alt_names (self , text ):
829+ for name in ("first-of-the-long-alternative-names.overflow.example.com" ,
830+ "sixth-of-the-long-alternative-names.overflow.example.com" ,
831+ "twelfth-of-the-long-alternative-names.overflow."
832+ "example.com" ):
833+ self .assertIn (name , text )
834+
835+ def test_extfile_larger_than_input_pem_der_out (self ):
836+ """x509 -req -outform der output may exceed the input PEM length.
837+
838+ The certificate is re-encoded after the extensions are applied, so
839+ its DER outgrows the CSR PEM that was read in. Nothing on the
840+ output path may be sized against that input."""
841+ out = _tmp ("tmp_bigext_der.cert" )
842+ self ._clean (out )
843+
844+ csr_pem_len = os .path .getsize (self .csr )
845+ self ._sign_with_big_ext (out , "-outform" , "der" )
846+
847+ # Without the extensions the output cannot outgrow the input, and
848+ # this case would no longer cover the sizing it is here to check.
849+ self .assertGreater (os .path .getsize (out ), csr_pem_len ,
850+ "certificate DER did not outgrow the input PEM" )
851+
852+ r2 = run_wolfssl ("x509" , "-in" , out , "-inform" , "der" ,
853+ "-text" , "-noout" )
854+ self .assertEqual (r2 .returncode , 0 , r2 .stderr )
855+ self ._assert_alt_names (r2 .stdout )
856+
857+ def test_extfile_larger_than_input_pem_pem_out (self ):
858+ """x509 -req PEM output covers the same sizing on the DerToPem path."""
859+ out = _tmp ("tmp_bigext_pem.cert" )
860+ self ._clean (out )
861+
862+ csr_pem_len = os .path .getsize (self .csr )
863+ self ._sign_with_big_ext (out )
864+
865+ with open (out , encoding = "utf-8" ) as f :
866+ pem = f .read ()
867+ self .assertTrue (pem .startswith ("-----BEGIN CERTIFICATE-----" ),
868+ "default output form should be PEM" )
869+ self .assertGreater (os .path .getsize (out ), csr_pem_len ,
870+ "certificate PEM did not outgrow the input PEM" )
871+
872+ r2 = run_wolfssl ("x509" , "-in" , out , "-text" , "-noout" )
873+ self .assertEqual (r2 .returncode , 0 , r2 .stderr )
874+ self ._assert_alt_names (r2 .stdout )
875+
754876
755877class TestReqConfigSubject (unittest .TestCase ):
756878 """Test subject from config file."""
0 commit comments