@@ -878,3 +878,186 @@ int test_x509_CertFromX509_akid_overflow(void)
878878#endif
879879 return EXPECT_RESULT ();
880880}
881+
882+ /* Test that ReqCertFromX509() rejects a CSR with an oversized
883+ * SubjectKeyIdentifier (> CTC_MAX_SKID_SIZE = 32 bytes). Before the fix
884+ * this would cause a heap buffer overflow into cert->skid[32]. */
885+ int test_x509_ReqCertFromX509_skid_overflow (void )
886+ {
887+ EXPECT_DECLS ;
888+ #if defined(WOLFSSL_CERT_REQ ) && defined(WOLFSSL_CERT_GEN ) && \
889+ defined(WOLFSSL_CERT_EXT ) && !defined(NO_BIO ) && \
890+ (defined(OPENSSL_EXTRA ) || defined(OPENSSL_ALL )) && \
891+ defined(HAVE_ECC )
892+
893+ /* Minimal DER-encoded CSR (PKCS#10) containing a SubjectKeyIdentifier
894+ * extension with a 64-byte value -- double the 32-byte CTC_MAX_SKID_SIZE
895+ * destination buffer.
896+ *
897+ * Structure:
898+ * SEQUENCE {
899+ * CertificationRequestInfo SEQUENCE {
900+ * version INTEGER 0
901+ * subject: CN=Test
902+ * subjectPKInfo: EC P-256 (generator point)
903+ * attributes [0] {
904+ * SEQUENCE {
905+ * OID 1.2.840.113549.1.9.14 (extensionRequest)
906+ * SET { SEQUENCE { -- Extensions
907+ * SEQUENCE { -- Extension
908+ * OID 2.5.29.14 (subjectKeyIdentifier)
909+ * OCTET STRING { OCTET STRING (64 bytes of 0x41) }
910+ * }
911+ * }}
912+ * }
913+ * }
914+ * }
915+ * signatureAlgorithm: ecdsa-with-SHA256
916+ * signatureValue: dummy
917+ * }
918+ */
919+ static const unsigned char crafted_csr_der [] = {
920+ 0x30 , 0x81 , 0xE7 , 0x30 , 0x81 , 0xCD , 0x02 , 0x01 ,
921+ 0x00 , 0x30 , 0x0F , 0x31 , 0x0D , 0x30 , 0x0B , 0x06 ,
922+ 0x03 , 0x55 , 0x04 , 0x03 , 0x0C , 0x04 , 0x54 , 0x65 ,
923+ 0x73 , 0x74 , 0x30 , 0x59 , 0x30 , 0x13 , 0x06 , 0x07 ,
924+ 0x2A , 0x86 , 0x48 , 0xCE , 0x3D , 0x02 , 0x01 , 0x06 ,
925+ 0x08 , 0x2A , 0x86 , 0x48 , 0xCE , 0x3D , 0x03 , 0x01 ,
926+ 0x07 , 0x03 , 0x42 , 0x00 , 0x04 , 0x6B , 0x17 , 0xD1 ,
927+ 0xF2 , 0xE1 , 0x2C , 0x42 , 0x47 , 0xF8 , 0xBC , 0xE6 ,
928+ 0xE5 , 0x63 , 0xA4 , 0x40 , 0xF2 , 0x77 , 0x03 , 0x7D ,
929+ 0x81 , 0x2D , 0xEB , 0x33 , 0xA0 , 0xF4 , 0xA1 , 0x39 ,
930+ 0x45 , 0xD8 , 0x98 , 0xC2 , 0x96 , 0x4F , 0xE3 , 0x42 ,
931+ 0xE2 , 0xFE , 0x1A , 0x7F , 0x9B , 0x8E , 0xE7 , 0xEB ,
932+ 0x4A , 0x7C , 0x0F , 0x9E , 0x16 , 0x2B , 0xCE , 0x33 ,
933+ 0x57 , 0x6B , 0x31 , 0x5E , 0xCE , 0xCB , 0xB6 , 0x40 ,
934+ 0x68 , 0x37 , 0xBF , 0x51 , 0xF5 , 0xA0 , 0x5C , 0x30 ,
935+ 0x5A , 0x06 , 0x09 , 0x2A , 0x86 , 0x48 , 0x86 , 0xF7 ,
936+ 0x0D , 0x01 , 0x09 , 0x0E , 0x31 , 0x4D , 0x30 , 0x4B ,
937+ 0x30 , 0x49 , 0x06 , 0x03 , 0x55 , 0x1D , 0x0E , 0x04 ,
938+ 0x42 , 0x04 , 0x40 ,
939+ /* 64 bytes of 0x41 -- oversized SKID value */
940+ 0x41 , 0x41 , 0x41 , 0x41 , 0x41 , 0x41 , 0x41 , 0x41 ,
941+ 0x41 , 0x41 , 0x41 , 0x41 , 0x41 , 0x41 , 0x41 , 0x41 ,
942+ 0x41 , 0x41 , 0x41 , 0x41 , 0x41 , 0x41 , 0x41 , 0x41 ,
943+ 0x41 , 0x41 , 0x41 , 0x41 , 0x41 , 0x41 , 0x41 , 0x41 ,
944+ 0x41 , 0x41 , 0x41 , 0x41 , 0x41 , 0x41 , 0x41 , 0x41 ,
945+ 0x41 , 0x41 , 0x41 , 0x41 , 0x41 , 0x41 , 0x41 , 0x41 ,
946+ 0x41 , 0x41 , 0x41 , 0x41 , 0x41 , 0x41 , 0x41 , 0x41 ,
947+ 0x41 , 0x41 , 0x41 , 0x41 , 0x41 , 0x41 , 0x41 , 0x41 ,
948+ /* end of SKID payload */
949+ 0x30 , 0x0A , 0x06 , 0x08 , 0x2A , 0x86 , 0x48 , 0xCE ,
950+ 0x3D , 0x04 , 0x03 , 0x02 , 0x03 , 0x09 , 0x00 , 0x30 ,
951+ 0x06 , 0x02 , 0x01 , 0x01 , 0x02 , 0x01 , 0x01
952+ };
953+
954+ WOLFSSL_X509 * req = NULL ;
955+ WOLFSSL_BIO * bio = NULL ;
956+
957+ /* Step 1: Parse the crafted CSR -- this should succeed (the parser
958+ * dynamically allocates subjKeyId to the parsed size). */
959+ req = wolfSSL_X509_REQ_d2i (NULL , crafted_csr_der ,
960+ (int )sizeof (crafted_csr_der ));
961+ ExpectNotNull (req );
962+
963+ /* Step 2: Attempt to re-encode. Before the fix, this triggered a
964+ * heap buffer overflow in ReqCertFromX509() writing 64 bytes into
965+ * cert->skid[32]. With the fix, it must return failure. */
966+ bio = wolfSSL_BIO_new (wolfSSL_BIO_s_mem ());
967+ ExpectNotNull (bio );
968+ ExpectIntNE (wolfSSL_i2d_X509_REQ_bio (bio , req ), WOLFSSL_SUCCESS );
969+
970+ wolfSSL_BIO_free (bio );
971+ wolfSSL_X509_free (req );
972+ #endif
973+ return EXPECT_RESULT ();
974+ }
975+
976+ /* Positive / boundary companion to test_x509_ReqCertFromX509_skid_overflow.
977+ * Verifies that a CSR with a SubjectKeyIdentifier of exactly
978+ * CTC_MAX_SKID_SIZE (32) bytes -- the boundary of the bounds check in
979+ * ReqCertFromX509() -- is accepted and that the SKID round-trips with the
980+ * correct length and bytes through the sign / re-encode / re-parse path.
981+ * This kills boundary mutations (">" -> ">=") and copy-size mutations on
982+ * the XMEMCPY into cert->skid that the negative test alone cannot catch. */
983+ int test_x509_ReqCertFromX509_skid_boundary (void )
984+ {
985+ EXPECT_DECLS ;
986+ #if defined(WOLFSSL_CERT_REQ ) && defined(WOLFSSL_CERT_GEN ) && \
987+ defined(WOLFSSL_CERT_EXT ) && !defined(NO_BIO ) && \
988+ (defined(OPENSSL_EXTRA ) || defined(OPENSSL_ALL )) && \
989+ defined(HAVE_ECC ) && defined(USE_CERT_BUFFERS_256 )
990+
991+ WOLFSSL_EVP_PKEY * priv = NULL ;
992+ WOLFSSL_EVP_PKEY * pub = NULL ;
993+ WOLFSSL_X509 * req = NULL ;
994+ WOLFSSL_X509 * parsed = NULL ;
995+ WOLFSSL_X509_NAME * name = NULL ;
996+ unsigned char * der = NULL ;
997+ int derSz ;
998+ const unsigned char * ecPriv = ecc_clikey_der_256 ;
999+ const unsigned char * ecPub = ecc_clikeypub_der_256 ;
1000+ unsigned char expected_skid [CTC_MAX_SKID_SIZE ];
1001+
1002+ XMEMSET (expected_skid , 0x41 , sizeof (expected_skid ));
1003+
1004+ /* Load a real ECC keypair so that the CSR can actually be signed
1005+ * (ReqCertFromX509() is invoked from the signing path). */
1006+ ExpectNotNull (priv = wolfSSL_d2i_PrivateKey (EVP_PKEY_EC , NULL , & ecPriv ,
1007+ (long )sizeof_ecc_clikey_der_256 ));
1008+ ExpectNotNull (pub = wolfSSL_d2i_PUBKEY (NULL , & ecPub ,
1009+ (long )sizeof_ecc_clikeypub_der_256 ));
1010+
1011+ ExpectNotNull (req = wolfSSL_X509_REQ_new ());
1012+ ExpectNotNull (name = wolfSSL_X509_NAME_new ());
1013+ ExpectIntEQ (wolfSSL_X509_NAME_add_entry_by_txt (name , "commonName" ,
1014+ MBSTRING_UTF8 , (const byte * )"Test" , 4 , -1 , 0 ),
1015+ WOLFSSL_SUCCESS );
1016+ ExpectIntEQ (wolfSSL_X509_REQ_set_subject_name (req , name ), WOLFSSL_SUCCESS );
1017+ ExpectIntEQ (wolfSSL_X509_REQ_set_pubkey (req , pub ), WOLFSSL_SUCCESS );
1018+
1019+ /* Inject exactly CTC_MAX_SKID_SIZE bytes of SKID directly on the req
1020+ * (tests/api/test_x509.c already includes <wolfssl/internal.h>). This
1021+ * is the boundary value of the bounds check in ReqCertFromX509(). */
1022+ if (EXPECT_SUCCESS () && req != NULL ) {
1023+ req -> subjKeyId = (byte * )XMALLOC (sizeof (expected_skid ), NULL ,
1024+ DYNAMIC_TYPE_X509_EXT );
1025+ ExpectNotNull (req -> subjKeyId );
1026+ if (req -> subjKeyId != NULL ) {
1027+ XMEMCPY (req -> subjKeyId , expected_skid , sizeof (expected_skid ));
1028+ req -> subjKeyIdSz = (word32 )sizeof (expected_skid );
1029+ }
1030+ }
1031+
1032+ /* Signing invokes wolfssl_x509_make_der() -> ReqCertFromX509(), which
1033+ * executes the bounds check and the XMEMCPY into cert->skid. A
1034+ * ">=" boundary mutation would make this step fail for a 32-byte
1035+ * SKID. */
1036+ ExpectIntEQ (wolfSSL_X509_REQ_sign (req , priv , wolfSSL_EVP_sha256 ()),
1037+ WOLFSSL_SUCCESS );
1038+
1039+ /* Re-encode the now-signed CSR and parse it back to verify the SKID
1040+ * round-trips with the correct length and bytes. This verifies the
1041+ * output of the XMEMCPY in ReqCertFromX509(). */
1042+ ExpectIntGT ((derSz = wolfSSL_i2d_X509_REQ (req , & der )), 0 );
1043+ ExpectNotNull (der );
1044+
1045+ ExpectNotNull (parsed = wolfSSL_X509_REQ_d2i (NULL , der , derSz ));
1046+ if (parsed != NULL ) {
1047+ ExpectIntEQ ((int )parsed -> subjKeyIdSz , CTC_MAX_SKID_SIZE );
1048+ ExpectNotNull (parsed -> subjKeyId );
1049+ if (parsed -> subjKeyId != NULL ) {
1050+ ExpectIntEQ (XMEMCMP (parsed -> subjKeyId , expected_skid ,
1051+ CTC_MAX_SKID_SIZE ), 0 );
1052+ }
1053+ }
1054+
1055+ wolfSSL_X509_free (parsed );
1056+ XFREE (der , NULL , DYNAMIC_TYPE_OPENSSL );
1057+ wolfSSL_X509_NAME_free (name );
1058+ wolfSSL_X509_free (req );
1059+ wolfSSL_EVP_PKEY_free (pub );
1060+ wolfSSL_EVP_PKEY_free (priv );
1061+ #endif
1062+ return EXPECT_RESULT ();
1063+ }
0 commit comments