@@ -1630,6 +1630,307 @@ int test_ParseCert_SM3wSM2_short_pubkey(void)
16301630 return EXPECT_RESULT ();
16311631}
16321632
1633+ #if !defined(NO_CERTS ) && !defined(NO_ASN ) && !defined(NO_RSA )
1634+ /* Number of bytes needed to DER-encode the definite length "len".
1635+ * Only handles len < 0x10000, which is all this test needs. */
1636+ static int dnb_lenSz (int len )
1637+ {
1638+ if (len < 0x80 )
1639+ return 1 ;
1640+ if (len < 0x100 )
1641+ return 2 ;
1642+ return 3 ;
1643+ }
1644+
1645+ /* Write the DER definite length "len" to out. Returns the bytes written. */
1646+ static int dnb_encodeLen (byte * out , int len )
1647+ {
1648+ int i = 0 ;
1649+
1650+ if (len < 0x80 ) {
1651+ out [i ++ ] = (byte )len ;
1652+ }
1653+ else if (len < 0x100 ) {
1654+ out [i ++ ] = 0x81 ;
1655+ out [i ++ ] = (byte )len ;
1656+ }
1657+ else {
1658+ out [i ++ ] = 0x82 ;
1659+ out [i ++ ] = (byte )(len >> 8 );
1660+ out [i ++ ] = (byte )(len & 0xff );
1661+ }
1662+ return i ;
1663+ }
1664+
1665+ /* Build one RDN: SET { SEQUENCE { <oidTlv>, <valTag> <val> } }, where oidTlv is
1666+ * the full DER attribute-type OID (tag, length and content). Returns the total
1667+ * number of bytes written to out. */
1668+ static int dnb_buildRdn (byte * out , const byte * oidTlv , int oidTlvLen ,
1669+ byte valTag , const byte * val , int valLen )
1670+ {
1671+ int attrTlvLen ;
1672+ int seqContentLen ;
1673+ int setContentLen ;
1674+ int idx = 0 ;
1675+
1676+ attrTlvLen = 1 + dnb_lenSz (valLen ) + valLen ;
1677+ seqContentLen = oidTlvLen + attrTlvLen ;
1678+ setContentLen = 1 + dnb_lenSz (seqContentLen ) + seqContentLen ;
1679+
1680+ out [idx ++ ] = 0x31 ; /* SET OF */
1681+ idx += dnb_encodeLen (& out [idx ], setContentLen );
1682+ out [idx ++ ] = 0x30 ; /* SEQUENCE */
1683+ idx += dnb_encodeLen (& out [idx ], seqContentLen );
1684+ XMEMCPY (& out [idx ], oidTlv , (size_t )oidTlvLen );
1685+ idx += oidTlvLen ;
1686+ out [idx ++ ] = valTag ; /* string tag */
1687+ idx += dnb_encodeLen (& out [idx ], valLen );
1688+ XMEMCPY (& out [idx ], val , (size_t )valLen );
1689+ idx += valLen ;
1690+
1691+ return idx ;
1692+ }
1693+ #endif /* !NO_CERTS && !NO_ASN && !NO_RSA */
1694+
1695+ /* Regression test for a 1-byte out-of-bounds NUL write in GetCertName().
1696+ * When a Subject DN exactly fills the WC_ASN_NAME_MAX character buffer the
1697+ * classic (WOLFSSL_ASN_ORIGINAL) parser wrote the string terminator one byte
1698+ * past cert->subject. Each certificate below is built so its second (boundary)
1699+ * RDN would land the running index on exactly WC_ASN_NAME_MAX, which must now
1700+ * be rejected as too big, leaving only the first RDN. Several attribute types
1701+ * are exercised because the too-big guards differ per attribute: commonName
1702+ * uses the "/CN=" prefix, emailAddress takes the distinct email branch with the
1703+ * longer "/emailAddress=" prefix, and (under WOLFSSL_CERT_EXT) jurisdictionC
1704+ * takes the JOI branch. Each attribute is checked on both boundary sides: at
1705+ * the cap (dropped) and one byte under it (kept in full), so an over-tightening
1706+ * off-by-one on any single guard is caught too. Runs under both ASN parsers. */
1707+ int test_ParseCert_dnBufferBoundary (void )
1708+ {
1709+ EXPECT_DECLS ;
1710+
1711+ #if !defined(NO_CERTS ) && !defined(NO_ASN ) && !defined(NO_RSA )
1712+ /* 2048-bit RSA SubjectPublicKeyInfo, extracted with OpenSSL from
1713+ * certs/client-cert.pem, so the key decodes and the parse succeeds. */
1714+ static const byte rsaSpki [] = {
1715+ 0x30 , 0x82 , 0x01 , 0x22 , 0x30 , 0x0d , 0x06 , 0x09 , 0x2a , 0x86 , 0x48 , 0x86 ,
1716+ 0xf7 , 0x0d , 0x01 , 0x01 , 0x01 , 0x05 , 0x00 , 0x03 , 0x82 , 0x01 , 0x0f , 0x00 ,
1717+ 0x30 , 0x82 , 0x01 , 0x0a , 0x02 , 0x82 , 0x01 , 0x01 , 0x00 , 0xc3 , 0x03 , 0xd1 ,
1718+ 0x2b , 0xfe , 0x39 , 0xa4 , 0x32 , 0x45 , 0x3b , 0x53 , 0xc8 , 0x84 , 0x2b , 0x2a ,
1719+ 0x7c , 0x74 , 0x9a , 0xbd , 0xaa , 0x2a , 0x52 , 0x07 , 0x47 , 0xd6 , 0xa6 , 0x36 ,
1720+ 0xb2 , 0x07 , 0x32 , 0x8e , 0xd0 , 0xba , 0x69 , 0x7b , 0xc6 , 0xc3 , 0x44 , 0x9e ,
1721+ 0xd4 , 0x81 , 0x48 , 0xfd , 0x2d , 0x68 , 0xa2 , 0x8b , 0x67 , 0xbb , 0xa1 , 0x75 ,
1722+ 0xc8 , 0x36 , 0x2c , 0x4a , 0xd2 , 0x1b , 0xf7 , 0x8b , 0xba , 0xcf , 0x0d , 0xf9 ,
1723+ 0xef , 0xec , 0xf1 , 0x81 , 0x1e , 0x7b , 0x9b , 0x03 , 0x47 , 0x9a , 0xbf , 0x65 ,
1724+ 0xcc , 0x7f , 0x65 , 0x24 , 0x69 , 0xa6 , 0xe8 , 0x14 , 0x89 , 0x5b , 0xe4 , 0x34 ,
1725+ 0xf7 , 0xc5 , 0xb0 , 0x14 , 0x93 , 0xf5 , 0x67 , 0x7b , 0x3a , 0x7a , 0x78 , 0xe1 ,
1726+ 0x01 , 0x56 , 0x56 , 0x91 , 0xa6 , 0x13 , 0x42 , 0x8d , 0xd2 , 0x3c , 0x40 , 0x9c ,
1727+ 0x4c , 0xef , 0xd1 , 0x86 , 0xdf , 0x37 , 0x51 , 0x1b , 0x0c , 0xa1 , 0x3b , 0xf5 ,
1728+ 0xf1 , 0xa3 , 0x4a , 0x35 , 0xe4 , 0xe1 , 0xce , 0x96 , 0xdf , 0x1b , 0x7e , 0xbf ,
1729+ 0x4e , 0x97 , 0xd0 , 0x10 , 0xe8 , 0xa8 , 0x08 , 0x30 , 0x81 , 0xaf , 0x20 , 0x0b ,
1730+ 0x43 , 0x14 , 0xc5 , 0x74 , 0x67 , 0xb4 , 0x32 , 0x82 , 0x6f , 0x8d , 0x86 , 0xc2 ,
1731+ 0x88 , 0x40 , 0x99 , 0x36 , 0x83 , 0xba , 0x1e , 0x40 , 0x72 , 0x22 , 0x17 , 0xd7 ,
1732+ 0x52 , 0x65 , 0x24 , 0x73 , 0xb0 , 0xce , 0xef , 0x19 , 0xcd , 0xae , 0xff , 0x78 ,
1733+ 0x6c , 0x7b , 0xc0 , 0x12 , 0x03 , 0xd4 , 0x4e , 0x72 , 0x0d , 0x50 , 0x6d , 0x3b ,
1734+ 0xa3 , 0x3b , 0xa3 , 0x99 , 0x5e , 0x9d , 0xc8 , 0xd9 , 0x0c , 0x85 , 0xb3 , 0xd9 ,
1735+ 0x8a , 0xd9 , 0x54 , 0x26 , 0xdb , 0x6d , 0xfa , 0xac , 0xbb , 0xff , 0x25 , 0x4c ,
1736+ 0xc4 , 0xd1 , 0x79 , 0xf4 , 0x71 , 0xd3 , 0x86 , 0x40 , 0x18 , 0x13 , 0xb0 , 0x63 ,
1737+ 0xb5 , 0x72 , 0x4e , 0x30 , 0xc4 , 0x97 , 0x84 , 0x86 , 0x2d , 0x56 , 0x2f , 0xd7 ,
1738+ 0x15 , 0xf7 , 0x7f , 0xc0 , 0xae , 0xf5 , 0xfc , 0x5b , 0xe5 , 0xfb , 0xa1 , 0xba ,
1739+ 0xd3 , 0x02 , 0x03 , 0x01 , 0x00 , 0x01
1740+ };
1741+ /* Certificate fields up to (not including) the subject Name. The two 0x0000
1742+ * placeholders are the outer and tbsCertificate SEQUENCE lengths, patched
1743+ * once the subject size is known. */
1744+ static const byte certPrefix [] = {
1745+ /* Certificate SEQUENCE (length patched) */
1746+ 0x30 , 0x82 , 0x00 , 0x00 ,
1747+ /* tbsCertificate SEQUENCE (length patched) */
1748+ 0x30 , 0x82 , 0x00 , 0x00 ,
1749+ /* version [0] INTEGER 2 */
1750+ 0xa0 , 0x03 , 0x02 , 0x01 , 0x02 ,
1751+ /* serialNumber INTEGER 1 */
1752+ 0x02 , 0x01 , 0x01 ,
1753+ /* signature AlgorithmIdentifier: sha256WithRSAEncryption */
1754+ 0x30 , 0x0d , 0x06 , 0x09 , 0x2a , 0x86 , 0x48 , 0x86 , 0xf7 , 0x0d , 0x01 , 0x01 ,
1755+ 0x0b , 0x05 , 0x00 ,
1756+ /* issuer Name: /CN=Test */
1757+ 0x30 , 0x0f , 0x31 , 0x0d , 0x30 , 0x0b , 0x06 , 0x03 , 0x55 , 0x04 , 0x03 , 0x13 ,
1758+ 0x04 , 0x54 , 0x65 , 0x73 , 0x74 ,
1759+ /* validity: notBefore 20000101000000Z, notAfter 20491231235959Z */
1760+ 0x30 , 0x1e ,
1761+ 0x17 , 0x0d , 0x30 , 0x30 , 0x30 , 0x31 , 0x30 , 0x31 , 0x30 , 0x30 , 0x30 , 0x30 ,
1762+ 0x30 , 0x30 , 0x5a ,
1763+ 0x17 , 0x0d , 0x34 , 0x39 , 0x31 , 0x32 , 0x33 , 0x31 , 0x32 , 0x33 , 0x35 , 0x39 ,
1764+ 0x35 , 0x39 , 0x5a
1765+ };
1766+ /* Outer signatureAlgorithm and a placeholder signatureValue. Not checked
1767+ * because NO_VERIFY is used, but the structure must be present. */
1768+ static const byte certSuffix [] = {
1769+ /* signatureAlgorithm: sha256WithRSAEncryption */
1770+ 0x30 , 0x0d , 0x06 , 0x09 , 0x2a , 0x86 , 0x48 , 0x86 , 0xf7 , 0x0d , 0x01 , 0x01 ,
1771+ 0x0b , 0x05 , 0x00 ,
1772+ /* signatureValue BIT STRING */
1773+ 0x03 , 0x05 , 0x00 , 0xde , 0xad , 0xbe , 0xef
1774+ };
1775+ /* commonName OID (2.5.4.3). */
1776+ static const byte cnOid [] = { 0x06 , 0x03 , 0x55 , 0x04 , 0x03 };
1777+ /* emailAddress OID (1.2.840.113549.1.9.1, PKCS#9). */
1778+ static const byte emailOid [] = {
1779+ 0x06 , 0x09 , 0x2a , 0x86 , 0x48 , 0x86 , 0xf7 , 0x0d , 0x01 , 0x09 , 0x01
1780+ };
1781+ #ifdef WOLFSSL_CERT_EXT
1782+ /* jurisdictionCountryName OID (ASN_JOI_PREFIX + ASN_JOI_C). */
1783+ static const byte joiCOid [] = {
1784+ 0x06 , 0x0b , 0x2b , 0x06 , 0x01 , 0x04 , 0x01 , 0x82 , 0x37 , 0x3c , 0x02 , 0x01 ,
1785+ 0x03
1786+ };
1787+ #endif
1788+ /* One entry per boundary attribute. valLen is sized relative to the buffer
1789+ * cap: after the leading /CN=A (index 5) the parser adds copyLen + strLen,
1790+ * where commonName copyLen is 4 ("/CN="), emailAddress copyLen is
1791+ * sizeof(WOLFSSL_EMAIL_ADDR) - 1 ("/emailAddress="), and jurisdictionC
1792+ * copyLen is sizeof(WOLFSSL_JOI_C) - 1 ("/jurisdictionC="). The reject cases
1793+ * land on exactly WC_ASN_NAME_MAX (must be dropped); the accept case lands
1794+ * one byte under (largest name that still fits, must be kept in full). */
1795+ struct {
1796+ const byte * oid ;
1797+ int oidLen ;
1798+ byte valTag ;
1799+ int valLen ;
1800+ int expectFull ; /* 1: second RDN kept in full; 0: dropped */
1801+ } cases [6 ];
1802+ DecodedCert cert ;
1803+ byte * der = NULL ;
1804+ byte * val2 = NULL ;
1805+ byte rdn1 [16 ];
1806+ int numCases = 4 ;
1807+ int valLen ;
1808+ int rdn1Len ;
1809+ int rdn2Len ;
1810+ int nameContentLen ;
1811+ int tbsContentLen ;
1812+ int outerContentLen ;
1813+ int pos ;
1814+ int derSz ;
1815+ int c ;
1816+
1817+ /* Each attribute is tested on both boundary sides: valLen at the cap
1818+ * (copyLen + strLen == WC_ASN_NAME_MAX - idx) must be dropped, and one byte
1819+ * under the cap must be kept in full. Both sides are covered per attribute
1820+ * because the too-big guards are independent comparison sites. */
1821+ /* commonName (final catch-all guard). */
1822+ cases [0 ].oid = cnOid ;
1823+ cases [0 ].oidLen = (int )sizeof (cnOid );
1824+ cases [0 ].valTag = 0x13 ; /* PrintableString */
1825+ cases [0 ].valLen = WC_ASN_NAME_MAX - 9 ;
1826+ cases [0 ].expectFull = 0 ;
1827+ cases [1 ].oid = cnOid ;
1828+ cases [1 ].oidLen = (int )sizeof (cnOid );
1829+ cases [1 ].valTag = 0x13 ;
1830+ cases [1 ].valLen = WC_ASN_NAME_MAX - 10 ;
1831+ cases [1 ].expectFull = 1 ;
1832+ /* emailAddress (distinct email branch guard). */
1833+ cases [2 ].oid = emailOid ;
1834+ cases [2 ].oidLen = (int )sizeof (emailOid );
1835+ cases [2 ].valTag = 0x16 ; /* IA5String */
1836+ cases [2 ].valLen = WC_ASN_NAME_MAX - 5 -
1837+ ((int )sizeof (WOLFSSL_EMAIL_ADDR ) - 1 );
1838+ cases [2 ].expectFull = 0 ;
1839+ cases [3 ].oid = emailOid ;
1840+ cases [3 ].oidLen = (int )sizeof (emailOid );
1841+ cases [3 ].valTag = 0x16 ;
1842+ cases [3 ].valLen = WC_ASN_NAME_MAX - 6 -
1843+ ((int )sizeof (WOLFSSL_EMAIL_ADDR ) - 1 );
1844+ cases [3 ].expectFull = 1 ;
1845+ #ifdef WOLFSSL_CERT_EXT
1846+ /* jurisdictionCountryName (JOI branch guard). */
1847+ cases [4 ].oid = joiCOid ;
1848+ cases [4 ].oidLen = (int )sizeof (joiCOid );
1849+ cases [4 ].valTag = 0x13 ; /* PrintableString */
1850+ cases [4 ].valLen = WC_ASN_NAME_MAX - 5 -
1851+ ((int )sizeof (WOLFSSL_JOI_C ) - 1 );
1852+ cases [4 ].expectFull = 0 ;
1853+ cases [5 ].oid = joiCOid ;
1854+ cases [5 ].oidLen = (int )sizeof (joiCOid );
1855+ cases [5 ].valTag = 0x13 ;
1856+ cases [5 ].valLen = WC_ASN_NAME_MAX - 6 -
1857+ ((int )sizeof (WOLFSSL_JOI_C ) - 1 );
1858+ cases [5 ].expectFull = 1 ;
1859+ numCases = 6 ;
1860+ #endif
1861+
1862+ ExpectNotNull (val2 = (byte * )XMALLOC ((size_t )WC_ASN_NAME_MAX , NULL ,
1863+ DYNAMIC_TYPE_TMP_BUFFER ));
1864+ ExpectNotNull (der = (byte * )XMALLOC (2048 , NULL , DYNAMIC_TYPE_TMP_BUFFER ));
1865+
1866+ for (c = 0 ; (der != NULL ) && (val2 != NULL ) && (c < numCases ); c ++ ) {
1867+ valLen = cases [c ].valLen ;
1868+ XMEMSET (val2 , 'B' , (size_t )valLen );
1869+
1870+ /* Fixed leading fields. */
1871+ XMEMCPY (der , certPrefix , sizeof (certPrefix ));
1872+ pos = (int )sizeof (certPrefix );
1873+
1874+ /* First RDN: /CN=A (a short name that must survive). */
1875+ rdn1Len = dnb_buildRdn (rdn1 , cnOid , (int )sizeof (cnOid ), 0x13 ,
1876+ (const byte * )"A" , 1 );
1877+
1878+ /* Second RDN length, computed the same way dnb_buildRdn() lays it
1879+ * out. */
1880+ rdn2Len = 1 + dnb_lenSz (valLen ) + valLen ; /* value TLV */
1881+ rdn2Len = cases [c ].oidLen + rdn2Len ; /* SEQUENCE content */
1882+ rdn2Len = 1 + dnb_lenSz (rdn2Len ) + rdn2Len ; /* SET content */
1883+ rdn2Len = 1 + dnb_lenSz (rdn2Len ) + rdn2Len ; /* SET TLV */
1884+ nameContentLen = rdn1Len + rdn2Len ;
1885+
1886+ /* Subject Name SEQUENCE header, then the two RDNs in order. */
1887+ der [pos ++ ] = 0x30 ;
1888+ pos += dnb_encodeLen (& der [pos ], nameContentLen );
1889+ XMEMCPY (& der [pos ], rdn1 , (size_t )rdn1Len );
1890+ pos += rdn1Len ;
1891+ pos += dnb_buildRdn (& der [pos ], cases [c ].oid , cases [c ].oidLen ,
1892+ cases [c ].valTag , val2 , valLen );
1893+
1894+ /* SubjectPublicKeyInfo. */
1895+ XMEMCPY (& der [pos ], rsaSpki , sizeof (rsaSpki ));
1896+ pos += (int )sizeof (rsaSpki );
1897+
1898+ /* tbsCertificate content spans from offset 8 to here. */
1899+ tbsContentLen = pos - 8 ;
1900+
1901+ /* Outer signature algorithm and value. */
1902+ XMEMCPY (& der [pos ], certSuffix , sizeof (certSuffix ));
1903+ pos += (int )sizeof (certSuffix );
1904+ derSz = pos ;
1905+ outerContentLen = derSz - 4 ;
1906+
1907+ /* Patch the two SEQUENCE lengths (both use the 0x82 long form). */
1908+ der [6 ] = (byte )(tbsContentLen >> 8 );
1909+ der [7 ] = (byte )(tbsContentLen & 0xff );
1910+ der [2 ] = (byte )(outerContentLen >> 8 );
1911+ der [3 ] = (byte )(outerContentLen & 0xff );
1912+
1913+ wc_InitDecodedCert (& cert , der , (word32 )derSz , NULL );
1914+ ExpectIntEQ (wc_ParseCert (& cert , CERT_TYPE , NO_VERIFY , NULL ), 0 );
1915+ if (cases [c ].expectFull ) {
1916+ /* Largest name that still fits: the second RDN is kept and the
1917+ * subject fills the buffer up to the in-bounds terminator. */
1918+ ExpectIntEQ ((int )XSTRLEN (cert .subject ), WC_ASN_NAME_MAX - 1 );
1919+ }
1920+ else {
1921+ /* The boundary attribute is dropped so the terminator stays in
1922+ * bounds; only the first RDN remains. */
1923+ ExpectStrEQ (cert .subject , "/CN=A" );
1924+ }
1925+ wc_FreeDecodedCert (& cert );
1926+ }
1927+
1928+ XFREE (der , NULL , DYNAMIC_TYPE_TMP_BUFFER );
1929+ XFREE (val2 , NULL , DYNAMIC_TYPE_TMP_BUFFER );
1930+ #endif /* !NO_CERTS && !NO_ASN && !NO_RSA */
1931+ return EXPECT_RESULT ();
1932+ }
1933+
16331934int test_SerialNumber0_RootCA (void )
16341935{
16351936 EXPECT_DECLS ;
0 commit comments