// Copyright (c) 2016, Google Inc. // SPDX-License-Identifier: ISC #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "internal.h" #include "../evp_extra/internal.h" #include "../internal.h" #include "../test/test_util.h" #include "../test/x509_util.h" #include "../fipsmodule/pqdsa/internal.h" #if defined(OPENSSL_THREADS) #include #endif static const char kX509ExtensionsCert[] = R"( -----BEGIN CERTIFICATE----- MIICwDCCAimgAwIBAgIEAJNOazANBgkqhkiG9w0BAQEFADAQMQ4wDAYDVQQKEwVjYXNzYTAeFw0xMTAz MzAxMTEwMzZaFw0xNDAzMzAxNTQwMzZaMC0xDjAMBgNVBAoMBWNhc9ijMRswGQYDVQQDExJlbXMuZ3J1 cG9jYXNzYdeckJIwgZ8wDQYJKoZIhvcNAQEJBQADgY0AMIGJAoGBAFUEB/i0583rnah0hLRk1hleI5T0 xw+naVIxs/h4ZHu19xva671kvXfN97PZBmzAwFAWXmfs5MUrviy6RjZjhc0Ad2510cmqWy9FUx4D7kjy iuZZIaA0xL0AAfvJbDkXrGpHZWz8BkANFZ/RHl961BRB3fTGvPWiZK6C4mCwPgIDaQABjwGjggEIMIIB BDALBgNVHRAEBAMCBaAwKwYDVR0VBCQKCf8O1s/Ozk3MzM8xNTo7MzZaMIIBBDALBgNVHRAEBAMCBaAw KwYDVR0VBCQKCf8O1s/OKoUDBwExNTo7MzZagQ8BBDALBgNVHRAEBAMCBaAwKwYDVR0VBCQKAYPxKTDO zs3MzM8xNTo7MzZagQ8yMDEzMDAxMzAxKjUwNlowEQYJKwYBBQUHMAECBAQDAgEHMBEGCWCGSAGG+EJZ AQQEAwIGQDAbBgNVHQ0EFDASMBAGCSqGSIb2fQcQBAQDAgWgMCsGA1UdFQQkCiFD8Skwzs7NzMzPMTU6 OzM2WoEPAQQwCwYDVR0QBAQDAgWgMA0GCSsGAQUFBzABBQUAA4GBAKTNw5fTOnPCcOFMARmAD1RtaAN8 0TBKIy2A0hG/2dlNeI6s0dqZe6juverYmC5sOResakdlbPwGQA0Vn9EeX8Yr67/d9Ma89aJkroLiXbA+ j2kCAwG+LLpGNmNwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBw5lmgITTEvXIj+8ls -----END CERTIFICATE----- )"; static const char kX509CustomExtensionsCA[] = R"( -----BEGIN CERTIFICATE----- MIIBxzCCAW2gAwIBAgIFAQAAAAAwCgYIKoZIzj0EAwIwJjEPMA0GA1UECgwGQW1h em9uMRMwEQYDVQQpDAo0Mjk0OTY3Mjk2MCIYDzIwMjUwMzI5MjA0OTE5WhgPOTk5 OTEyMzEyMzU5NTlaMCYxDzANBgNVBAoMBkFtYXpvbjETMBEGA1UEKQwKNDI5NDk2 NzI5NjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABL3eDQzFx4cherBJdIQsxMzZ rCtzXBTB3f/rRMLrtjxpk2/6h3ZbE4t8MmDbwVepAKYQgT1bjPUFn+edG2U8kRej gYMwgYAwEgYDVR0TAQH/BAgwBgEB/wIBADBLBgNVHSMERDBCgBTMNuas7QD5hDXY KS7k0WDN6ckMFqEqpCgwJjEPMA0GA1UECgwGQW1hem9uMRMwEQYDVQQpDAo0Mjk0 OTY3Mjk2MB0GA1UdDgQWBBTMNuas7QD5hDXYKS7k0WDN6ckMFjAKBggqhkjOPQQD AgNIADBFAiB3MJLK86+JyyoBr2s1Ugjvc7gWAHSk9OgXfyfsVmBV9gIhAPIUiYo8 Jx+IbRyNj2WfeCbn8v3fob0wkGsKf1TSVcZ8 -----END CERTIFICATE----- )"; static const char kX509CustomExtensionsCert[] = R"( -----BEGIN CERTIFICATE----- MIIB6zCCAZGgAwIBAgIFAQAAAAAwCgYIKoZIzj0EAwIwJjEPMA0GA1UECgwGQW1h em9uMRMwEQYDVQQpDAo0Mjk0OTY3Mjk2MCIYDzIwMjUwMzI5MjA0OTE5WhgPOTk5 OTEyMzEyMzU5NTlaMBExDzANBgNVBAoMBkFtYXpvbjBZMBMGByqGSM49AgEGCCqG SM49AwEHA0IABNbNswB+jmoICPKu567Odfq83s9P0N82kFYnyANgmztgHqoK7yIX 0meBn5N9Y4m3wAmvokYeK7dU1oRSM397unmjgbwwgbkwDAYDVR0TAQH/BAIwADAO BgNVHQ8BAf8EBAMCB4AwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwEwFQYHK4E7gcR0 BQEB/wQHcHJlc2VudDBLBgNVHSMERDBCgBTMNuas7QD5hDXYKS7k0WDN6ckMFqEq pCgwJjEPMA0GA1UECgwGQW1hem9uMRMwEQYDVQQpDAo0Mjk0OTY3Mjk2MB0GA1Ud DgQWBBRiexFm2K7Ou2dx4+c0LjQOsuqHJjAKBggqhkjOPQQDAgNIADBFAiAxH63Q eK26A9QPOkqi+5Hvrptpb9HRstSC6emJdaEB1QIhAKyhyLBPrG85QDoXrFcVZUA2 +StWnDVDGtgWM6tPz4Uw -----END CERTIFICATE----- )"; static const char kX509MultipleCustomExtensionsCA[] = R"( -----BEGIN CERTIFICATE----- MIIByDCCAW2gAwIBAgIFAQAAAAAwCgYIKoZIzj0EAwIwJjEPMA0GA1UECgwGQW1h em9uMRMwEQYDVQQpDAo0Mjk0OTY3Mjk2MCIYDzIwMjUwMzMwMjExMzA1WhgPOTk5 OTEyMzEyMzU5NTlaMCYxDzANBgNVBAoMBkFtYXpvbjETMBEGA1UEKQwKNDI5NDk2 NzI5NjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABMhC2xZcc7UouUMo1xPMiq8E Z7DdWJq0I9nPunowEwidaif/YU6tjAPVFPmcRIhRYvZH6HWyNc0gztcfgAxa7tej gYMwgYAwEgYDVR0TAQH/BAgwBgEB/wIBADBLBgNVHSMERDBCgBRMh4uFf12IUZ1m v3WCstI0aqCBdKEqpCgwJjEPMA0GA1UECgwGQW1hem9uMRMwEQYDVQQpDAo0Mjk0 OTY3Mjk2MB0GA1UdDgQWBBRMh4uFf12IUZ1mv3WCstI0aqCBdDAKBggqhkjOPQQD AgNJADBGAiEAyZK6Elt1iqVV1Rys4G8HmIE7/hRW3rbQWiNPd4FnANACIQCgbgki hQaJgNo+8hOTEOQZsRSaIbu+F2afe6ncp996RQ== -----END CERTIFICATE----- )"; static const char kX509MultipleCustomExtensionsCert[] = R"( -----BEGIN CERTIFICATE----- MIICAjCCAaigAwIBAgIFAQAAAAAwCgYIKoZIzj0EAwIwJjEPMA0GA1UECgwGQW1h em9uMRMwEQYDVQQpDAo0Mjk0OTY3Mjk2MCIYDzIwMjUwMzMwMjExMzA1WhgPOTk5 OTEyMzEyMzU5NTlaMBExDzANBgNVBAoMBkFtYXpvbjBZMBMGByqGSM49AgEGCCqG SM49AwEHA0IABPVuvcRmJ8fqyZferbqGWP8Kd1yHHX+4gcglS5WV9Zt7T957fhNY QpimdCfV+KEJji8IwBc7vOk+1Db3ulQ0dZejgdMwgdAwDAYDVR0TAQH/BAIwADAO BgNVHQ8BAf8EBAMCB4AwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwEwFQYHK4E7gcR0 BQEB/wQHcHJlc2VudDAVBgcrgTuBxHQGAQH/BAdwcmVzZW50MEsGA1UdIwREMEKA FEyHi4V/XYhRnWa/dYKy0jRqoIF0oSqkKDAmMQ8wDQYDVQQKDAZBbWF6b24xEzAR BgNVBCkMCjQyOTQ5NjcyOTYwHQYDVR0OBBYEFGt+Hy7qdE2lFnnjYPGqeVvJ4uPf MAoGCCqGSM49BAMCA0gAMEUCIQC4aXyPOO6asCwoG1pGGmODmAEMA2tAXXNp67Oo hDO90wIgETGPNCQIHlvUXAfDmZdUPh+PKkv6paVhWMTXrsh19LQ= -----END CERTIFICATE----- )"; std::string GetTestData(const char *path); static const char kCrossSigningRootPEM[] = R"( -----BEGIN CERTIFICATE----- MIICcTCCAdqgAwIBAgIIagJHiPvE0MowDQYJKoZIhvcNAQELBQAwPDEaMBgGA1UE ChMRQm9yaW5nU1NMIFRFU1RJTkcxHjAcBgNVBAMTFUNyb3NzLXNpZ25pbmcgUm9v dCBDQTAgFw0xNTAxMDEwMDAwMDBaGA8yMTAwMDEwMTAwMDAwMFowPDEaMBgGA1UE ChMRQm9yaW5nU1NMIFRFU1RJTkcxHjAcBgNVBAMTFUNyb3NzLXNpZ25pbmcgUm9v dCBDQTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwo3qFvSB9Zmlbpzn9wJp ikI75Rxkatez8VkLqyxbOhPYl2Haz8F5p1gDG96dCI6jcLGgu3AKT9uhEQyyUko5 EKYasazSeA9CQrdyhPg0mkTYVETnPM1W/ebid1YtqQbq1CMWlq2aTDoSGAReGFKP RTdXAbuAXzpCfi/d8LqV13UCAwEAAaN6MHgwDgYDVR0PAQH/BAQDAgIEMB0GA1Ud JQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAPBgNVHRMBAf8EBTADAQH/MBkGA1Ud DgQSBBBHKHC7V3Z/3oLvEZx0RZRwMBsGA1UdIwQUMBKAEEcocLtXdn/egu8RnHRF lHAwDQYJKoZIhvcNAQELBQADgYEAnglibsy6mGtpIXivtlcz4zIEnHw/lNW+r/eC CY7evZTmOoOuC/x9SS3MF9vawt1HFUummWM6ZgErqVBOXIB4//ykrcCgf5ZbF5Hr +3EFprKhBqYiXdD8hpBkrBoXwn85LPYWNd2TceCrx0YtLIprE2R5MB2RIq8y4Jk3 YFXvkME= -----END CERTIFICATE----- )"; static const char kRootCAPEM[] = R"( -----BEGIN CERTIFICATE----- MIICVTCCAb6gAwIBAgIIAj5CwoHlWuYwDQYJKoZIhvcNAQELBQAwLjEaMBgGA1UE ChMRQm9yaW5nU1NMIFRFU1RJTkcxEDAOBgNVBAMTB1Jvb3QgQ0EwIBcNMTUwMTAx MDAwMDAwWhgPMjEwMDAxMDEwMDAwMDBaMC4xGjAYBgNVBAoTEUJvcmluZ1NTTCBU RVNUSU5HMRAwDgYDVQQDEwdSb290IENBMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB iQKBgQDpDn8RDOZa5oaDcPZRBy4CeBH1siSSOO4mYgLHlPE+oXdqwI/VImi2XeJM 2uCFETXCknJJjYG0iJdrt/yyRFvZTQZw+QzGj+mz36NqhGxDWb6dstB2m8PX+plZ w7jl81MDvUnWs8yiQ/6twgu5AbhWKZQDJKcNKCEpqa6UW0r5nwIDAQABo3oweDAO BgNVHQ8BAf8EBAMCAgQwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMA8G A1UdEwEB/wQFMAMBAf8wGQYDVR0OBBIEEEA31wH7QC+4HH5UBCeMWQEwGwYDVR0j BBQwEoAQQDfXAftAL7gcflQEJ4xZATANBgkqhkiG9w0BAQsFAAOBgQDXylEK77Za kKeY6ZerrScWyZhrjIGtHFu09qVpdJEzrk87k2G7iHHR9CAvSofCgEExKtWNS9dN +9WiZp/U48iHLk7qaYXdEuO07No4BYtXn+lkOykE+FUxmA4wvOF1cTd2tdj3MzX2 kfGIBAYhzGZWhY3JbhIfTEfY1PNM1pWChQ== -----END CERTIFICATE----- )"; static const char kRootCrossSignedPEM[] = R"( -----BEGIN CERTIFICATE----- MIICYzCCAcygAwIBAgIIAj5CwoHlWuYwDQYJKoZIhvcNAQELBQAwPDEaMBgGA1UE ChMRQm9yaW5nU1NMIFRFU1RJTkcxHjAcBgNVBAMTFUNyb3NzLXNpZ25pbmcgUm9v dCBDQTAgFw0xNTAxMDEwMDAwMDBaGA8yMTAwMDEwMTAwMDAwMFowLjEaMBgGA1UE ChMRQm9yaW5nU1NMIFRFU1RJTkcxEDAOBgNVBAMTB1Jvb3QgQ0EwgZ8wDQYJKoZI hvcNAQEBBQADgY0AMIGJAoGBAOkOfxEM5lrmhoNw9lEHLgJ4EfWyJJI47iZiAseU 8T6hd2rAj9UiaLZd4kza4IURNcKSckmNgbSIl2u3/LJEW9lNBnD5DMaP6bPfo2qE bENZvp2y0Habw9f6mVnDuOXzUwO9SdazzKJD/q3CC7kBuFYplAMkpw0oISmprpRb SvmfAgMBAAGjejB4MA4GA1UdDwEB/wQEAwICBDAdBgNVHSUEFjAUBggrBgEFBQcD AQYIKwYBBQUHAwIwDwYDVR0TAQH/BAUwAwEB/zAZBgNVHQ4EEgQQQDfXAftAL7gc flQEJ4xZATAbBgNVHSMEFDASgBBHKHC7V3Z/3oLvEZx0RZRwMA0GCSqGSIb3DQEB CwUAA4GBAErTxYJ0en9HVRHAAr5OO5wuk5Iq3VMc79TMyQLCXVL8YH8Uk7KEwv+q 9MEKZv2eR/Vfm4HlXlUuIqfgUXbwrAYC/YVVX86Wnbpy/jc73NYVCq8FEZeO+0XU 90SWAPDdp+iL7aZdimnMtG1qlM1edmz8AKbrhN/R3IbA2CL0nCWV -----END CERTIFICATE----- )"; static const char kIntermediatePEM[] = R"( -----BEGIN CERTIFICATE----- MIICXjCCAcegAwIBAgIJAKJMH+7rscPcMA0GCSqGSIb3DQEBCwUAMC4xGjAYBgNV BAoTEUJvcmluZ1NTTCBURVNUSU5HMRAwDgYDVQQDEwdSb290IENBMCAXDTE1MDEw MTAwMDAwMFoYDzIxMDAwMTAxMDAwMDAwWjA2MRowGAYDVQQKExFCb3JpbmdTU0wg VEVTVElORzEYMBYGA1UEAxMPSW50ZXJtZWRpYXRlIENBMIGfMA0GCSqGSIb3DQEB AQUAA4GNADCBiQKBgQC7YtI0l8ocTYJ0gKyXTtPL4iMJCNY4OcxXl48jkncVG1Hl blicgNUa1r9m9YFtVkxvBinb8dXiUpEGhVg4awRPDcatlsBSEBuJkiZGYbRcAmSu CmZYnf6u3aYQ18SU8WqVERPpE4cwVVs+6kwlzRw0+XDoZAczu8ZezVhCUc6NbQID AQABo3oweDAOBgNVHQ8BAf8EBAMCAgQwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsG AQUFBwMCMA8GA1UdEwEB/wQFMAMBAf8wGQYDVR0OBBIEEIwaaKi1dttdV3sfjRSy BqMwGwYDVR0jBBQwEoAQQDfXAftAL7gcflQEJ4xZATANBgkqhkiG9w0BAQsFAAOB gQCvnolNWEHuQS8PFVVyuLR+FKBeUUdrVbSfHSzTqNAqQGp0C9fk5oCzDq6ZgTfY ESXM4cJhb3IAnW0UM0NFsYSKQJ50JZL2L3z5ZLQhHdbs4RmODGoC40BVdnJ4/qgB aGSh09eQRvAVmbVCviDK2ipkWNegdyI19jFfNP5uIkGlYg== -----END CERTIFICATE----- )"; static const char kIntermediateSelfSignedPEM[] = R"( -----BEGIN CERTIFICATE----- MIICZjCCAc+gAwIBAgIJAKJMH+7rscPcMA0GCSqGSIb3DQEBCwUAMDYxGjAYBgNV BAoTEUJvcmluZ1NTTCBURVNUSU5HMRgwFgYDVQQDEw9JbnRlcm1lZGlhdGUgQ0Ew IBcNMTUwMTAxMDAwMDAwWhgPMjEwMDAxMDEwMDAwMDBaMDYxGjAYBgNVBAoTEUJv cmluZ1NTTCBURVNUSU5HMRgwFgYDVQQDEw9JbnRlcm1lZGlhdGUgQ0EwgZ8wDQYJ KoZIhvcNAQEBBQADgY0AMIGJAoGBALti0jSXyhxNgnSArJdO08viIwkI1jg5zFeX jyOSdxUbUeVuWJyA1RrWv2b1gW1WTG8GKdvx1eJSkQaFWDhrBE8Nxq2WwFIQG4mS JkZhtFwCZK4KZlid/q7dphDXxJTxapURE+kThzBVWz7qTCXNHDT5cOhkBzO7xl7N WEJRzo1tAgMBAAGjejB4MA4GA1UdDwEB/wQEAwICBDAdBgNVHSUEFjAUBggrBgEF BQcDAQYIKwYBBQUHAwIwDwYDVR0TAQH/BAUwAwEB/zAZBgNVHQ4EEgQQjBpoqLV2 211Xex+NFLIGozAbBgNVHSMEFDASgBCMGmiotXbbXVd7H40UsgajMA0GCSqGSIb3 DQEBCwUAA4GBALcccSrAQ0/EqQBsx0ZDTUydHXXNP2DrUkpUKmAXIe8McqIVSlkT 6H4xz7z8VRKBo9j+drjjtCw2i0CQc8aOLxRb5WJ8eVLnaW2XRlUqAzhF0CrulfVI E4Vs6ZLU+fra1WAuIj6qFiigRja+3YkZArG8tMA9vtlhTX/g7YBZIkqH -----END CERTIFICATE----- )"; static const char kLeafPEM[] = R"( -----BEGIN CERTIFICATE----- MIICXjCCAcegAwIBAgIIWjO48ufpunYwDQYJKoZIhvcNAQELBQAwNjEaMBgGA1UE ChMRQm9yaW5nU1NMIFRFU1RJTkcxGDAWBgNVBAMTD0ludGVybWVkaWF0ZSBDQTAg Fw0xNTAxMDEwMDAwMDBaGA8yMTAwMDEwMTAwMDAwMFowMjEaMBgGA1UEChMRQm9y aW5nU1NMIFRFU1RJTkcxFDASBgNVBAMTC2V4YW1wbGUuY29tMIGfMA0GCSqGSIb3 DQEBAQUAA4GNADCBiQKBgQDD0U0ZYgqShJ7oOjsyNKyVXEHqeafmk/bAoPqY/h1c oPw2E8KmeqiUSoTPjG5IXSblOxcqpbAXgnjPzo8DI3GNMhAf8SYNYsoH7gc7Uy7j 5x8bUrisGnuTHqkqH6d4/e7ETJ7i3CpR8bvK16DggEvQTudLipz8FBHtYhFakfdh TwIDAQABo3cwdTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEG CCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwGQYDVR0OBBIEEKN5pvbur7mlXjeMEYA0 4nUwGwYDVR0jBBQwEoAQjBpoqLV2211Xex+NFLIGozANBgkqhkiG9w0BAQsFAAOB gQBj/p+JChp//LnXWC1k121LM/ii7hFzQzMrt70bny406SGz9jAjaPOX4S3gt38y rhjpPukBlSzgQXFg66y6q5qp1nQTD1Cw6NkKBe9WuBlY3iYfmsf7WT8nhlT1CttU xNCwyMX9mtdXdQicOfNjIGUCD5OLV5PgHFPRKiHHioBAhg== -----END CERTIFICATE----- )"; static const char kLeafNoKeyUsagePEM[] = R"( -----BEGIN CERTIFICATE----- MIICNTCCAZ6gAwIBAgIJAIFQGaLQ0G2mMA0GCSqGSIb3DQEBCwUAMDYxGjAYBgNV BAoTEUJvcmluZ1NTTCBURVNUSU5HMRgwFgYDVQQDEw9JbnRlcm1lZGlhdGUgQ0Ew IBcNMTUwMTAxMDAwMDAwWhgPMjEwMDAxMDEwMDAwMDBaMDcxGjAYBgNVBAoTEUJv cmluZ1NTTCBURVNUSU5HMRkwFwYDVQQDExBldmlsLmV4YW1wbGUuY29tMIGfMA0G CSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOKoZe75NPz77EOaMMl4/0s3PyQw++zJvp ejHAxZiTPCJgMbEHLrSzNoHdopg+CLUH5bE4wTXM8w9Inv5P8OAFJt7gJuPUunmk j+NoU3QfzOR6BroePcz1vXX9jyVHRs087M/sLqWRHu9IR+/A+UTcBaWaFiDVUxtJ YOwFMwjNPQIDAQABo0gwRjAMBgNVHRMBAf8EAjAAMBkGA1UdDgQSBBBJfLEUWHq1 27rZ1AVx2J5GMBsGA1UdIwQUMBKAEIwaaKi1dttdV3sfjRSyBqMwDQYJKoZIhvcN AQELBQADgYEALVKN2Y3LZJOtu6SxFIYKxbLaXhTGTdIjxipZhmbBRDFjbZjZZOTe 6Oo+VDNPYco4rBexK7umYXJyfTqoY0E8dbiImhTcGTEj7OAB3DbBomgU1AYe+t2D uwBqh4Y3Eto+Zn4pMVsxGEfUpjzjZDel7bN1/oU/9KWPpDfywfUmjgk= -----END CERTIFICATE----- )"; static const char kForgeryPEM[] = R"( -----BEGIN CERTIFICATE----- MIICZzCCAdCgAwIBAgIIdTlMzQoKkeMwDQYJKoZIhvcNAQELBQAwNzEaMBgGA1UE ChMRQm9yaW5nU1NMIFRFU1RJTkcxGTAXBgNVBAMTEGV2aWwuZXhhbXBsZS5jb20w IBcNMTUwMTAxMDAwMDAwWhgPMjEwMDAxMDEwMDAwMDBaMDoxGjAYBgNVBAoTEUJv cmluZ1NTTCBURVNUSU5HMRwwGgYDVQQDExNmb3JnZXJ5LmV4YW1wbGUuY29tMIGf MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDADTwruBQZGb7Ay6s9HiYv5d1lwtEy xQdA2Sy8Rn8uA20Q4KgqwVY7wzIZ+z5Butrsmwb70gdG1XU+yRaDeE7XVoW6jSpm 0sw35/5vJbTcL4THEFbnX0OPZnvpuZDFUkvVtq5kxpDWsVyM24G8EEq7kPih3Sa3 OMhXVXF8kso6UQIDAQABo3cwdTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYI KwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwGQYDVR0OBBIEEEYJ/WHM 8p64erPWIg4/liwwGwYDVR0jBBQwEoAQSXyxFFh6tdu62dQFcdieRjANBgkqhkiG 9w0BAQsFAAOBgQA+zH7bHPElWRWJvjxDqRexmYLn+D3Aivs8XgXQJsM94W0EzSUf DSLfRgaQwcb2gg2xpDFoG+W0vc6O651uF23WGt5JaFFJJxqjII05IexfCNhuPmp4 4UZAXPttuJXpn74IY1tuouaM06B3vXKZR+/ityKmfJvSwxacmFcK+2ziAg== -----END CERTIFICATE----- )"; // kExamplePSSCert is an example RSA-PSS self-signed certificate, signed with // the default hash functions. static const char kExamplePSSCert[] = R"( -----BEGIN CERTIFICATE----- MIICYjCCAcagAwIBAgIJAI3qUyT6SIfzMBIGCSqGSIb3DQEBCjAFogMCAWowRTEL MAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVy bmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0xNDEwMDkxOTA5NTVaFw0xNTEwMDkxOTA5 NTVaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQK DBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwgZ8wDQYJKoZIhvcNAQEBBQADgY0A MIGJAoGBAPi4bIO0vNmoV8CltFl2jFQdeesiUgR+0zfrQf2D+fCmhRU0dXFahKg8 0u9aTtPel4rd/7vPCqqGkr64UOTNb4AzMHYTj8p73OxaymPHAyXvqIqDWHYg+hZ3 13mSYwFIGth7Z/FSVUlO1m5KXNd6NzYM3t2PROjCpywrta9kS2EHAgMBAAGjUDBO MB0GA1UdDgQWBBTQQfuJQR6nrVrsNF1JEflVgXgfEzAfBgNVHSMEGDAWgBTQQfuJ QR6nrVrsNF1JEflVgXgfEzAMBgNVHRMEBTADAQH/MBIGCSqGSIb3DQEBCjAFogMC AWoDgYEASUy2RZcgNbNQZA0/7F+V1YTLEXwD16bm+iSVnzGwtexmQVEYIZG74K/w xbdZQdTbpNJkp1QPjPfh0zsatw6dmt5QoZ8K8No0DjR9dgf+Wvv5WJvJUIQBoAVN Z0IL+OQFz6+LcTHxD27JJCebrATXZA0wThGTQDm7crL+a+SujBY= -----END CERTIFICATE----- )"; // kExampleRsassaPssCert is an example RSA-PSS self-signed certificate, // signed with sha256. The Public Key Algorithm of 'kExamplePSSCert' is 'rsaEncryption'. // But the Public Key Algorithm of'kExampleRsassaPssCert' is 'rsassaPss'. static const char kExampleRsassaPssCert[] = R"( -----BEGIN CERTIFICATE----- MIIDfDCCAjmgAwIBAgIUEIxD6A5SEeKV47rjkU8lPp4YOcEwOAYJKoZIhvcNAQEK MCugDTALBglghkgBZQMEAgGhGjAYBgkqhkiG9w0BAQgwCwYJYIZIAWUDBAIBMA0x CzAJBgNVBAMMAkNBMB4XDTIxMDIwMTAzMjA0NFoXDTIxMDMwMzAzMjA0NFowDTEL MAkGA1UEAwwCQ0EwggFNMDgGCSqGSIb3DQEBCjAroA0wCwYJYIZIAWUDBAIBoRow GAYJKoZIhvcNAQEIMAsGCWCGSAFlAwQCAQOCAQ8AMIIBCgKCAQEA1uY4qXBckPXW r3i7AAXzemo8DBJWMrb1kb2tOyIGuz7GAYkU7cwOpbGAaWsjg/IfHBxinTBhlEUl 8PbjsITcXIA9y9ZM1z9lH3IV+TapcIBlzpL7eaE5Z/m0f1usdNIdEbNYN1iu/ITQ 5+iWq8ycYAEleRifqLs3qDBWAUDkKE11CiF89OFe1ESHVfinyMM2MBjIvm4aLgg2 RKJo+H634tN7z17Rixd7eHV1mTUeSrxIPbclG8zxHL+Lmtm6ygJ/aY6+7ddNjvqM dkPjK6i+sfIk0/A0MNSOhTxo6vcKRM9msKkoVBkAtX/OZaOuztcgiJbB6WICqxU9 cDS5+aWS3wIDAQABo1MwUTAdBgNVHQ4EFgQUMVnzC8gWnX/1TWGYSzhPfcmVVbow HwYDVR0jBBgwFoAUMVnzC8gWnX/1TWGYSzhPfcmVVbowDwYDVR0TAQH/BAUwAwEB /zA4BgkqhkiG9w0BAQowK6ANMAsGCWCGSAFlAwQCAaEaMBgGCSqGSIb3DQEBCDAL BglghkgBZQMEAgEDggEBAI/ImUkyQWZZADALJIoyZHU1i3sRVNLJPKYU8OR0pjja SKCsU/+xRNi/WuyjH/oXOZyIxta+QVW6zncMVqItKxF2upTYZk8QuwVKfKYY9uO+ 9e4IwoOAUJdbQ3MvC16bDgNyovZsI5Om3mhKX6fCamVtPe14YGn4MzmusOlRVCoT F70J6OcMpIR+umax9pJ/7o5D6Tl6wZPxUq3nyk8jQsYBk+d47CWKJNbnXa0VhPuL 1i+8ourm0ccQ2ku3yAElaJM7MeiavcXufOhp3CGwKU4j12aktR7JJsHFJuXqcDBk lCVcLZYHYFEQtpAMBDE66eBnJysn199+p9Gou4JlhJI= -----END CERTIFICATE----- )"; // kBadPSSCertPEM is a self-signed RSA-PSS certificate with bad parameters. static const char kBadPSSCertPEM[] = R"( -----BEGIN CERTIFICATE----- MIIDdjCCAjqgAwIBAgIJANcwZLyfEv7DMD4GCSqGSIb3DQEBCjAxoA0wCwYJYIZI AWUDBAIBoRowGAYJKoZIhvcNAQEIMAsGCWCGSAFlAwQCAaIEAgIA3jAnMSUwIwYD VQQDDBxUZXN0IEludmFsaWQgUFNTIGNlcnRpZmljYXRlMB4XDTE1MTEwNDE2MDIz NVoXDTE1MTIwNDE2MDIzNVowJzElMCMGA1UEAwwcVGVzdCBJbnZhbGlkIFBTUyBj ZXJ0aWZpY2F0ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMTaM7WH qVCAGAIA+zL1KWvvASTrhlq+1ePdO7wsrWX2KiYoTYrJYTnxhLnn0wrHqApt79nL IBG7cfShyZqFHOY/IzlYPMVt+gPo293gw96Fds5JBsjhjkyGnOyr9OUntFqvxDbT IIFU7o9IdxD4edaqjRv+fegVE+B79pDk4s0ujsk6dULtCg9Rst0ucGFo19mr+b7k dbfn8pZ72ZNDJPueVdrUAWw9oll61UcYfk75XdrLk6JlL41GrYHc8KlfXf43gGQq QfrpHkg4Ih2cI6Wt2nhFGAzrlcorzLliQIUJRIhM8h4IgDfpBpaPdVQLqS2pFbXa 5eQjqiyJwak2vJ8CAwEAAaNQME4wHQYDVR0OBBYEFCt180N4oGUt5LbzBwQ4Ia+2 4V97MB8GA1UdIwQYMBaAFCt180N4oGUt5LbzBwQ4Ia+24V97MAwGA1UdEwQFMAMB Af8wMQYJKoZIhvcNAQEKMCSgDTALBglghkgBZQMEAgGhDTALBgkqhkiG9w0BAQii BAICAN4DggEBAAjBtm90lGxgddjc4Xu/nbXXFHVs2zVcHv/mqOZoQkGB9r/BVgLb xhHrFZ2pHGElbUYPfifdS9ztB73e1d4J+P29o0yBqfd4/wGAc/JA8qgn6AAEO/Xn plhFeTRJQtLZVl75CkHXgUGUd3h+ADvKtcBuW9dSUncaUrgNKR8u/h/2sMG38RWY DzBddC/66YTa3r7KkVUfW7yqRQfELiGKdcm+bjlTEMsvS+EhHup9CzbpoCx2Fx9p NPtFY3yEObQhmL1JyoCRWqBE75GzFPbRaiux5UpEkns+i3trkGssZzsOuVqHNTNZ lC9+9hPHIoc9UMmAQNo1vGIW3NWVoeGbaJ8= -----END CERTIFICATE----- )"; static const char kRSAKey[] = R"( -----BEGIN RSA PRIVATE KEY----- MIICXgIBAAKBgQDYK8imMuRi/03z0K1Zi0WnvfFHvwlYeyK9Na6XJYaUoIDAtB92 kWdGMdAQhLciHnAjkXLI6W15OoV3gA/ElRZ1xUpxTMhjP6PyY5wqT5r6y8FxbiiF KKAnHmUcrgfVW28tQ+0rkLGMryRtrukXOgXBv7gcrmU7G1jC2a7WqmeI8QIDAQAB AoGBAIBy09Fd4DOq/Ijp8HeKuCMKTHqTW1xGHshLQ6jwVV2vWZIn9aIgmDsvkjCe i6ssZvnbjVcwzSoByhjN8ZCf/i15HECWDFFh6gt0P5z0MnChwzZmvatV/FXCT0j+ WmGNB/gkehKjGXLLcjTb6dRYVJSCZhVuOLLcbWIV10gggJQBAkEA8S8sGe4ezyyZ m4e9r95g6s43kPqtj5rewTsUxt+2n4eVodD+ZUlCULWVNAFLkYRTBCASlSrm9Xhj QpmWAHJUkQJBAOVzQdFUaewLtdOJoPCtpYoY1zd22eae8TQEmpGOR11L6kbxLQsk aMly/DOnOaa82tqAGTdqDEZgSNmCeKKknmECQAvpnY8GUOVAubGR6c+W90iBuQLj LtFp/9ihd2w/PoDwrHZaoUYVcT4VSfJQog/k7kjE4MYXYWL8eEKg3WTWQNECQQDk 104Wi91Umd1PzF0ijd2jXOERJU1wEKe6XLkYYNHWQAe5l4J4MWj9OdxFXAxIuuR/ tfDwbqkta4xcux67//khAkEAvvRXLHTaa6VFzTaiiO8SaFsHV3lQyXOtMrBpB5jd moZWgjHvB2W9Ckn7sDqsPB+U2tyX0joDdQEyuiMECDY8oQ== -----END RSA PRIVATE KEY----- )"; static const char kP256Key[] = R"( -----BEGIN PRIVATE KEY----- MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgBw8IcnrUoEqc3VnJ TYlodwi1b8ldMHcO6NHJzgqLtGqhRANCAATmK2niv2Wfl74vHg2UikzVl2u3qR4N Rvvdqakendy6WgHn1peoChj5w8SjHlbifINI2xYaHPUdfvGULUvPciLB -----END PRIVATE KEY----- )"; // kCRLTestRootKey is the private key for kCRLTestRoot. static const char kCRLTestRootKey[] = R"( -----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEAo16WiLWZuaymsD8n5SKPmxV1y6jjgr3BS/dUBpbrzd1aeFzN lI8l2jfAnzUyp+I21RQ+nh/MhqjGElkTtK9xMn1Y+S9GMRh+5R/Du0iCb1tCZIPY 07Tgrb0KMNWe0v2QKVVruuYSgxIWodBfxlKO64Z8AJ5IbnWpuRqO6rctN9qUoMlT IAB6dL4G0tDJ/PGFWOJYwOMEIX54bly2wgyYJVBKiRRt4f7n8H922qmvPNA9idmX 9G1VAtgV6x97XXi7ULORIQvn9lVQF6nTYDBJhyuPB+mLThbLP2o9orxGx7aCtnnB ZUIxUvHNOI0FaSaZH7Fi0xsZ/GkG2HZe7ImPJwIDAQABAoIBAQCJF9MTHfHGkk+/ DwCXlA0Wg0e6hBuHl10iNobYkMWIl/xXjOknhYiqOqb181py76472SVC5ERprC+r Lf0PXzqKuA117mnkwT2bYLCL9Skf8WEhoFLQNbVlloF6wYjqXcYgKYKh8HgQbZl4 aLg2YQl2NADTNABsUWj/4H2WEelsODVviqfFs725lFg9KHDI8zxAZXLzDt/M9uVL GxJiX12tr0AwaeAFZ1oPM/y+LznM3N3+Ht3jHHw3jZ/u8Z1RdAmdpu3bZ6tbwGBr 9edsH5rKkm9aBvMrY7eX5VHqaqyRNFyG152ZOJh4XiiFG7EmgTPCpaHo50Y018Re grVtk+FBAoGBANY3lY+V8ZOwMxSHes+kTnoimHO5Ob7nxrOC71i27x+4HHsYUeAr /zOOghiDIn+oNkuiX5CIOWZKx159Bp65CPpCbTb/fh+HYnSgXFgCw7XptycO7LXM 5GwR5jSfpfzBFdYxjxoUzDMFBwTEYRTm0HkUHkH+s+ajjw5wqqbcGLcfAoGBAMM8 DKW6Tb66xsf708f0jonAjKYTLZ+WOcwsBEWSFHoY8dUjvW5gqx5acHTEsc5ZTeh4 BCFLa+Mn9cuJWVJNs09k7Xb2PNl92HQ4GN2vbdkJhExbkT6oLDHg1hVD0w8KLfz1 lTAW6pS+6CdOHMEJpvqx89EgU/1GgIQ1fXYczE75AoGAKeJoXdDFkUjsU+FBhAPu TDcjc80Nm2QaF9NMFR5/lsYa236f06MGnQAKM9zADBHJu/Qdl1brUjLg1HrBppsr RDNkw1IlSOjhuUf5hkPUHGd8Jijm440SRIcjabqla8wdBupdvo2+d2NOQgJbsQiI ToQ+fkzcxAXK3Nnuo/1436UCgYBjLH7UNOZHS8OsVM0I1r8NVKVdu4JCfeJQR8/H s2P5ffBir+wLRMnH+nMDreMQiibcPxMCArkERAlE4jlgaJ38Z62E76KLbLTmnJRt EC9Bv+bXjvAiHvWMRMUbOj/ddPNVez7Uld+FvdBaHwDWQlvzHzBWfBCOKSEhh7Z6 qDhUqQKBgQDPMDx2i5rfmQp3imV9xUcCkIRsyYQVf8Eo7NV07IdUy/otmksgn4Zt Lbf3v2dvxOpTNTONWjp2c+iUQo8QxJCZr5Sfb21oQ9Ktcrmc/CY7LeBVDibXwxdM vRG8kBzvslFWh7REzC3u06GSVhyKDfW93kN2cKVwGoahRlhj7oHuZQ== -----END RSA PRIVATE KEY----- )"; static const char kCRLTestRoot[] = R"( -----BEGIN CERTIFICATE----- MIIDbzCCAlegAwIBAgIJAODri7v0dDUFMA0GCSqGSIb3DQEBCwUAME4xCzAJBgNV BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBW aWV3MRIwEAYDVQQKDAlCb3JpbmdTU0wwHhcNMTYwOTI2MTUwNjI2WhcNMjYwOTI0 MTUwNjI2WjBOMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQG A1UEBwwNTW91bnRhaW4gVmlldzESMBAGA1UECgwJQm9yaW5nU1NMMIIBIjANBgkq hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAo16WiLWZuaymsD8n5SKPmxV1y6jjgr3B S/dUBpbrzd1aeFzNlI8l2jfAnzUyp+I21RQ+nh/MhqjGElkTtK9xMn1Y+S9GMRh+ 5R/Du0iCb1tCZIPY07Tgrb0KMNWe0v2QKVVruuYSgxIWodBfxlKO64Z8AJ5IbnWp uRqO6rctN9qUoMlTIAB6dL4G0tDJ/PGFWOJYwOMEIX54bly2wgyYJVBKiRRt4f7n 8H922qmvPNA9idmX9G1VAtgV6x97XXi7ULORIQvn9lVQF6nTYDBJhyuPB+mLThbL P2o9orxGx7aCtnnBZUIxUvHNOI0FaSaZH7Fi0xsZ/GkG2HZe7ImPJwIDAQABo1Aw TjAdBgNVHQ4EFgQUWPt3N5cZ/CRvubbrkqfBnAqhq94wHwYDVR0jBBgwFoAUWPt3 N5cZ/CRvubbrkqfBnAqhq94wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC AQEAORu6M0MOwXy+3VEBwNilfTxyqDfruQsc1jA4PT8Oe8zora1WxE1JB4q2FJOz EAuM3H/NXvEnBuN+ITvKZAJUfm4NKX97qmjMJwLKWe1gVv+VQTr63aR7mgWJReQN XdMztlVeZs2dppV6uEg3ia1X0G7LARxGpA9ETbMyCpb39XxlYuTClcbA5ftDN99B 3Xg9KNdd++Ew22O3HWRDvdDpTO/JkzQfzi3sYwUtzMEonENhczJhGf7bQMmvL/w5 24Wxj4Z7KzzWIHsNqE/RIs6RV3fcW61j/mRgW2XyoWnMVeBzvcJr9NXp4VQYmFPw amd8GKMZQvP0ufGnUn7D7uartA== -----END CERTIFICATE----- )"; static const char kCRLTestLeaf[] = R"( -----BEGIN CERTIFICATE----- MIIDkDCCAnigAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwTjELMAkGA1UEBhMCVVMx EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxEjAQ BgNVBAoMCUJvcmluZ1NTTDAeFw0xNjA5MjYxNTA4MzFaFw0xNzA5MjYxNTA4MzFa MEsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQKDAlC b3JpbmdTU0wxEzARBgNVBAMMCmJvcmluZy5zc2wwggEiMA0GCSqGSIb3DQEBAQUA A4IBDwAwggEKAoIBAQDc5v1S1M0W+QWM+raWfO0LH8uvqEwuJQgODqMaGnSlWUx9 8iQcnWfjyPja3lWg9K62hSOFDuSyEkysKHDxijz5R93CfLcfnVXjWQDJe7EJTTDP ozEvxN6RjAeYv7CF000euYr3QT5iyBjg76+bon1p0jHZBJeNPP1KqGYgyxp+hzpx e0gZmTlGAXd8JQK4v8kpdYwD6PPifFL/jpmQpqOtQmH/6zcLjY4ojmqpEdBqIKIX +saA29hMq0+NK3K+wgg31RU+cVWxu3tLOIiesETkeDgArjWRS1Vkzbi4v9SJxtNu OZuAxWiynRJw3JwH/OFHYZIvQqz68ZBoj96cepjPAgMBAAGjezB5MAkGA1UdEwQC MAAwLAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRl MB0GA1UdDgQWBBTGn0OVVh/aoYt0bvEKG+PIERqnDzAfBgNVHSMEGDAWgBRY+3c3 lxn8JG+5tuuSp8GcCqGr3jANBgkqhkiG9w0BAQsFAAOCAQEAd2nM8gCQN2Dc8QJw XSZXyuI3DBGGCHcay/3iXu0JvTC3EiQo8J6Djv7WLI0N5KH8mkm40u89fJAB2lLZ ShuHVtcC182bOKnePgwp9CNwQ21p0rDEu/P3X46ZvFgdxx82E9xLa0tBB8PiPDWh lV16jbaKTgX5AZqjnsyjR5o9/mbZVupZJXx5Syq+XA8qiJfstSYJs4KyKK9UOjql ICkJVKpi2ahDBqX4MOH4SLfzVk8pqSpviS6yaA1RXqjpkxiN45WWaXDldVHMSkhC 5CNXsXi4b1nAntu89crwSLA3rEwzCWeYj+BX7e1T9rr3oJdwOU/2KQtW1js1yQUG tjJMFw== -----END CERTIFICATE----- )"; static const char kBasicCRL[] = R"( -----BEGIN X509 CRL----- MIIBpzCBkAIBATANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJVUzETMBEGA1UE CAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzESMBAGA1UECgwJ Qm9yaW5nU1NMFw0xNjA5MjYxNTEwNTVaFw0xNjEwMjYxNTEwNTVaoA4wDDAKBgNV HRQEAwIBATANBgkqhkiG9w0BAQsFAAOCAQEAnrBKKgvd9x9zwK9rtUvVeFeJ7+LN ZEAc+a5oxpPNEsJx6hXoApYEbzXMxuWBQoCs5iEBycSGudct21L+MVf27M38KrWo eOkq0a2siqViQZO2Fb/SUFR0k9zb8xl86Zf65lgPplALun0bV/HT7MJcl04Tc4os dsAReBs5nqTGNEd5AlC1iKHvQZkM//MD51DspKnDpsDiUVi54h9C1SpfZmX8H2Vv diyu0fZ/bPAM3VAGawatf/SyWfBMyKpoPXEG39oAzmjjOj8en82psn7m474IGaho /vBbhl1ms5qQiLYPjm4YELtnXQoFyC72tBjbdFd/ZE9k4CNKDbxFUXFbkw== -----END X509 CRL----- )"; static const char kRevokedCRL[] = R"( -----BEGIN X509 CRL----- MIIB6DCB0QIBATANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJVUzETMBEGA1UE CAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzESMBAGA1UECgwJ Qm9yaW5nU1NMFw0xNjA5MjYxNTEyNDRaFw0xNjEwMjYxNTEyNDRaMD8wEwICEAAX DTE2MDkyNjE1MTIyNlowEwICD/8XDTE2MDkyNjE1MTIyNlowEwICEAEXDTE2MDky NjE1MTIyNlqgDjAMMAoGA1UdFAQDAgECMA0GCSqGSIb3DQEBCwUAA4IBAQAuepA9 byX4PkpJcYKb+Ofn6f/mgB4Sh1BBRp0HMFm7Q3jryXB6yPZYp7UtwAufZUzbV42U kOifOtDyQbu+fcpnpb4D9oWoFlr4DGQ+n23wujHizoTEYhhcZMj4U5yooDfmK4lI GU6zzQZKG+1PaS6Dm4f6+kA+ekVUP+ZVjPqHP/K7Uv6akSKV7gAWs49N5QjrJKMQ 3Igrbg4Et2ipaYgThGj8t1MUZdVY4UPtQ8oltSHkFEvH4PxOW/xKpHT4QQMl/WTT QsQqOlRJBG2ci7pu1fzOylY35YFEAApkND/MkQjQfylNNgCzDWWAPQx0pDvVKQ0v WXdfcGJRL1D3xRXk -----END X509 CRL----- )"; static const char kBadIssuerCRL[] = R"( -----BEGIN X509 CRL----- MIIBwjCBqwIBATANBgkqhkiG9w0BAQsFADBSMQswCQYDVQQGEwJVUzETMBEGA1UE CAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEWMBQGA1UECgwN Tm90IEJvcmluZ1NTTBcNMTYwOTI2MTUxMjQ0WhcNMTYxMDI2MTUxMjQ0WjAVMBMC AhAAFw0xNjA5MjYxNTEyMjZaoA4wDDAKBgNVHRQEAwIBAjANBgkqhkiG9w0BAQsF AAOCAQEAlBmjOA3Fs5UCq3GbyPEzHkfAabL0HqOQaCP12btmvIf/z8kcjMGHmjjP t85dHbI4Ak/G9wtRT4E/j9i5KML+6yfhRyUTUJKIK/kbqgkj06uuYWuFipURlpDB cm81RzZaMQvmlN5YKfzZV/clLX6mJiXpNQg6zjhj6wBAMI9ZfwVHmCjRqnwVmCUL TybvuTmkryGBqREwmSbHFFjg5ixG/ztwzON/Ly78aJ8Bql6ktCl9+/gh6VJcoZ0q L8V8aT8+Ghfi+zrXM8S9BmLQ9n0fQe0wzKrDZh14EK4sb7zmOzFHSxm3eEXyS98g Od4cjsc3ymNk88S4jpnLRtIVxZB+SQ== -----END X509 CRL----- )"; // kKnownCriticalCRL is kBasicCRL but with a critical issuing distribution point // extension. static const char kKnownCriticalCRL[] = R"( -----BEGIN X509 CRL----- MIIBuDCBoQIBATANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJVUzETMBEGA1UE CAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzESMBAGA1UECgwJ Qm9yaW5nU1NMFw0xNjA5MjYxNTEwNTVaFw0xNjEwMjYxNTEwNTVaoB8wHTAKBgNV HRQEAwIBATAPBgNVHRwBAf8EBTADgQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAs37Jq 3Htcehm6C2PKXOHekwTqTLOPWsYHfF68kYhdzcopDZBeoKE7jLRkRRGFDaR/tfUs kwLSDNSQ8EwPb9PT1X8kmFn9QmJgWD6f6BzaH5ZZ9iBUwOcvrydlb/jnjdIZHQxs fKOAceW5XX3f7DANC3qwYLsQZR/APkfV8nXjPYVUz1kKj04uq/BbQviInjyUYixN xDx+GDWVVXccehcwAu983kAqP+JDaVQPBVksLuBXz2adrEWwvbLCnZeL3zH1IY9h 6MFO6echpvGbU/H+dRX9UkhdJ7gdwKVD3RjfJl+DRVox9lz8Pbo5H699Tkv9/DQP 9dMWxqhQlv23osLp -----END X509 CRL----- )"; // kUnknownCriticalCRL is kBasicCRL but with an unknown critical extension. static const char kUnknownCriticalCRL[] = R"( -----BEGIN X509 CRL----- MIIBvDCBpQIBATANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJVUzETMBEGA1UE CAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzESMBAGA1UECgwJ Qm9yaW5nU1NMFw0xNjA5MjYxNTEwNTVaFw0xNjEwMjYxNTEwNTVaoCMwITAKBgNV HRQEAwIBATATBgwqhkiG9xIEAYS3CQABAf8EADANBgkqhkiG9w0BAQsFAAOCAQEA GvBP0xqL509InMj/3493YVRV+ldTpBv5uTD6jewzf5XdaxEQ/VjTNe5zKnxbpAib Kf7cwX0PMSkZjx7k7kKdDlEucwVvDoqC+O9aJcqVmM6GDyNb9xENxd0XCXja6MZC yVgP4AwLauB2vSiEprYJyI1APph3iAEeDm60lTXX/wBM/tupQDDujKh2GPyvBRfJ +wEDwGg3ICwvu4gO4zeC5qnFR+bpL9t5tOMAQnVZ0NWv+k7mkd2LbHdD44dxrfXC nhtfERx99SDmC/jtUAJrGhtCO8acr7exCeYcduN7KKCm91OeCJKK6OzWst0Og1DB kwzzU2rL3G65CrZ7H0SZsQ== -----END X509 CRL----- )"; // kUnknownCriticalCRL2 is kBasicCRL but with a critical issuing distribution // point extension followed by an unknown critical extension static const char kUnknownCriticalCRL2[] = R"( -----BEGIN X509 CRL----- MIIBzTCBtgIBATANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJVUzETMBEGA1UE CAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzESMBAGA1UECgwJ Qm9yaW5nU1NMFw0xNjA5MjYxNTEwNTVaFw0xNjEwMjYxNTEwNTVaoDQwMjAKBgNV HRQEAwIBATAPBgNVHRwBAf8EBTADgQH/MBMGDCqGSIb3EgQBhLcJAAEB/wQAMA0G CSqGSIb3DQEBCwUAA4IBAQBgSogsC5kf2wzr+0hmZtmLXYd0itAiYO0Gh9AyaEOO myJFuqICHBSLXXUgwNkTUa2x2I/ivyReVFV756VOlWoaV2wJUs0zeCeVBgC9ZFsq 5a+8OGgXwgoYESFV5Y3QRF2a1Ytzfbw/o6xLXzTngvMsLOs12D4B5SkopyEZibF4 tXlRZyvEudTg3CCrjNP+p/GV07nZ3wcMmKJwQeilgzFUV7NaVCCo9jvPBGp0RxAN KNif7jmjK4hD5mswo/Eq5kxQIc+mTfuUFdgHuAu1hfLYe0YK+Hr4RFf6Qy4hl7Ne YjqkkSVIcr87u+8AznwdstnQzsyD27Jt7SjVORkYRywi -----END X509 CRL----- )"; // kBadExtensionCRL is kBasicCRL but with an incorrectly-encoded issuing // distribution point extension. static const char kBadExtensionCRL[] = R"( -----BEGIN X509 CRL----- MIIBujCBowIBATANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJVUzETMBEGA1UE CAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzESMBAGA1UECgwJ Qm9yaW5nU1NMFw0xNjA5MjYxNTEwNTVaFw0xNjEwMjYxNTEwNTVaoCEwHzAKBgNV HRQEAwIBATARBgNVHRwBAf8EBzAFoQMBAf8wDQYJKoZIhvcNAQELBQADggEBAA+3 i+5e5Ub8sccfgOBs6WVJFI9c8gvJjrJ8/dYfFIAuCyeocs7DFXn1n13CRZ+URR/Q mVWgU28+xeusuSPYFpd9cyYTcVyNUGNTI3lwgcE/yVjPaOmzSZKdPakApRxtpKKQ NN/56aQz3bnT/ZSHQNciRB8U6jiD9V30t0w+FDTpGaG+7bzzUH3UVF9xf9Ctp60A 3mfLe0scas7owSt4AEFuj2SPvcE7yvdOXbu+IEv21cEJUVExJAbhvIweHXh6yRW+ 7VVeiNzdIjkZjyTmAzoXGha4+wbxXyBRbfH+XWcO/H+8nwyG8Gktdu2QB9S9nnIp o/1TpfOMSGhMyMoyPrk= -----END X509 CRL----- )"; // kAlgorithmMismatchCRL is kBasicCRL but with mismatched AlgorithmIdentifiers // in the outer structure and signed portion. The signature reflects the signed // portion. static const char kAlgorithmMismatchCRL[] = R"( -----BEGIN X509 CRL----- MIIBpzCBkAIBATANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJVUzETMBEGA1UE CAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzESMBAGA1UECgwJ Qm9yaW5nU1NMFw0xNjA5MjYxNTEwNTVaFw0xNjEwMjYxNTEwNTVaoA4wDDAKBgNV HRQEAwIBATANBgkqhkiG9w0BAQwFAAOCAQEAnrBKKgvd9x9zwK9rtUvVeFeJ7+LN ZEAc+a5oxpPNEsJx6hXoApYEbzXMxuWBQoCs5iEBycSGudct21L+MVf27M38KrWo eOkq0a2siqViQZO2Fb/SUFR0k9zb8xl86Zf65lgPplALun0bV/HT7MJcl04Tc4os dsAReBs5nqTGNEd5AlC1iKHvQZkM//MD51DspKnDpsDiUVi54h9C1SpfZmX8H2Vv diyu0fZ/bPAM3VAGawatf/SyWfBMyKpoPXEG39oAzmjjOj8en82psn7m474IGaho /vBbhl1ms5qQiLYPjm4YELtnXQoFyC72tBjbdFd/ZE9k4CNKDbxFUXFbkw== -----END X509 CRL----- )"; // kAlgorithmMismatchCRL2 is kBasicCRL but with mismatched AlgorithmIdentifiers // in the outer structure and signed portion. The signature reflects the outer // structure. static const char kAlgorithmMismatchCRL2[] = R"( -----BEGIN X509 CRL----- MIIBpzCBkAIBATANBgkqhkiG9w0BAQwFADBOMQswCQYDVQQGEwJVUzETMBEGA1UE CAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzESMBAGA1UECgwJ Qm9yaW5nU1NMFw0xNjA5MjYxNTEwNTVaFw0xNjEwMjYxNTEwNTVaoA4wDDAKBgNV HRQEAwIBATANBgkqhkiG9w0BAQsFAAOCAQEAjCWtU7AK8nQ5TCFfzvbU04MWNuLp iZfqapRSRyMta4pyRomK773rEmJmYOc/ZNeIphVOlupMgGC2wyv5Z/SD1mxccJbv SlUWciwjskjgvyyU9KnJ5xPgf3e3Fl3G0u9yJEFd4mg6fRavs5pEDX56b0f+SkG+ Vl1FZU94Uylm2kCqk9fRpTxualPGP6dksj3Aitt4x2Vdni4sUfg9vIEEOx2jnisq iLqpT94IdETCWAciE0dgbogdOOsNzMqSASfHM/XPigYLXpYgfaR8fca6OKDwFsVH SrkFz8Se3F6mCHnbDzYElbmA46iKU2J12LTrso3Ewq/qHq0mebfp2z0y6g== -----END X509 CRL----- )"; // kEd25519Cert is a self-signed Ed25519 certificate. static const char kEd25519Cert[] = R"( -----BEGIN CERTIFICATE----- MIIBkTCCAUOgAwIBAgIJAJwooam0UCDmMAUGAytlcDBFMQswCQYDVQQGEwJBVTET MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ dHkgTHRkMB4XDTE0MDQyMzIzMjE1N1oXDTE0MDUyMzIzMjE1N1owRTELMAkGA1UE BhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdp ZGdpdHMgUHR5IEx0ZDAqMAUGAytlcAMhANdamAGCsQq31Uv+08lkBzoO4XLz2qYj Ja8CGmj3B1Eao1AwTjAdBgNVHQ4EFgQUoux7eV+fJK2v3ah6QPU/lj1/+7UwHwYD VR0jBBgwFoAUoux7eV+fJK2v3ah6QPU/lj1/+7UwDAYDVR0TBAUwAwEB/zAFBgMr ZXADQQBuCzqji8VP9xU8mHEMjXGChX7YP5J664UyVKHKH9Z1u4wEbB8dJ3ScaWSL r+VHVKUhsrvcdCelnXRrrSD7xWAL -----END CERTIFICATE----- )"; // kEd25519CertNull is an invalid self-signed Ed25519 with an explicit NULL in // the signature algorithm. static const char kEd25519CertNull[] = R"( -----BEGIN CERTIFICATE----- MIIBlTCCAUWgAwIBAgIJAJwooam0UCDmMAcGAytlcAUAMEUxCzAJBgNVBAYTAkFV MRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRz IFB0eSBMdGQwHhcNMTQwNDIzMjMyMTU3WhcNMTQwNTIzMjMyMTU3WjBFMQswCQYD VQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQg V2lkZ2l0cyBQdHkgTHRkMCowBQYDK2VwAyEA11qYAYKxCrfVS/7TyWQHOg7hcvPa piMlrwIaaPcHURqjUDBOMB0GA1UdDgQWBBSi7Ht5X58kra/dqHpA9T+WPX/7tTAf BgNVHSMEGDAWgBSi7Ht5X58kra/dqHpA9T+WPX/7tTAMBgNVHRMEBTADAQH/MAcG AytlcAUAA0EA70uefNocdJohkKPNROKVyBuBD3LXMyvmdTklsaxSRY3PcZdOohlr recgVPpVS7B+d9g4EwtZXIh4lodTBDHBBw== -----END CERTIFICATE----- )"; // kX25519 is the example X25519 certificate from // https://tools.ietf.org/html/rfc8410#section-10.2 static const char kX25519Cert[] = R"( -----BEGIN CERTIFICATE----- MIIBLDCB36ADAgECAghWAUdKKo3DMDAFBgMrZXAwGTEXMBUGA1UEAwwOSUVURiBUZX N0IERlbW8wHhcNMTYwODAxMTIxOTI0WhcNNDAxMjMxMjM1OTU5WjAZMRcwFQYDVQQD DA5JRVRGIFRlc3QgRGVtbzAqMAUGAytlbgMhAIUg8AmJMKdUdIt93LQ+91oNvzoNJj ga9OukqY6qm05qo0UwQzAPBgNVHRMBAf8EBTADAQEAMA4GA1UdDwEBAAQEAwIDCDAg BgNVHQ4BAQAEFgQUmx9e7e0EM4Xk97xiPFl1uQvIuzswBQYDK2VwA0EAryMB/t3J5v /BzKc9dNZIpDmAgs3babFOTQbs+BolzlDUwsPrdGxO3YNGhW7Ibz3OGhhlxXrCe1Cg w1AH9efZBw== -----END CERTIFICATE----- )"; // This certificate is the example certificate provided in section 3 of //https://datatracker.ietf.org/doc/draft-ietf-lamps-dilithium-certificates/ static const char kMLDSA65Cert[] = R"( -----BEGIN CERTIFICATE----- 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 Lw== -----END CERTIFICATE----- )"; // kMLDSA65CertNull is an invalid self-signed MLDSA65 with an explicit // NULL in the signature algorithm. static const char kMLDSA65CertNull[] = R"( -----BEGIN CERTIFICATE----- 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 HCIv -----END CERTIFICATE----- )"; // kMLDSA65CertParam is an invalid self-signed MLDSA65 with an explicit // NULL in the AlgorithmIdentifier parameters. static const char kMLDSA65CertParam[] = R"( -----BEGIN CERTIFICATE----- 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 DhQcIi8= -----END CERTIFICATE----- )"; // kSANTypesLeaf is a leaf certificate (signed by |kSANTypesRoot|) which // contains SANS for example.com, test@example.com, 127.0.0.1, and // https://example.com/. (The latter is useless for now since crypto/x509 // doesn't deal with URI SANs directly.) static const char kSANTypesLeaf[] = R"( -----BEGIN CERTIFICATE----- MIIClzCCAgCgAwIBAgIJAOjwnT/iW+qmMA0GCSqGSIb3DQEBCwUAMCsxFzAVBgNV BAoTDkJvcmluZ1NTTCBUZXN0MRAwDgYDVQQDEwdSb290IENBMB4XDTE1MDEwMTAw MDAwMFoXDTI1MDEwMTAwMDAwMFowLzEXMBUGA1UEChMOQm9yaW5nU1NMIFRlc3Qx FDASBgNVBAMTC2V4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB gQDbRn2TLhInBki8Bighq37EtqJd/h5SRYh6NkelCA2SQlvCgcC+l3mYQPtPbRT9 KxOLwqUuZ9jUCZ7WIji3Sgt0cyvCNPHRk+WW2XR781ifbGE8wLBB1NkrKyQjd1sc O711Xc4gVM+hY4cdHiTE8x0aUIuqthRD7ZendWL0FMhS1wIDAQABo4G+MIG7MA4G A1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDAYD VR0TAQH/BAIwADAZBgNVHQ4EEgQQn5EWH0NDPkmm3m22gNefYDAbBgNVHSMEFDAS gBBAN9cB+0AvuBx+VAQnjFkBMEQGA1UdEQQ9MDuCC2V4YW1wbGUuY29tgRB0ZXN0 QGV4YW1wbGUuY29thwR/AAABhhRodHRwczovL2V4YW1wbGUuY29tLzANBgkqhkiG 9w0BAQsFAAOBgQBtwJvY6+Tk6D6DOtDVaNoJ5y8E25CCuE/Ga4OuIcYJas+yLckf dZwUV3GUG2oBXl2MrpUFxXd4hKBO1CmlBY+hZEeIx0Yp6QWK9P/vnZeydOTP26mk jusJ2PqSmtKNU1Zcaba4d29oFejmOAfeguhR8AHpsc/zHEaS5Q9cJsuJcw== -----END CERTIFICATE----- )"; // -----BEGIN RSA PRIVATE KEY----- // MIICWwIBAAKBgQDbRn2TLhInBki8Bighq37EtqJd/h5SRYh6NkelCA2SQlvCgcC+ // l3mYQPtPbRT9KxOLwqUuZ9jUCZ7WIji3Sgt0cyvCNPHRk+WW2XR781ifbGE8wLBB // 1NkrKyQjd1scO711Xc4gVM+hY4cdHiTE8x0aUIuqthRD7ZendWL0FMhS1wIDAQAB // AoGACwf7z0i1DxOI2zSwFimLghfyCSp8mgT3fbZ3Wj0SebYu6ZUffjceneM/AVrq // gGYHYLOVHcWJqfkl7X3hPo9SDhzLx0mM545/q21ZWCwjhswH7WiCEqV2/zeDO9WU // NIO1VU0VoLm0AQ7ZvwnyB+fpgF9kkkDtbBJW7XWrfNVtlnECQQD97YENpEJ3X1kj // 3rrkrHWDkKAyoWWY1i8Fm7LnganC9Bv6AVwgn5ZlE/479aWHF8vbOFEA3pFPiNZJ // t9FTCfpJAkEA3RCXjGI0Y6GALFLwEs+nL/XZAfJaIpJEZVLCVosYQOSaMS4SchfC // GGYVquT7ZgKk9uvz89Fg87OtBMWS9lrkHwJADGkGLKeBhBoJ3kHtem2fVK3F1pOi // xoR5SdnhNYVVyaxqjZ5xZTrHe+stOrr3uxGDqhQniVZXXb6/Ul0Egv1y2QJAVg/h // kAujba4wIhFf2VLyOZ+yjil1ocPj0LZ5Zgvcs1bMGJ1hHP3W2HzVrqRaowoggui1 // HpTC891dXGA2qKYV7QJAFDmT2A7OVvh3y4AEgzVwHrDmCMwMHKjCIntS7fjxrJnF // YvJUG1zoHwUVrxxbR3DbpTODlktLcl/0b97D0IkH3w== // -----END RSA PRIVATE KEY----- static const char kSANTypesRoot[] = R"( -----BEGIN CERTIFICATE----- MIICTTCCAbagAwIBAgIIAj5CwoHlWuYwDQYJKoZIhvcNAQELBQAwKzEXMBUGA1UE ChMOQm9yaW5nU1NMIFRlc3QxEDAOBgNVBAMTB1Jvb3QgQ0EwHhcNMTUwMTAxMDAw MDAwWhcNMjUwMTAxMDAwMDAwWjArMRcwFQYDVQQKEw5Cb3JpbmdTU0wgVGVzdDEQ MA4GA1UEAxMHUm9vdCBDQTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA6Q5/ EQzmWuaGg3D2UQcuAngR9bIkkjjuJmICx5TxPqF3asCP1SJotl3iTNrghRE1wpJy SY2BtIiXa7f8skRb2U0GcPkMxo/ps9+jaoRsQ1m+nbLQdpvD1/qZWcO45fNTA71J 1rPMokP+rcILuQG4VimUAySnDSghKamulFtK+Z8CAwEAAaN6MHgwDgYDVR0PAQH/ BAQDAgIEMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAPBgNVHRMBAf8E BTADAQH/MBkGA1UdDgQSBBBAN9cB+0AvuBx+VAQnjFkBMBsGA1UdIwQUMBKAEEA3 1wH7QC+4HH5UBCeMWQEwDQYJKoZIhvcNAQELBQADgYEAc4N6hTE62/3gwg+kyc2f c/Jj1mHrOt+0NRaBnmvbmNpsEjHS96Ef4Wt/ZlPXPkkv1C1VosJnOIMF3Q522wRH bqaxARldS12VAa3gcWisDWD+SqSyDxjyojz0XDiJkTrFuCTCUiZO+1GLB7SO10Ms d5YVX0c90VMnUhF/dlrqS9U= -----END CERTIFICATE----- )"; // -----BEGIN RSA PRIVATE KEY----- // MIICXAIBAAKBgQDpDn8RDOZa5oaDcPZRBy4CeBH1siSSOO4mYgLHlPE+oXdqwI/V // Imi2XeJM2uCFETXCknJJjYG0iJdrt/yyRFvZTQZw+QzGj+mz36NqhGxDWb6dstB2 // m8PX+plZw7jl81MDvUnWs8yiQ/6twgu5AbhWKZQDJKcNKCEpqa6UW0r5nwIDAQAB // AoGALEF5daZqc+aEsp8X1yky3nsoheyPL0kqSBWii33IFemZgKcSaRnAoqjPWWLS // 8dHj0I/4rej2MW8iuezVSpDak9tK5boHORC3w4p/wifkizQkLt1DANxTVbzcKvrt // aZ7LjVaKkhjRJbLddniowFHkkWVbUccjvzcUd7Y2VuLbAhECQQDq4FE88aHio8zg // bxSd0PwjEFwLYQTR19u812SoR8PmR6ofIL+pDwOV+fVs+OGcAAOgkhIukOrksQ4A // 1cKtnyhXAkEA/gRI+u3tZ7UE1twIkBfZ6IvCdRodkPqHAYIxMRLzL+MhyZt4MEGc // Ngb/F6U9/WOBFnoR/PI7IwE3ejutzKcL+QJBAKh+6eilk7QKPETZi1m3/dmNt+p1 // 3EZJ65pqjwxmB3Rg/vs7vCMk4TarTdSyKu+F1xRPFfoP/mK3Xctdjj6NyhsCQAYF // 7/0TOzfkUPMPUJyqFB6xgbDpJ55ScnUUsznoqx+NkTWInDb4t02IqO/UmT2y6FKy // Hk8TJ1fTJY+ebqaVp3ECQApx9gQ+n0zIhx97FMUuiRse73xkcW4+pZ8nF+8DmeQL // /JKuuFGmzkG+rUbXFmo/Zg2ozVplw71NnQJ4znPsf7A= // -----END RSA PRIVATE KEY----- // The following four certificates were generated with this Go program, varying // |includeNetscapeExtension| and defining rootKeyPEM and rootCertPEM to be // strings containing the kSANTypesRoot, above. // package main // import ( // "crypto/ecdsa" // "crypto/elliptic" // "crypto/rand" // "crypto/x509" // "crypto/x509/pkix" // "encoding/asn1" // "encoding/pem" // "math/big" // "os" // "time" // ) // const includeNetscapeExtension = true // func main() { // block, _ := pem.Decode([]byte(rootKeyPEM)) // rootPriv, _ := x509.ParsePKCS1PrivateKey(block.Bytes) // block, _ = pem.Decode([]byte(rootCertPEM)) // root, _ := x509.ParseCertificate(block.Bytes) // interTemplate := &x509.Certificate{ // SerialNumber: big.NewInt(2), // Subject: pkix.Name{ // CommonName: "No Basic Constraints (Netscape)", // }, // NotBefore: time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC), // NotAfter: time.Date(2099, time.January, 1, 0, 0, 0, 0, time.UTC), // } // if includeNetscapeExtension { // interTemplate.ExtraExtensions = []pkix.Extension{ // pkix.Extension{ // Id: asn1.ObjectIdentifier([]int{2, 16, 840, 1, 113730, 1, 1}), // Value: []byte{0x03, 0x02, 2, 0x04}, // }, // } // } else { // interTemplate.KeyUsage = x509.KeyUsageCertSign // } // interKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) // interDER, err := x509.CreateCertificate(rand.Reader, interTemplate, root, &interKey.PublicKey, rootPriv) // if err != nil { // panic(err) // } // pem.Encode(os.Stdout, &pem.Block{Type: "CERTIFICATE", Bytes: interDER}) // inter, _ := x509.ParseCertificate(interDER) // leafTemplate := &x509.Certificate{ // SerialNumber: big.NewInt(3), // Subject: pkix.Name{ // CommonName: "Leaf from CA with no Basic Constraints", // }, // NotBefore: time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC), // NotAfter: time.Date(2099, time.January, 1, 0, 0, 0, 0, time.UTC), // BasicConstraintsValid: true, // } // leafKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) // leafDER, err := x509.CreateCertificate(rand.Reader, leafTemplate, inter, &leafKey.PublicKey, interKey) // if err != nil { // panic(err) // } // pem.Encode(os.Stdout, &pem.Block{Type: "CERTIFICATE", Bytes: leafDER}) // } // kNoBasicConstraintsCertSignIntermediate doesn't have isCA set, but contains // certSign in the keyUsage. static const char kNoBasicConstraintsCertSignIntermediate[] = R"( -----BEGIN CERTIFICATE----- MIIBqjCCAROgAwIBAgIBAjANBgkqhkiG9w0BAQsFADArMRcwFQYDVQQKEw5Cb3Jp bmdTU0wgVGVzdDEQMA4GA1UEAxMHUm9vdCBDQTAgFw0wMDAxMDEwMDAwMDBaGA8y MDk5MDEwMTAwMDAwMFowHzEdMBsGA1UEAxMUTm8gQmFzaWMgQ29uc3RyYWludHMw WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASEFMblfxIEDO8My7wHtHWTuDzNyID1 OsPkMGkn32O/pSyXxXuAqDeFoMVffUMTyfm8JcYugSEbrv2qEXXM4bZRoy8wLTAO BgNVHQ8BAf8EBAMCAgQwGwYDVR0jBBQwEoAQQDfXAftAL7gcflQEJ4xZATANBgkq hkiG9w0BAQsFAAOBgQC1Lh6hIAm3K5kRh5iIydU0YAEm7eV6ZSskERDUq3DLJyl9 ZUZCHUzvb464dkwZjeNzaUVS1pdElJslwX3DtGgeJLJGCnk8zUjBjaNrrDm0kzPW xKt/6oif1ci/KCKqKNXJAIFbc4e+IiBpenwpxHk3If4NM+Ek0nKoO8Uj0NkgTQ== -----END CERTIFICATE----- )"; static const char kNoBasicConstraintsCertSignLeaf[] = R"( -----BEGIN CERTIFICATE----- MIIBUDCB96ADAgECAgEDMAoGCCqGSM49BAMCMB8xHTAbBgNVBAMTFE5vIEJhc2lj IENvbnN0cmFpbnRzMCAXDTAwMDEwMTAwMDAwMFoYDzIwOTkwMTAxMDAwMDAwWjAx MS8wLQYDVQQDEyZMZWFmIGZyb20gQ0Egd2l0aCBubyBCYXNpYyBDb25zdHJhaW50 czBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABEsYPMwzdJKjB+2gpC90ib2ilHoB w/arQ6ikUX0CNUDDaKaOu/jF39ogzVlg4lDFrjCKShSfCCcrwgONv70IZGijEDAO MAwGA1UdEwEB/wQCMAAwCgYIKoZIzj0EAwIDSAAwRQIgbV7R99yM+okXSIs6Fp3o eCOXiDL60IBxaTOcLS44ywcCIQDbn87Gj5cFgHBYAkzdHqDsyGXkxQTHDq9jmX24 Djy3Zw== -----END CERTIFICATE----- )"; // kNoBasicConstraintsNetscapeCAIntermediate doesn't have isCA set, but contains // a Netscape certificate-type extension that asserts a type of "SSL CA". static const char kNoBasicConstraintsNetscapeCAIntermediate[] = R"( -----BEGIN CERTIFICATE----- MIIBuDCCASGgAwIBAgIBAjANBgkqhkiG9w0BAQsFADArMRcwFQYDVQQKEw5Cb3Jp bmdTU0wgVGVzdDEQMA4GA1UEAxMHUm9vdCBDQTAgFw0wMDAxMDEwMDAwMDBaGA8y MDk5MDEwMTAwMDAwMFowKjEoMCYGA1UEAxMfTm8gQmFzaWMgQ29uc3RyYWludHMg KE5ldHNjYXBlKTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCeMbmCaOtMzXBqi PrCdNOH23CkaawUA+pAezitAN4RXS1O2CGK5sJjGPVVeogROU8G7/b+mU+ciZIzH 1PP8FJKjMjAwMBsGA1UdIwQUMBKAEEA31wH7QC+4HH5UBCeMWQEwEQYJYIZIAYb4 QgEBBAQDAgIEMA0GCSqGSIb3DQEBCwUAA4GBAAgNWjh7cfBTClTAk+Ml//5xb9Ju tkBhG6Rm+kkMD+qiSMO6t7xS7CsA0+jIBjkdEYaLZ3oxtQCBdZsVNxUvRxZ0AUfF G3DtRFTsrI1f7IQhpMuqEMF4shPW+5x54hrq0Fo6xMs6XoinJZcTUaaB8EeXRF6M P9p6HuyLrmn0c/F0 -----END CERTIFICATE----- )"; static const char kNoBasicConstraintsNetscapeCALeaf[] = R"( -----BEGIN CERTIFICATE----- MIIBXDCCAQKgAwIBAgIBAzAKBggqhkjOPQQDAjAqMSgwJgYDVQQDEx9ObyBCYXNp YyBDb25zdHJhaW50cyAoTmV0c2NhcGUpMCAXDTAwMDEwMTAwMDAwMFoYDzIwOTkw MTAxMDAwMDAwWjAxMS8wLQYDVQQDEyZMZWFmIGZyb20gQ0Egd2l0aCBubyBCYXNp YyBDb25zdHJhaW50czBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABDlJKolDu3R2 tPqSDycr0QJcWhxdBv76V0EEVflcHRxED6vAioTEcnQszt1OfKtBZvjlo0yp6i6Q DaYit0ZInmWjEDAOMAwGA1UdEwEB/wQCMAAwCgYIKoZIzj0EAwIDSAAwRQIhAJsh aZL6BHeEfoUBj1oZ2Ln91qzj3UCVMJ+vrmwAFdYyAiA3wp2JphgchvmoUFuzPXwj XyPwWPbymSTpzKhB4xB7qQ== -----END CERTIFICATE----- )"; static const char kSelfSignedMismatchAlgorithms[] = R"( -----BEGIN CERTIFICATE----- MIIFMjCCAxqgAwIBAgIJAL0mG5fOeJ7xMA0GCSqGSIb3DQEBDQUAMC0xCzAJBgNV BAYTAkdCMQ8wDQYDVQQHDAZMb25kb24xDTALBgNVBAoMBFRlc3QwIBcNMTgwOTE3 MTIxNzU3WhgPMjExODA4MjQxMjE3NTdaMC0xCzAJBgNVBAYTAkdCMQ8wDQYDVQQH DAZMb25kb24xDTALBgNVBAoMBFRlc3QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw ggIKAoICAQDCMhBrRAGGw+n2GdctBr/cEK4FZA6ajiHjihgpCHoSBdyL4R2jGKLS g0WgaMXa1HpkKN7LcIySosEBPlmcRkr1RqbEvQStOSvoFCXYvtx3alM6HTbXMcDR mqoKoABP6LXsPSoMWIgqMtP2X9EOppzHVIK1yFYFfbIlvYUV2Ka+MuMe0Vh5wvD1 4GanPb+cWSKgdRSVQovCCMY3yWtZKVEaxRpCsk/mYYIFWz0tcgMjIKwDx1XXgiAV nU6NK43xbaw3XhtnaD/pv9lhTTbNrlcln9LjTD097BaK4R+1AEPHnpfxA9Ui3upn kbsNUdGdOB0ksZi/vd7lh833YgquQUIAhYrbfvq/HFCpVV1gljzlS3sqULYpLE// i3OsuL2mE+CYIJGpIi2GeJJWXciNMTJDOqTn+fRDtVb4RPp4Y70DJirp7XzaBi3q H0edANCzPSRCDbZsOhzIXhXshldiXVRX666DDlbMQgLTEnNKrkwv6DmU8o15XQsb 8k1Os2YwXmkEOxUQ7AJZXVTZSf6UK9Znmdq1ZrHjybMfRUkHVxJcnKvrxfryralv gzfvu+D6HuxrCo3Ojqa+nDgIbxKEBtdrcsMhq1jWPFhjwo1fSadAkKOfdCAuXJRD THg3b4Sf+W7Cpc570YHrIpBf7WFl2XsPcEM0mJZ5+yATASCubNozQwIDAQABo1Mw UTAdBgNVHQ4EFgQUES0hupZSqY21JOba10QyZuxm91EwHwYDVR0jBBgwFoAUES0h upZSqY21JOba10QyZuxm91EwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsF AAOCAgEABTN5S30ng/RMpBweDm2N561PdpaCdiRXtAFCRVWR2mkDYC/Xj9Vqe6be PyM7L/5OKYVjzF1yJu67z/dx+ja5o+41g17jdqla7hyPx+9B4uRyDh+1KJTa+duj mw/aA1LCr6O6W4WizDOsChJ6FaB2Y1+GlFnKWb5nUdhVJqXQE1WOX9dZnw8Y4Npd VmAsjWot0BZorJrt3fwfcv3QfA896twkbo7Llv/8qzg4sXZXZ4ZtgAOqnPngiSn+ JT/vYCXZ406VvAFpFqMcVz2dO/VGuL8lGIMHRKNyafrsV81EzH1W/XmRWOgvgj6r yQI63ln/AMY72HQ97xLkE1xKunGz6bK5Ug5+O43Uftc4Mb6MUgzo+ZqEQ3Ob+cAV cvjmtwDaPO/O39O5Xq0tLTlkn2/cKf4OQ6S++GDxzyRVHh5JXgP4j9+jfZY57Woy R1bE7N50JjY4cDermBJKdlBIjL7UPhqmLyaG7V0hBitFlgGBUCcJtJOV0xYd5aF3 pxNkvMXhBmh95fjxJ0cJjpO7tN1RAwtMMNgsl7OUbuVRQCHOPW5DgP5qY21jDeRn BY82382l+9QzykmJLI5MZnmj4BA9uIDCwMtoTTvP++SsvhUAbuvh7MOOUQL0EY4m KStYq7X9PKseN+PvmfeoffIKc5R/Ha39oi7cGMVHCr8aiEhsf94= -----END CERTIFICATE----- )"; // kCommonNameWithSANs is a leaf certificate signed by kSANTypesRoot, with // *.host1.test as the common name and a SAN list of *.host2.test and // foo.host3.test. static const char kCommonNameWithSANs[] = R"( -----BEGIN CERTIFICATE----- MIIB2zCCAUSgAwIBAgIBAzANBgkqhkiG9w0BAQsFADArMRcwFQYDVQQKEw5Cb3Jp bmdTU0wgVGVzdDEQMA4GA1UEAxMHUm9vdCBDQTAgFw0wMDAxMDEwMDAwMDBaGA8y MDk5MDEwMTAwMDAwMFowNzEeMBwGA1UEChMVQ29tbW9uIG5hbWUgd2l0aCBTQU5z MRUwEwYDVQQDDAwqLmhvc3QxLnRlc3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC AASgWzfnFnpQrokSLIC+LhCKJDUAY/2usfIDpOnafYoYCasbYetkmOslgyY4Nn07 zjvjNROprA/0bdULXAkdL9bNo0gwRjAbBgNVHSMEFDASgBBAN9cB+0AvuBx+VAQn jFkBMCcGA1UdEQQgMB6CDCouaG9zdDIudGVzdIIOZm9vLmhvc3QzLnRlc3QwDQYJ KoZIhvcNAQELBQADgYEAtv2e3hBhsslXB1HTxgusjoschWOVtvGZUaYlhkKzKTCL 4YpDn50BccnucBU/b9phYvaEZtyzOv4ZXhxTGyLnLrIVB9x5ikfCcfl+LNYNjDwM enm/h1zOfJ7wXLyscD4kU29Wc/zxBd70thIgLYn16CC1S9NtXKsXXDXv5VVH/bg= -----END CERTIFICATE----- )"; // kCommonNameWithSANs is a leaf certificate signed by kSANTypesRoot, with // *.host1.test as the common name and no SAN list. static const char kCommonNameWithoutSANs[] = R"( -----BEGIN CERTIFICATE----- MIIBtTCCAR6gAwIBAgIBAzANBgkqhkiG9w0BAQsFADArMRcwFQYDVQQKEw5Cb3Jp bmdTU0wgVGVzdDEQMA4GA1UEAxMHUm9vdCBDQTAgFw0wMDAxMDEwMDAwMDBaGA8y MDk5MDEwMTAwMDAwMFowOjEhMB8GA1UEChMYQ29tbW9uIG5hbWUgd2l0aG91dCBT QU5zMRUwEwYDVQQDDAwqLmhvc3QxLnRlc3QwWTATBgcqhkjOPQIBBggqhkjOPQMB BwNCAARt2vjlIrPE+kr11VS1rRP/AYQu4fvf1bNw/K9rwYlVBhmLMPYasEmpCtKE 0bDIFydtDYC3wZDpSS+YiaG40sdAox8wHTAbBgNVHSMEFDASgBBAN9cB+0AvuBx+ VAQnjFkBMA0GCSqGSIb3DQEBCwUAA4GBAHRbIeaCEytOpJpw9O2dlB656AHe1+t5 4JiS5mvtzoVOLn7fFk5EFQtZS7sG1Uc2XjlSw+iyvFoTFEqfKyU/mIdc2vBuPwA2 +YXT8aE4S+UZ9oz5j0gDpikGnkSCW0cyHD8L8fntNjaQRSaM482JpmtdmuxClmWO pFFXI2B5usgI -----END CERTIFICATE----- )"; // kCommonNameWithEmailSAN is a leaf certificate signed by kSANTypesRoot, with // *.host1.test as the common name and the email address test@host2.test in the // SAN list. static const char kCommonNameWithEmailSAN[] = R"( -----BEGIN CERTIFICATE----- MIIBvDCCASWgAwIBAgIBAjANBgkqhkiG9w0BAQsFADArMRcwFQYDVQQKEw5Cb3Jp bmdTU0wgVGVzdDEQMA4GA1UEAxMHUm9vdCBDQTAgFw0wMDAxMDEwMDAwMDBaGA8y MDk5MDEwMTAwMDAwMFowFzEVMBMGA1UEAwwMKi5ob3N0MS50ZXN0MFkwEwYHKoZI zj0CAQYIKoZIzj0DAQcDQgAEtevOxcTjpPzlNGoUMFfZyr1k03/Hiuh+EsnuScDs 8XLKi6fDkvSaDClI99ycabQZRPIrvyT+dglDC6ugQd+CYqNJMEcwDAYDVR0TAQH/ BAIwADAbBgNVHSMEFDASgBBAN9cB+0AvuBx+VAQnjFkBMBoGA1UdEQQTMBGBD3Rl c3RAaG9zdDIudGVzdDANBgkqhkiG9w0BAQsFAAOBgQCGbqb78OWJWl4zb+qw0Dz2 HJgZZJt6/+nNG/XJKdaYeS4eofsbwsJI4fuuOF6ZvYCJxVNtGqdfZDgycvFA9hjv NGosBF1/spP17cmzTahLjxs71jDvHV/EQJbKGl/Zpta1Em1VrzSrwoOFabPXzZTJ aet/mER21Z/9ZsTUoJQPJw== -----END CERTIFICATE----- )"; // kCommonNameWithIPSAN is a leaf certificate signed by kSANTypesRoot, with // *.host1.test as the common name and the IP address 127.0.0.1 in the // SAN list. static const char kCommonNameWithIPSAN[] = R"( -----BEGIN CERTIFICATE----- MIIBsTCCARqgAwIBAgIBAjANBgkqhkiG9w0BAQsFADArMRcwFQYDVQQKEw5Cb3Jp bmdTU0wgVGVzdDEQMA4GA1UEAxMHUm9vdCBDQTAgFw0wMDAxMDEwMDAwMDBaGA8y MDk5MDEwMTAwMDAwMFowFzEVMBMGA1UEAwwMKi5ob3N0MS50ZXN0MFkwEwYHKoZI zj0CAQYIKoZIzj0DAQcDQgAEFKrgkxm8PysXbwnHQeTD3p8YY0+sY4ssnZgmj8wX KTyn893fdBHWlz71GO6t82wMTF5d+ZYwI2XU52pfl4SB2aM+MDwwDAYDVR0TAQH/ BAIwADAbBgNVHSMEFDASgBBAN9cB+0AvuBx+VAQnjFkBMA8GA1UdEQQIMAaHBH8A AAEwDQYJKoZIhvcNAQELBQADgYEAQWZ8Oj059ZjS109V/ijMYT28xuAN5n6HHxCO DopTP56Zu9+gme5wTETWEfocspZvgecoUOcedTFoKSQ7JafO09NcVLA+D6ddYpju mgfuiLy9dDhqvX/NHaLBMxOBWWbOLwWE+ibyX+pOzjWRCw1L7eUXOr6PhZAOQsmU D0+O6KI= -----END CERTIFICATE----- )"; // The following six certificates are issued by |kSANTypesRoot| and have // different extended key usage values. They were created with the following // Go program: // // func main() { // block, _ := pem.Decode([]byte(rootKeyPEM)) // rootPriv, _ := x509.ParsePKCS1PrivateKey(block.Bytes) // block, _ = pem.Decode([]byte(rootCertPEM)) // root, _ := x509.ParseCertificate(block.Bytes) // // leafTemplate := &x509.Certificate{ // SerialNumber: big.NewInt(3), // Subject: pkix.Name{ // CommonName: "EKU msSGC", // }, // NotBefore: time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC), // NotAfter: time.Date(2099, time.January, 1, 0, 0, 0, 0, time.UTC), // BasicConstraintsValid: true, // ExtKeyUsage: []x509.ExtKeyUsage{FILL IN HERE}, // } // leafKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) // leafDER, err := x509.CreateCertificate(rand.Reader, leafTemplate, root, &leafKey.PublicKey, rootPriv) // if err != nil { // panic(err) // } // pem.Encode(os.Stdout, &pem.Block{Type: "CERTIFICATE", Bytes: leafDER}) // } static const char kMicrosoftSGCCert[] = R"( -----BEGIN CERTIFICATE----- MIIBtDCCAR2gAwIBAgIBAzANBgkqhkiG9w0BAQsFADArMRcwFQYDVQQKEw5Cb3Jp bmdTU0wgVGVzdDEQMA4GA1UEAxMHUm9vdCBDQTAgFw0wMDAxMDEwMDAwMDBaGA8y MDk5MDEwMTAwMDAwMFowFDESMBAGA1UEAxMJRUtVIG1zU0dDMFkwEwYHKoZIzj0C AQYIKoZIzj0DAQcDQgAEEn61v3Vs+q6bTyyRnrJvuKBE8PTNVLbXGB52jig4Qse2 mGygNEysS0uzZ0luz+rn2hDRUFL6sHLUs1d8UMbI/6NEMEIwFQYDVR0lBA4wDAYK KwYBBAGCNwoDAzAMBgNVHRMBAf8EAjAAMBsGA1UdIwQUMBKAEEA31wH7QC+4HH5U BCeMWQEwDQYJKoZIhvcNAQELBQADgYEAgDQI9RSo3E3ZVnU71TV/LjG9xwHtfk6I rlNnlJJ0lsTHAuMc1mwCbzhtsmasetwYlIa9G8GFWB9Gh/QqHA7G649iGGmXShqe aVDuWgeSEJxBPE2jILoMm4pEYF7jfonTn7XXX6O78yuSlP+NPIU0gUKHkWZ1sWk0 cC4l0r/6jik= -----END CERTIFICATE----- )"; static const char kNetscapeSGCCert[] = R"( -----BEGIN CERTIFICATE----- MIIBszCCARygAwIBAgIBAzANBgkqhkiG9w0BAQsFADArMRcwFQYDVQQKEw5Cb3Jp bmdTU0wgVGVzdDEQMA4GA1UEAxMHUm9vdCBDQTAgFw0wMDAxMDEwMDAwMDBaGA8y MDk5MDEwMTAwMDAwMFowFDESMBAGA1UEAxMJRUtVIG1zU0dDMFkwEwYHKoZIzj0C AQYIKoZIzj0DAQcDQgAE3NbT+TnBfq1DWJCezjaUL52YhDU7cOkI2S2PoWgJ1v7x kKLwBonUFZjppZs69SyBHeJdti+KoJ3qTW+hCG08EaNDMEEwFAYDVR0lBA0wCwYJ YIZIAYb4QgQBMAwGA1UdEwEB/wQCMAAwGwYDVR0jBBQwEoAQQDfXAftAL7gcflQE J4xZATANBgkqhkiG9w0BAQsFAAOBgQBuiyVcfazekHkCWksxdFmjPmMtWCxFjkzc 8VBxFE0CfSHQAfZ8J7tXd1FbAq/eXdZvvo8v0JB4sOM4Ex1ob1fuvDFHdSAHAD7W dhKIjJyzVojoxjCjyue0XMeEPl7RiqbdxoS/R5HFAqAF0T2OeQAqP9gTpOXoau1M RQHX6HQJJg== -----END CERTIFICATE----- )"; static const char kServerEKUCert[] = R"( -----BEGIN CERTIFICATE----- MIIBsjCCARugAwIBAgIBAzANBgkqhkiG9w0BAQsFADArMRcwFQYDVQQKEw5Cb3Jp bmdTU0wgVGVzdDEQMA4GA1UEAxMHUm9vdCBDQTAgFw0wMDAxMDEwMDAwMDBaGA8y MDk5MDEwMTAwMDAwMFowFDESMBAGA1UEAxMJRUtVIG1zU0dDMFkwEwYHKoZIzj0C AQYIKoZIzj0DAQcDQgAEDd35i+VWPwIOKLrLWTuP5cqD+yJDB5nujEzPgkXP5LKJ SZRbHTqTdpYZB2jy6y90RY2Bsjx7FfZ7nN5G2g1GOKNCMEAwEwYDVR0lBAwwCgYI KwYBBQUHAwEwDAYDVR0TAQH/BAIwADAbBgNVHSMEFDASgBBAN9cB+0AvuBx+VAQn jFkBMA0GCSqGSIb3DQEBCwUAA4GBAIKmbMBjuivL/rxDu7u7Vr3o3cdmEggBJxwL iatNW3x1wg0645aNYOktW/iQ7mAAiziTY73GFyfiJDWqnY+CwA94ZWyQidjHdN/I 6BR52sN/dkYEoInYEbmDNMc/if+T0yqeBQLP4BeKLiT8p0qqaimae6LgibS19hDP 2hoEMdz2 -----END CERTIFICATE----- )"; static const char kServerEKUPlusMicrosoftSGCCert[] = R"( -----BEGIN CERTIFICATE----- MIIBvjCCASegAwIBAgIBAzANBgkqhkiG9w0BAQsFADArMRcwFQYDVQQKEw5Cb3Jp bmdTU0wgVGVzdDEQMA4GA1UEAxMHUm9vdCBDQTAgFw0wMDAxMDEwMDAwMDBaGA8y MDk5MDEwMTAwMDAwMFowFDESMBAGA1UEAxMJRUtVIG1zU0dDMFkwEwYHKoZIzj0C AQYIKoZIzj0DAQcDQgAEDO1MYPxq+U4oXMIK8UnsS4C696wpcu4UOmcMJJ5CUd5Z ZpJShN6kYKnrb3GK/6xEgbUGntmrzSRG5FYqk6QgD6NOMEwwHwYDVR0lBBgwFgYI KwYBBQUHAwEGCisGAQQBgjcKAwMwDAYDVR0TAQH/BAIwADAbBgNVHSMEFDASgBBA N9cB+0AvuBx+VAQnjFkBMA0GCSqGSIb3DQEBCwUAA4GBAHOu2IBa4lHzVGS36HxS SejUE87Ji1ysM6BgkYbfxfS9MuV+J3UnqH57JjbH/3CFl4ZDWceF6SGBSCn8LqKa KHpwoNFU3zA99iQzVJgbUyN0PbKwHEanLyKDJZyFk71R39ToxhSNQgaQYjZYCy1H 5V9oXd1bodEqVsOZ/mur24Ku -----END CERTIFICATE----- )"; static const char kAnyEKU[] = R"( -----BEGIN CERTIFICATE----- MIIBrjCCARegAwIBAgIBAzANBgkqhkiG9w0BAQsFADArMRcwFQYDVQQKEw5Cb3Jp bmdTU0wgVGVzdDEQMA4GA1UEAxMHUm9vdCBDQTAgFw0wMDAxMDEwMDAwMDBaGA8y MDk5MDEwMTAwMDAwMFowFDESMBAGA1UEAxMJRUtVIG1zU0dDMFkwEwYHKoZIzj0C AQYIKoZIzj0DAQcDQgAE9nsLABDporlTvx1OBUc4Hd5vxfX+8nS/OhbHmKtFLYNu 1CLLrImbwMQYD2G+PgLO6sQHmASq2jmJKp6ZWsRkTqM+MDwwDwYDVR0lBAgwBgYE VR0lADAMBgNVHRMBAf8EAjAAMBsGA1UdIwQUMBKAEEA31wH7QC+4HH5UBCeMWQEw DQYJKoZIhvcNAQELBQADgYEAxgjgn1SAzQ+2GeCicZ5ndvVhKIeFelGCQ989XTVq uUbAYBW6v8GXNuVzoXYxDgNSanF6U+w+INrJ6daKVrIxAxdk9QFgBXqJoupuRAA3 /OqnmYux0EqOTLbTK1P8DhaiaD0KV6dWGUwzqsgBmPkZ0lgNaPjvb1mKV3jhBkjz L6A= -----END CERTIFICATE----- )"; static const char kNoEKU[] = R"( -----BEGIN CERTIFICATE----- MIIBnTCCAQagAwIBAgIBAzANBgkqhkiG9w0BAQsFADArMRcwFQYDVQQKEw5Cb3Jp bmdTU0wgVGVzdDEQMA4GA1UEAxMHUm9vdCBDQTAgFw0wMDAxMDEwMDAwMDBaGA8y MDk5MDEwMTAwMDAwMFowFDESMBAGA1UEAxMJRUtVIG1zU0dDMFkwEwYHKoZIzj0C AQYIKoZIzj0DAQcDQgAEpSFSqbYY86ZcMamE606dqdyjWlwhSHKOLUFsUUIzkMPz KHRu/x3Yzi8+Hm8eFK/TnCbkpYsYw4hIw00176dYzaMtMCswDAYDVR0TAQH/BAIw ADAbBgNVHSMEFDASgBBAN9cB+0AvuBx+VAQnjFkBMA0GCSqGSIb3DQEBCwUAA4GB AHvYzynIkjLThExHRS+385hfv4vgrQSMmCM1SAnEIjSBGsU7RPgiGAstN06XivuF T1fNugRmTu4OtOIbfdYkcjavJufw9hR9zWTt77CNMTy9XmOZLgdS5boFTtLCztr3 TXHOSQQD8Dl4BK0wOet+TP6LBEjHlRFjAqK4bu9xpxV2 -----END CERTIFICATE----- )"; // CRLFromPEM parses the given, NUL-terminated PEM block and returns an // |X509_CRL*|. static bssl::UniquePtr CRLFromPEM(const char *pem) { bssl::UniquePtr bio(BIO_new_mem_buf(pem, strlen(pem))); return bssl::UniquePtr( PEM_read_bio_X509_CRL(bio.get(), nullptr, nullptr, nullptr)); } // CSRFromPEM parses the given, NUL-terminated PEM block and returns an // |X509_REQ*|. static bssl::UniquePtr CSRFromPEM(const char *pem) { bssl::UniquePtr bio(BIO_new_mem_buf(pem, strlen(pem))); return bssl::UniquePtr( PEM_read_bio_X509_REQ(bio.get(), nullptr, nullptr, nullptr)); } // PrivateKeyFromPEM parses the given, NUL-terminated PEM block and returns an // |EVP_PKEY*|. static bssl::UniquePtr PrivateKeyFromPEM(const char *pem) { bssl::UniquePtr bio( BIO_new_mem_buf(const_cast(pem), strlen(pem))); return bssl::UniquePtr( PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr)); } TEST(X509Test, X509Extensions) { bssl::UniquePtr cert(CertFromPEM(kX509ExtensionsCert)); ASSERT_TRUE(cert); for (int i = 0; i < X509_get_ext_count(cert.get()); i++) { const X509_EXTENSION *ext = X509_get_ext(cert.get(), i); void *parsed = X509V3_EXT_d2i(ext); if (parsed != nullptr) { int nid = OBJ_obj2nid(X509_EXTENSION_get_object(ext)); ASSERT_TRUE(X509V3_EXT_free(nid, parsed)); } } } TEST(X509Test, TestVerify) { // cross_signing_root // | // root_cross_signed root // \ / // intermediate // | | // leaf leaf_no_key_usage // | // forgery bssl::UniquePtr cross_signing_root(CertFromPEM(kCrossSigningRootPEM)); bssl::UniquePtr root(CertFromPEM(kRootCAPEM)); bssl::UniquePtr root_cross_signed(CertFromPEM(kRootCrossSignedPEM)); bssl::UniquePtr intermediate(CertFromPEM(kIntermediatePEM)); bssl::UniquePtr intermediate_self_signed( CertFromPEM(kIntermediateSelfSignedPEM)); bssl::UniquePtr leaf(CertFromPEM(kLeafPEM)); bssl::UniquePtr leaf_no_key_usage(CertFromPEM(kLeafNoKeyUsagePEM)); bssl::UniquePtr forgery(CertFromPEM(kForgeryPEM)); ASSERT_TRUE(cross_signing_root); ASSERT_TRUE(root); ASSERT_TRUE(root_cross_signed); ASSERT_TRUE(intermediate); ASSERT_TRUE(intermediate_self_signed); ASSERT_TRUE(leaf); ASSERT_TRUE(forgery); ASSERT_TRUE(leaf_no_key_usage); // Most of these tests work with or without |X509_V_FLAG_TRUSTED_FIRST|, // though in different ways. for (bool trusted_first : {true, false}) { SCOPED_TRACE(trusted_first); bool override_depth = false; int depth = -1; auto configure_callback = [&](X509_STORE_CTX *ctx) { X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(ctx); // Note we need the callback to clear the flag. Setting |flags| to zero // only skips setting new flags. if (!trusted_first) { X509_VERIFY_PARAM_clear_flags(param, X509_V_FLAG_TRUSTED_FIRST); } if (override_depth) { X509_VERIFY_PARAM_set_depth(param, depth); } }; // No trust anchors configured. EXPECT_EQ(X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, Verify(leaf.get(), /*roots=*/{}, /*intermediates=*/{}, /*crls=*/{}, /*flags=*/0, configure_callback)); EXPECT_EQ( X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, Verify(leaf.get(), /*roots=*/{}, {intermediate.get()}, /*crls=*/{}, /*flags=*/0, configure_callback)); // Each chain works individually. EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {intermediate.get()}, /*crls=*/{}, /*flags=*/0, configure_callback)); EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {cross_signing_root.get()}, {intermediate.get(), root_cross_signed.get()}, /*crls=*/{}, /*flags=*/0, configure_callback)); // When both roots are available, we pick one or the other. EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {cross_signing_root.get(), root.get()}, {intermediate.get(), root_cross_signed.get()}, /*crls=*/{}, /*flags=*/0, configure_callback)); // This is the “altchains” test – we remove the cross-signing CA but include // the cross-sign in the intermediates. With |trusted_first|, we // preferentially stop path-building at |intermediate|. Without // |trusted_first|, the "altchains" logic repairs it. EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {intermediate.get(), root_cross_signed.get()}, /*crls=*/{}, /*flags=*/0, configure_callback)); // If |X509_V_FLAG_NO_ALT_CHAINS| is set and |trusted_first| is disabled, we // get stuck on |root_cross_signed|. If either feature is enabled, we can // build the path. // // This test exists to confirm our current behavior, but these modes are // just workarounds for not having an actual path-building verifier. If we // fix it, this test can be removed. EXPECT_EQ(trusted_first ? X509_V_OK : X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, Verify(leaf.get(), {root.get()}, {intermediate.get(), root_cross_signed.get()}, /*crls=*/{}, /*flags=*/X509_V_FLAG_NO_ALT_CHAINS, configure_callback)); // |forgery| is signed by |leaf_no_key_usage|, but is rejected because the // leaf is not a CA. EXPECT_EQ(X509_V_ERR_INVALID_CA, Verify(forgery.get(), {intermediate_self_signed.get()}, {leaf_no_key_usage.get()}, /*crls=*/{}, /*flags=*/0, configure_callback)); // Test that one cannot skip Basic Constraints checking with a contorted set // of roots and intermediates. This is a regression test for CVE-2015-1793. EXPECT_EQ(X509_V_ERR_INVALID_CA, Verify(forgery.get(), {intermediate_self_signed.get(), root_cross_signed.get()}, {leaf_no_key_usage.get(), intermediate.get()}, /*crls=*/{}, /*flags=*/0, configure_callback)); // Test depth limits. |configure_callback| looks at |override_depth| and // |depth|. Negative numbers have historically worked, so test those too. for (int d : {-4, -3, -2, -1, 0, 1, 2, 3, 4, INT_MAX - 3, INT_MAX - 2, INT_MAX - 1, INT_MAX}) { SCOPED_TRACE(d); override_depth = true; depth = d; // A chain with a leaf, two intermediates, and a root is depth two. EXPECT_EQ( depth >= 2 ? X509_V_OK : X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, Verify(leaf.get(), {cross_signing_root.get()}, {intermediate.get(), root_cross_signed.get()}, /*crls=*/{}, /*flags=*/0, configure_callback)); // A chain with a leaf, a root, and no intermediates is depth zero. EXPECT_EQ( depth >= 0 ? X509_V_OK : X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, Verify(root_cross_signed.get(), {cross_signing_root.get()}, {}, /*crls=*/{}, /*flags=*/0, configure_callback)); // An explicitly trusted self-signed certificate is unaffected by depth // checks. EXPECT_EQ(X509_V_OK, Verify(cross_signing_root.get(), {cross_signing_root.get()}, {}, /*crls=*/{}, /*flags=*/0, configure_callback)); } } } TEST(X509Test, PartialChain) { bssl::UniquePtr root(CertFromPEM(kRootCAPEM)); bssl::UniquePtr intermediate(CertFromPEM(kIntermediatePEM)); bssl::UniquePtr leaf(CertFromPEM(kLeafPEM)); ASSERT_TRUE(root); ASSERT_TRUE(intermediate); ASSERT_TRUE(leaf); // We're intentionally placing the intermediate cert in the trust store here. // Many TLS implementations set |X509_V_FLAG_PARTIAL_CHAIN|, which allows // non-self-signed certificates in the trust store to be trusted. // See https://github.com/openssl/openssl/issues/7871. bssl::UniquePtr intermediates_stack(CertsToStack({})); bssl::UniquePtr roots_stack( CertsToStack({intermediate.get(), root.get()})); for (bool partial_chain : {true, false}) { SCOPED_TRACE(partial_chain); bssl::UniquePtr ctx(X509_STORE_CTX_new()); bssl::UniquePtr store(X509_STORE_new()); ASSERT_TRUE(ctx); ASSERT_TRUE(store); ASSERT_TRUE(X509_STORE_CTX_init(ctx.get(), store.get(), leaf.get(), intermediates_stack.get())); X509_STORE_CTX_set0_trusted_stack(ctx.get(), roots_stack.get()); X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(ctx.get()); time_t current_time = time(nullptr); X509_VERIFY_PARAM_set_time_posix(param, current_time); if (partial_chain) { X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_PARTIAL_CHAIN); } EXPECT_EQ(X509_verify_cert(ctx.get()), 1); STACK_OF(X509) *chain = X509_STORE_CTX_get0_chain(ctx.get()); ASSERT_TRUE(chain); // |root| will be included in the chain if |X509_V_FLAG_PARTIAL_CHAIN| is // not set. EXPECT_EQ(sk_X509_num(chain), partial_chain ? 2u : 3u); } } #if defined(OPENSSL_THREADS) // Verifying the same |X509| objects on two threads should be safe. TEST(X509Test, VerifyThreads) { bssl::UniquePtr root(CertFromPEM(kRootCAPEM)); bssl::UniquePtr intermediate(CertFromPEM(kIntermediatePEM)); bssl::UniquePtr leaf(CertFromPEM(kLeafPEM)); ASSERT_TRUE(root); ASSERT_TRUE(intermediate); ASSERT_TRUE(leaf); const size_t kNumThreads = 10; std::vector threads; for (size_t i = 0; i < kNumThreads; i++) { threads.emplace_back([&] { EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {intermediate.get()}, /*crls=*/{})); }); } for (auto &thread : threads) { thread.join(); } } // Using the same CRL on two threads should be safe. TEST(X509Test, CRLThreads) { bssl::UniquePtr root(CertFromPEM(kCRLTestRoot)); bssl::UniquePtr leaf(CertFromPEM(kCRLTestLeaf)); bssl::UniquePtr basic_crl(CRLFromPEM(kBasicCRL)); bssl::UniquePtr revoked_crl(CRLFromPEM(kRevokedCRL)); ASSERT_TRUE(root); ASSERT_TRUE(leaf); ASSERT_TRUE(basic_crl); ASSERT_TRUE(revoked_crl); const size_t kNumThreads = 10; std::vector threads; for (size_t i = 0; i < kNumThreads; i++) { threads.emplace_back([&] { EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {root.get()}, {basic_crl.get()}, X509_V_FLAG_CRL_CHECK)); }); threads.emplace_back([&] { EXPECT_EQ(X509_V_ERR_CERT_REVOKED, Verify(leaf.get(), {root.get()}, {root.get()}, {revoked_crl.get()}, X509_V_FLAG_CRL_CHECK)); }); } for (auto &thread : threads) { thread.join(); } // TODO(crbug.com/boringssl/600): Add a thread that iterates // |X509_CRL_get_REVOKED| and a thread that calls |X509_CRL_print|. Those // currently do not work correctly. } TEST(X509Test, StoreThreads) { bssl::UniquePtr root(CertFromPEM(kRootCAPEM)); bssl::UniquePtr intermediate(CertFromPEM(kIntermediatePEM)); bssl::UniquePtr leaf(CertFromPEM(kLeafPEM)); ASSERT_TRUE(root); ASSERT_TRUE(intermediate); ASSERT_TRUE(leaf); bssl::UniquePtr intermediates = CertsToStack({intermediate.get()}); ASSERT_TRUE(intermediates); // Some unrelated certificates. bssl::UniquePtr other1(CertFromPEM(kCRLTestRoot)); bssl::UniquePtr other2(CertFromPEM(kCRLTestLeaf)); ASSERT_TRUE(other1); ASSERT_TRUE(other2); bssl::UniquePtr store(X509_STORE_new()); ASSERT_TRUE(store); ASSERT_TRUE(X509_STORE_add_cert(store.get(), root.get())); const size_t kNumThreads = 10; std::vector threads; for (size_t i = 0; i < kNumThreads; i++) { threads.emplace_back([&] { bssl::UniquePtr ctx(X509_STORE_CTX_new()); ASSERT_TRUE(ctx); ASSERT_TRUE(X509_STORE_CTX_init(ctx.get(), store.get(), leaf.get(), intermediates.get())); X509_STORE_CTX_set_time_posix(ctx.get(), /*flags=*/0, kReferenceTime); ASSERT_TRUE(X509_verify_cert(ctx.get())); ASSERT_EQ(X509_STORE_CTX_get_error(ctx.get()), X509_V_OK); }); threads.emplace_back([&] { ASSERT_TRUE(X509_STORE_add_cert(store.get(), other1.get())); }); threads.emplace_back([&] { ASSERT_TRUE(X509_STORE_add_cert(store.get(), other2.get())); }); } for (auto &thread : threads) { thread.join(); } } #endif // OPENSSL_THREADS static const char kHostname[] = "example.com"; static const char kWrongHostname[] = "example2.com"; static const char kEmail[] = "test@example.com"; static const char kWrongEmail[] = "test2@example.com"; static const uint8_t kIP[4] = {127, 0, 0, 1}; static const uint8_t kWrongIP[4] = {127, 0, 0, 2}; static const char kIPString[] = "127.0.0.1"; static const char kWrongIPString[] = "127.0.0.2"; TEST(X509Test, ZeroLengthsWithX509PARAM) { bssl::UniquePtr leaf(CertFromPEM(kSANTypesLeaf)); bssl::UniquePtr root(CertFromPEM(kSANTypesRoot)); ASSERT_TRUE(leaf); ASSERT_TRUE(root); std::vector empty_crls; struct X509Test { const char *correct_value; size_t correct_value_len; const char *incorrect_value; size_t incorrect_value_len; int (*func)(X509_VERIFY_PARAM *, const char *, size_t); int mismatch_error; }; const std::vector kTests = { {kHostname, strlen(kHostname), kWrongHostname, strlen(kWrongHostname), X509_VERIFY_PARAM_set1_host, X509_V_ERR_HOSTNAME_MISMATCH}, {kEmail, strlen(kEmail), kWrongEmail, strlen(kWrongEmail), X509_VERIFY_PARAM_set1_email, X509_V_ERR_EMAIL_MISMATCH}, }; for (size_t i = 0; i < kTests.size(); i++) { SCOPED_TRACE(i); const X509Test &test = kTests[i]; // The correct value should work. ASSERT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {}, empty_crls, 0, [&test](X509_STORE_CTX *ctx) { X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(ctx); ASSERT_TRUE(test.func(param, test.correct_value, test.correct_value_len)); })); // The wrong value should trigger a verification error. ASSERT_EQ(test.mismatch_error, Verify(leaf.get(), {root.get()}, {}, empty_crls, 0, [&test](X509_STORE_CTX *ctx) { X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(ctx); ASSERT_TRUE(test.func(param, test.incorrect_value, test.incorrect_value_len)); })); // AWS-LC supports passing zero as the length for host and email for // backwards compatibility with OpenSSL. ASSERT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {}, empty_crls, 0, [&test](X509_STORE_CTX *ctx) { X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(ctx); ASSERT_TRUE(test.func(param, test.correct_value, 0)); })); // AWS-LC allows an empty value with zero as the length for backwards // compatibility with OpenSSL. ASSERT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {}, empty_crls, 0, [&test](X509_STORE_CTX *ctx) { X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(ctx); ASSERT_TRUE(test.func(param, nullptr, 0)); })); // Passing a value with embedded NULs should also be an error and should // also cause verification to fail. ASSERT_EQ(X509_V_ERR_INVALID_CALL, Verify(leaf.get(), {root.get()}, {}, empty_crls, 0, [&test](X509_STORE_CTX *ctx) { X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(ctx); ASSERT_FALSE(test.func(param, "a", 2)); })); } // |X509_VERIFY_PARAM_set1_host| has additional strange behavior. // AWS-LC/OpenSSL allows an empty value with a non-zero length for backwards // compatibility with OpenSSL. We do not recommend this behavior. ASSERT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {}, empty_crls, 0, [&](X509_STORE_CTX *ctx) { X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(ctx); ASSERT_TRUE(X509_VERIFY_PARAM_set1_host( param, nullptr, strlen(kHostname))); })); // IP addresses work slightly differently: // The correct value should still work. ASSERT_EQ( X509_V_OK, Verify(leaf.get(), {root.get()}, {}, empty_crls, 0, [](X509_STORE_CTX *ctx) { X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(ctx); ASSERT_TRUE(X509_VERIFY_PARAM_set1_ip(param, kIP, sizeof(kIP))); })); // Incorrect values should still fail. ASSERT_EQ(X509_V_ERR_IP_ADDRESS_MISMATCH, Verify(leaf.get(), {root.get()}, {}, empty_crls, 0, [](X509_STORE_CTX *ctx) { X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(ctx); ASSERT_TRUE(X509_VERIFY_PARAM_set1_ip(param, kWrongIP, sizeof(kWrongIP))); })); // Zero length values should trigger an error when setting and cause // verification to always fail. ASSERT_EQ(X509_V_ERR_INVALID_CALL, Verify(leaf.get(), {root.get()}, {}, empty_crls, 0, [](X509_STORE_CTX *ctx) { X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(ctx); ASSERT_FALSE(X509_VERIFY_PARAM_set1_ip(param, kIP, 0)); })); // ... and so should NULL values. ASSERT_EQ(X509_V_ERR_INVALID_CALL, Verify(leaf.get(), {root.get()}, {}, empty_crls, 0, [](X509_STORE_CTX *ctx) { X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(ctx); ASSERT_FALSE(X509_VERIFY_PARAM_set1_ip(param, nullptr, 0)); })); // Zero bytes in an IP address are, of course, fine. This is tested above // because |kIP| contains zeros. } TEST(X509Test, ZeroLengthsWithCheckFunctions) { bssl::UniquePtr leaf(CertFromPEM(kSANTypesLeaf)); ASSERT_TRUE(leaf); EXPECT_EQ( 1, X509_check_host(leaf.get(), kHostname, strlen(kHostname), 0, nullptr)); EXPECT_NE(1, X509_check_host(leaf.get(), kWrongHostname, strlen(kWrongHostname), 0, nullptr)); EXPECT_EQ(1, X509_check_email(leaf.get(), kEmail, strlen(kEmail), 0)); EXPECT_NE(1, X509_check_email(leaf.get(), kWrongEmail, strlen(kWrongEmail), 0)); EXPECT_EQ(1, X509_check_ip(leaf.get(), kIP, sizeof(kIP), 0)); EXPECT_NE(1, X509_check_ip(leaf.get(), kWrongIP, sizeof(kWrongIP), 0)); EXPECT_EQ(1, X509_check_ip_asc(leaf.get(), kIPString, 0)); EXPECT_NE(1, X509_check_ip_asc(leaf.get(), kWrongIPString, 0)); // AWS-LC like OpenSSL supports passing zero as the length for host and email. EXPECT_EQ(1, X509_check_host(leaf.get(), kHostname, 0, 0, nullptr)); EXPECT_NE(1, X509_check_host(leaf.get(), kWrongHostname, 0, 0, nullptr)); EXPECT_EQ(1, X509_check_email(leaf.get(), kEmail, 0, 0)); EXPECT_NE(1, X509_check_email(leaf.get(), kWrongEmail, 0, 0)); // AWS-LC like OpenSSL does not support passing in 0 for the length of the ip // address bytes EXPECT_NE(1, X509_check_ip(leaf.get(), kIP, 0, 0)); EXPECT_NE(1, X509_check_ip(leaf.get(), kWrongIP, 0, 0)); // Unlike all the other functions, |X509_check_ip_asc| doesn't take a length, // so it cannot be zero. } TEST(X509Test, WrongLengthCheckFunctions) { bssl::UniquePtr leaf(CertFromPEM(kSANTypesLeaf)); EXPECT_EQ(-2, X509_check_host(leaf.get(), kHostname, strlen(kHostname) + 1, 0, nullptr)); EXPECT_NE(1, X509_check_host(leaf.get(), kHostname, strlen(kHostname) - 1, 0, nullptr)); EXPECT_EQ(-2, X509_check_email(leaf.get(), kEmail, strlen(kEmail) + 1, 0)); EXPECT_NE(1, X509_check_email(leaf.get(), kEmail, strlen(kEmail) - 1, 0)); } TEST(X509Test, MatchFoundSetsPeername) { bssl::UniquePtr leaf(CertFromPEM(kSANTypesLeaf)); char *peername = nullptr; EXPECT_NE(1, X509_check_host(leaf.get(), kWrongHostname, strlen(kWrongHostname), 0, &peername)); ASSERT_EQ(nullptr, peername); EXPECT_EQ(1, X509_check_host(leaf.get(), kHostname, strlen(kHostname), 0, &peername)); EXPECT_STREQ(peername, kHostname); OPENSSL_free(peername); } TEST(X509Test, TestCRL) { bssl::UniquePtr root(CertFromPEM(kCRLTestRoot)); bssl::UniquePtr leaf(CertFromPEM(kCRLTestLeaf)); bssl::UniquePtr basic_crl(CRLFromPEM(kBasicCRL)); bssl::UniquePtr revoked_crl(CRLFromPEM(kRevokedCRL)); bssl::UniquePtr bad_issuer_crl(CRLFromPEM(kBadIssuerCRL)); bssl::UniquePtr known_critical_crl(CRLFromPEM(kKnownCriticalCRL)); bssl::UniquePtr unknown_critical_crl( CRLFromPEM(kUnknownCriticalCRL)); bssl::UniquePtr unknown_critical_crl2( CRLFromPEM(kUnknownCriticalCRL2)); bssl::UniquePtr algorithm_mismatch_crl( CRLFromPEM(kAlgorithmMismatchCRL)); bssl::UniquePtr algorithm_mismatch_crl2( CRLFromPEM(kAlgorithmMismatchCRL2)); ASSERT_TRUE(root); ASSERT_TRUE(leaf); ASSERT_TRUE(basic_crl); ASSERT_TRUE(revoked_crl); ASSERT_TRUE(bad_issuer_crl); ASSERT_TRUE(known_critical_crl); ASSERT_TRUE(unknown_critical_crl); ASSERT_TRUE(unknown_critical_crl2); ASSERT_TRUE(algorithm_mismatch_crl); ASSERT_TRUE(algorithm_mismatch_crl2); EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {root.get()}, {basic_crl.get()}, X509_V_FLAG_CRL_CHECK)); EXPECT_EQ( X509_V_ERR_CERT_REVOKED, Verify(leaf.get(), {root.get()}, {root.get()}, {basic_crl.get(), revoked_crl.get()}, X509_V_FLAG_CRL_CHECK)); std::vector empty_crls; EXPECT_EQ(X509_V_ERR_UNABLE_TO_GET_CRL, Verify(leaf.get(), {root.get()}, {root.get()}, empty_crls, X509_V_FLAG_CRL_CHECK)); EXPECT_EQ(X509_V_ERR_UNABLE_TO_GET_CRL, Verify(leaf.get(), {root.get()}, {root.get()}, {bad_issuer_crl.get()}, X509_V_FLAG_CRL_CHECK)); EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {root.get()}, {known_critical_crl.get()}, X509_V_FLAG_CRL_CHECK)); EXPECT_EQ(X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION, Verify(leaf.get(), {root.get()}, {root.get()}, {unknown_critical_crl.get()}, X509_V_FLAG_CRL_CHECK)); EXPECT_EQ(X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION, Verify(leaf.get(), {root.get()}, {root.get()}, {unknown_critical_crl2.get()}, X509_V_FLAG_CRL_CHECK)); EXPECT_EQ(X509_V_ERR_CRL_SIGNATURE_FAILURE, Verify(leaf.get(), {root.get()}, {root.get()}, {algorithm_mismatch_crl.get()}, X509_V_FLAG_CRL_CHECK)); EXPECT_EQ(X509_V_ERR_CRL_SIGNATURE_FAILURE, Verify(leaf.get(), {root.get()}, {root.get()}, {algorithm_mismatch_crl2.get()}, X509_V_FLAG_CRL_CHECK)); // The CRL is valid for a month. EXPECT_EQ(X509_V_ERR_CRL_HAS_EXPIRED, Verify(leaf.get(), {root.get()}, {root.get()}, {basic_crl.get()}, X509_V_FLAG_CRL_CHECK, [](X509_STORE_CTX *ctx) { X509_STORE_CTX_set_time_posix( ctx, /*flags=*/0, kReferenceTime + 2 * 30 * 24 * 3600); })); // X509_V_FLAG_NO_CHECK_TIME suppresses the validity check. EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {root.get()}, {basic_crl.get()}, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_NO_CHECK_TIME, [](X509_STORE_CTX *ctx) { X509_STORE_CTX_set_time_posix( ctx, /*flags=*/0, kReferenceTime + 2 * 30 * 24 * 3600); })); // We no longer support indirect or delta CRLs. EXPECT_EQ(X509_V_ERR_INVALID_CALL, Verify(leaf.get(), {root.get()}, {root.get()}, {basic_crl.get()}, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_EXTENDED_CRL_SUPPORT)); EXPECT_EQ(X509_V_ERR_INVALID_CALL, Verify(leaf.get(), {root.get()}, {root.get()}, {basic_crl.get()}, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_USE_DELTAS)); // Parsing kBadExtensionCRL should fail. EXPECT_FALSE(CRLFromPEM(kBadExtensionCRL)); // Ensure X509_OBJECT_get0_X509_CRL only returns a CRL if the X509 object is valid X509_OBJECT validCRL; validCRL.type = X509_LU_CRL; validCRL.data.crl = basic_crl.get(); ASSERT_EQ(basic_crl.get(), X509_OBJECT_get0_X509_CRL(&validCRL)); X509_OBJECT invalidCRL; invalidCRL.type = X509_LU_X509; invalidCRL.data.x509 = leaf.get(); ASSERT_EQ(nullptr, X509_OBJECT_get0_X509_CRL(&invalidCRL)); } // Helper to create a GENERAL_NAME with a URI. static bssl::UniquePtr MakeURIGeneralName(const char *uri) { bssl::UniquePtr name(GENERAL_NAME_new()); if (!name) { return nullptr; } name->type = GEN_URI; name->d.uniformResourceIdentifier = ASN1_IA5STRING_new(); if (!name->d.uniformResourceIdentifier || !ASN1_STRING_set(name->d.uniformResourceIdentifier, uri, strlen(uri))) { return nullptr; } return name; } // Helper to create a DIST_POINT_NAME from a URI. Caller takes ownership. static DIST_POINT_NAME *MakeDistPointName(const char *uri) { DIST_POINT_NAME *dpn = DIST_POINT_NAME_new(); if (!dpn) { return nullptr; } dpn->type = 0; // fullname dpn->name.fullname = sk_GENERAL_NAME_new_null(); if (!dpn->name.fullname) { DIST_POINT_NAME_free(dpn); return nullptr; } auto gn = MakeURIGeneralName(uri); if (!gn || !bssl::PushToStack(dpn->name.fullname, std::move(gn))) { DIST_POINT_NAME_free(dpn); return nullptr; } return dpn; } // Helper to create a leaf cert with a CRLDP extension and sign it. static bssl::UniquePtr MakeCRLDPLeaf( X509 *issuer_cert, EVP_PKEY *issuer_key, int serial, CRL_DIST_POINTS *crldp) { bssl::UniquePtr leaf_key(EVP_PKEY_new()); bssl::UniquePtr rsa(RSA_new()); bssl::UniquePtr e(BN_new()); if (!leaf_key || !rsa || !e || !BN_set_word(e.get(), RSA_F4) || !RSA_generate_key_ex(rsa.get(), 2048, e.get(), nullptr) || !EVP_PKEY_assign_RSA(leaf_key.get(), rsa.release())) { return nullptr; } bssl::UniquePtr leaf(X509_new()); if (!leaf || !X509_set_version(leaf.get(), X509_VERSION_3) || !X509_set_issuer_name(leaf.get(), X509_get_subject_name(issuer_cert)) || !X509_NAME_add_entry_by_txt( X509_get_subject_name(leaf.get()), "CN", MBSTRING_UTF8, reinterpret_cast("Leaf"), -1, -1, 0) || !X509_set_pubkey(leaf.get(), leaf_key.get()) || !ASN1_TIME_adj(X509_getm_notBefore(leaf.get()), kReferenceTime, -1, 0) || !ASN1_TIME_adj(X509_getm_notAfter(leaf.get()), kReferenceTime, 1, 0)) { return nullptr; } bssl::UniquePtr sn(ASN1_INTEGER_new()); if (!sn || !ASN1_INTEGER_set(sn.get(), serial) || !X509_set_serialNumber(leaf.get(), sn.get())) { return nullptr; } if (!X509_add1_ext_i2d(leaf.get(), NID_crl_distribution_points, crldp, /*crit=*/0, /*flags=*/0)) { return nullptr; } if (!X509_sign(leaf.get(), issuer_key, EVP_sha256())) { return nullptr; } return leaf; } // Helper to create a CRL, optionally with an IDP extension and revoked serials. static bssl::UniquePtr MakeTestCRL( X509 *issuer_cert, EVP_PKEY *key, const char *idp_uri, const std::vector &revoked_serials) { bssl::UniquePtr crl(X509_CRL_new()); if (!crl) { return nullptr; } if (!X509_CRL_set_version(crl.get(), X509_CRL_VERSION_2) || !X509_CRL_set_issuer_name(crl.get(), X509_get_subject_name(issuer_cert))) { return nullptr; } bssl::UniquePtr last_update(ASN1_TIME_new()); if (!last_update || !ASN1_TIME_set_posix(last_update.get(), kReferenceTime) || !X509_CRL_set1_lastUpdate(crl.get(), last_update.get())) { return nullptr; } bssl::UniquePtr next_update(ASN1_TIME_new()); if (!next_update || !ASN1_TIME_adj(next_update.get(), kReferenceTime, 30, 0) || !X509_CRL_set1_nextUpdate(crl.get(), next_update.get())) { return nullptr; } for (int serial : revoked_serials) { bssl::UniquePtr rev(X509_REVOKED_new()); bssl::UniquePtr sn(ASN1_INTEGER_new()); bssl::UniquePtr rev_time(ASN1_TIME_new()); if (!rev || !sn || !rev_time || !ASN1_INTEGER_set(sn.get(), serial) || !X509_REVOKED_set_serialNumber(rev.get(), sn.get()) || !ASN1_TIME_set_posix(rev_time.get(), kReferenceTime) || !X509_REVOKED_set_revocationDate(rev.get(), rev_time.get()) || !X509_CRL_add0_revoked(crl.get(), rev.get())) { return nullptr; } rev.release(); // Ownership transferred to crl. } if (idp_uri) { ISSUING_DIST_POINT *idp = ISSUING_DIST_POINT_new(); if (!idp) { return nullptr; } idp->distpoint = MakeDistPointName(idp_uri); if (!idp->distpoint || !X509_CRL_add1_ext_i2d(crl.get(), NID_issuing_distribution_point, idp, /*crit=*/1, /*flags=*/0)) { ISSUING_DIST_POINT_free(idp); return nullptr; } ISSUING_DIST_POINT_free(idp); } if (!X509_CRL_sign(crl.get(), key, EVP_sha256())) { return nullptr; } // Re-encode and re-parse so that internal fields like crl->idp and // crl->idp_flags are populated from the IDP extension. These are only // set during parsing (ASN1_OP_D2I_POST), not programmatic construction. uint8_t *der = nullptr; int der_len = i2d_X509_CRL(crl.get(), &der); if (der_len <= 0) { return nullptr; } const uint8_t *inp = der; crl.reset(d2i_X509_CRL(nullptr, &inp, der_len)); OPENSSL_free(der); return crl; } // Test that CRL distribution point scope checking (crl_crldp_check) correctly // matches a cert's CRLDP against a CRL's Issuing Distribution Point (IDP). TEST(X509Test, CRLDistributionPointScope) { bssl::UniquePtr root(CertFromPEM(kCRLTestRoot)); bssl::UniquePtr key(PrivateKeyFromPEM(kCRLTestRootKey)); ASSERT_TRUE(root); ASSERT_TRUE(key); const int kLeafSerial = 0x1000; const char *kCRLURI = "http://example.com/crl.pem"; const char *kOtherURI = "http://other.example.com/crl.pem"; // Scenario 1: Leaf with a single clean CRLDP (distpoint URI only, no reasons, // no CRLissuer). CRL has a matching IDP and revokes the leaf's serial. // Verify that the CRL is considered in scope and the cert is revoked. { bssl::UniquePtr crldp(sk_DIST_POINT_new_null()); ASSERT_TRUE(crldp); bssl::UniquePtr dp(DIST_POINT_new()); ASSERT_TRUE(dp); dp->distpoint = MakeDistPointName(kCRLURI); ASSERT_TRUE(dp->distpoint); ASSERT_TRUE(bssl::PushToStack(crldp.get(), std::move(dp))); auto leaf = MakeCRLDPLeaf(root.get(), key.get(), kLeafSerial, crldp.get()); ASSERT_TRUE(leaf); auto crl = MakeTestCRL(root.get(), key.get(), kCRLURI, {kLeafSerial}); ASSERT_TRUE(crl); EXPECT_EQ(X509_V_ERR_CERT_REVOKED, Verify(leaf.get(), {root.get()}, {root.get()}, {crl.get()}, X509_V_FLAG_CRL_CHECK)); } // Scenario 2: Leaf with two DPs: // DP1: distpoint=kOtherURI + reasons + CRLissuer (should be skipped) // DP2: distpoint=kCRLURI (clean, matches the revoking CRL) // CRL-A (IDP=kCRLURI) revokes the leaf. CRL-B (IDP=kOtherURI) has no // revocations. DP1 should be skipped and DP2 should match CRL-A. { bssl::UniquePtr crldp(sk_DIST_POINT_new_null()); ASSERT_TRUE(crldp); // DP1: distpoint + reasons + CRLissuer bssl::UniquePtr dp1(DIST_POINT_new()); ASSERT_TRUE(dp1); dp1->distpoint = MakeDistPointName(kOtherURI); ASSERT_TRUE(dp1->distpoint); dp1->reasons = ASN1_BIT_STRING_new(); ASSERT_TRUE(dp1->reasons); ASN1_BIT_STRING_set_bit(dp1->reasons, 1, 1); // keyCompromise dp1->CRLissuer = sk_GENERAL_NAME_new_null(); ASSERT_TRUE(dp1->CRLissuer); bssl::UniquePtr issuer_name(GENERAL_NAME_new()); ASSERT_TRUE(issuer_name); issuer_name->type = GEN_DIRNAME; issuer_name->d.directoryName = X509_NAME_new(); ASSERT_TRUE(issuer_name->d.directoryName); ASSERT_TRUE(X509_NAME_add_entry_by_txt( issuer_name->d.directoryName, "O", MBSTRING_ASC, reinterpret_cast("Other Issuer"), -1, -1, 0)); ASSERT_TRUE(bssl::PushToStack(dp1->CRLissuer, std::move(issuer_name))); ASSERT_TRUE(bssl::PushToStack(crldp.get(), std::move(dp1))); // DP2: clean distpoint only bssl::UniquePtr dp2(DIST_POINT_new()); ASSERT_TRUE(dp2); dp2->distpoint = MakeDistPointName(kCRLURI); ASSERT_TRUE(dp2->distpoint); ASSERT_TRUE(bssl::PushToStack(crldp.get(), std::move(dp2))); auto leaf = MakeCRLDPLeaf(root.get(), key.get(), kLeafSerial, crldp.get()); ASSERT_TRUE(leaf); auto crl_a = MakeTestCRL(root.get(), key.get(), kCRLURI, {kLeafSerial}); ASSERT_TRUE(crl_a); auto crl_b = MakeTestCRL(root.get(), key.get(), kOtherURI, {}); ASSERT_TRUE(crl_b); EXPECT_EQ(X509_V_ERR_CERT_REVOKED, Verify(leaf.get(), {root.get()}, {root.get()}, {crl_a.get(), crl_b.get()}, X509_V_FLAG_CRL_CHECK)); } } TEST(X509Test, TestX509GettersSetters) { bssl::UniquePtr obj(X509_OBJECT_new()); bssl::UniquePtr x509(CertFromPEM(kCRLTestRoot)); bssl::UniquePtr crl(CRLFromPEM(kBasicCRL)); ASSERT_TRUE(obj); ASSERT_TRUE(x509); ASSERT_TRUE(crl); EXPECT_EQ(0, X509_OBJECT_get0_X509(obj.get())); EXPECT_EQ(0, X509_OBJECT_get0_X509_CRL(obj.get())); EXPECT_EQ(0, X509_OBJECT_set1_X509(nullptr, x509.get())); EXPECT_EQ(0, X509_OBJECT_set1_X509_CRL(nullptr, crl.get())); EXPECT_EQ(1, X509_OBJECT_set1_X509(obj.get(), x509.get())); EXPECT_EQ(x509.get(), X509_OBJECT_get0_X509(obj.get())); EXPECT_EQ(1, X509_OBJECT_set1_X509_CRL(obj.get(), crl.get())); EXPECT_EQ(crl.get(), X509_OBJECT_get0_X509_CRL(obj.get())); } TEST(X509Test, ManyNamesAndConstraints) { bssl::UniquePtr many_constraints(CertFromPEM( GetTestData("crypto/x509/test/many_constraints.pem").c_str())); ASSERT_TRUE(many_constraints); bssl::UniquePtr many_names1( CertFromPEM(GetTestData("crypto/x509/test/many_names1.pem").c_str())); ASSERT_TRUE(many_names1); bssl::UniquePtr many_names2( CertFromPEM(GetTestData("crypto/x509/test/many_names2.pem").c_str())); ASSERT_TRUE(many_names2); bssl::UniquePtr many_names3( CertFromPEM(GetTestData("crypto/x509/test/many_names3.pem").c_str())); ASSERT_TRUE(many_names3); bssl::UniquePtr some_names1( CertFromPEM(GetTestData("crypto/x509/test/some_names1.pem").c_str())); ASSERT_TRUE(some_names1); bssl::UniquePtr some_names2( CertFromPEM(GetTestData("crypto/x509/test/some_names2.pem").c_str())); ASSERT_TRUE(some_names2); bssl::UniquePtr some_names3( CertFromPEM(GetTestData("crypto/x509/test/some_names3.pem").c_str())); ASSERT_TRUE(some_names3); EXPECT_EQ(X509_V_ERR_UNSPECIFIED, Verify(many_names1.get(), {many_constraints.get()}, {many_constraints.get()}, {})); EXPECT_EQ(X509_V_ERR_UNSPECIFIED, Verify(many_names2.get(), {many_constraints.get()}, {many_constraints.get()}, {})); EXPECT_EQ(X509_V_ERR_UNSPECIFIED, Verify(many_names3.get(), {many_constraints.get()}, {many_constraints.get()}, {})); EXPECT_EQ(X509_V_OK, Verify(some_names1.get(), {many_constraints.get()}, {many_constraints.get()}, {})); EXPECT_EQ(X509_V_OK, Verify(some_names2.get(), {many_constraints.get()}, {many_constraints.get()}, {})); EXPECT_EQ(X509_V_OK, Verify(some_names3.get(), {many_constraints.get()}, {many_constraints.get()}, {})); } static bssl::UniquePtr MakeGeneralName(int type, const std::string &value) { if (type != GEN_EMAIL && type != GEN_DNS && type != GEN_URI) { // This function only supports the IA5String types. return nullptr; } bssl::UniquePtr str(ASN1_IA5STRING_new()); bssl::UniquePtr name(GENERAL_NAME_new()); if (!str || !name || !ASN1_STRING_set(str.get(), value.data(), value.size())) { return nullptr; } name->type = type; name->d.ia5 = str.release(); return name; } static bool AddExtendedKeyUsage(X509 *x509, const std::vector &eku_nids) { bssl::UniquePtr objs(sk_ASN1_OBJECT_new_null()); if (objs == nullptr) { return false; } for (int nid : eku_nids) { if (!sk_ASN1_OBJECT_push(objs.get(), OBJ_nid2obj(nid))) { return false; } } return X509_add1_ext_i2d(x509, NID_ext_key_usage, objs.get(), /*crit=*/1, /*flags=*/0); } enum class KeyUsage : int { kDigitalSignature = 0, kNonRepudiation = 1, kKeyEncipherment = 2, kDataEncipherment = 3, kKeyAgreement = 4, kKeyCertSign = 5, kCRLSign = 6, kEncipherOnly = 7, kDecipherOnly = 8, }; static bool AddKeyUsage(X509 *x509, const std::vector usages) { bssl::UniquePtr str(ASN1_BIT_STRING_new()); if (str == nullptr) { return false; } for (KeyUsage usage : usages) { if (!ASN1_BIT_STRING_set_bit(str.get(), static_cast(usage), 1)) { return false; } } return X509_add1_ext_i2d(x509, NID_key_usage, str.get(), /*crit=*/1, /*flags=*/0); } // Creates a NAME_CONSTRAINTS with a single permitted or excluded subtree. static bssl::UniquePtr MakeNameConstraint( int type, const std::string &name, bool excluded) { bssl::UniquePtr nc(NAME_CONSTRAINTS_new()); if (!nc) { return nullptr; } STACK_OF(GENERAL_SUBTREE) **rule = excluded ? &nc->excludedSubtrees : &nc->permittedSubtrees; *rule = sk_GENERAL_SUBTREE_new_null(); if (!*rule) { return nullptr; } bssl::UniquePtr subtree(GENERAL_SUBTREE_new()); if (!subtree) { return nullptr; } GENERAL_NAME_free(subtree->base); subtree->base = MakeGeneralName(type, name).release(); if (!subtree->base) { return nullptr; } if (!bssl::PushToStack(*rule, std::move(subtree))) { return nullptr; } return nc; } TEST(X509Test, NameConstraints) { bssl::UniquePtr key = PrivateKeyFromPEM(kP256Key); ASSERT_TRUE(key); const struct { int type; std::string name; std::string constraint; int permit_result; int exclude_result; } kTests[] = { // Empty string matches everything. {GEN_DNS, "foo.example.com", "", X509_V_OK, X509_V_ERR_EXCLUDED_VIOLATION}, // Name constraints match the entire subtree. {GEN_DNS, "foo.example.com", "example.com", X509_V_OK, X509_V_ERR_EXCLUDED_VIOLATION}, {GEN_DNS, "foo.example.com", "EXAMPLE.COM", X509_V_OK, X509_V_ERR_EXCLUDED_VIOLATION}, {GEN_DNS, "foo.example.com", "xample.com", X509_V_ERR_PERMITTED_VIOLATION, X509_V_OK}, {GEN_DNS, "foo.example.com", "unrelated.much.longer.name.example", X509_V_ERR_PERMITTED_VIOLATION, X509_V_OK}, // A leading dot means at least one component must be added. {GEN_DNS, "foo.example.com", ".example.com", X509_V_OK, X509_V_ERR_EXCLUDED_VIOLATION}, {GEN_DNS, "foo.example.com", "foo.example.com", X509_V_OK, X509_V_ERR_EXCLUDED_VIOLATION}, {GEN_DNS, "foo.example.com", ".foo.example.com", X509_V_ERR_PERMITTED_VIOLATION, X509_V_OK}, {GEN_DNS, "foo.example.com", ".xample.com", X509_V_ERR_PERMITTED_VIOLATION, X509_V_OK}, {GEN_DNS, "foo.example.com", ".unrelated.much.longer.name.example", X509_V_ERR_PERMITTED_VIOLATION, X509_V_OK}, // Trailing dot is ignored. {GEN_DNS, "foo.example.com.", "example.com", X509_V_OK, X509_V_ERR_EXCLUDED_VIOLATION}, {GEN_DNS, "foo.example.com", "example.com.", X509_V_OK, X509_V_ERR_EXCLUDED_VIOLATION}, // NUL bytes, if not rejected, should not confuse the matching logic. {GEN_DNS, std::string({'a', '\0', 'a'}), std::string({'a', '\0', 'b'}), X509_V_ERR_PERMITTED_VIOLATION, X509_V_OK}, // Wildcard DNS names against name constraints. {GEN_DNS, "*.com", "foo.example.com", X509_V_ERR_PERMITTED_VIOLATION, X509_V_OK}, {GEN_DNS, "*.example.com", "foo.example.com", X509_V_ERR_PERMITTED_VIOLATION, X509_V_ERR_EXCLUDED_VIOLATION}, {GEN_DNS, "*.foo.example.com", "foo.example.com", X509_V_OK, X509_V_ERR_EXCLUDED_VIOLATION}, {GEN_DNS, "*.sub.foo.example.com", "foo.example.com", X509_V_OK, X509_V_ERR_EXCLUDED_VIOLATION}, {GEN_DNS, "*.bar.example.com", "foo.example.com", X509_V_ERR_PERMITTED_VIOLATION, X509_V_OK}, {GEN_DNS, "*.example.com", "net", X509_V_ERR_PERMITTED_VIOLATION, X509_V_OK}, {GEN_DNS, "*.example.com", "com", X509_V_OK, X509_V_ERR_EXCLUDED_VIOLATION}, // Names must be emails. {GEN_EMAIL, "not-an-email.example", "not-an-email.example", X509_V_ERR_UNSUPPORTED_NAME_SYNTAX, X509_V_ERR_UNSUPPORTED_NAME_SYNTAX}, // A leading dot matches all local names and all subdomains {GEN_EMAIL, "foo@bar.example.com", ".example.com", X509_V_OK, X509_V_ERR_EXCLUDED_VIOLATION}, {GEN_EMAIL, "foo@bar.example.com", ".EXAMPLE.COM", X509_V_OK, X509_V_ERR_EXCLUDED_VIOLATION}, {GEN_EMAIL, "foo@bar.example.com", ".bar.example.com", X509_V_ERR_PERMITTED_VIOLATION, X509_V_OK}, // Without a leading dot, the host must match exactly. {GEN_EMAIL, "foo@example.com", "example.com", X509_V_OK, X509_V_ERR_EXCLUDED_VIOLATION}, {GEN_EMAIL, "foo@example.com", "EXAMPLE.COM", X509_V_OK, X509_V_ERR_EXCLUDED_VIOLATION}, {GEN_EMAIL, "foo@bar.example.com", "example.com", X509_V_ERR_PERMITTED_VIOLATION, X509_V_OK}, // If the constraint specifies a mailbox, it specifies the whole thing. // The halves are compared insensitively. {GEN_EMAIL, "foo@example.com", "foo@example.com", X509_V_OK, X509_V_ERR_EXCLUDED_VIOLATION}, {GEN_EMAIL, "foo@example.com", "foo@EXAMPLE.COM", X509_V_OK, X509_V_ERR_EXCLUDED_VIOLATION}, {GEN_EMAIL, "foo@example.com", "FOO@example.com", X509_V_ERR_PERMITTED_VIOLATION, X509_V_OK}, {GEN_EMAIL, "foo@example.com", "bar@example.com", X509_V_ERR_PERMITTED_VIOLATION, X509_V_OK}, // OpenSSL ignores a stray leading @. {GEN_EMAIL, "foo@example.com", "@example.com", X509_V_OK, X509_V_ERR_EXCLUDED_VIOLATION}, {GEN_EMAIL, "foo@example.com", "@EXAMPLE.COM", X509_V_OK, X509_V_ERR_EXCLUDED_VIOLATION}, {GEN_EMAIL, "foo@bar.example.com", "@example.com", X509_V_ERR_PERMITTED_VIOLATION, X509_V_OK}, // Basic syntax check. {GEN_URI, "not-a-url", "not-a-url", X509_V_ERR_UNSUPPORTED_NAME_SYNTAX, X509_V_ERR_UNSUPPORTED_NAME_SYNTAX}, {GEN_URI, "foo:not-a-url", "not-a-url", X509_V_ERR_UNSUPPORTED_NAME_SYNTAX, X509_V_ERR_UNSUPPORTED_NAME_SYNTAX}, {GEN_URI, "foo:/not-a-url", "not-a-url", X509_V_ERR_UNSUPPORTED_NAME_SYNTAX, X509_V_ERR_UNSUPPORTED_NAME_SYNTAX}, {GEN_URI, "foo:///not-a-url", "not-a-url", X509_V_ERR_UNSUPPORTED_NAME_SYNTAX, X509_V_ERR_UNSUPPORTED_NAME_SYNTAX}, {GEN_URI, "foo://:not-a-url", "not-a-url", X509_V_ERR_UNSUPPORTED_NAME_SYNTAX, X509_V_ERR_UNSUPPORTED_NAME_SYNTAX}, {GEN_URI, "foo://", "not-a-url", X509_V_ERR_UNSUPPORTED_NAME_SYNTAX, X509_V_ERR_UNSUPPORTED_NAME_SYNTAX}, // Hosts are an exact match. {GEN_URI, "foo://example.com", "example.com", X509_V_OK, X509_V_ERR_EXCLUDED_VIOLATION}, {GEN_URI, "foo://example.com:443", "example.com", X509_V_OK, X509_V_ERR_EXCLUDED_VIOLATION}, {GEN_URI, "foo://example.com/whatever", "example.com", X509_V_OK, X509_V_ERR_EXCLUDED_VIOLATION}, {GEN_URI, "foo://bar.example.com", "example.com", X509_V_ERR_PERMITTED_VIOLATION, X509_V_OK}, {GEN_URI, "foo://bar.example.com:443", "example.com", X509_V_ERR_PERMITTED_VIOLATION, X509_V_OK}, {GEN_URI, "foo://bar.example.com/whatever", "example.com", X509_V_ERR_PERMITTED_VIOLATION, X509_V_OK}, {GEN_URI, "foo://bar.example.com", "xample.com", X509_V_ERR_PERMITTED_VIOLATION, X509_V_OK}, {GEN_URI, "foo://bar.example.com:443", "xample.com", X509_V_ERR_PERMITTED_VIOLATION, X509_V_OK}, {GEN_URI, "foo://bar.example.com/whatever", "xample.com", X509_V_ERR_PERMITTED_VIOLATION, X509_V_OK}, {GEN_URI, "foo://example.com", "some-other-name.example", X509_V_ERR_PERMITTED_VIOLATION, X509_V_OK}, {GEN_URI, "foo://example.com:443", "some-other-name.example", X509_V_ERR_PERMITTED_VIOLATION, X509_V_OK}, {GEN_URI, "foo://example.com/whatever", "some-other-name.example", X509_V_ERR_PERMITTED_VIOLATION, X509_V_OK}, // A leading dot allows components to be added. {GEN_URI, "foo://example.com", ".example.com", X509_V_ERR_PERMITTED_VIOLATION, X509_V_OK}, {GEN_URI, "foo://example.com:443", ".example.com", X509_V_ERR_PERMITTED_VIOLATION, X509_V_OK}, {GEN_URI, "foo://example.com/whatever", ".example.com", X509_V_ERR_PERMITTED_VIOLATION, X509_V_OK}, {GEN_URI, "foo://bar.example.com", ".example.com", X509_V_OK, X509_V_ERR_EXCLUDED_VIOLATION}, {GEN_URI, "foo://bar.example.com:443", ".example.com", X509_V_OK, X509_V_ERR_EXCLUDED_VIOLATION}, {GEN_URI, "foo://bar.example.com/whatever", ".example.com", X509_V_OK, X509_V_ERR_EXCLUDED_VIOLATION}, {GEN_URI, "foo://example.com", ".some-other-name.example", X509_V_ERR_PERMITTED_VIOLATION, X509_V_OK}, {GEN_URI, "foo://example.com:443", ".some-other-name.example", X509_V_ERR_PERMITTED_VIOLATION, X509_V_OK}, {GEN_URI, "foo://example.com/whatever", ".some-other-name.example", X509_V_ERR_PERMITTED_VIOLATION, X509_V_OK}, {GEN_URI, "foo://example.com", ".xample.com", X509_V_ERR_PERMITTED_VIOLATION, X509_V_OK}, {GEN_URI, "foo://example.com:443", ".xample.com", X509_V_ERR_PERMITTED_VIOLATION, X509_V_OK}, {GEN_URI, "foo://example.com/whatever", ".xample.com", X509_V_ERR_PERMITTED_VIOLATION, X509_V_OK}, // RFC 5280 §4.2.1.10 specifies URI name constraints "MUST be specified // as a fully qualified domain name". IPv6 literal URIs are not domain // names and cannot be reliably matched by string comparison due to // multiple equivalent textual representations. They are rejected as // unsupported syntax (fail-closed). {GEN_URI, "foo://[2001:db8::1]", "[2001:db8::1]", X509_V_ERR_UNSUPPORTED_NAME_SYNTAX, X509_V_ERR_UNSUPPORTED_NAME_SYNTAX}, // An incomplete IPv6 literal is also rejected. {GEN_URI, "foo://[2001:db8::1", "[2001:db8::1]", X509_V_ERR_UNSUPPORTED_NAME_SYNTAX, X509_V_ERR_UNSUPPORTED_NAME_SYNTAX}, }; for (const auto &t : kTests) { SCOPED_TRACE(t.type); SCOPED_TRACE(t.name); SCOPED_TRACE(t.constraint); for (bool exclude : {false, true}) { SCOPED_TRACE(exclude); bssl::UniquePtr name = MakeGeneralName(t.type, t.name); ASSERT_TRUE(name); bssl::UniquePtr names(GENERAL_NAMES_new()); ASSERT_TRUE(names); ASSERT_TRUE(bssl::PushToStack(names.get(), std::move(name))); bssl::UniquePtr nc = MakeNameConstraint(t.type, t.constraint, /*excluded=*/exclude); ASSERT_TRUE(nc); bssl::UniquePtr root = MakeTestCert("Root", "Root", key.get(), /*is_ca=*/true); ASSERT_TRUE(root); ASSERT_TRUE(X509_add1_ext_i2d(root.get(), NID_name_constraints, nc.get(), /*crit=*/1, /*flags=*/0)); ASSERT_TRUE(X509_sign(root.get(), key.get(), EVP_sha256())); bssl::UniquePtr leaf = MakeTestCert("Root", "Leaf", key.get(), /*is_ca=*/false); ASSERT_TRUE(leaf); ASSERT_TRUE(X509_add1_ext_i2d(leaf.get(), NID_subject_alt_name, names.get(), /*crit=*/0, /*flags=*/0)); ASSERT_TRUE(X509_sign(leaf.get(), key.get(), EVP_sha256())); int got_result = Verify(leaf.get(), {root.get()}, {}, {}, 0); int want_result = exclude ? t.exclude_result : t.permit_result; EXPECT_EQ(want_result, got_result) << "got \"" << X509_verify_cert_error_string(got_result) << "\", want \"" << X509_verify_cert_error_string(want_result) << "\""; } } } // Test that wildcard CNs are checked against name constraints when no // dNSName SAN is present. TEST(X509Test, NameConstraintsWildcardCN) { bssl::UniquePtr key = PrivateKeyFromPEM(kP256Key); ASSERT_TRUE(key); // Permitted subtree: only .example.com bssl::UniquePtr nc = MakeNameConstraint(GEN_DNS, ".example.com", /*excluded=*/false); ASSERT_TRUE(nc); bssl::UniquePtr root = MakeTestCert("Root", "Root", key.get(), /*is_ca=*/true); ASSERT_TRUE(root); ASSERT_TRUE(X509_add1_ext_i2d(root.get(), NID_name_constraints, nc.get(), /*crit=*/1, /*flags=*/0)); ASSERT_TRUE(X509_sign(root.get(), key.get(), EVP_sha256())); // Wildcard CN outside permitted subtree, no SAN. Should be rejected. bssl::UniquePtr leaf = MakeTestCert("Root", "*.evil.com", key.get(), /*is_ca=*/false); ASSERT_TRUE(leaf); ASSERT_TRUE(X509_sign(leaf.get(), key.get(), EVP_sha256())); EXPECT_EQ(X509_V_ERR_PERMITTED_VIOLATION, Verify(leaf.get(), {root.get()}, {}, {}, 0)); // Wildcard CN inside permitted subtree, no SAN. Should be permitted. bssl::UniquePtr leaf_ok = MakeTestCert("Root", "*.example.com", key.get(), /*is_ca=*/false); ASSERT_TRUE(leaf_ok); ASSERT_TRUE(X509_sign(leaf_ok.get(), key.get(), EVP_sha256())); EXPECT_EQ(X509_V_OK, Verify(leaf_ok.get(), {root.get()}, {}, {}, 0)); // Non-wildcard CN outside permitted subtree, no SAN. Sanity check. bssl::UniquePtr leaf_bad = MakeTestCert("Root", "foo.evil.com", key.get(), /*is_ca=*/false); ASSERT_TRUE(leaf_bad); ASSERT_TRUE(X509_sign(leaf_bad.get(), key.get(), EVP_sha256())); EXPECT_EQ(X509_V_ERR_PERMITTED_VIOLATION, Verify(leaf_bad.get(), {root.get()}, {}, {}, 0)); // Excluded subtree: wildcard CN inside excluded namespace should be rejected. bssl::UniquePtr nc_excl = MakeNameConstraint(GEN_DNS, ".evil.com", /*excluded=*/true); ASSERT_TRUE(nc_excl); bssl::UniquePtr root_excl = MakeTestCert("Root2", "Root2", key.get(), /*is_ca=*/true); ASSERT_TRUE(root_excl); ASSERT_TRUE(X509_add1_ext_i2d(root_excl.get(), NID_name_constraints, nc_excl.get(), /*crit=*/1, /*flags=*/0)); ASSERT_TRUE(X509_sign(root_excl.get(), key.get(), EVP_sha256())); bssl::UniquePtr leaf_excl = MakeTestCert("Root2", "*.evil.com", key.get(), /*is_ca=*/false); ASSERT_TRUE(leaf_excl); ASSERT_TRUE(X509_sign(leaf_excl.get(), key.get(), EVP_sha256())); EXPECT_EQ(X509_V_ERR_EXCLUDED_VIOLATION, Verify(leaf_excl.get(), {root_excl.get()}, {}, {}, 0)); // Wildcard CN outside excluded namespace should not be rejected. bssl::UniquePtr leaf_excl_ok = MakeTestCert("Root2", "*.good.com", key.get(), /*is_ca=*/false); ASSERT_TRUE(leaf_excl_ok); ASSERT_TRUE(X509_sign(leaf_excl_ok.get(), key.get(), EVP_sha256())); EXPECT_EQ(X509_V_OK, Verify(leaf_excl_ok.get(), {root_excl.get()}, {}, {}, 0)); // Excluded subtree "com": wildcard CN *.example.com is under "com". bssl::UniquePtr nc_excl_com = MakeNameConstraint(GEN_DNS, "com", /*excluded=*/true); ASSERT_TRUE(nc_excl_com); bssl::UniquePtr root_excl_com = MakeTestCert("Root3", "Root3", key.get(), /*is_ca=*/true); ASSERT_TRUE(root_excl_com); ASSERT_TRUE(X509_add1_ext_i2d(root_excl_com.get(), NID_name_constraints, nc_excl_com.get(), /*crit=*/1, /*flags=*/0)); ASSERT_TRUE(X509_sign(root_excl_com.get(), key.get(), EVP_sha256())); bssl::UniquePtr leaf_excl_com = MakeTestCert("Root3", "*.example.com", key.get(), /*is_ca=*/false); ASSERT_TRUE(leaf_excl_com); ASSERT_TRUE(X509_sign(leaf_excl_com.get(), key.get(), EVP_sha256())); EXPECT_EQ(X509_V_ERR_EXCLUDED_VIOLATION, Verify(leaf_excl_com.get(), {root_excl_com.get()}, {}, {}, 0)); // Non-ASCII multi-label CN under a constrained CA should be rejected. // Per RFC 6125, internationalized names should use punycode (A-label) form. bssl::UniquePtr leaf_utf8 = MakeTestCert("Root", "r\xc3\xa4ger.evil.com", key.get(), /*is_ca=*/false); ASSERT_TRUE(leaf_utf8); ASSERT_TRUE(X509_sign(leaf_utf8.get(), key.get(), EVP_sha256())); EXPECT_EQ(X509_V_ERR_UNSUPPORTED_NAME_SYNTAX, Verify(leaf_utf8.get(), {root.get()}, {}, {}, 0)); // Control character in multi-label CN should also be rejected. bssl::UniquePtr leaf_ctrl = MakeTestCert("Root", "foo\x01.evil.com", key.get(), /*is_ca=*/false); ASSERT_TRUE(leaf_ctrl); ASSERT_TRUE(X509_sign(leaf_ctrl.get(), key.get(), EVP_sha256())); EXPECT_EQ(X509_V_ERR_UNSUPPORTED_NAME_SYNTAX, Verify(leaf_ctrl.get(), {root.get()}, {}, {}, 0)); // Space in multi-label CN should also be rejected. bssl::UniquePtr leaf_space = MakeTestCert("Root", "foo .evil.com", key.get(), /*is_ca=*/false); ASSERT_TRUE(leaf_space); ASSERT_TRUE(X509_sign(leaf_space.get(), key.get(), EVP_sha256())); EXPECT_EQ(X509_V_ERR_UNSUPPORTED_NAME_SYNTAX, Verify(leaf_space.get(), {root.get()}, {}, {}, 0)); // The punycode equivalent should be checked normally against constraints. bssl::UniquePtr leaf_punycode = MakeTestCert("Root", "xn--rger-koa.evil.com", key.get(), /*is_ca=*/false); ASSERT_TRUE(leaf_punycode); ASSERT_TRUE(X509_sign(leaf_punycode.get(), key.get(), EVP_sha256())); EXPECT_EQ(X509_V_ERR_PERMITTED_VIOLATION, Verify(leaf_punycode.get(), {root.get()}, {}, {}, 0)); // Punycode CN inside the permitted subtree should pass. bssl::UniquePtr leaf_punycode_ok = MakeTestCert("Root", "xn--rger-koa.example.com", key.get(), /*is_ca=*/false); ASSERT_TRUE(leaf_punycode_ok); ASSERT_TRUE(X509_sign(leaf_punycode_ok.get(), key.get(), EVP_sha256())); EXPECT_EQ(X509_V_OK, Verify(leaf_punycode_ok.get(), {root.get()}, {}, {}, 0)); } TEST(X509Test, PrintGeneralName) { // TODO(https://crbug.com/boringssl/430): Add more tests. Also fix the // external projects that use this to extract the SAN list and unexport. bssl::UniquePtr gen = MakeGeneralName(GEN_DNS, "example.com"); ASSERT_TRUE(gen); bssl::UniquePtr values( i2v_GENERAL_NAME(nullptr, gen.get(), nullptr)); ASSERT_TRUE(values); ASSERT_EQ(1u, sk_CONF_VALUE_num(values.get())); const CONF_VALUE *value = sk_CONF_VALUE_value(values.get(), 0); EXPECT_STREQ(value->name, "DNS"); EXPECT_STREQ(value->value, "example.com"); } TEST(X509Test, TestPSS) { bssl::UniquePtr cert(CertFromPEM(kExamplePSSCert)); ASSERT_TRUE(cert); bssl::UniquePtr pkey(X509_get_pubkey(cert.get())); ASSERT_TRUE(pkey); ASSERT_TRUE(X509_verify(cert.get(), pkey.get())); } TEST(X509Test, TestRsaSsaPss) { bssl::UniquePtr cert(CertFromPEM(kExampleRsassaPssCert)); ASSERT_TRUE(cert); bssl::UniquePtr pkey(X509_get_pubkey(cert.get())); ASSERT_TRUE(pkey); ASSERT_TRUE(X509_verify(cert.get(), pkey.get())); } TEST(X509Test, TestPSSBadParameters) { bssl::UniquePtr cert(CertFromPEM(kBadPSSCertPEM)); ASSERT_TRUE(cert); bssl::UniquePtr pkey(X509_get_pubkey(cert.get())); ASSERT_TRUE(pkey); ASSERT_FALSE(X509_verify(cert.get(), pkey.get())); ERR_clear_error(); } TEST(X509Test, TestEd25519) { bssl::UniquePtr cert(CertFromPEM(kEd25519Cert)); ASSERT_TRUE(cert); bssl::UniquePtr pkey(X509_get_pubkey(cert.get())); ASSERT_TRUE(pkey); ASSERT_TRUE(X509_verify(cert.get(), pkey.get())); } TEST(X509Test, TestEd25519BadParameters) { bssl::UniquePtr cert(CertFromPEM(kEd25519CertNull)); ASSERT_TRUE(cert); bssl::UniquePtr pkey(X509_get_pubkey(cert.get())); ASSERT_TRUE(pkey); ASSERT_FALSE(X509_verify(cert.get(), pkey.get())); EXPECT_TRUE( ErrorEquals(ERR_get_error(), ERR_LIB_X509, X509_R_INVALID_PARAMETER)); ERR_clear_error(); } TEST(X509Test, TestX25519) { bssl::UniquePtr cert(CertFromPEM(kX25519Cert)); ASSERT_TRUE(cert); bssl::UniquePtr pkey(X509_get_pubkey(cert.get())); ASSERT_TRUE(pkey); EXPECT_EQ(EVP_PKEY_id(pkey.get()), EVP_PKEY_X25519); constexpr uint8_t kExpectedPublicValue[] = { 0x85, 0x20, 0xf0, 0x09, 0x89, 0x30, 0xa7, 0x54, 0x74, 0x8b, 0x7d, 0xdc, 0xb4, 0x3e, 0xf7, 0x5a, 0x0d, 0xbf, 0x3a, 0x0d, 0x26, 0x38, 0x1a, 0xf4, 0xeb, 0xa4, 0xa9, 0x8e, 0xaa, 0x9b, 0x4e, 0x6a, }; uint8_t public_value[sizeof(kExpectedPublicValue)]; size_t public_value_size = sizeof(public_value); ASSERT_TRUE(EVP_PKEY_get_raw_public_key(pkey.get(), public_value, &public_value_size)); EXPECT_EQ(Bytes(kExpectedPublicValue), Bytes(public_value, public_value_size)); } static bssl::UniquePtr ReencodeCertificate(X509 *cert) { uint8_t *der = nullptr; int len = i2d_X509(cert, &der); bssl::UniquePtr free_der(der); if (len <= 0) { return nullptr; } const uint8_t *inp = der; return bssl::UniquePtr(d2i_X509(nullptr, &inp, len)); } static bssl::UniquePtr ReencodeCRL(X509_CRL *crl) { uint8_t *der = nullptr; int len = i2d_X509_CRL(crl, &der); bssl::UniquePtr free_der(der); if (len <= 0) { return nullptr; } const uint8_t *inp = der; return bssl::UniquePtr(d2i_X509_CRL(nullptr, &inp, len)); } static bssl::UniquePtr ReencodeCSR(X509_REQ *req) { uint8_t *der = nullptr; int len = i2d_X509_REQ(req, &der); bssl::UniquePtr free_der(der); if (len <= 0) { return nullptr; } const uint8_t *inp = der; return bssl::UniquePtr(d2i_X509_REQ(nullptr, &inp, len)); } static bool SignatureRoundTrips(EVP_MD_CTX *md_ctx, EVP_PKEY *pkey) { // Make a certificate like signed with |md_ctx|'s settings.' bssl::UniquePtr cert(CertFromPEM(kLeafPEM)); if (!cert || !X509_sign_ctx(cert.get(), md_ctx)) { return false; } // Ensure that |pkey| may still be used to verify the resulting signature. All // settings in |md_ctx| must have been serialized appropriately. if (!X509_verify(cert.get(), pkey)) { return false; } // Re-encode the certificate. X509 objects contain a cached TBSCertificate // encoding and |X509_sign_ctx| should have dropped that cache. bssl::UniquePtr copy = ReencodeCertificate(cert.get()); return copy && X509_verify(copy.get(), pkey); } TEST(X509Test, RSASign) { bssl::UniquePtr pkey(PrivateKeyFromPEM(kRSAKey)); ASSERT_TRUE(pkey); // Test PKCS#1 v1.5. bssl::ScopedEVP_MD_CTX md_ctx; ASSERT_TRUE( EVP_DigestSignInit(md_ctx.get(), NULL, EVP_sha256(), NULL, pkey.get())); ASSERT_TRUE(SignatureRoundTrips(md_ctx.get(), pkey.get())); // Test RSA-PSS with custom parameters. md_ctx.Reset(); EVP_PKEY_CTX *pkey_ctx; ASSERT_TRUE(EVP_DigestSignInit(md_ctx.get(), &pkey_ctx, EVP_sha256(), NULL, pkey.get())); ASSERT_TRUE(EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING)); ASSERT_TRUE(EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, EVP_sha512())); ASSERT_TRUE(SignatureRoundTrips(md_ctx.get(), pkey.get())); // RSA-PSS with salt length matching hash length should work when passing in // -1 or the value explicitly. md_ctx.Reset(); ASSERT_TRUE(EVP_DigestSignInit(md_ctx.get(), &pkey_ctx, EVP_sha256(), NULL, pkey.get())); ASSERT_TRUE(EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING)); ASSERT_TRUE(EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, -1)); ASSERT_TRUE(SignatureRoundTrips(md_ctx.get(), pkey.get())); md_ctx.Reset(); ASSERT_TRUE(EVP_DigestSignInit(md_ctx.get(), &pkey_ctx, EVP_sha256(), NULL, pkey.get())); ASSERT_TRUE(EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING)); ASSERT_TRUE(EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, 32)); ASSERT_TRUE(SignatureRoundTrips(md_ctx.get(), pkey.get())); } // Test the APIs for signing a certificate, particularly whether they correctly // handle the TBSCertificate cache. TEST(X509Test, SignCertificate) { const int kSignatureNID = NID_sha384WithRSAEncryption; const EVP_MD *kSignatureHash = EVP_sha384(); bssl::UniquePtr pkey(PrivateKeyFromPEM(kRSAKey)); ASSERT_TRUE(pkey); bssl::UniquePtr algor(X509_ALGOR_new()); ASSERT_TRUE(algor); ASSERT_TRUE(X509_ALGOR_set0(algor.get(), OBJ_nid2obj(kSignatureNID), V_ASN1_NULL, nullptr)); // Test both signing with |X509_sign| and constructing a signature manually. for (bool sign_manual : {true, false}) { SCOPED_TRACE(sign_manual); // Test certificates made both from other certificates and |X509_new|, in // case there are bugs in filling in fields from different states. (Parsed // certificates contain a TBSCertificate cache, and |X509_new| initializes // fields based on complex ASN.1 template logic.) for (bool new_cert : {true, false}) { SCOPED_TRACE(new_cert); bssl::UniquePtr cert; if (new_cert) { cert.reset(X509_new()); ASSERT_TRUE(cert); // Fill in some fields for the certificate arbitrarily. EXPECT_TRUE(X509_set_version(cert.get(), X509_VERSION_3)); EXPECT_TRUE( ASN1_INTEGER_set_int64(X509_get_serialNumber(cert.get()), 1)); EXPECT_TRUE(X509_gmtime_adj(X509_getm_notBefore(cert.get()), 0)); EXPECT_TRUE( X509_gmtime_adj(X509_getm_notAfter(cert.get()), 60 * 60 * 24)); X509_NAME *subject = X509_get_subject_name(cert.get()); X509_NAME_add_entry_by_txt(subject, "CN", MBSTRING_ASC, reinterpret_cast("Test"), -1, -1, 0); EXPECT_TRUE(X509_set_issuer_name(cert.get(), subject)); EXPECT_TRUE(X509_set_pubkey(cert.get(), pkey.get())); } else { // Extract fields from a parsed certificate. cert = CertFromPEM(kLeafPEM); ASSERT_TRUE(cert); // We should test with a different algorithm from what is already in the // certificate. EXPECT_NE(kSignatureNID, X509_get_signature_nid(cert.get())); } if (sign_manual) { // Fill in the signature algorithm. ASSERT_TRUE(X509_set1_signature_algo(cert.get(), algor.get())); // Extract the TBSCertificiate. uint8_t *tbs_cert = nullptr; int tbs_cert_len = i2d_re_X509_tbs(cert.get(), &tbs_cert); bssl::UniquePtr free_tbs_cert(tbs_cert); ASSERT_GT(tbs_cert_len, 0); // Generate a signature externally and fill it in. bssl::ScopedEVP_MD_CTX md_ctx; ASSERT_TRUE(EVP_DigestSignInit(md_ctx.get(), nullptr, kSignatureHash, nullptr, pkey.get())); size_t sig_len; ASSERT_TRUE(EVP_DigestSign(md_ctx.get(), nullptr, &sig_len, tbs_cert, tbs_cert_len)); std::vector sig(sig_len); ASSERT_TRUE(EVP_DigestSign(md_ctx.get(), sig.data(), &sig_len, tbs_cert, tbs_cert_len)); sig.resize(sig_len); ASSERT_TRUE( X509_set1_signature_value(cert.get(), sig.data(), sig.size())); } else { int ret = X509_sign(cert.get(), pkey.get(), EVP_sha384()); ASSERT_GT(ret, 0); // |X509_sign| returns the length of the signature on success. const ASN1_BIT_STRING *sig; X509_get0_signature(&sig, /*out_alg=*/nullptr, cert.get()); EXPECT_EQ(ret, ASN1_STRING_length(sig)); } // Check the signature. EXPECT_TRUE(X509_verify(cert.get(), pkey.get())); // Re-encode the certificate. X509 objects contain a cached TBSCertificate // encoding and re-signing should have dropped that cache. bssl::UniquePtr copy = ReencodeCertificate(cert.get()); ASSERT_TRUE(copy); EXPECT_TRUE(X509_verify(copy.get(), pkey.get())); } } } // Test the APIs for signing a CRL, particularly whether they correctly handle // the TBSCertList cache. TEST(X509Test, SignCRL) { const int kSignatureNID = NID_sha384WithRSAEncryption; const EVP_MD *kSignatureHash = EVP_sha384(); bssl::UniquePtr pkey(PrivateKeyFromPEM(kRSAKey)); ASSERT_TRUE(pkey); bssl::UniquePtr algor(X509_ALGOR_new()); ASSERT_TRUE(algor); ASSERT_TRUE(X509_ALGOR_set0(algor.get(), OBJ_nid2obj(kSignatureNID), V_ASN1_NULL, nullptr)); // Test both signing with |X509_CRL_sign| and constructing a signature // manually. for (bool sign_manual : {true, false}) { SCOPED_TRACE(sign_manual); // Test CRLs made both from other CRLs and |X509_CRL_new|, in case there are // bugs in filling in fields from different states. (Parsed CRLs contain a // TBSCertList cache, and |X509_CRL_new| initializes fields based on complex // ASN.1 template logic.) for (bool new_crl : {true, false}) { SCOPED_TRACE(new_crl); bssl::UniquePtr crl; if (new_crl) { crl.reset(X509_CRL_new()); ASSERT_TRUE(crl); // Fill in some fields for the certificate arbitrarily. ASSERT_TRUE(X509_CRL_set_version(crl.get(), X509_CRL_VERSION_2)); bssl::UniquePtr last_update(ASN1_TIME_new()); ASSERT_TRUE(last_update); ASSERT_TRUE(ASN1_TIME_set_posix(last_update.get(), kReferenceTime)); ASSERT_TRUE(X509_CRL_set1_lastUpdate(crl.get(), last_update.get())); bssl::UniquePtr issuer(X509_NAME_new()); ASSERT_TRUE(issuer); ASSERT_TRUE(X509_NAME_add_entry_by_txt( issuer.get(), "CN", MBSTRING_ASC, reinterpret_cast("Test"), -1, -1, 0)); EXPECT_TRUE(X509_CRL_set_issuer_name(crl.get(), issuer.get())); } else { // Extract fields from a parsed CRL. crl = CRLFromPEM(kBasicCRL); ASSERT_TRUE(crl); // We should test with a different algorithm from what is already in the // CRL. EXPECT_NE(kSignatureNID, X509_CRL_get_signature_nid(crl.get())); } if (sign_manual) { // Fill in the signature algorithm. ASSERT_TRUE(X509_CRL_set1_signature_algo(crl.get(), algor.get())); // Extract the TBSCertList. uint8_t *tbs = nullptr; int tbs_len = i2d_re_X509_CRL_tbs(crl.get(), &tbs); bssl::UniquePtr free_tbs(tbs); ASSERT_GT(tbs_len, 0); // Generate a signature externally and fill it in. bssl::ScopedEVP_MD_CTX md_ctx; ASSERT_TRUE(EVP_DigestSignInit(md_ctx.get(), nullptr, kSignatureHash, nullptr, pkey.get())); size_t sig_len; ASSERT_TRUE( EVP_DigestSign(md_ctx.get(), nullptr, &sig_len, tbs, tbs_len)); std::vector sig(sig_len); ASSERT_TRUE( EVP_DigestSign(md_ctx.get(), sig.data(), &sig_len, tbs, tbs_len)); sig.resize(sig_len); ASSERT_TRUE( X509_CRL_set1_signature_value(crl.get(), sig.data(), sig.size())); } else { ASSERT_TRUE(X509_CRL_sign(crl.get(), pkey.get(), EVP_sha384())); } // Check the signature. EXPECT_TRUE(X509_CRL_verify(crl.get(), pkey.get())); // Re-encode the CRL. X509_CRL objects contain a cached TBSCertList // encoding and re-signing should have dropped that cache. bssl::UniquePtr copy = ReencodeCRL(crl.get()); ASSERT_TRUE(copy); EXPECT_TRUE(X509_CRL_verify(copy.get(), pkey.get())); } } } static const char kTestCSR[] = R"( -----BEGIN CERTIFICATE REQUEST----- MIICVDCCATwCAQAwDzENMAsGA1UEAwwEVGVzdDCCASIwDQYJKoZIhvcNAQEBBQAD ggEPADCCAQoCggEBAK+UkwcNJfRhg5MzIQzxDdrqF9a76jNoK/BwCflKYFX7QEqf rsLkI0J+m60fUD0v50LnKwbGoMFKZ1R/3cBNXLcdXb7ZP/ZJ7A7QwUrL+W9n3sov U8/HSU3rHbg+V5L6egSZYuhDHoXKi33HDOL4DVUzMoU1ykmP4QwF1wUXHLqvqjbU teQBoJWO53/XOGQu8bX04muCFnHZWT2Ubqol70JwPU2PqDU1EBlgUFO79NEmflev b++H8tu42UCDUZXD9k5weftjneO4cud3IsUX6mDsyf7k1e2mxsS4TSZsJcG0iLBX HSr1udXazQsjlAKjJkoI3cWshF6LGRWssAtbGiUCAwEAAaAAMA0GCSqGSIb3DQEB CwUAA4IBAQAniYZL+amXu+wED+AwBZz+zPuxY16bveF27/gxcs/jq6hVpEQvMxfO jfAGeDRtAU7DMxdJPjvWwwNe2JlTMSRoVDMYaiKqB5yxIYa2cjQvp7swSxuFJwbG T8h7/d7yqem6NYYzgYsNOE5QJyNu/PsIEdvzrysfDAnREiT2ituOcVpiqUZq3DTj NaTd1GNG3j4E87ZUmayUJD5nH91UNzKvJbpfo+bLyfy73x4QeU0SRitsZmbSBTAi s9+zmCErxzMlAdJHGzxPkXmtvBnUzGRIsAD5h/DjYNUmQJkB60yplt84ZgThhx54 rZGEJG3+X9OuhczVKGJyg+3gU7oDbecc -----END CERTIFICATE REQUEST----- )"; // Test the APIs for signing a CSR, particularly whether they correctly handle // the CertificationRequestInfo cache. TEST(X509Test, SignCSR) { const int kSignatureNID = NID_sha384WithRSAEncryption; const EVP_MD *kSignatureHash = EVP_sha384(); bssl::UniquePtr pkey(PrivateKeyFromPEM(kRSAKey)); ASSERT_TRUE(pkey); bssl::UniquePtr algor(X509_ALGOR_new()); ASSERT_TRUE(algor); ASSERT_TRUE(X509_ALGOR_set0(algor.get(), OBJ_nid2obj(kSignatureNID), V_ASN1_NULL, nullptr)); // Test both signing with |X509_REQ_sign| and constructing a signature // manually. for (bool sign_manual : {true, false}) { SCOPED_TRACE(sign_manual); // Test CSRs made both from other CSRs and |X509_REQ_new|, in case there are // bugs in filling in fields from different states. (Parsed CSRs contain a // CertificationRequestInfo cache, and |X509_REQ_new| initializes fields // based on complex ASN.1 template logic.) for (bool new_csr : {true, false}) { SCOPED_TRACE(new_csr); bssl::UniquePtr csr; if (new_csr) { csr.reset(X509_REQ_new()); ASSERT_TRUE(csr); bssl::UniquePtr subject(X509_NAME_new()); ASSERT_TRUE(subject); ASSERT_TRUE(X509_NAME_add_entry_by_txt( subject.get(), "CN", MBSTRING_ASC, reinterpret_cast("New CSR"), -1, -1, 0)); EXPECT_TRUE(X509_REQ_set_subject_name(csr.get(), subject.get())); } else { // Extract fields from a parsed CSR. csr = CSRFromPEM(kTestCSR); ASSERT_TRUE(csr); } // Override the public key from the CSR unconditionally. Unlike // certificates and CRLs, CSRs do not contain a signed copy of the // signature algorithm, so we use a different field to confirm // |i2d_re_X509_REQ_tbs| clears the cache as expected. EXPECT_TRUE(X509_REQ_set_pubkey(csr.get(), pkey.get())); if (sign_manual) { // Fill in the signature algorithm. ASSERT_TRUE(X509_REQ_set1_signature_algo(csr.get(), algor.get())); // Extract the CertificationRequestInfo. uint8_t *tbs = nullptr; int tbs_len = i2d_re_X509_REQ_tbs(csr.get(), &tbs); bssl::UniquePtr free_tbs(tbs); ASSERT_GT(tbs_len, 0); // Generate a signature externally and fill it in. bssl::ScopedEVP_MD_CTX md_ctx; ASSERT_TRUE(EVP_DigestSignInit(md_ctx.get(), nullptr, kSignatureHash, nullptr, pkey.get())); size_t sig_len; ASSERT_TRUE( EVP_DigestSign(md_ctx.get(), nullptr, &sig_len, tbs, tbs_len)); std::vector sig(sig_len); ASSERT_TRUE( EVP_DigestSign(md_ctx.get(), sig.data(), &sig_len, tbs, tbs_len)); sig.resize(sig_len); ASSERT_TRUE( X509_REQ_set1_signature_value(csr.get(), sig.data(), sig.size())); } else { ASSERT_TRUE(X509_REQ_sign(csr.get(), pkey.get(), EVP_sha384())); } // Check the signature. EXPECT_TRUE(X509_REQ_verify(csr.get(), pkey.get())); // Re-encode the CSR. X509_REQ objects contain a cached // CertificationRequestInfo encoding and re-signing should have dropped // that cache. bssl::UniquePtr copy = ReencodeCSR(csr.get()); ASSERT_TRUE(copy); EXPECT_TRUE(X509_REQ_verify(copy.get(), pkey.get())); // Check the signature was over the new public key. bssl::UniquePtr copy_pubkey(X509_REQ_get_pubkey(copy.get())); ASSERT_TRUE(copy_pubkey); EXPECT_EQ(1, EVP_PKEY_cmp(pkey.get(), copy_pubkey.get())); // Check again specifically with |X509_REQ_check_private_key|. EXPECT_TRUE(X509_REQ_check_private_key(csr.get(), pkey.get())); } } } TEST(X509Test, PqdsaCSR) { for (int val: std::vector{44, 65, 87}) { std::ostringstream path; path << "crypto/x509/test/csr-mldsa" << val << ".pem"; bssl::UniquePtr csr = CSRFromPEM(GetTestData(path.str().c_str()).c_str()); ASSERT_TRUE(csr); // Test signature verification EVP_PKEY* pub_key = X509_REQ_get0_pubkey(csr.get()); ASSERT_TRUE(pub_key); ASSERT_EQ(1, X509_REQ_verify(csr.get(), pub_key)); // Test version EXPECT_EQ(X509_REQ_VERSION_1, X509_REQ_get_version(csr.get())); // Test subject name - verify "Generic" is parsed correctly X509_NAME *subject = X509_REQ_get_subject_name(csr.get()); ASSERT_TRUE(subject); char *subject_str = X509_NAME_oneline(subject, nullptr, 0); ASSERT_TRUE(subject_str); EXPECT_STREQ("/CN=Generic", subject_str); OPENSSL_free(subject_str); // Test signature algorithm NID int sig_nid = X509_REQ_get_signature_nid(csr.get()); EXPECT_NE(NID_undef, sig_nid); switch (val) { case 44: EXPECT_EQ(NID_MLDSA44, sig_nid); break; case 65: EXPECT_EQ(NID_MLDSA65, sig_nid); break; case 87: EXPECT_EQ(NID_MLDSA87, sig_nid); break; default: ADD_FAILURE() << "Invalid NID"; } // Test signature and algorithm retrieval const ASN1_BIT_STRING *sig = nullptr; const X509_ALGOR *alg = nullptr; X509_REQ_get0_signature(csr.get(), &sig, &alg); ASSERT_TRUE(sig); ASSERT_TRUE(alg); // Test attribute count int attr_count = X509_REQ_get_attr_count(csr.get()); EXPECT_GE(attr_count, 0); // Test extensions (may be NULL if no extensions present) bssl::UniquePtr exts(X509_REQ_get_extensions(csr.get())); // Extensions are optional, so we just verify the function doesn't crash } } TEST(X509Test, Ed25519Sign) { uint8_t pub_bytes[32], priv_bytes[64]; ED25519_keypair(pub_bytes, priv_bytes); bssl::UniquePtr pub( EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, nullptr, pub_bytes, 32)); ASSERT_TRUE(pub); bssl::UniquePtr priv( EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, nullptr, priv_bytes, 32)); ASSERT_TRUE(priv); bssl::ScopedEVP_MD_CTX md_ctx; ASSERT_TRUE( EVP_DigestSignInit(md_ctx.get(), nullptr, nullptr, nullptr, priv.get())); ASSERT_TRUE(SignatureRoundTrips(md_ctx.get(), pub.get())); } TEST(X509Test, MLDSA65SignVerifyCert) { // This test generates a MLDSA65 keypair, generates and signs a // certificate, then verifies the certificate's signature. // Generate mldsa key bssl::UniquePtr ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_PQDSA, nullptr)); ASSERT_TRUE(ctx); ASSERT_TRUE(EVP_PKEY_CTX_pqdsa_set_params(ctx.get(), NID_MLDSA65)); ASSERT_TRUE(EVP_PKEY_keygen_init(ctx.get())); EVP_PKEY *raw = nullptr; ASSERT_TRUE(EVP_PKEY_keygen(ctx.get(), &raw)); bssl::UniquePtr pkey(raw); ctx.reset(EVP_PKEY_CTX_new(pkey.get(), nullptr)); bssl::UniquePtr leaf = MakeTestCert("Intermediate", "Leaf", pkey.get(), /*is_ca=*/false); ASSERT_TRUE(leaf); bssl::ScopedEVP_MD_CTX md_ctx; EVP_DigestSignInit(md_ctx.get(), nullptr, nullptr, nullptr, pkey.get()); ASSERT_TRUE(X509_sign_ctx(leaf.get(), md_ctx.get())); ASSERT_TRUE(X509_verify(leaf.get(), pkey.get())); } TEST(X509Test, TestMLDSA65) { // This test decodes a MLDSA65 certificate from the PEM encoding, // extracts the public key, and then verifies the certificate. bssl::UniquePtr cert(CertFromPEM(kMLDSA65Cert)); ASSERT_TRUE(cert); bssl::UniquePtr pkey(X509_get_pubkey(cert.get())); ASSERT_TRUE(pkey); ASSERT_TRUE(X509_verify(cert.get(), pkey.get())); } TEST(X509Test, TestBadSigAlgMLDSA65) { // This test generates a MLDSA65 certificate from the PEM encoding // kMLDSA65CertNull that has an explicit NULL in the signature algorithm. // After extracting the public key, verification should fail. bssl::UniquePtr cert(CertFromPEM(kMLDSA65CertNull)); ASSERT_TRUE(cert); bssl::UniquePtr pkey(X509_get_pubkey(cert.get())); ASSERT_TRUE(pkey); ASSERT_FALSE(X509_verify(cert.get(), pkey.get())); uint32_t err = ERR_get_error(); ASSERT_EQ(ERR_LIB_X509, ERR_GET_LIB(err)); ASSERT_EQ(X509_R_SIGNATURE_ALGORITHM_MISMATCH, ERR_GET_REASON(err)); ERR_clear_error(); } TEST(X509Test, TestBadParamsMLDSA65) { // This test generates a MLDSA65 certificate from the PEM encoding // kMLDSA65CertParam that has an explicit NULL in the parameters field. // After extracting the public key, verification should fail. bssl::UniquePtr cert(CertFromPEM(kMLDSA65CertParam)); ASSERT_TRUE(cert); bssl::UniquePtr pkey(X509_get_pubkey(cert.get())); ASSERT_TRUE(pkey); ASSERT_FALSE(X509_verify(cert.get(), pkey.get())); uint32_t err = ERR_get_error(); ASSERT_EQ(ERR_LIB_X509, ERR_GET_LIB(err)); ASSERT_EQ(X509_R_INVALID_PARAMETER, ERR_GET_REASON(err)); ERR_clear_error(); } static bool PEMToDER(bssl::UniquePtr *out, size_t *out_len, const char *pem) { bssl::UniquePtr bio(BIO_new_mem_buf(pem, strlen(pem))); if (!bio) { return false; } char *name, *header; uint8_t *data; long data_len; if (!PEM_read_bio(bio.get(), &name, &header, &data, &data_len)) { fprintf(stderr, "failed to read PEM data.\n"); return false; } OPENSSL_free(name); OPENSSL_free(header); out->reset(data); *out_len = data_len; return true; } TEST(X509Test, TestFromBuffer) { size_t data_len; bssl::UniquePtr data; ASSERT_TRUE(PEMToDER(&data, &data_len, kRootCAPEM)); bssl::UniquePtr buf( CRYPTO_BUFFER_new(data.get(), data_len, nullptr)); ASSERT_TRUE(buf); bssl::UniquePtr root(X509_parse_from_buffer(buf.get())); ASSERT_TRUE(root); const uint8_t *enc_pointer = root->cert_info->enc.enc; const uint8_t *buf_pointer = CRYPTO_BUFFER_data(buf.get()); ASSERT_GE(enc_pointer, buf_pointer); ASSERT_LT(enc_pointer, buf_pointer + CRYPTO_BUFFER_len(buf.get())); buf.reset(); /* This ensures the X509 took a reference to |buf|, otherwise this will be a * reference to free memory and ASAN should notice. */ ASSERT_EQ(0x30, enc_pointer[0]); } TEST(X509Test, TestFromBufferWithTrailingData) { size_t data_len; bssl::UniquePtr data; ASSERT_TRUE(PEMToDER(&data, &data_len, kRootCAPEM)); std::unique_ptr trailing_data(new uint8_t[data_len + 1]); OPENSSL_memcpy(trailing_data.get(), data.get(), data_len); bssl::UniquePtr buf_trailing_data( CRYPTO_BUFFER_new(trailing_data.get(), data_len + 1, nullptr)); ASSERT_TRUE(buf_trailing_data); bssl::UniquePtr root_trailing_data( X509_parse_from_buffer(buf_trailing_data.get())); ASSERT_FALSE(root_trailing_data); } TEST(X509Test, TestFromBufferModified) { size_t data_len; bssl::UniquePtr data; ASSERT_TRUE(PEMToDER(&data, &data_len, kRootCAPEM)); bssl::UniquePtr buf( CRYPTO_BUFFER_new(data.get(), data_len, nullptr)); ASSERT_TRUE(buf); bssl::UniquePtr root(X509_parse_from_buffer(buf.get())); ASSERT_TRUE(root); bssl::UniquePtr fourty_two(ASN1_INTEGER_new()); ASN1_INTEGER_set_int64(fourty_two.get(), 42); X509_set_serialNumber(root.get(), fourty_two.get()); ASSERT_EQ(static_cast(data_len), i2d_X509(root.get(), nullptr)); // Re-encode the TBSCertificate. i2d_re_X509_tbs(root.get(), nullptr); ASSERT_NE(static_cast(data_len), i2d_X509(root.get(), nullptr)); } TEST(X509Test, TestFromBufferReused) { size_t data_len; bssl::UniquePtr data; ASSERT_TRUE(PEMToDER(&data, &data_len, kRootCAPEM)); bssl::UniquePtr buf( CRYPTO_BUFFER_new(data.get(), data_len, nullptr)); ASSERT_TRUE(buf); bssl::UniquePtr root(X509_parse_from_buffer(buf.get())); ASSERT_TRUE(root); size_t data2_len; bssl::UniquePtr data2; ASSERT_TRUE(PEMToDER(&data2, &data2_len, kLeafPEM)); X509 *x509p = root.get(); const uint8_t *inp = data2.get(); X509 *ret = d2i_X509(&x509p, &inp, data2_len); ASSERT_EQ(root.get(), ret); ASSERT_EQ(nullptr, root->buf); // Free |data2| and ensure that |root| took its own copy. Otherwise the // following will trigger a use-after-free. data2.reset(); uint8_t *i2d = nullptr; int i2d_len = i2d_X509(root.get(), &i2d); ASSERT_GE(i2d_len, 0); bssl::UniquePtr i2d_storage(i2d); ASSERT_TRUE(PEMToDER(&data2, &data2_len, kLeafPEM)); ASSERT_EQ(static_cast(data2_len), i2d_len); ASSERT_EQ(0, OPENSSL_memcmp(data2.get(), i2d, i2d_len)); ASSERT_EQ(nullptr, root->buf); } TEST(X509Test, TestFailedParseFromBuffer) { static const uint8_t kNonsense[] = {1, 2, 3, 4, 5}; bssl::UniquePtr buf( CRYPTO_BUFFER_new(kNonsense, sizeof(kNonsense), nullptr)); ASSERT_TRUE(buf); bssl::UniquePtr cert(X509_parse_from_buffer(buf.get())); ASSERT_FALSE(cert); ERR_clear_error(); // Test a buffer with trailing data. size_t data_len; bssl::UniquePtr data; ASSERT_TRUE(PEMToDER(&data, &data_len, kRootCAPEM)); std::unique_ptr data_with_trailing_byte(new uint8_t[data_len + 1]); OPENSSL_memcpy(data_with_trailing_byte.get(), data.get(), data_len); data_with_trailing_byte[data_len] = 0; bssl::UniquePtr buf_with_trailing_byte( CRYPTO_BUFFER_new(data_with_trailing_byte.get(), data_len + 1, nullptr)); ASSERT_TRUE(buf_with_trailing_byte); bssl::UniquePtr root( X509_parse_from_buffer(buf_with_trailing_byte.get())); ASSERT_FALSE(root); ERR_clear_error(); } TEST(X509Test, TestPrintUTCTIME) { static const struct { const char *val, *want; } asn1_utctime_tests[] = { {"", "Bad time value"}, // Correct RFC 5280 form. Test years < 2000 and > 2000. {"090303125425Z", "Mar 3 12:54:25 2009 GMT"}, {"900303125425Z", "Mar 3 12:54:25 1990 GMT"}, {"000303125425Z", "Mar 3 12:54:25 2000 GMT"}, // Correct form, bad values. {"000000000000Z", "Bad time value"}, {"999999999999Z", "Bad time value"}, // Missing components. {"090303125425", "Bad time value"}, {"9003031254", "Bad time value"}, {"9003031254Z", "Bad time value"}, // GENERALIZEDTIME confused for UTCTIME. {"20090303125425Z", "Bad time value"}, // Legal ASN.1, but not legal RFC 5280. {"9003031254+0800", "Bad time value"}, {"9003031254-0800", "Bad time value"}, // Trailing garbage. {"9003031254Z ", "Bad time value"}, }; for (auto t : asn1_utctime_tests) { SCOPED_TRACE(t.val); bssl::UniquePtr tm(ASN1_UTCTIME_new()); ASSERT_TRUE(tm); bssl::UniquePtr bio(BIO_new(BIO_s_mem())); ASSERT_TRUE(bio); // Use this instead of ASN1_UTCTIME_set() because some callers get // type-confused and pass ASN1_GENERALIZEDTIME to ASN1_UTCTIME_print(). // ASN1_UTCTIME_set_string() is stricter, and would reject the inputs in // question. ASSERT_TRUE(ASN1_STRING_set(tm.get(), t.val, strlen(t.val))); const int ok = ASN1_UTCTIME_print(bio.get(), tm.get()); const uint8_t *contents; size_t len; ASSERT_TRUE(BIO_mem_contents(bio.get(), &contents, &len)); EXPECT_EQ(ok, (strcmp(t.want, "Bad time value") != 0) ? 1 : 0); EXPECT_EQ(t.want, std::string(reinterpret_cast(contents), len)); } } TEST(X509Test, PrettyPrintIntegers) { static const char *kTests[] = { // Small numbers are pretty-printed in decimal. "0", "-1", "1", "42", "-42", "256", "-256", "4886718345", "-4886718345", // Large numbers are pretty-printed in hex to avoid taking quadratic time. "0x0123456789012345678901234567890123", "-0x0123456789012345678901234567890123", }; for (const char *in : kTests) { SCOPED_TRACE(in); BIGNUM *bn = nullptr; ASSERT_TRUE(BN_asc2bn(&bn, in)); bssl::UniquePtr free_bn(bn); { bssl::UniquePtr asn1(BN_to_ASN1_INTEGER(bn, nullptr)); ASSERT_TRUE(asn1); bssl::UniquePtr out(i2s_ASN1_INTEGER(nullptr, asn1.get())); ASSERT_TRUE(out.get()); EXPECT_STREQ(in, out.get()); } { bssl::UniquePtr asn1(BN_to_ASN1_ENUMERATED(bn, nullptr)); ASSERT_TRUE(asn1); bssl::UniquePtr out(i2s_ASN1_ENUMERATED(nullptr, asn1.get())); ASSERT_TRUE(out.get()); EXPECT_STREQ(in, out.get()); } } } TEST(X509Test, X509AlgorSetMd) { bssl::UniquePtr alg(X509_ALGOR_new()); ASSERT_TRUE(alg); EXPECT_TRUE(X509_ALGOR_set_md(alg.get(), EVP_sha256())); const ASN1_OBJECT *obj; const void *pval; int ptype = 0; X509_ALGOR_get0(&obj, &ptype, &pval, alg.get()); EXPECT_TRUE(obj); EXPECT_EQ(OBJ_obj2nid(obj), NID_sha256); EXPECT_EQ(ptype, V_ASN1_NULL); // OpenSSL has V_ASN1_UNDEF EXPECT_EQ(pval, nullptr); EXPECT_TRUE(X509_ALGOR_set_md(alg.get(), EVP_md5())); X509_ALGOR_get0(&obj, &ptype, &pval, alg.get()); EXPECT_EQ(OBJ_obj2nid(obj), NID_md5); EXPECT_EQ(ptype, V_ASN1_NULL); EXPECT_EQ(pval, nullptr); } TEST(X509Test, X509NameSet) { bssl::UniquePtr name(X509_NAME_new()); ASSERT_TRUE(name); EXPECT_TRUE(X509_NAME_add_entry_by_txt( name.get(), "C", MBSTRING_ASC, reinterpret_cast("US"), -1, -1, 0)); EXPECT_EQ(X509_NAME_entry_count(name.get()), 1); EXPECT_TRUE(X509_NAME_add_entry_by_txt( name.get(), "C", MBSTRING_ASC, reinterpret_cast("CA"), -1, -1, 0)); EXPECT_EQ(X509_NAME_entry_count(name.get()), 2); EXPECT_TRUE(X509_NAME_add_entry_by_txt( name.get(), "C", MBSTRING_ASC, reinterpret_cast("UK"), -1, -1, 0)); EXPECT_EQ(X509_NAME_entry_count(name.get()), 3); EXPECT_TRUE(X509_NAME_add_entry_by_txt( name.get(), "C", MBSTRING_ASC, reinterpret_cast("JP"), -1, 1, 0)); EXPECT_EQ(X509_NAME_entry_count(name.get()), 4); // Check that the correct entries get incremented when inserting new entry. EXPECT_EQ(X509_NAME_ENTRY_set(X509_NAME_get_entry(name.get(), 1)), 1); EXPECT_EQ(X509_NAME_ENTRY_set(X509_NAME_get_entry(name.get(), 2)), 2); } // Tests that |X509_NAME_hash| and |X509_NAME_hash_old|'s values never change. // These functions figure into |X509_LOOKUP_hash_dir|'s on-disk format, so they // must remain stable. In particular, if we ever remove name canonicalization, // we'll need to preserve it for |X509_NAME_hash|. TEST(X509Test, NameHash) { struct { std::vector name_der; uint32_t hash; uint32_t hash_old; } kTests[] = { // SEQUENCE { // SET { // SEQUENCE { // # commonName // OBJECT_IDENTIFIER { 2.5.4.3 } // UTF8String { "Test Name" } // } // } // } {{0x30, 0x14, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x09, 0x54, 0x65, 0x73, 0x74, 0x20, 0x4e, 0x61, 0x6d, 0x65}, 0xc90fba01, 0x8c0d4fea}, // This name canonicalizes to the same value, with OpenSSL's algorithm, as // the above input, so |hash| matches. |hash_old| doesn't use // canonicalization and does not match. // // SEQUENCE { // SET { // SEQUENCE { // # commonName // OBJECT_IDENTIFIER { 2.5.4.3 } // BMPString { // u"\x09\n\x0b\x0c\x0d tEST\x09\n\x0b\x0c\x0d " // u"\x09\n\x0b\x0c\x0d nAME\x09\n\x0b\x0c\x0d " // } // } // } // } {{0x30, 0x4b, 0x31, 0x49, 0x30, 0x47, 0x06, 0x03, 0x55, 0x04, 0x03, 0x1e, 0x40, 0x00, 0x09, 0x00, 0x0a, 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x0d, 0x00, 0x20, 0x00, 0x74, 0x00, 0x45, 0x00, 0x53, 0x00, 0x54, 0x00, 0x09, 0x00, 0x0a, 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x0d, 0x00, 0x20, 0x00, 0x09, 0x00, 0x0a, 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x0d, 0x00, 0x20, 0x00, 0x6e, 0x00, 0x41, 0x00, 0x4d, 0x00, 0x45, 0x00, 0x09, 0x00, 0x0a, 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x0d, 0x00, 0x20}, 0xc90fba01, 0xbe2dd8c8}, }; for (const auto &t : kTests) { SCOPED_TRACE(Bytes(t.name_der)); const uint8_t *der = t.name_der.data(); bssl::UniquePtr name( d2i_X509_NAME(nullptr, &der, t.name_der.size())); ASSERT_TRUE(name); EXPECT_EQ(t.hash, X509_NAME_hash(name.get())); EXPECT_EQ(t.hash_old, X509_NAME_hash_old(name.get())); } } TEST(X509Test, NoBasicConstraintsCertSign) { bssl::UniquePtr root(CertFromPEM(kSANTypesRoot)); bssl::UniquePtr intermediate( CertFromPEM(kNoBasicConstraintsCertSignIntermediate)); bssl::UniquePtr leaf(CertFromPEM(kNoBasicConstraintsCertSignLeaf)); ASSERT_TRUE(root); ASSERT_TRUE(intermediate); ASSERT_TRUE(leaf); // The intermediate has keyUsage certSign, but is not marked as a CA in the // basicConstraints. EXPECT_EQ(X509_V_ERR_INVALID_CA, Verify(leaf.get(), {root.get()}, {intermediate.get()}, {}, 0)); // |X509_check_purpose| with |X509_PURPOSE_ANY| and purpose -1 do not check // basicConstraints, but other purpose types do. (This is redundant with the // actual basicConstraints check, but |X509_check_purpose| is public API.) EXPECT_TRUE(X509_check_purpose(intermediate.get(), -1, /*ca=*/1)); EXPECT_TRUE( X509_check_purpose(intermediate.get(), X509_PURPOSE_ANY, /*ca=*/1)); EXPECT_FALSE(X509_check_purpose(intermediate.get(), X509_PURPOSE_SSL_SERVER, /*ca=*/1)); } TEST(X509Test, NoBasicConstraintsNetscapeCA) { bssl::UniquePtr root(CertFromPEM(kSANTypesRoot)); bssl::UniquePtr intermediate( CertFromPEM(kNoBasicConstraintsNetscapeCAIntermediate)); bssl::UniquePtr leaf(CertFromPEM(kNoBasicConstraintsNetscapeCALeaf)); ASSERT_TRUE(root); ASSERT_TRUE(intermediate); ASSERT_TRUE(leaf); // The intermediate has a Netscape certificate type of "SSL CA", but is not // marked as a CA in the basicConstraints. EXPECT_EQ(X509_V_ERR_INVALID_CA, Verify(leaf.get(), {root.get()}, {intermediate.get()}, {}, 0)); } TEST(X509Test, MismatchAlgorithms) { bssl::UniquePtr cert(CertFromPEM(kSelfSignedMismatchAlgorithms)); ASSERT_TRUE(cert); bssl::UniquePtr pkey(X509_get_pubkey(cert.get())); ASSERT_TRUE(pkey); EXPECT_FALSE(X509_verify(cert.get(), pkey.get())); EXPECT_TRUE(ErrorEquals(ERR_get_error(), ERR_LIB_X509, X509_R_SIGNATURE_ALGORITHM_MISMATCH)); } TEST(X509Test, PEMX509Info) { std::string cert = kRootCAPEM; auto cert_obj = CertFromPEM(kRootCAPEM); ASSERT_TRUE(cert_obj); std::string rsa = kRSAKey; auto rsa_obj = PrivateKeyFromPEM(kRSAKey); ASSERT_TRUE(rsa_obj); std::string crl = kBasicCRL; auto crl_obj = CRLFromPEM(kBasicCRL); ASSERT_TRUE(crl_obj); std::string unknown = "-----BEGIN UNKNOWN-----\n" "AAAA\n" "-----END UNKNOWN-----\n"; std::string invalid = "-----BEGIN CERTIFICATE-----\n" "AAAA\n" "-----END CERTIFICATE-----\n"; // Each X509_INFO contains at most one certificate, CRL, etc. The format // creates a new X509_INFO when a repeated type is seen. std::string pem = // The first few entries have one of everything in different orders. cert + rsa + crl + rsa + crl + cert + // Unknown types are ignored. crl + unknown + cert + rsa + // Seeing a new certificate starts a new entry, so now we have a bunch of // certificate-only entries. cert + cert + cert + // The key folds into the certificate's entry. cert + rsa + // Doubled keys also start new entries. rsa + rsa + rsa + rsa + crl + // As do CRLs. crl + crl; const struct ExpectedInfo { const X509 *cert; const EVP_PKEY *key; const X509_CRL *crl; } kExpected[] = { {cert_obj.get(), rsa_obj.get(), crl_obj.get()}, {cert_obj.get(), rsa_obj.get(), crl_obj.get()}, {cert_obj.get(), rsa_obj.get(), crl_obj.get()}, {cert_obj.get(), nullptr, nullptr}, {cert_obj.get(), nullptr, nullptr}, {cert_obj.get(), nullptr, nullptr}, {cert_obj.get(), rsa_obj.get(), nullptr}, {nullptr, rsa_obj.get(), nullptr}, {nullptr, rsa_obj.get(), nullptr}, {nullptr, rsa_obj.get(), nullptr}, {nullptr, rsa_obj.get(), crl_obj.get()}, {nullptr, nullptr, crl_obj.get()}, {nullptr, nullptr, crl_obj.get()}, }; auto check_info = [](const ExpectedInfo *expected, const X509_INFO *info) { if (expected->cert != nullptr) { EXPECT_EQ(0, X509_cmp(expected->cert, info->x509)); } else { EXPECT_EQ(nullptr, info->x509); } if (expected->crl != nullptr) { EXPECT_EQ(0, X509_CRL_cmp(expected->crl, info->crl)); } else { EXPECT_EQ(nullptr, info->crl); } if (expected->key != nullptr) { ASSERT_NE(nullptr, info->x_pkey); // EVP_PKEY_cmp returns one if the keys are equal. EXPECT_EQ(1, EVP_PKEY_cmp(expected->key, info->x_pkey->dec_pkey)); } else { EXPECT_EQ(nullptr, info->x_pkey); } }; bssl::UniquePtr bio(BIO_new_mem_buf(pem.data(), pem.size())); ASSERT_TRUE(bio); bssl::UniquePtr infos( PEM_X509_INFO_read_bio(bio.get(), nullptr, nullptr, nullptr)); ASSERT_TRUE(infos); ASSERT_EQ(OPENSSL_ARRAY_SIZE(kExpected), sk_X509_INFO_num(infos.get())); for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(kExpected); i++) { SCOPED_TRACE(i); check_info(&kExpected[i], sk_X509_INFO_value(infos.get(), i)); } // Passing an existing stack appends to it. bio.reset(BIO_new_mem_buf(pem.data(), pem.size())); ASSERT_TRUE(bio); ASSERT_EQ(infos.get(), PEM_X509_INFO_read_bio(bio.get(), infos.get(), nullptr, nullptr)); ASSERT_EQ(2 * OPENSSL_ARRAY_SIZE(kExpected), sk_X509_INFO_num(infos.get())); for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(kExpected); i++) { SCOPED_TRACE(i); check_info(&kExpected[i], sk_X509_INFO_value(infos.get(), i)); check_info( &kExpected[i], sk_X509_INFO_value(infos.get(), i + OPENSSL_ARRAY_SIZE(kExpected))); } // Gracefully handle errors in both the append and fresh cases. std::string bad_pem = cert + cert + invalid; bio.reset(BIO_new_mem_buf(bad_pem.data(), bad_pem.size())); ASSERT_TRUE(bio); bssl::UniquePtr infos2( PEM_X509_INFO_read_bio(bio.get(), nullptr, nullptr, nullptr)); EXPECT_FALSE(infos2); bio.reset(BIO_new_mem_buf(bad_pem.data(), bad_pem.size())); ASSERT_TRUE(bio); EXPECT_FALSE( PEM_X509_INFO_read_bio(bio.get(), infos.get(), nullptr, nullptr)); EXPECT_EQ(2 * OPENSSL_ARRAY_SIZE(kExpected), sk_X509_INFO_num(infos.get())); } TEST(X509Test, ReadBIOEmpty) { bssl::UniquePtr bio(BIO_new_mem_buf(nullptr, 0)); ASSERT_TRUE(bio); // CPython expects |ASN1_R_HEADER_TOO_LONG| on EOF, to terminate a series of // certificates. bssl::UniquePtr x509(d2i_X509_bio(bio.get(), nullptr)); EXPECT_FALSE(x509); EXPECT_TRUE( ErrorEquals(ERR_get_error(), ERR_LIB_ASN1, ASN1_R_HEADER_TOO_LONG)); } TEST(X509Test, ReadBIOOneByte) { bssl::UniquePtr bio(BIO_new_mem_buf("\x30", 1)); ASSERT_TRUE(bio); // CPython expects |ASN1_R_HEADER_TOO_LONG| on EOF, to terminate a series of // certificates. This EOF appeared after some data, however, so we do not wish // to signal EOF. bssl::UniquePtr x509(d2i_X509_bio(bio.get(), nullptr)); EXPECT_FALSE(x509); EXPECT_TRUE( ErrorEquals(ERR_get_error(), ERR_LIB_ASN1, ASN1_R_NOT_ENOUGH_DATA)); } TEST(X509Test, PartialBIOReturn) { // Create a filter BIO that only reads and writes one byte at a time. bssl::UniquePtr method(BIO_meth_new(0, nullptr)); ASSERT_TRUE(method); ASSERT_TRUE(BIO_meth_set_create(method.get(), [](BIO *b) -> int { BIO_set_init(b, 1); return 1; })); ASSERT_TRUE( BIO_meth_set_read(method.get(), [](BIO *b, char *out, int len) -> int { return BIO_read(BIO_next(b), out, std::min(len, 1)); })); ASSERT_TRUE(BIO_meth_set_write( method.get(), [](BIO *b, const char *in, int len) -> int { return BIO_write(BIO_next(b), in, std::min(len, 1)); })); bssl::UniquePtr bio(BIO_new(method.get())); ASSERT_TRUE(bio); BIO *mem_bio = BIO_new(BIO_s_mem()); ASSERT_TRUE(mem_bio); BIO_push(bio.get(), mem_bio); // BIO_push takes ownership. bssl::UniquePtr cert(CertFromPEM(kLeafPEM)); ASSERT_TRUE(cert); uint8_t *der = nullptr; int der_len = i2d_X509(cert.get(), &der); ASSERT_GT(der_len, 0); bssl::UniquePtr free_der(der); // Write the certificate into the BIO. Though we only write one byte at a // time, the write should succeed. ASSERT_EQ(1, i2d_X509_bio(bio.get(), cert.get())); const uint8_t *der2; size_t der2_len; ASSERT_TRUE(BIO_mem_contents(mem_bio, &der2, &der2_len)); EXPECT_EQ(Bytes(der, static_cast(der_len)), Bytes(der2, der2_len)); // Read the certificate back out of the BIO. Though we only read one byte at a // time, the read should succeed. bssl::UniquePtr cert2(d2i_X509_bio(bio.get(), nullptr)); ASSERT_TRUE(cert2); EXPECT_EQ(0, X509_cmp(cert.get(), cert2.get())); } TEST(X509Test, CommonNameFallback) { bssl::UniquePtr root = CertFromPEM(kSANTypesRoot); ASSERT_TRUE(root); bssl::UniquePtr with_sans = CertFromPEM(kCommonNameWithSANs); ASSERT_TRUE(with_sans); bssl::UniquePtr without_sans = CertFromPEM(kCommonNameWithoutSANs); ASSERT_TRUE(without_sans); bssl::UniquePtr with_email = CertFromPEM(kCommonNameWithEmailSAN); ASSERT_TRUE(with_email); bssl::UniquePtr with_ip = CertFromPEM(kCommonNameWithIPSAN); ASSERT_TRUE(with_ip); auto verify_cert = [&](X509 *leaf, unsigned flags, const char *host) { return Verify(leaf, {root.get()}, {}, {}, 0, [&](X509_STORE_CTX *ctx) { X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(ctx); ASSERT_TRUE(X509_VERIFY_PARAM_set1_host(param, host, strlen(host))); X509_VERIFY_PARAM_set_hostflags(param, flags); }); }; // Certificate Subject commonName will be checked by default except // if the EE has a DNS SAN. EXPECT_EQ(X509_V_ERR_HOSTNAME_MISMATCH, verify_cert(with_sans.get(), 0 /* no flags */, "foo.host1.test")); EXPECT_EQ(X509_V_OK, verify_cert(with_sans.get(), 0 /* no flags */, "foo.host2.test")); EXPECT_EQ(X509_V_OK, verify_cert(with_sans.get(), 0 /* no flags */, "foo.host3.test")); EXPECT_EQ(X509_V_OK, verify_cert(without_sans.get(), 0 /* no flags */, "foo.host1.test")); EXPECT_EQ(X509_V_OK, verify_cert(with_email.get(), 0 /* no flags */, "foo.host1.test")); EXPECT_EQ(X509_V_OK, verify_cert(with_ip.get(), 0 /* no flags */, "foo.host1.test")); // X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT behavior is supported. EXPECT_EQ(X509_V_OK, verify_cert(with_sans.get(), X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT, "foo.host1.test")); EXPECT_EQ(X509_V_OK, verify_cert(with_sans.get(), X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT, "foo.host2.test")); EXPECT_EQ(X509_V_OK, verify_cert(with_sans.get(), X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT, "foo.host3.test")); EXPECT_EQ(X509_V_OK, verify_cert(without_sans.get(), X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT, "foo.host1.test")); EXPECT_EQ(X509_V_OK, verify_cert(with_email.get(), X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT, "foo.host1.test")); EXPECT_EQ(X509_V_OK, verify_cert(with_ip.get(), X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT, "foo.host1.test")); // X509_CHECK_FLAG_NEVER_CHECK_SUBJECT implements the correct behavior: the // common name is never checked. EXPECT_EQ(X509_V_ERR_HOSTNAME_MISMATCH, verify_cert(with_sans.get(), X509_CHECK_FLAG_NEVER_CHECK_SUBJECT, "foo.host1.test")); EXPECT_EQ(X509_V_OK, verify_cert(with_sans.get(), X509_CHECK_FLAG_NEVER_CHECK_SUBJECT, "foo.host2.test")); EXPECT_EQ(X509_V_OK, verify_cert(with_sans.get(), X509_CHECK_FLAG_NEVER_CHECK_SUBJECT, "foo.host3.test")); EXPECT_EQ(X509_V_ERR_HOSTNAME_MISMATCH, verify_cert(without_sans.get(), X509_CHECK_FLAG_NEVER_CHECK_SUBJECT, "foo.host1.test")); EXPECT_EQ(X509_V_ERR_HOSTNAME_MISMATCH, verify_cert(with_email.get(), X509_CHECK_FLAG_NEVER_CHECK_SUBJECT, "foo.host1.test")); EXPECT_EQ(X509_V_ERR_HOSTNAME_MISMATCH, verify_cert(with_ip.get(), X509_CHECK_FLAG_NEVER_CHECK_SUBJECT, "foo.host1.test")); } TEST(X509Test, ServerGatedCryptoEKUs) { bssl::UniquePtr root = CertFromPEM(kSANTypesRoot); ASSERT_TRUE(root); bssl::UniquePtr ms_sgc = CertFromPEM(kMicrosoftSGCCert); ASSERT_TRUE(ms_sgc); bssl::UniquePtr ns_sgc = CertFromPEM(kNetscapeSGCCert); ASSERT_TRUE(ns_sgc); bssl::UniquePtr server_eku = CertFromPEM(kServerEKUCert); ASSERT_TRUE(server_eku); bssl::UniquePtr server_eku_plus_ms_sgc = CertFromPEM(kServerEKUPlusMicrosoftSGCCert); ASSERT_TRUE(server_eku_plus_ms_sgc); bssl::UniquePtr any_eku = CertFromPEM(kAnyEKU); ASSERT_TRUE(any_eku); bssl::UniquePtr no_eku = CertFromPEM(kNoEKU); ASSERT_TRUE(no_eku); auto verify_cert = [&root](X509 *leaf) { return Verify(leaf, {root.get()}, /*intermediates=*/{}, /*crls=*/{}, /*flags=*/0, [&](X509_STORE_CTX *ctx) { X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(ctx); ASSERT_TRUE(X509_VERIFY_PARAM_set_purpose( param, X509_PURPOSE_SSL_SERVER)); }); }; // Neither the Microsoft nor Netscape SGC EKU should be sufficient for // |X509_PURPOSE_SSL_SERVER|. The "any" EKU probably, technically, should be. // However, we've never accepted it and it's not acceptable in leaf // certificates by the Baseline, so perhaps we don't need this complexity. for (X509 *leaf : {ms_sgc.get(), ns_sgc.get(), any_eku.get()}) { EXPECT_EQ(X509_V_ERR_INVALID_PURPOSE, verify_cert(leaf)); } // The server-auth EKU is sufficient, and it doesn't matter if an SGC EKU is // also included. Lastly, not specifying an EKU is also valid. for (X509 *leaf : {server_eku.get(), server_eku_plus_ms_sgc.get(), no_eku.get()}) { EXPECT_EQ(X509_V_OK, verify_cert(leaf)); } } // Test that invalid extensions are rejected by, if not the parser, at least the // verifier. TEST(X509Test, InvalidExtensions) { bssl::UniquePtr root = CertFromPEM( GetTestData("crypto/x509/test/invalid_extension_root.pem").c_str()); ASSERT_TRUE(root); bssl::UniquePtr intermediate = CertFromPEM( GetTestData("crypto/x509/test/invalid_extension_intermediate.pem") .c_str()); ASSERT_TRUE(intermediate); bssl::UniquePtr leaf = CertFromPEM( GetTestData("crypto/x509/test/invalid_extension_leaf.pem").c_str()); ASSERT_TRUE(leaf); // Sanity-check that the baseline chain is accepted. EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {intermediate.get()}, {})); static const char *kExtensions[] = { "authority_key_identifier", "basic_constraints", "ext_key_usage", "key_usage", "name_constraints", "subject_alt_name", "subject_key_identifier", }; for (const char *ext : kExtensions) { SCOPED_TRACE(ext); bssl::UniquePtr invalid_root = CertFromPEM( GetTestData((std::string("crypto/x509/test/invalid_extension_root_") + ext + ".pem") .c_str()) .c_str()); ASSERT_TRUE(invalid_root); bssl::UniquePtr invalid_intermediate = CertFromPEM( GetTestData( (std::string("crypto/x509/test/invalid_extension_intermediate_") + ext + ".pem") .c_str()) .c_str()); ASSERT_TRUE(invalid_intermediate); bssl::UniquePtr invalid_leaf = CertFromPEM( GetTestData((std::string("crypto/x509/test/invalid_extension_leaf_") + ext + ".pem") .c_str()) .c_str()); ASSERT_TRUE(invalid_leaf); bssl::UniquePtr trailing_leaf = CertFromPEM( GetTestData((std::string("crypto/x509/test/trailing_data_leaf_") + ext + ".pem") .c_str()) .c_str()); ASSERT_TRUE(trailing_leaf); EXPECT_EQ( X509_V_ERR_INVALID_EXTENSION, Verify(invalid_leaf.get(), {root.get()}, {intermediate.get()}, {})); EXPECT_EQ( X509_V_ERR_INVALID_EXTENSION, Verify(trailing_leaf.get(), {root.get()}, {intermediate.get()}, {})); // If the invalid extension is on an intermediate or root, // |X509_verify_cert| notices by way of being unable to build a path to // a valid issuer. EXPECT_EQ( X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, Verify(leaf.get(), {root.get()}, {invalid_intermediate.get()}, {})); EXPECT_EQ( X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, Verify(leaf.get(), {invalid_root.get()}, {intermediate.get()}, {})); } } // kExplicitDefaultVersionPEM is an X.509v1 certificate with the version number // encoded explicitly, rather than omitted as required by DER. static const char kExplicitDefaultVersionPEM[] = R"( -----BEGIN CERTIFICATE----- MIIBfTCCASSgAwIBAAIJANlMBNpJfb/rMAkGByqGSM49BAEwRTELMAkGA1UEBhMC QVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdp dHMgUHR5IEx0ZDAeFw0xNDA0MjMyMzIxNTdaFw0xNDA1MjMyMzIxNTdaMEUxCzAJ BgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5l dCBXaWRnaXRzIFB0eSBMdGQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATmK2ni v2Wfl74vHg2UikzVl2u3qR4NRvvdqakendy6WgHn1peoChj5w8SjHlbifINI2xYa HPUdfvGULUvPciLBMAkGByqGSM49BAEDSAAwRQIhAPKgNV5ROjbDgnmb7idQhY5w BnSVV9IpdAD0vhWHXcQHAiB8HnkUaiGD8Hp0aHlfFJmaaLTxy54VXuYfMlJhXnXJ FA== -----END CERTIFICATE----- )"; // kNegativeVersionPEM is an X.509 certificate with a negative version number. static const char kNegativeVersionPEM[] = R"( -----BEGIN CERTIFICATE----- MIIBfTCCASSgAwIB/wIJANlMBNpJfb/rMAkGByqGSM49BAEwRTELMAkGA1UEBhMC QVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdp dHMgUHR5IEx0ZDAeFw0xNDA0MjMyMzIxNTdaFw0xNDA1MjMyMzIxNTdaMEUxCzAJ BgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5l dCBXaWRnaXRzIFB0eSBMdGQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATmK2ni v2Wfl74vHg2UikzVl2u3qR4NRvvdqakendy6WgHn1peoChj5w8SjHlbifINI2xYa HPUdfvGULUvPciLBMAkGByqGSM49BAEDSAAwRQIhAPKgNV5ROjbDgnmb7idQhY5w BnSVV9IpdAD0vhWHXcQHAiB8HnkUaiGD8Hp0aHlfFJmaaLTxy54VXuYfMlJhXnXJ FA== -----END CERTIFICATE----- )"; // kFutureVersionPEM is an X.509 certificate with a version number value of // three, which is not defined. (v3 has value two). static const char kFutureVersionPEM[] = R"( -----BEGIN CERTIFICATE----- MIIBfTCCASSgAwIBAwIJANlMBNpJfb/rMAkGByqGSM49BAEwRTELMAkGA1UEBhMC QVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdp dHMgUHR5IEx0ZDAeFw0xNDA0MjMyMzIxNTdaFw0xNDA1MjMyMzIxNTdaMEUxCzAJ BgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5l dCBXaWRnaXRzIFB0eSBMdGQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATmK2ni v2Wfl74vHg2UikzVl2u3qR4NRvvdqakendy6WgHn1peoChj5w8SjHlbifINI2xYa HPUdfvGULUvPciLBMAkGByqGSM49BAEDSAAwRQIhAPKgNV5ROjbDgnmb7idQhY5w BnSVV9IpdAD0vhWHXcQHAiB8HnkUaiGD8Hp0aHlfFJmaaLTxy54VXuYfMlJhXnXJ FA== -----END CERTIFICATE----- )"; // kOverflowVersionPEM is an X.509 certificate with a version field which // overflows |uint64_t|. static const char kOverflowVersionPEM[] = R"( -----BEGIN CERTIFICATE----- MIIBoDCCAUegJgIkAP////////////////////////////////////////////// AgkA2UwE2kl9v+swCQYHKoZIzj0EATBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwK U29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMB4X DTE0MDQyMzIzMjE1N1oXDTE0MDUyMzIzMjE1N1owRTELMAkGA1UEBhMCQVUxEzAR BgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5 IEx0ZDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOYraeK/ZZ+Xvi8eDZSKTNWX a7epHg1G+92pqR6d3LpaAefWl6gKGPnDxKMeVuJ8g0jbFhoc9R1+8ZQtS89yIsEw CQYHKoZIzj0EAQNIADBFAiEA8qA1XlE6NsOCeZvuJ1CFjnAGdJVX0il0APS+FYdd xAcCIHweeRRqIYPwenRoeV8UmZpotPHLnhVe5h8yUmFedckU -----END CERTIFICATE----- )"; // kV1WithExtensionsPEM is an X.509v1 certificate with extensions. static const char kV1WithExtensionsPEM[] = R"( -----BEGIN CERTIFICATE----- MIIByjCCAXECCQDZTATaSX2/6zAJBgcqhkjOPQQBMEUxCzAJBgNVBAYTAkFVMRMw EQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0 eSBMdGQwHhcNMTQwNDIzMjMyMTU3WhcNMTQwNTIzMjMyMTU3WjBFMQswCQYDVQQG EwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lk Z2l0cyBQdHkgTHRkMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE5itp4r9ln5e+ Lx4NlIpM1Zdrt6keDUb73ampHp3culoB59aXqAoY+cPEox5W4nyDSNsWGhz1HX7x lC1Lz3IiwaNQME4wHQYDVR0OBBYEFKuE0qyrlfCCThZ4B1VXX+QmjYLRMB8GA1Ud IwQYMBaAFKuE0qyrlfCCThZ4B1VXX+QmjYLRMAwGA1UdEwQFMAMBAf8wCQYHKoZI zj0EAQNIADBFAiEA8qA1XlE6NsOCeZvuJ1CFjnAGdJVX0il0APS+FYddxAcCIHwe eRRqIYPwenRoeV8UmZpotPHLnhVe5h8yUmFedckU -----END CERTIFICATE----- )"; // kV2WithExtensionsPEM is an X.509v2 certificate with extensions. static const char kV2WithExtensionsPEM[] = R"( -----BEGIN CERTIFICATE----- MIIBzzCCAXagAwIBAQIJANlMBNpJfb/rMAkGByqGSM49BAEwRTELMAkGA1UEBhMC QVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdp dHMgUHR5IEx0ZDAeFw0xNDA0MjMyMzIxNTdaFw0xNDA1MjMyMzIxNTdaMEUxCzAJ BgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5l dCBXaWRnaXRzIFB0eSBMdGQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATmK2ni v2Wfl74vHg2UikzVl2u3qR4NRvvdqakendy6WgHn1peoChj5w8SjHlbifINI2xYa HPUdfvGULUvPciLBo1AwTjAdBgNVHQ4EFgQUq4TSrKuV8IJOFngHVVdf5CaNgtEw HwYDVR0jBBgwFoAUq4TSrKuV8IJOFngHVVdf5CaNgtEwDAYDVR0TBAUwAwEB/zAJ BgcqhkjOPQQBA0gAMEUCIQDyoDVeUTo2w4J5m+4nUIWOcAZ0lVfSKXQA9L4Vh13E BwIgfB55FGohg/B6dGh5XxSZmmi08cueFV7mHzJSYV51yRQ= -----END CERTIFICATE----- )"; // kV1WithIssuerUniqueIDPEM is an X.509v1 certificate with an issuerUniqueID. static const char kV1WithIssuerUniqueIDPEM[] = R"( -----BEGIN CERTIFICATE----- MIIBgzCCASoCCQDZTATaSX2/6zAJBgcqhkjOPQQBMEUxCzAJBgNVBAYTAkFVMRMw EQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0 eSBMdGQwHhcNMTQwNDIzMjMyMTU3WhcNMTQwNTIzMjMyMTU3WjBFMQswCQYDVQQG EwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lk Z2l0cyBQdHkgTHRkMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE5itp4r9ln5e+ Lx4NlIpM1Zdrt6keDUb73ampHp3culoB59aXqAoY+cPEox5W4nyDSNsWGhz1HX7x lC1Lz3IiwYEJAAEjRWeJq83vMAkGByqGSM49BAEDSAAwRQIhAPKgNV5ROjbDgnmb 7idQhY5wBnSVV9IpdAD0vhWHXcQHAiB8HnkUaiGD8Hp0aHlfFJmaaLTxy54VXuYf MlJhXnXJFA== -----END CERTIFICATE----- )"; // kV1WithSubjectUniqueIDPEM is an X.509v1 certificate with an issuerUniqueID. static const char kV1WithSubjectUniqueIDPEM[] = R"( -----BEGIN CERTIFICATE----- MIIBgzCCASoCCQDZTATaSX2/6zAJBgcqhkjOPQQBMEUxCzAJBgNVBAYTAkFVMRMw EQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0 eSBMdGQwHhcNMTQwNDIzMjMyMTU3WhcNMTQwNTIzMjMyMTU3WjBFMQswCQYDVQQG EwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lk Z2l0cyBQdHkgTHRkMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE5itp4r9ln5e+ Lx4NlIpM1Zdrt6keDUb73ampHp3culoB59aXqAoY+cPEox5W4nyDSNsWGhz1HX7x lC1Lz3IiwYIJAAEjRWeJq83vMAkGByqGSM49BAEDSAAwRQIhAPKgNV5ROjbDgnmb 7idQhY5wBnSVV9IpdAD0vhWHXcQHAiB8HnkUaiGD8Hp0aHlfFJmaaLTxy54VXuYf MlJhXnXJFA== -----END CERTIFICATE----- )"; // kV1CRLWithExtensionsPEM is a v1 CRL with extensions. static const char kV1CRLWithExtensionsPEM[] = R"( -----BEGIN X509 CRL----- MIIBpDCBjTANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJVUzETMBEGA1UECAwK Q2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzESMBAGA1UECgwJQm9y aW5nU1NMFw0xNjA5MjYxNTEwNTVaFw0xNjEwMjYxNTEwNTVaoA4wDDAKBgNVHRQE AwIBATANBgkqhkiG9w0BAQsFAAOCAQEAnrBKKgvd9x9zwK9rtUvVeFeJ7+LNZEAc +a5oxpPNEsJx6hXoApYEbzXMxuWBQoCs5iEBycSGudct21L+MVf27M38KrWoeOkq 0a2siqViQZO2Fb/SUFR0k9zb8xl86Zf65lgPplALun0bV/HT7MJcl04Tc4osdsAR eBs5nqTGNEd5AlC1iKHvQZkM//MD51DspKnDpsDiUVi54h9C1SpfZmX8H2Vvdiyu 0fZ/bPAM3VAGawatf/SyWfBMyKpoPXEG39oAzmjjOj8en82psn7m474IGaho/vBb hl1ms5qQiLYPjm4YELtnXQoFyC72tBjbdFd/ZE9k4CNKDbxFUXFbkw== -----END X509 CRL----- )"; // kExplicitDefaultVersionCRLPEM is a v1 CRL with an explicitly-encoded version // field. static const char kExplicitDefaultVersionCRLPEM[] = R"( -----BEGIN X509 CRL----- MIIBlzCBgAIBADANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJVUzETMBEGA1UE CAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzESMBAGA1UECgwJ Qm9yaW5nU1NMFw0xNjA5MjYxNTEwNTVaFw0xNjEwMjYxNTEwNTVaMA0GCSqGSIb3 DQEBCwUAA4IBAQCesEoqC933H3PAr2u1S9V4V4nv4s1kQBz5rmjGk80SwnHqFegC lgRvNczG5YFCgKzmIQHJxIa51y3bUv4xV/bszfwqtah46SrRrayKpWJBk7YVv9JQ VHST3NvzGXzpl/rmWA+mUAu6fRtX8dPswlyXThNziix2wBF4GzmepMY0R3kCULWI oe9BmQz/8wPnUOykqcOmwOJRWLniH0LVKl9mZfwfZW92LK7R9n9s8AzdUAZrBq1/ 9LJZ8EzIqmg9cQbf2gDOaOM6Px6fzamyfubjvggZqGj+8FuGXWazmpCItg+ObhgQ u2ddCgXILva0GNt0V39kT2TgI0oNvEVRcVuT -----END X509 CRL----- )"; // kV3CRLPEM is a v3 CRL. CRL versions only go up to v2. static const char kV3CRLPEM[] = R"( -----BEGIN X509 CRL----- MIIBpzCBkAIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJVUzETMBEGA1UE CAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzESMBAGA1UECgwJ Qm9yaW5nU1NMFw0xNjA5MjYxNTEwNTVaFw0xNjEwMjYxNTEwNTVaoA4wDDAKBgNV HRQEAwIBATANBgkqhkiG9w0BAQsFAAOCAQEAnrBKKgvd9x9zwK9rtUvVeFeJ7+LN ZEAc+a5oxpPNEsJx6hXoApYEbzXMxuWBQoCs5iEBycSGudct21L+MVf27M38KrWo eOkq0a2siqViQZO2Fb/SUFR0k9zb8xl86Zf65lgPplALun0bV/HT7MJcl04Tc4os dsAReBs5nqTGNEd5AlC1iKHvQZkM//MD51DspKnDpsDiUVi54h9C1SpfZmX8H2Vv diyu0fZ/bPAM3VAGawatf/SyWfBMyKpoPXEG39oAzmjjOj8en82psn7m474IGaho /vBbhl1ms5qQiLYPjm4YELtnXQoFyC72tBjbdFd/ZE9k4CNKDbxFUXFbkw== -----END X509 CRL----- )"; // kV2CSRPEM is a v2 CSR. CSR versions only go up to v1. static const char kV2CSRPEM[] = R"( -----BEGIN CERTIFICATE REQUEST----- MIHJMHECAQEwDzENMAsGA1UEAwwEVGVzdDBZMBMGByqGSM49AgEGCCqGSM49AwEH A0IABJjsayyAQod1J7UJYNT8AH4WWxLdKV0ozhrIz6hCzBAze7AqXWOSH8G+1EWC pSfL3oMQNtBdJS0kpXXaUqEAgTSgADAKBggqhkjOPQQDAgNIADBFAiAUXVaEYATg 4Cc917T73KBImxh6xyhsA5pKuYpq1S4m9wIhAK+G93HR4ur7Ghel6+zUTvIAsj9e rsn4lSYsqI4OI4ei -----END CERTIFICATE REQUEST----- )"; // kV3CSRPEM is a v3 CSR. CSR versions only go up to v1. static const char kV3CSRPEM[] = R"( -----BEGIN CERTIFICATE REQUEST----- MIHJMHECAQIwDzENMAsGA1UEAwwEVGVzdDBZMBMGByqGSM49AgEGCCqGSM49AwEH A0IABJjsayyAQod1J7UJYNT8AH4WWxLdKV0ozhrIz6hCzBAze7AqXWOSH8G+1EWC pSfL3oMQNtBdJS0kpXXaUqEAgTSgADAKBggqhkjOPQQDAgNIADBFAiAUXVaEYATg 4Cc917T73KBImxh6xyhsA5pKuYpq1S4m9wIhAK+G93HR4ur7Ghel6+zUTvIAsj9e rsn4lSYsqI4OI4ei -----END CERTIFICATE REQUEST----- )"; // Test that the library enforces versions are valid and match the fields // present. TEST(X509Test, InvalidVersion) { // kExplicitDefaultVersionPEM is invalid but, for now, we accept it. See // https://crbug.com/boringssl/364. EXPECT_TRUE(CertFromPEM(kExplicitDefaultVersionPEM)); EXPECT_TRUE(CRLFromPEM(kExplicitDefaultVersionCRLPEM)); EXPECT_FALSE(CertFromPEM(kNegativeVersionPEM)); EXPECT_FALSE(CertFromPEM(kFutureVersionPEM)); EXPECT_FALSE(CertFromPEM(kOverflowVersionPEM)); EXPECT_FALSE(CertFromPEM(kV1WithExtensionsPEM)); EXPECT_FALSE(CertFromPEM(kV2WithExtensionsPEM)); EXPECT_FALSE(CertFromPEM(kV1WithIssuerUniqueIDPEM)); EXPECT_FALSE(CertFromPEM(kV1WithSubjectUniqueIDPEM)); EXPECT_FALSE(CRLFromPEM(kV1CRLWithExtensionsPEM)); EXPECT_FALSE(CRLFromPEM(kV3CRLPEM)); EXPECT_FALSE(CSRFromPEM(kV2CSRPEM)); // kV3CSRPEM is invalid but, for now, we accept it. See // https://github.com/certbot/certbot/pull/9334 EXPECT_TRUE(CSRFromPEM(kV3CSRPEM)); bssl::UniquePtr x509(X509_new()); ASSERT_TRUE(x509); EXPECT_FALSE(X509_set_version(x509.get(), -1)); EXPECT_FALSE(X509_set_version(x509.get(), X509_VERSION_3 + 1)); EXPECT_FALSE(X509_set_version(x509.get(), 9999)); bssl::UniquePtr crl(X509_CRL_new()); ASSERT_TRUE(crl); EXPECT_FALSE(X509_CRL_set_version(crl.get(), -1)); EXPECT_FALSE(X509_CRL_set_version(crl.get(), X509_CRL_VERSION_2 + 1)); EXPECT_FALSE(X509_CRL_set_version(crl.get(), 9999)); bssl::UniquePtr req(X509_REQ_new()); ASSERT_TRUE(req); EXPECT_FALSE(X509_REQ_set_version(req.get(), -1)); EXPECT_FALSE(X509_REQ_set_version(req.get(), X509_REQ_VERSION_1 + 1)); EXPECT_FALSE(X509_REQ_set_version(req.get(), 9999)); } // Unlike upstream OpenSSL, we require a non-null store in // |X509_STORE_CTX_init|. TEST(X509Test, NullStore) { bssl::UniquePtr leaf(CertFromPEM(kLeafPEM)); ASSERT_TRUE(leaf); bssl::UniquePtr ctx(X509_STORE_CTX_new()); ASSERT_TRUE(ctx); EXPECT_FALSE(X509_STORE_CTX_init(ctx.get(), nullptr, leaf.get(), nullptr)); } TEST(X509Test, StoreCtxReuse) { bssl::UniquePtr leaf(CertFromPEM(kLeafPEM)); ASSERT_TRUE(leaf); bssl::UniquePtr store(X509_STORE_new()); ASSERT_TRUE(store); bssl::UniquePtr ctx(X509_STORE_CTX_new()); ASSERT_TRUE(ctx); ASSERT_TRUE(X509_STORE_CTX_init(ctx.get(), store.get(), leaf.get(), nullptr)); // Re-initializing |ctx| should not leak memory. ASSERT_TRUE(X509_STORE_CTX_init(ctx.get(), store.get(), leaf.get(), nullptr)); } TEST(X509Test, BasicConstraints) { const uint32_t kFlagMask = EXFLAG_CA | EXFLAG_BCONS | EXFLAG_INVALID; static const struct { const char *file; uint32_t flags; int path_len; } kTests[] = { {"basic_constraints_none.pem", 0, -1}, {"basic_constraints_ca.pem", EXFLAG_CA | EXFLAG_BCONS, -1}, {"basic_constraints_ca_pathlen_0.pem", EXFLAG_CA | EXFLAG_BCONS, 0}, {"basic_constraints_ca_pathlen_1.pem", EXFLAG_CA | EXFLAG_BCONS, 1}, {"basic_constraints_ca_pathlen_10.pem", EXFLAG_CA | EXFLAG_BCONS, 10}, {"basic_constraints_leaf.pem", EXFLAG_BCONS, -1}, {"invalid_extension_leaf_basic_constraints.pem", EXFLAG_INVALID, -1}, }; for (const auto &test : kTests) { SCOPED_TRACE(test.file); std::string path = "crypto/x509/test/"; path += test.file; bssl::UniquePtr cert = CertFromPEM(GetTestData(path.c_str()).c_str()); ASSERT_TRUE(cert); EXPECT_EQ(test.flags, X509_get_extension_flags(cert.get()) & kFlagMask); EXPECT_EQ(test.path_len, X509_get_pathlen(cert.get())); } } // The following strings are test certificates signed by kP256Key and kRSAKey, // with missing, NULL, or invalid algorithm parameters. static const char kP256NoParam[] = R"( -----BEGIN CERTIFICATE----- MIIBIDCBxqADAgECAgIE0jAKBggqhkjOPQQDAjAPMQ0wCwYDVQQDEwRUZXN0MCAX DTAwMDEwMTAwMDAwMFoYDzIxMDAwMTAxMDAwMDAwWjAPMQ0wCwYDVQQDEwRUZXN0 MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE5itp4r9ln5e+Lx4NlIpM1Zdrt6ke DUb73ampHp3culoB59aXqAoY+cPEox5W4nyDSNsWGhz1HX7xlC1Lz3IiwaMQMA4w DAYDVR0TBAUwAwEB/zAKBggqhkjOPQQDAgNJADBGAiEAqdIiF+bN9Cl44oUeICpy aXd7HqhpVUaglYKw9ChmNUACIQCpMdL0fNkFNDbRww9dSl/y7kBdk/tp16HiqeSy gGzFYg== -----END CERTIFICATE----- )"; static const char kP256NullParam[] = R"( -----BEGIN CERTIFICATE----- MIIBJDCByKADAgECAgIE0jAMBggqhkjOPQQDAgUAMA8xDTALBgNVBAMTBFRlc3Qw IBcNMDAwMTAxMDAwMDAwWhgPMjEwMDAxMDEwMDAwMDBaMA8xDTALBgNVBAMTBFRl c3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATmK2niv2Wfl74vHg2UikzVl2u3 qR4NRvvdqakendy6WgHn1peoChj5w8SjHlbifINI2xYaHPUdfvGULUvPciLBoxAw DjAMBgNVHRMEBTADAQH/MAwGCCqGSM49BAMCBQADSQAwRgIhAKILHmyo+F3Cn/VX UUeSXOQQKX5aLzsQitwwmNF3ZgH3AiEAsYHcrVj/ftmoQIORARkQ/+PrqntXev8r t6uPxHrmpUY= -----END CERTIFICATE----- )"; static const char kP256InvalidParam[] = R"( -----BEGIN CERTIFICATE----- MIIBMTCBz6ADAgECAgIE0jATBggqhkjOPQQDAgQHZ2FyYmFnZTAPMQ0wCwYDVQQD EwRUZXN0MCAXDTAwMDEwMTAwMDAwMFoYDzIxMDAwMTAxMDAwMDAwWjAPMQ0wCwYD VQQDEwRUZXN0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE5itp4r9ln5e+Lx4N lIpM1Zdrt6keDUb73ampHp3culoB59aXqAoY+cPEox5W4nyDSNsWGhz1HX7xlC1L z3IiwaMQMA4wDAYDVR0TBAUwAwEB/zATBggqhkjOPQQDAgQHZ2FyYmFnZQNIADBF AiAglpDf/YhN89LeJ2WAs/F0SJIrsuhS4uoInIz6WXUiuQIhAIu5Pwhp5E3Pbo8y fLULTZnynuQUULQkRcF7S7T2WpIL -----END CERTIFICATE----- )"; static const char kRSANoParam[] = R"( -----BEGIN CERTIFICATE----- MIIBWzCBx6ADAgECAgIE0jALBgkqhkiG9w0BAQswDzENMAsGA1UEAxMEVGVzdDAg Fw0wMDAxMDEwMDAwMDBaGA8yMTAwMDEwMTAwMDAwMFowDzENMAsGA1UEAxMEVGVz dDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOYraeK/ZZ+Xvi8eDZSKTNWXa7ep Hg1G+92pqR6d3LpaAefWl6gKGPnDxKMeVuJ8g0jbFhoc9R1+8ZQtS89yIsGjEDAO MAwGA1UdEwQFMAMBAf8wCwYJKoZIhvcNAQELA4GBAC1f8W3W0Ao7CPfIBQYDSbPh brZpbxdBU5x27JOS7iSa+Lc9pEH5VCX9vIypHVHXLPEfZ38yIt11eiyrmZB6w62N l9kIeZ6FVPmC30d3sXx70Jjs+ZX9yt7kD1gLyNAQQfeYfa4rORAZT1n2YitD74NY TWUH2ieFP3l+ecj1SeQR -----END CERTIFICATE----- )"; static const char kRSANullParam[] = R"( -----BEGIN CERTIFICATE----- MIIBXzCByaADAgECAgIE0jANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDEwRUZXN0 MCAXDTAwMDEwMTAwMDAwMFoYDzIxMDAwMTAxMDAwMDAwWjAPMQ0wCwYDVQQDEwRU ZXN0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE5itp4r9ln5e+Lx4NlIpM1Zdr t6keDUb73ampHp3culoB59aXqAoY+cPEox5W4nyDSNsWGhz1HX7xlC1Lz3IiwaMQ MA4wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOBgQAzVcfIv+Rq1KrMXqIL fPq/cWZjgqFZA1RGaGElNaqp+rkJfamq5tDGzckWpebrK+jjRN7yIlcWDtPpy3Gy seZfvtBDR0TwJm0S/pQl8prKB4wgALcwe3bmi56Rq85nzY5ZLNcP16LQxL+jAAua SwmQUz4bRpckRBj+sIyp1We+pg== -----END CERTIFICATE----- )"; static const char kRSAInvalidParam[] = R"( -----BEGIN CERTIFICATE----- MIIBbTCB0KADAgECAgIE0jAUBgkqhkiG9w0BAQsEB2dhcmJhZ2UwDzENMAsGA1UE AxMEVGVzdDAgFw0wMDAxMDEwMDAwMDBaGA8yMTAwMDEwMTAwMDAwMFowDzENMAsG A1UEAxMEVGVzdDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOYraeK/ZZ+Xvi8e DZSKTNWXa7epHg1G+92pqR6d3LpaAefWl6gKGPnDxKMeVuJ8g0jbFhoc9R1+8ZQt S89yIsGjEDAOMAwGA1UdEwQFMAMBAf8wFAYJKoZIhvcNAQELBAdnYXJiYWdlA4GB AHTJ6cWWjCNrZhqiWWVI3jdK+h5xpRG8jGMXxR4JnjtoYRRusJLOXhmapwCB6fA0 4vc+66O27v36yDmQX+tIc/hDrTpKNJptU8q3n2VagREvoHhkOTYkcCeS8vmnMtn8 5OMNZ/ajVwOssw61GcAlScRqEHkZFBoGp7e+QpgB2tf9 -----END CERTIFICATE----- )"; static const char kRSAStrippedJCAKey[] = R"( -----BEGIN PRIVATE KEY----- MIGyAgEAMA0GCSqGSIb3DQEBAQUABIGdMIGaAgEAAkEAzVnSq89o3KlYlMWcB/3UFpD5isq2aGsYcnmr P4iGyFmlOvYR+DdkmiuxcKTZu/16uUi9BbQLtXyzhV3qX24YOwIBAAJAAfe+YQ8XviWRR7utBxaTlbPF 8GKI5O9ByLcJwQ4Z3Ima9xdb14zqcXjA+Ox93ePHC0ruax1n+TptsoDhd+RoAQIBAAIBAAIBAAIBAAIB AA== -----END PRIVATE KEY----- )"; // Make sure we can load stripped private RSA keys that JCA uses. TEST(X509Test, RSAStrippedJCAKey) { bssl::UniquePtr key = PrivateKeyFromPEM(kRSAStrippedJCAKey); ASSERT_TRUE(key); } TEST(X509Test, AlgorithmParameters) { // P-256 parameters should be omitted, but we accept NULL ones. bssl::UniquePtr key = PrivateKeyFromPEM(kP256Key); ASSERT_TRUE(key); bssl::UniquePtr cert = CertFromPEM(kP256NoParam); ASSERT_TRUE(cert); EXPECT_TRUE(X509_verify(cert.get(), key.get())); cert = CertFromPEM(kP256NullParam); ASSERT_TRUE(cert); EXPECT_TRUE(X509_verify(cert.get(), key.get())); cert = CertFromPEM(kP256InvalidParam); ASSERT_TRUE(cert); EXPECT_FALSE(X509_verify(cert.get(), key.get())); EXPECT_TRUE( ErrorEquals(ERR_get_error(), ERR_LIB_X509, X509_R_INVALID_PARAMETER)); // RSA parameters should be NULL, but we accept omitted ones. key = PrivateKeyFromPEM(kRSAKey); ASSERT_TRUE(key); cert = CertFromPEM(kRSANoParam); ASSERT_TRUE(cert); EXPECT_TRUE(X509_verify(cert.get(), key.get())); cert = CertFromPEM(kRSANullParam); ASSERT_TRUE(cert); EXPECT_TRUE(X509_verify(cert.get(), key.get())); cert = CertFromPEM(kRSAInvalidParam); ASSERT_TRUE(cert); EXPECT_FALSE(X509_verify(cert.get(), key.get())); EXPECT_TRUE( ErrorEquals(ERR_get_error(), ERR_LIB_X509, X509_R_INVALID_PARAMETER)); } TEST(X509Test, GeneralName) { const std::vector kNames[] = { // [0] { // OBJECT_IDENTIFIER { 1.2.840.113554.4.1.72585.2.1 } // [0] { // SEQUENCE {} // } // } {0xa0, 0x13, 0x06, 0x0d, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x01, 0xa0, 0x02, 0x30, 0x00}, // [0] { // OBJECT_IDENTIFIER { 1.2.840.113554.4.1.72585.2.1 } // [0] { // [APPLICATION 0] {} // } // } {0xa0, 0x13, 0x06, 0x0d, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x01, 0xa0, 0x02, 0x60, 0x00}, // [0] { // OBJECT_IDENTIFIER { 1.2.840.113554.4.1.72585.2.1 } // [0] { // UTF8String { "a" } // } // } {0xa0, 0x14, 0x06, 0x0d, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x01, 0xa0, 0x03, 0x0c, 0x01, 0x61}, // [0] { // OBJECT_IDENTIFIER { 1.2.840.113554.4.1.72585.2.2 } // [0] { // UTF8String { "a" } // } // } {0xa0, 0x14, 0x06, 0x0d, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x02, 0xa0, 0x03, 0x0c, 0x01, 0x61}, // [0] { // OBJECT_IDENTIFIER { 1.2.840.113554.4.1.72585.2.1 } // [0] { // UTF8String { "b" } // } // } {0xa0, 0x14, 0x06, 0x0d, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x01, 0xa0, 0x03, 0x0c, 0x01, 0x62}, // [0] { // OBJECT_IDENTIFIER { 1.2.840.113554.4.1.72585.2.1 } // [0] { // BOOLEAN { TRUE } // } // } {0xa0, 0x14, 0x06, 0x0d, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x01, 0xa0, 0x03, 0x01, 0x01, 0xff}, // [0] { // OBJECT_IDENTIFIER { 1.2.840.113554.4.1.72585.2.1 } // [0] { // BOOLEAN { FALSE } // } // } {0xa0, 0x14, 0x06, 0x0d, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x01, 0xa0, 0x03, 0x01, 0x01, 0x00}, // [1 PRIMITIVE] { "a" } {0x81, 0x01, 0x61}, // [1 PRIMITIVE] { "b" } {0x81, 0x01, 0x62}, // [2 PRIMITIVE] { "a" } {0x82, 0x01, 0x61}, // [2 PRIMITIVE] { "b" } {0x82, 0x01, 0x62}, // [3] {}. Regression test for CVE-2023-0286. {0xa3, 0x00}, // [4] { // SEQUENCE { // SET { // SEQUENCE { // # commonName // OBJECT_IDENTIFIER { 2.5.4.3 } // UTF8String { "a" } // } // } // } // } {0xa4, 0x0e, 0x30, 0x0c, 0x31, 0x0a, 0x30, 0x08, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x01, 0x61}, // [4] { // SEQUENCE { // SET { // SEQUENCE { // # commonName // OBJECT_IDENTIFIER { 2.5.4.3 } // UTF8String { "b" } // } // } // } // } {0xa4, 0x0e, 0x30, 0x0c, 0x31, 0x0a, 0x30, 0x08, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x01, 0x62}, // [5] { // [1] { // UTF8String { "a" } // } // } {0xa5, 0x05, 0xa1, 0x03, 0x0c, 0x01, 0x61}, // [5] { // [1] { // UTF8String { "b" } // } // } {0xa5, 0x05, 0xa1, 0x03, 0x0c, 0x01, 0x62}, // [5] { // [0] { // UTF8String {} // } // [1] { // UTF8String { "a" } // } // } {0xa5, 0x09, 0xa0, 0x02, 0x0c, 0x00, 0xa1, 0x03, 0x0c, 0x01, 0x61}, // [5] { // [0] { // UTF8String { "a" } // } // [1] { // UTF8String { "a" } // } // } {0xa5, 0x0a, 0xa0, 0x03, 0x0c, 0x01, 0x61, 0xa1, 0x03, 0x0c, 0x01, 0x61}, // [5] { // [0] { // UTF8String { "b" } // } // [1] { // UTF8String { "a" } // } // } {0xa5, 0x0a, 0xa0, 0x03, 0x0c, 0x01, 0x62, 0xa1, 0x03, 0x0c, 0x01, 0x61}, // [6 PRIMITIVE] { "a" } {0x86, 0x01, 0x61}, // [6 PRIMITIVE] { "b" } {0x86, 0x01, 0x62}, // [7 PRIMITIVE] { `11111111` } {0x87, 0x04, 0x11, 0x11, 0x11, 0x11}, // [7 PRIMITIVE] { `22222222`} {0x87, 0x04, 0x22, 0x22, 0x22, 0x22}, // [7 PRIMITIVE] { `11111111111111111111111111111111` } {0x87, 0x10, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, // [7 PRIMITIVE] { `22222222222222222222222222222222` } {0x87, 0x10, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22}, // [8 PRIMITIVE] { 1.2.840.113554.4.1.72585.2.1 } {0x88, 0x0d, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x01}, // [8 PRIMITIVE] { 1.2.840.113554.4.1.72585.2.2 } {0x88, 0x0d, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x02}, }; // Every name should be equal to itself and not equal to any others. for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(kNames); i++) { SCOPED_TRACE(Bytes(kNames[i])); const uint8_t *ptr = kNames[i].data(); bssl::UniquePtr a( d2i_GENERAL_NAME(nullptr, &ptr, kNames[i].size())); ASSERT_TRUE(a); ASSERT_EQ(ptr, kNames[i].data() + kNames[i].size()); uint8_t *enc = nullptr; int enc_len = i2d_GENERAL_NAME(a.get(), &enc); ASSERT_GE(enc_len, 0); bssl::UniquePtr free_enc(enc); EXPECT_EQ(Bytes(enc, enc_len), Bytes(kNames[i])); for (size_t j = 0; j < OPENSSL_ARRAY_SIZE(kNames); j++) { SCOPED_TRACE(Bytes(kNames[j])); ptr = kNames[j].data(); bssl::UniquePtr b( d2i_GENERAL_NAME(nullptr, &ptr, kNames[j].size())); ASSERT_TRUE(b); ASSERT_EQ(ptr, kNames[j].data() + kNames[j].size()); if (i == j) { EXPECT_EQ(GENERAL_NAME_cmp(a.get(), b.get()), 0); } else { EXPECT_NE(GENERAL_NAME_cmp(a.get(), b.get()), 0); } } } } // Test that extracting fields of an |X509_ALGOR| works correctly. TEST(X509Test, X509AlgorExtract) { static const char kTestOID[] = "1.2.840.113554.4.1.72585.2"; const struct { int param_type; std::vector param_der; } kTests[] = { // No parameter. {V_ASN1_UNDEF, {}}, // BOOLEAN { TRUE } {V_ASN1_BOOLEAN, {0x01, 0x01, 0xff}}, // BOOLEAN { FALSE } {V_ASN1_BOOLEAN, {0x01, 0x01, 0x00}}, // OCTET_STRING { "a" } {V_ASN1_OCTET_STRING, {0x04, 0x01, 0x61}}, // BIT_STRING { `01` `00` } {V_ASN1_BIT_STRING, {0x03, 0x02, 0x01, 0x00}}, // INTEGER { -1 } {V_ASN1_INTEGER, {0x02, 0x01, 0xff}}, // OBJECT_IDENTIFIER { 1.2.840.113554.4.1.72585.2 } {V_ASN1_OBJECT, {0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02}}, // NULL {} {V_ASN1_NULL, {0x05, 0x00}}, // SEQUENCE {} {V_ASN1_SEQUENCE, {0x30, 0x00}}, // SET {} {V_ASN1_SET, {0x31, 0x00}}, // [0] { UTF8String { "a" } } {V_ASN1_OTHER, {0xa0, 0x03, 0x0c, 0x01, 0x61}}, }; for (const auto &t : kTests) { SCOPED_TRACE(Bytes(t.param_der)); // Assemble an AlgorithmIdentifier with the parameter. bssl::ScopedCBB cbb; CBB seq, oid; ASSERT_TRUE(CBB_init(cbb.get(), 64)); ASSERT_TRUE(CBB_add_asn1(cbb.get(), &seq, CBS_ASN1_SEQUENCE)); ASSERT_TRUE(CBB_add_asn1(&seq, &oid, CBS_ASN1_OBJECT)); ASSERT_TRUE(CBB_add_asn1_oid_from_text(&oid, kTestOID, strlen(kTestOID))); ASSERT_TRUE(CBB_add_bytes(&seq, t.param_der.data(), t.param_der.size())); ASSERT_TRUE(CBB_flush(cbb.get())); const uint8_t *ptr = CBB_data(cbb.get()); bssl::UniquePtr alg( d2i_X509_ALGOR(nullptr, &ptr, CBB_len(cbb.get()))); ASSERT_TRUE(alg); const ASN1_OBJECT *obj; int param_type; const void *param_value; X509_ALGOR_get0(&obj, ¶m_type, ¶m_value, alg.get()); EXPECT_EQ(param_type, t.param_type); char oid_buf[sizeof(kTestOID)]; ASSERT_EQ(int(sizeof(oid_buf) - 1), OBJ_obj2txt(oid_buf, sizeof(oid_buf), obj, /*always_return_oid=*/1)); EXPECT_STREQ(oid_buf, kTestOID); // |param_type| and |param_value| must be consistent with |ASN1_TYPE|. if (param_type == V_ASN1_UNDEF) { EXPECT_EQ(nullptr, param_value); } else { bssl::UniquePtr param(ASN1_TYPE_new()); ASSERT_TRUE(param); ASSERT_TRUE(ASN1_TYPE_set1(param.get(), param_type, param_value)); uint8_t *param_der = nullptr; int param_len = i2d_ASN1_TYPE(param.get(), ¶m_der); ASSERT_GE(param_len, 0); bssl::UniquePtr free_param_der(param_der); EXPECT_EQ(Bytes(param_der, param_len), Bytes(t.param_der)); } } } // Test the various |X509_ATTRIBUTE| creation functions. TEST(X509Test, Attribute) { // The expected attribute values are: // 1. BMPString U+2603 // 2. BMPString "test" // 3. INTEGER -1 (not valid for friendlyName) static const uint8_t kTest1[] = {0x26, 0x03}; // U+2603 SNOWMAN static const uint8_t kTest1UTF8[] = {0xe2, 0x98, 0x83}; static const uint8_t kTest2[] = {0, 't', 0, 'e', 0, 's', 0, 't'}; constexpr uint32_t kTest1Mask = 1 << 0; constexpr uint32_t kTest2Mask = 1 << 1; constexpr uint32_t kTest3Mask = 1 << 2; auto check_attribute = [&](X509_ATTRIBUTE *attr, uint32_t mask) { EXPECT_EQ(NID_friendlyName, OBJ_obj2nid(X509_ATTRIBUTE_get0_object(attr))); int idx = 0; if (mask & kTest1Mask) { // The first attribute should contain |kTest1|. const ASN1_TYPE *value = X509_ATTRIBUTE_get0_type(attr, idx); ASSERT_TRUE(value); EXPECT_EQ(V_ASN1_BMPSTRING, value->type); EXPECT_EQ(Bytes(kTest1), Bytes(ASN1_STRING_get0_data(value->value.bmpstring), ASN1_STRING_length(value->value.bmpstring))); // |X509_ATTRIBUTE_get0_data| requires the type match. EXPECT_FALSE( X509_ATTRIBUTE_get0_data(attr, idx, V_ASN1_OCTET_STRING, nullptr)); const ASN1_BMPSTRING *bmpstring = static_cast( X509_ATTRIBUTE_get0_data(attr, idx, V_ASN1_BMPSTRING, nullptr)); ASSERT_TRUE(bmpstring); EXPECT_EQ(Bytes(kTest1), Bytes(ASN1_STRING_get0_data(bmpstring), ASN1_STRING_length(bmpstring))); idx++; } if (mask & kTest2Mask) { const ASN1_TYPE *value = X509_ATTRIBUTE_get0_type(attr, idx); ASSERT_TRUE(value); EXPECT_EQ(V_ASN1_BMPSTRING, value->type); EXPECT_EQ(Bytes(kTest2), Bytes(ASN1_STRING_get0_data(value->value.bmpstring), ASN1_STRING_length(value->value.bmpstring))); idx++; } if (mask & kTest3Mask) { const ASN1_TYPE *value = X509_ATTRIBUTE_get0_type(attr, idx); ASSERT_TRUE(value); EXPECT_EQ(V_ASN1_INTEGER, value->type); int64_t v; ASSERT_TRUE(ASN1_INTEGER_get_int64(&v, value->value.integer)); EXPECT_EQ(v, -1); idx++; } EXPECT_FALSE(X509_ATTRIBUTE_get0_type(attr, idx)); }; bssl::UniquePtr str(ASN1_STRING_type_new(V_ASN1_BMPSTRING)); ASSERT_TRUE(str); ASSERT_TRUE(ASN1_STRING_set(str.get(), kTest1, sizeof(kTest1))); // Test |X509_ATTRIBUTE_create|. bssl::UniquePtr attr( X509_ATTRIBUTE_create(NID_friendlyName, V_ASN1_BMPSTRING, str.get())); ASSERT_TRUE(attr); str.release(); // |X509_ATTRIBUTE_create| takes ownership on success. check_attribute(attr.get(), kTest1Mask); // Test the |MBSTRING_*| form of |X509_ATTRIBUTE_set1_data|. attr.reset(X509_ATTRIBUTE_new()); ASSERT_TRUE(attr); ASSERT_TRUE( X509_ATTRIBUTE_set1_object(attr.get(), OBJ_nid2obj(NID_friendlyName))); ASSERT_TRUE(X509_ATTRIBUTE_set1_data(attr.get(), MBSTRING_UTF8, kTest1UTF8, sizeof(kTest1UTF8))); check_attribute(attr.get(), kTest1Mask); // Test the |ASN1_STRING| form of |X509_ATTRIBUTE_set1_data|. ASSERT_TRUE(X509_ATTRIBUTE_set1_data(attr.get(), V_ASN1_BMPSTRING, kTest2, sizeof(kTest2))); check_attribute(attr.get(), kTest1Mask | kTest2Mask); // The |ASN1_STRING| form of |X509_ATTRIBUTE_set1_data| should correctly // handle negative integers. const uint8_t kOne = 1; ASSERT_TRUE( X509_ATTRIBUTE_set1_data(attr.get(), V_ASN1_NEG_INTEGER, &kOne, 1)); check_attribute(attr.get(), kTest1Mask | kTest2Mask | kTest3Mask); // Test the |ASN1_TYPE| form of |X509_ATTRIBUTE_set1_data|. attr.reset(X509_ATTRIBUTE_new()); ASSERT_TRUE(attr); ASSERT_TRUE( X509_ATTRIBUTE_set1_object(attr.get(), OBJ_nid2obj(NID_friendlyName))); str.reset(ASN1_STRING_type_new(V_ASN1_BMPSTRING)); ASSERT_TRUE(str); ASSERT_TRUE(ASN1_STRING_set(str.get(), kTest1, sizeof(kTest1))); ASSERT_TRUE( X509_ATTRIBUTE_set1_data(attr.get(), V_ASN1_BMPSTRING, str.get(), -1)); check_attribute(attr.get(), kTest1Mask); // An |attrtype| of zero leaves the attribute empty. attr.reset(X509_ATTRIBUTE_create_by_NID( nullptr, NID_friendlyName, /*attrtype=*/0, /*data=*/nullptr, /*len=*/0)); ASSERT_TRUE(attr); check_attribute(attr.get(), 0); } // Test that, by default, |X509_V_FLAG_TRUSTED_FIRST| is set, which means we'll // skip over server-sent expired intermediates when there is a local trust // anchor that works better. TEST(X509Test, TrustedFirst) { // Generate the following certificates: // // Root 2 (in store, expired) // | // Root 1 (in store) Root 1 (cross-sign) // \ / // Intermediate // | // Leaf bssl::UniquePtr key = PrivateKeyFromPEM(kP256Key); ASSERT_TRUE(key); bssl::UniquePtr root2 = MakeTestCert("Root 2", "Root 2", key.get(), /*is_ca=*/true); ASSERT_TRUE(root2); ASSERT_TRUE(ASN1_TIME_adj(X509_getm_notAfter(root2.get()), kReferenceTime, /*offset_day=*/0, /*offset_sec=*/-1)); ASSERT_TRUE(X509_sign(root2.get(), key.get(), EVP_sha256())); bssl::UniquePtr root1 = MakeTestCert("Root 1", "Root 1", key.get(), /*is_ca=*/true); ASSERT_TRUE(root1); ASSERT_TRUE(X509_sign(root1.get(), key.get(), EVP_sha256())); bssl::UniquePtr root1_cross = MakeTestCert("Root 2", "Root 1", key.get(), /*is_ca=*/true); ASSERT_TRUE(root1_cross); ASSERT_TRUE(X509_sign(root1_cross.get(), key.get(), EVP_sha256())); bssl::UniquePtr intermediate = MakeTestCert("Root 1", "Intermediate", key.get(), /*is_ca=*/true); ASSERT_TRUE(intermediate); ASSERT_TRUE(X509_sign(intermediate.get(), key.get(), EVP_sha256())); bssl::UniquePtr leaf = MakeTestCert("Intermediate", "Leaf", key.get(), /*is_ca=*/false); ASSERT_TRUE(leaf); ASSERT_TRUE(X509_sign(leaf.get(), key.get(), EVP_sha256())); // As a control, confirm that |leaf| -> |intermediate| -> |root1| is valid, // but the path through |root1_cross| is expired. EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root1.get()}, {intermediate.get()}, {})); EXPECT_EQ(X509_V_ERR_CERT_HAS_EXPIRED, Verify(leaf.get(), {root2.get()}, {intermediate.get(), root1_cross.get()}, {})); // By default, we should find the |leaf| -> |intermediate| -> |root2| chain, // skipping |root1_cross|. EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root1.get(), root2.get()}, {intermediate.get(), root1_cross.get()}, {})); // When |X509_V_FLAG_TRUSTED_FIRST| is disabled, we get stuck on the expired // intermediate. Note we need the callback to clear the flag. Setting |flags| // to zero only skips setting new flags. // // This test exists to confirm our current behavior, but these modes are just // workarounds for not having an actual path-building verifier. If we fix it, // this test can be removed. EXPECT_EQ(X509_V_ERR_CERT_HAS_EXPIRED, Verify(leaf.get(), {root1.get(), root2.get()}, {intermediate.get(), root1_cross.get()}, {}, /*flags=*/0, [&](X509_STORE_CTX *ctx) { X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(ctx); X509_VERIFY_PARAM_clear_flags(param, X509_V_FLAG_TRUSTED_FIRST); })); // Even when |X509_V_FLAG_TRUSTED_FIRST| is disabled, if |root2| is not // trusted, the alt chains logic recovers the path. EXPECT_EQ( X509_V_OK, Verify(leaf.get(), {root1.get()}, {intermediate.get(), root1_cross.get()}, {}, /*flags=*/0, [&](X509_STORE_CTX *ctx) { X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(ctx); X509_VERIFY_PARAM_clear_flags(param, X509_V_FLAG_TRUSTED_FIRST); })); } // Test that notBefore and notAfter checks work correctly. TEST(X509Test, Expiry) { bssl::UniquePtr key = PrivateKeyFromPEM(kP256Key); ASSERT_TRUE(key); auto make_cert = [&](const char *issuer, const char *subject, bool is_ca, int not_before_offset, int not_after_offset) -> bssl::UniquePtr { bssl::UniquePtr cert = MakeTestCert(issuer, subject, key.get(), is_ca); if (cert == nullptr || !ASN1_TIME_adj(X509_getm_notBefore(cert.get()), kReferenceTime, /*offset_day=*/not_before_offset, /*offset_sec=*/0) || !ASN1_TIME_adj(X509_getm_notAfter(cert.get()), kReferenceTime, /*offset_day=*/not_after_offset, /*offset_sec=*/0) || !X509_sign(cert.get(), key.get(), EVP_sha256())) { return nullptr; } return cert; }; struct Certs { bssl::UniquePtr not_yet_valid = nullptr, valid = nullptr, expired = nullptr; }; auto make_certs = [&](const char *issuer, const char *subject, bool is_ca) -> Certs { Certs certs; certs.not_yet_valid = make_cert(issuer, subject, is_ca, /*not_before_offset=*/1, /*not_after_offset=*/2); certs.valid = make_cert(issuer, subject, is_ca, /*not_before_offset=*/-1, /*not_after_offset=*/1); certs.expired = make_cert(issuer, subject, is_ca, /*not_before_offset=*/-2, /*not_after_offset=*/-1); if (certs.not_yet_valid == nullptr || certs.valid == nullptr || certs.expired == nullptr) { return Certs{}; } return certs; }; Certs root = make_certs("Root", "Root", /*is_ca=*/true); ASSERT_TRUE(root.valid); Certs root_cross = make_certs("Root 2", "Root", /*is_ca=*/true); ASSERT_TRUE(root_cross.valid); Certs intermediate = make_certs("Root", "Intermediate", /*is_ca=*/true); ASSERT_TRUE(intermediate.valid); Certs leaf = make_certs("Intermediate", "Leaf", /*is_ca=*/false); ASSERT_TRUE(leaf.valid); for (bool check_time : {true, false}) { SCOPED_TRACE(check_time); for (bool partial_chain : {true, false}) { SCOPED_TRACE(partial_chain); unsigned long flags = 0; if (!check_time) { flags |= X509_V_FLAG_NO_CHECK_TIME; } if (partial_chain) { flags |= X509_V_FLAG_PARTIAL_CHAIN; } int not_yet_valid = check_time ? X509_V_ERR_CERT_NOT_YET_VALID : X509_V_OK; int has_expired = check_time ? X509_V_ERR_CERT_HAS_EXPIRED : X509_V_OK; EXPECT_EQ(not_yet_valid, Verify(leaf.not_yet_valid.get(), {root.valid.get()}, {intermediate.valid.get()}, {}, flags)); EXPECT_EQ(not_yet_valid, Verify(leaf.valid.get(), {root.valid.get()}, {intermediate.not_yet_valid.get()}, {}, flags)); EXPECT_EQ(not_yet_valid, Verify(leaf.valid.get(), {root.not_yet_valid.get()}, {intermediate.valid.get()}, {}, flags)); EXPECT_EQ(X509_V_OK, Verify(leaf.valid.get(), {root.valid.get()}, {intermediate.valid.get()}, {}, flags)); EXPECT_EQ(has_expired, Verify(leaf.expired.get(), {root.valid.get()}, {intermediate.valid.get()}, {}, flags)); EXPECT_EQ(has_expired, Verify(leaf.valid.get(), {root.valid.get()}, {intermediate.expired.get()}, {}, flags)); EXPECT_EQ(has_expired, Verify(leaf.valid.get(), {root.expired.get()}, {intermediate.valid.get()}, {}, flags)); if (!partial_chain) { // By default, non-self-signed certificates are not valid trust anchors. EXPECT_EQ(X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT, Verify(leaf.valid.get(), {root_cross.valid.get()}, {intermediate.valid.get()}, {}, flags)); } else { // |X509_V_FLAG_PARTIAL_CHAIN| allows non-self-signed trust anchors. EXPECT_EQ(X509_V_OK, Verify(leaf.valid.get(), {root_cross.valid.get()}, {intermediate.valid.get()}, {}, flags)); // Expiry of the trust anchor must still be checked. EXPECT_EQ(not_yet_valid, Verify(leaf.valid.get(), {root_cross.not_yet_valid.get()}, {intermediate.valid.get()}, {}, flags)); EXPECT_EQ(has_expired, Verify(leaf.valid.get(), {root_cross.expired.get()}, {intermediate.valid.get()}, {}, flags)); } // When the trust anchor is the target certificate, expiry should also be // checked. EXPECT_EQ(X509_V_OK, Verify(root.valid.get(), {root.valid.get()}, {}, {}, flags)); EXPECT_EQ(not_yet_valid, Verify(root.not_yet_valid.get(), {root.not_yet_valid.get()}, {}, {}, flags)); EXPECT_EQ(has_expired, Verify(root.expired.get(), {root.expired.get()}, {}, {}, flags)); } } // X509_V_FLAG_USE_CHECK_TIME is an internal flag, but one caller relies on // being able to clear it to restore the system time. Using the system time, // all certificates in this test should read as expired. EXPECT_EQ(X509_V_ERR_CERT_HAS_EXPIRED, Verify(leaf.valid.get(), {root.valid.get()}, {intermediate.valid.get()}, {}, 0, [](X509_STORE_CTX *ctx) { X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(ctx); X509_VERIFY_PARAM_clear_flags(param, X509_V_FLAG_USE_CHECK_TIME); })); } // Test that we don't break the search if an expired candidate cert exists. TEST(X509Test, ExpiredCandidate) { // Generate the following certificates: // // Root (in store) // | | // Intermediate 1 Intermediate 2 (expired) // \ / // Leaf bssl::UniquePtr key = PrivateKeyFromPEM(kP256Key); ASSERT_TRUE(key); bssl::UniquePtr root = MakeTestCert("Root", "Root", key.get(), /*is_ca=*/true); ASSERT_TRUE(root); ASSERT_TRUE(X509_sign(root.get(), key.get(), EVP_sha256())); bssl::UniquePtr intermediate1 = MakeTestCert("Root", "Intermediate", key.get(), /*is_ca=*/true); ASSERT_TRUE(intermediate1); ASSERT_TRUE(X509_sign(intermediate1.get(), key.get(), EVP_sha256())); bssl::UniquePtr intermediate2 = MakeTestCert("Root", "Intermediate", key.get(), /*is_ca=*/true); ASSERT_TRUE(intermediate2); ASSERT_TRUE(ASN1_TIME_adj(X509_getm_notAfter(intermediate2.get()), kReferenceTime, /*offset_day=*/0, /*offset_sec=*/-1)); ASSERT_TRUE(X509_sign(intermediate2.get(), key.get(), EVP_sha256())); bssl::UniquePtr leaf = MakeTestCert("Intermediate", "Leaf", key.get(), /*is_ca=*/false); ASSERT_TRUE(leaf); ASSERT_TRUE(X509_sign(leaf.get(), key.get(), EVP_sha256())); // As a control, confirm that |leaf| -> |intermediate1| -> |root1| is valid, // but the path through |intermediate2| is expired. EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {intermediate1.get()}, {})); EXPECT_EQ(X509_V_ERR_CERT_HAS_EXPIRED, Verify(leaf.get(), {root.get()}, {intermediate2.get()}, {})); // We should skip over expired candidate certificates and continue looking. // The test with the expired cert coming first in the stack would fail without // support for this. EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {intermediate1.get(), intermediate2.get()}, {})); EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {intermediate2.get(), intermediate1.get()}, {})); // Test that |X509_STORE_CTX_get1_issuer| prioritizes non-expired certs. // With this set up, |intermediate2| would have been returned if expired certs // weren't filtered. bssl::UniquePtr intermediates_stack( CertsToStack({intermediate1.get(), intermediate2.get()})); bssl::UniquePtr ctx(X509_STORE_CTX_new()); bssl::UniquePtr store(X509_STORE_new()); // TODO: Remove this ifdef when we pull in google/boringssl@1340a5b. #if defined(OPENSSL_WINDOWS) ASSERT_TRUE(X509_STORE_add_cert(store.get(), intermediate1.get())); ASSERT_TRUE(X509_STORE_add_cert(store.get(), intermediate2.get())); #else ASSERT_TRUE(X509_STORE_add_cert(store.get(), intermediate2.get())); ASSERT_TRUE(X509_STORE_add_cert(store.get(), intermediate1.get())); #endif ASSERT_TRUE(X509_STORE_CTX_init(ctx.get(), store.get(), leaf.get(), intermediates_stack.get())); X509 *issuer; EXPECT_TRUE(X509_STORE_CTX_get1_issuer(&issuer, ctx.get(), leaf.get())); EXPECT_TRUE(issuer); // Check that we return the non-expired |intermediate1|. EXPECT_EQ(X509_cmp(issuer, intermediate1.get()), 0); bssl::UniquePtr free(issuer); } TEST(X509Test, SignatureVerification) { bssl::UniquePtr key = PrivateKeyFromPEM(kP256Key); ASSERT_TRUE(key); struct Certs { bssl::UniquePtr valid = nullptr; bssl::UniquePtr bad_key_type = nullptr, bad_key = nullptr; bssl::UniquePtr bad_sig_type = nullptr, bad_sig = nullptr; }; auto make_certs = [&](const char *issuer, const char *subject, bool is_ca) -> Certs { Certs certs; certs.valid = MakeTestCert(issuer, subject, key.get(), is_ca); if (certs.valid == nullptr || !X509_sign(certs.valid.get(), key.get(), EVP_sha256())) { return Certs{}; } static const uint8_t kInvalid[] = {'i', 'n', 'v', 'a', 'l', 'i', 'd'}; // Extracting the algorithm identifier from |certs.valid|'s SPKI, with // OpenSSL's API, is very tedious. Instead, we'll just rely on knowing it is // ecPublicKey with P-256 as parameters. const ASN1_BIT_STRING *pubkey = X509_get0_pubkey_bitstr(certs.valid.get()); int pubkey_len = ASN1_STRING_length(pubkey); // Sign a copy of the certificate where the key type is an unsupported OID. bssl::UniquePtr pubkey_data(static_cast( OPENSSL_memdup(ASN1_STRING_get0_data(pubkey), pubkey_len))); certs.bad_key_type = MakeTestCert(issuer, subject, key.get(), is_ca); if (pubkey_data == nullptr || certs.bad_key_type == nullptr || !X509_PUBKEY_set0_param(X509_get_X509_PUBKEY(certs.bad_key_type.get()), OBJ_nid2obj(NID_subject_alt_name), V_ASN1_UNDEF, /*param_value=*/nullptr, pubkey_data.release(), pubkey_len) || !X509_sign(certs.bad_key_type.get(), key.get(), EVP_sha256())) { return Certs{}; } // Sign a copy of the certificate where the key data is unparsable. pubkey_data.reset( static_cast(OPENSSL_memdup(kInvalid, sizeof(kInvalid)))); certs.bad_key = MakeTestCert(issuer, subject, key.get(), is_ca); if (pubkey_data == nullptr || certs.bad_key == nullptr || !X509_PUBKEY_set0_param(X509_get_X509_PUBKEY(certs.bad_key.get()), OBJ_nid2obj(NID_X9_62_id_ecPublicKey), V_ASN1_OBJECT, OBJ_nid2obj(NID_X9_62_prime256v1), pubkey_data.release(), sizeof(kInvalid)) || !X509_sign(certs.bad_key.get(), key.get(), EVP_sha256())) { return Certs{}; } bssl::UniquePtr wrong_algo(X509_ALGOR_new()); if (wrong_algo == nullptr || !X509_ALGOR_set0(wrong_algo.get(), OBJ_nid2obj(NID_subject_alt_name), V_ASN1_NULL, nullptr)) { return Certs{}; } certs.bad_sig_type.reset(X509_dup(certs.valid.get())); if (certs.bad_sig_type == nullptr || !X509_set1_signature_algo(certs.bad_sig_type.get(), wrong_algo.get())) { return Certs{}; } certs.bad_sig.reset(X509_dup(certs.valid.get())); if (certs.bad_sig == nullptr || !X509_set1_signature_value(certs.bad_sig.get(), kInvalid, sizeof(kInvalid))) { return Certs{}; } return certs; }; Certs root(make_certs("Root", "Root", /*is_ca=*/true)); ASSERT_TRUE(root.valid); Certs intermediate(make_certs("Root", "Intermediate", /*is_ca=*/true)); ASSERT_TRUE(intermediate.valid); Certs leaf(make_certs("Intermediate", "Leaf", /*is_ca=*/false)); ASSERT_TRUE(leaf.valid); // Check the base chain. EXPECT_EQ(X509_V_OK, Verify(leaf.valid.get(), {root.valid.get()}, {intermediate.valid.get()}, {})); // An invalid or unsupported signature in the leaf or intermediate is noticed. EXPECT_EQ(X509_V_ERR_CERT_SIGNATURE_FAILURE, Verify(leaf.bad_sig.get(), {root.valid.get()}, {intermediate.valid.get()}, {})); EXPECT_EQ(X509_V_ERR_CERT_SIGNATURE_FAILURE, Verify(leaf.bad_sig_type.get(), {root.valid.get()}, {intermediate.valid.get()}, {})); EXPECT_EQ(X509_V_ERR_CERT_SIGNATURE_FAILURE, Verify(leaf.valid.get(), {root.valid.get()}, {intermediate.bad_sig.get()}, {})); EXPECT_EQ(X509_V_ERR_CERT_SIGNATURE_FAILURE, Verify(leaf.valid.get(), {root.valid.get()}, {intermediate.bad_sig_type.get()}, {})); // By default, the redundant root signature is not checked. EXPECT_EQ(X509_V_OK, Verify(leaf.valid.get(), {root.bad_sig.get()}, {intermediate.valid.get()}, {})); EXPECT_EQ(X509_V_OK, Verify(leaf.valid.get(), {root.bad_sig_type.get()}, {intermediate.valid.get()}, {})); // The caller can request checking it, although it's pointless. EXPECT_EQ( X509_V_ERR_CERT_SIGNATURE_FAILURE, Verify(leaf.valid.get(), {root.bad_sig.get()}, {intermediate.valid.get()}, {}, X509_V_FLAG_CHECK_SS_SIGNATURE)); EXPECT_EQ( X509_V_ERR_CERT_SIGNATURE_FAILURE, Verify(leaf.valid.get(), {root.bad_sig_type.get()}, {intermediate.valid.get()}, {}, X509_V_FLAG_CHECK_SS_SIGNATURE)); // The above also applies when accepting a trusted, self-signed root as the // target certificate. EXPECT_EQ(X509_V_OK, Verify(root.bad_sig.get(), {root.bad_sig.get()}, {}, {})); EXPECT_EQ(X509_V_OK, Verify(root.bad_sig_type.get(), {root.bad_sig_type.get()}, {}, {})); EXPECT_EQ(X509_V_ERR_CERT_SIGNATURE_FAILURE, Verify(root.bad_sig.get(), {root.bad_sig.get()}, {}, {}, X509_V_FLAG_CHECK_SS_SIGNATURE)); EXPECT_EQ(X509_V_ERR_CERT_SIGNATURE_FAILURE, Verify(root.bad_sig_type.get(), {root.bad_sig_type.get()}, {}, {}, X509_V_FLAG_CHECK_SS_SIGNATURE)); // If an intermediate is a trust anchor, the redundant signature is always // ignored, even with |X509_V_FLAG_CHECK_SS_SIGNATURE|. (We cannot check the // signature without the key.) EXPECT_EQ(X509_V_OK, Verify(leaf.valid.get(), {intermediate.bad_sig.get()}, {}, {}, X509_V_FLAG_CHECK_SS_SIGNATURE | X509_V_FLAG_PARTIAL_CHAIN)); EXPECT_EQ(X509_V_OK, Verify(leaf.valid.get(), {intermediate.bad_sig_type.get()}, {}, {}, X509_V_FLAG_CHECK_SS_SIGNATURE | X509_V_FLAG_PARTIAL_CHAIN)); EXPECT_EQ(X509_V_OK, Verify(leaf.valid.get(), {intermediate.bad_sig.get()}, {}, {}, X509_V_FLAG_PARTIAL_CHAIN)); EXPECT_EQ(X509_V_OK, Verify(leaf.valid.get(), {intermediate.bad_sig_type.get()}, {}, {}, X509_V_FLAG_PARTIAL_CHAIN)); // Bad keys in the root and intermediate are rejected. EXPECT_EQ(X509_V_ERR_UNSPECIFIED, Verify(leaf.valid.get(), {root.bad_key.get()}, {intermediate.valid.get()}, {})); EXPECT_EQ(X509_V_ERR_UNSPECIFIED, Verify(leaf.valid.get(), {root.bad_key_type.get()}, {intermediate.valid.get()}, {})); EXPECT_EQ(X509_V_ERR_UNSPECIFIED, Verify(leaf.valid.get(), {root.valid.get()}, {intermediate.bad_key.get()}, {})); EXPECT_EQ(X509_V_ERR_UNSPECIFIED, Verify(leaf.valid.get(), {root.valid.get()}, {intermediate.bad_key_type.get()}, {})); // Bad keys in the leaf are rejected. EXPECT_EQ(X509_V_ERR_UNSPECIFIED, Verify(leaf.bad_key.get(), {root.valid.get()}, {intermediate.valid.get()}, {})); EXPECT_EQ(X509_V_UNABLE_TO_GET_CERTS_PUBLIC_KEY, Verify(leaf.bad_key.get(), {root.valid.get()}, {intermediate.valid.get()}, {}, 0, [](X509_STORE_CTX *ctx) { X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(ctx); X509_VERIFY_PARAM_enable_ec_key_explicit_params(param); })); EXPECT_EQ(X509_V_ERR_UNSPECIFIED, Verify(leaf.bad_key_type.get(), {root.valid.get()}, {intermediate.valid.get()}, {})); EXPECT_EQ(X509_V_UNABLE_TO_GET_CERTS_PUBLIC_KEY, Verify(leaf.bad_key_type.get(), {root.valid.get()}, {intermediate.valid.get()}, {}, 0, [](X509_STORE_CTX *ctx) { X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(ctx); X509_VERIFY_PARAM_enable_ec_key_explicit_params(param); })); // At the time we go to verify signatures, it is possible that we have a // single-element certificate chain with a certificate that isn't self-signed. // This does not seem to be reachable except if missing trust anchors are // suppressed with the verify callback, but exercise this codepath anyway. EXPECT_EQ(X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE, Verify(leaf.valid.get(), {}, {}, {}, 0, [](X509_STORE_CTX *ctx) { X509_STORE_CTX_set_verify_cb( ctx, [](int ok, X509_STORE_CTX *ctx_inner) -> int { if (ok) { return ok; } // Suppress the missing issuer certificate. int err = X509_STORE_CTX_get_error(ctx_inner); return err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY; }); })); } // kConstructedBitString is an X.509 certificate where the signature is encoded // as a BER constructed BIT STRING. Note that, while OpenSSL's parser accepts // this input, it interprets the value incorrectly. static const char kConstructedBitString[] = R"( -----BEGIN CERTIFICATE----- MIIBJTCBxqADAgECAgIE0jAKBggqhkjOPQQDAjAPMQ0wCwYDVQQDEwRUZXN0MCAX DTAwMDEwMTAwMDAwMFoYDzIxMDAwMTAxMDAwMDAwWjAPMQ0wCwYDVQQDEwRUZXN0 MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE5itp4r9ln5e+Lx4NlIpM1Zdrt6ke DUb73ampHp3culoB59aXqAoY+cPEox5W4nyDSNsWGhz1HX7xlC1Lz3IiwaMQMA4w DAYDVR0TBAUwAwEB/zAKBggqhkjOPQQDAiNOAyQAMEYCIQCp0iIX5s30KXjihR4g KnJpd3seqGlVRqCVgrD0KGYDJgA1QAIhAKkx0vR82QU0NtHDD11KX/LuQF2T+2nX oeKp5LKAbMVi -----END CERTIFICATE----- )"; // kConstructedOctetString is an X.509 certificate where an extension is encoded // as a BER constructed OCTET STRING. static const char kConstructedOctetString[] = R"( -----BEGIN CERTIFICATE----- MIIBJDCByqADAgECAgIE0jAKBggqhkjOPQQDAjAPMQ0wCwYDVQQDEwRUZXN0MCAX DTAwMDEwMTAwMDAwMFoYDzIxMDAwMTAxMDAwMDAwWjAPMQ0wCwYDVQQDEwRUZXN0 MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE5itp4r9ln5e+Lx4NlIpM1Zdrt6ke DUb73ampHp3culoB59aXqAoY+cPEox5W4nyDSNsWGhz1HX7xlC1Lz3IiwaMUMBIw EAYDVR0TJAkEAzADAQQCAf8wCgYIKoZIzj0EAwIDSQAwRgIhAKnSIhfmzfQpeOKF HiAqcml3ex6oaVVGoJWCsPQoZjVAAiEAqTHS9HzZBTQ20cMPXUpf8u5AXZP7adeh 4qnksoBsxWI= -----END CERTIFICATE----- )"; // kIndefiniteLength is an X.509 certificate where the outermost SEQUENCE uses // BER indefinite-length encoding. static const char kIndefiniteLength[] = R"( -----BEGIN CERTIFICATE----- MIAwgcagAwIBAgICBNIwCgYIKoZIzj0EAwIwDzENMAsGA1UEAxMEVGVzdDAgFw0w MDAxMDEwMDAwMDBaGA8yMTAwMDEwMTAwMDAwMFowDzENMAsGA1UEAxMEVGVzdDBZ MBMGByqGSM49AgEGCCqGSM49AwEHA0IABOYraeK/ZZ+Xvi8eDZSKTNWXa7epHg1G +92pqR6d3LpaAefWl6gKGPnDxKMeVuJ8g0jbFhoc9R1+8ZQtS89yIsGjEDAOMAwG A1UdEwQFMAMBAf8wCgYIKoZIzj0EAwIDSQAwRgIhAKnSIhfmzfQpeOKFHiAqcml3 ex6oaVVGoJWCsPQoZjVAAiEAqTHS9HzZBTQ20cMPXUpf8u5AXZP7adeh4qnksoBs xWIAAA== -----END CERTIFICATE----- )"; // kNonZeroPadding is an X.09 certificate where the BIT STRING signature field // has non-zero padding values. static const char kNonZeroPadding[] = R"( -----BEGIN CERTIFICATE----- MIIB0DCCAXagAwIBAgIJANlMBNpJfb/rMAkGByqGSM49BAEwRTELMAkGA1UEBhMC QVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdp dHMgUHR5IEx0ZDAeFw0xNDA0MjMyMzIxNTdaFw0xNDA1MjMyMzIxNTdaMEUxCzAJ BgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5l dCBXaWRnaXRzIFB0eSBMdGQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATmK2ni v2Wfl74vHg2UikzVl2u3qR4NRvvdqakendy6WgHn1peoChj5w8SjHlbifINI2xYa HPUdfvGULUvPciLBo1AwTjAdBgNVHQ4EFgQUq4TSrKuV8IJOFngHVVdf5CaNgtEw HwYDVR0jBBgwFoAUq4TSrKuV8IJOFngHVVdf5CaNgtEwDAYDVR0TBAUwAwEB/zAJ BgcqhkjOPQQBA0kBMEUCIQDyoDVeUTo2w4J5m+4nUIWOcAZ0lVfSKXQA9L4Vh13E BwIgfB55FGohg/B6dGh5XxSZmmi08cueFV7mHzJSYV51yRQB -----END CERTIFICATE----- )"; // kHighTagNumber is an X.509 certificate where the outermost SEQUENCE tag uses // high tag number form. static const char kHighTagNumber[] = R"( -----BEGIN CERTIFICATE----- PxCCASAwgcagAwIBAgICBNIwCgYIKoZIzj0EAwIwDzENMAsGA1UEAxMEVGVzdDAg Fw0wMDAxMDEwMDAwMDBaGA8yMTAwMDEwMTAwMDAwMFowDzENMAsGA1UEAxMEVGVz dDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOYraeK/ZZ+Xvi8eDZSKTNWXa7ep Hg1G+92pqR6d3LpaAefWl6gKGPnDxKMeVuJ8g0jbFhoc9R1+8ZQtS89yIsGjEDAO MAwGA1UdEwQFMAMBAf8wCgYIKoZIzj0EAwIDSQAwRgIhAKnSIhfmzfQpeOKFHiAq cml3ex6oaVVGoJWCsPQoZjVAAiEAqTHS9HzZBTQ20cMPXUpf8u5AXZP7adeh4qnk soBsxWI= -----END CERTIFICATE----- )"; TEST(X509Test, BER) { // Constructed strings are forbidden in DER, but allowed in BER. AWS-LC has // reinstated support for implicit BER constructed strings in the ASN1 macros // to align with OpenSSL behavior. EXPECT_TRUE(CertFromPEM(kConstructedBitString)); EXPECT_TRUE(CertFromPEM(kConstructedOctetString)); // Indefinite lengths are forbidden in DER, but allowed in BER. AWS-LC has // reinstated indefinite BER support in the ASN1 macros to align with OpenSSL // behavior. EXPECT_TRUE(CertFromPEM(kIndefiniteLength)); // Padding bits in BIT STRINGs must be zero in BER. EXPECT_FALSE(CertFromPEM(kNonZeroPadding)); // Tags must be minimal in both BER and DER, though many BER decoders // incorrectly support non-minimal tags. EXPECT_FALSE(CertFromPEM(kHighTagNumber)); } TEST(X509Test, Names) { bssl::UniquePtr key = PrivateKeyFromPEM(kP256Key); ASSERT_TRUE(key); bssl::UniquePtr root = MakeTestCert("Root", "Root", key.get(), /*is_ca=*/true); ASSERT_TRUE(root); ASSERT_TRUE(X509_sign(root.get(), key.get(), EVP_sha256())); struct { std::vector> cert_subject; std::vector cert_dns_names; std::vector cert_emails; std::vector valid_dns_names; std::vector invalid_dns_names; std::vector valid_emails; std::vector invalid_emails; unsigned flags; } kTests[] = { // DNS names only match DNS names and do so case-insensitively. { /*cert_subject=*/{}, /*cert_dns_names=*/{"example.com", "WWW.EXAMPLE.COM"}, /*cert_emails=*/{}, /*valid_dns_names=*/ {"example.com", "EXAMPLE.COM", "www.example.com", "WWW.EXAMPLE.COM"}, /*invalid_dns_names=*/{"test.example.com", "example.org"}, /*valid_emails=*/{}, /*invalid_emails=*/{"test@example.com", "example.com"}, /*flags=*/0, }, // DNS wildcards match exactly one component. { /*cert_subject=*/{}, /*cert_dns_names=*/{"*.example.com", "*.EXAMPLE.ORG"}, /*cert_emails=*/{}, /*valid_dns_names=*/ {"www.example.com", "WWW.EXAMPLE.COM", "www.example.org", "WWW.EXAMPLE.ORG"}, /*invalid_dns_names=*/{"example.com", "test.www.example.com"}, /*valid_emails=*/{}, /*invalid_emails=*/{"test@example.com", "www.example.com"}, /*flags=*/0, }, // DNS wildcards can be disabled. // TODO(davidben): Can we remove this feature? Does anyone use it? { /*cert_subject=*/{}, /*cert_dns_names=*/{"example.com", "*.example.com"}, /*cert_emails=*/{}, /*valid_dns_names=*/{"example.com"}, /*invalid_dns_names=*/{"www.example.com"}, /*valid_emails=*/{}, /*invalid_emails=*/{}, /*flags=*/X509_CHECK_FLAG_NO_WILDCARDS, }, // Invalid DNS wildcards do not match. { /*cert_subject=*/{}, /*cert_dns_names=*/ {"a.*", "**.b.example", "*c.example", "d*.example", "e*e.example", "*", ".", "..", "*."}, /*cert_emails=*/{}, /*valid_dns_names=*/{}, /*invalid_dns_names=*/ {"a.example", "test.b.example", "cc.example", "dd.example", "eee.example", "f", "g."}, /*valid_emails=*/{}, /*invalid_emails=*/{}, /*flags=*/0, }, // IDNs match like any other DNS labels. { /*cert_subject=*/{}, /*cert_dns_names=*/ {"xn--rger-koa.a.example", "*.xn--rger-koa.b.example", "www.xn--rger-koa.c.example"}, /*cert_emails=*/{}, /*valid_dns_names=*/ {"xn--rger-koa.a.example", "www.xn--rger-koa.b.example", "www.xn--rger-koa.c.example"}, /*invalid_dns_names=*/ {"www.xn--rger-koa.a.example", "xn--rger-koa.b.example", "www.xn--rger-koa.d.example"}, /*valid_emails=*/{}, /*invalid_emails=*/{}, /*flags=*/0, }, // For now, DNS names are also extracted out of the common name, but only // there is no SAN list. // TODO(https://crbug.com/boringssl/464): Remove this. { /*cert_subject=*/{{NID_commonName, "a.example"}, {NID_commonName, "*.b.example"}}, /*cert_dns_names=*/{}, /*cert_emails=*/{}, /*valid_dns_names=*/ {"a.example", "A.EXAMPLE", "test.b.example", "TEST.B.EXAMPLE"}, /*invalid_dns_names=*/{}, /*valid_emails=*/{}, /*invalid_emails=*/{}, /*flags=*/0, }, { /*cert_subject=*/{{NID_commonName, "a.example"}, {NID_commonName, "*.b.example"}}, /*cert_dns_names=*/{"example.com"}, /*cert_emails=*/{}, /*valid_dns_names=*/{}, /*invalid_dns_names=*/ {"a.example", "A.EXAMPLE", "test.b.example", "TEST.B.EXAMPLE"}, /*valid_emails=*/{}, /*invalid_emails=*/{}, /*flags=*/0, }, // Other subject RDNs do not provide DNS names. { /*cert_subject=*/{{NID_organizationName, "example.com"}}, /*cert_dns_names=*/{}, /*cert_emails=*/{}, /*valid_dns_names=*/{}, /*invalid_dns_names=*/{"example.com"}, /*valid_emails=*/{}, /*invalid_emails=*/{}, /*flags=*/0, }, // Input DNS names cannot have wildcards. { /*cert_subject=*/{}, /*cert_dns_names=*/{"www.example.com"}, /*cert_emails=*/{}, /*valid_dns_names=*/{}, /*invalid_dns_names=*/{"*.example.com"}, /*valid_emails=*/{}, /*invalid_emails=*/{}, /*flags=*/0, }, // OpenSSL has some non-standard wildcard syntax for input DNS names. We // support this for compatibility. { /*cert_subject=*/{}, /*cert_dns_names=*/{"www.a.example", "*.b.test"}, /*cert_emails=*/{}, /*valid_dns_names=*/{".a.example", ".b.test", ".example", ".test"}, /*invalid_dns_names=*/ {".www.a.example", ".www.b.test"}, /*valid_emails=*/{}, /*invalid_emails=*/{}, /*flags=*/0, }, { /*cert_subject=*/{}, /*cert_dns_names=*/{"www.a.example", "*.b.test"}, /*cert_emails=*/{}, /*valid_dns_names=*/{".a.example", ".b.test"}, /*invalid_dns_names=*/ {".www.a.example", ".www.b.test", ".example", ".test"}, /*valid_emails=*/{}, /*invalid_emails=*/{}, /*flags=*/X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS, }, // Emails match case-sensitively before the '@' and case-insensitively // after. They do not match DNS names. { /*cert_subject=*/{}, /*cert_dns_names=*/{}, /*cert_emails=*/{"test@a.example", "TEST@B.EXAMPLE"}, /*valid_dns_names=*/{}, /*invalid_dns_names=*/{"a.example", "b.example"}, /*valid_emails=*/ {"test@a.example", "test@A.EXAMPLE", "TEST@b.example", "TEST@B.EXAMPLE"}, /*invalid_emails=*/ {"TEST@a.example", "test@B.EXAMPLE", "another-test@a.example", "est@a.example"}, /*flags=*/0, }, // Emails may also be found in the subject. { /*cert_subject=*/{{NID_pkcs9_emailAddress, "test@a.example"}, {NID_pkcs9_emailAddress, "TEST@B.EXAMPLE"}}, /*cert_dns_names=*/{}, /*cert_emails=*/{}, /*valid_dns_names=*/{}, /*invalid_dns_names=*/{"a.example", "b.example"}, /*valid_emails=*/ {"test@a.example", "test@A.EXAMPLE", "TEST@b.example", "TEST@B.EXAMPLE"}, /*invalid_emails=*/ {"TEST@a.example", "test@B.EXAMPLE", "another-test@a.example", "est@a.example"}, /*flags=*/0, }, // There are no email wildcard names. { /*cert_subject=*/{}, /*cert_dns_names=*/{}, /*cert_emails=*/{"test@*.a.example", "@b.example", "*@c.example"}, /*valid_dns_names=*/{}, /*invalid_dns_names=*/{}, /*valid_emails=*/{}, /*invalid_emails=*/ {"test@test.a.example", "test@b.example", "test@c.example"}, /*flags=*/0, }, // Unrelated RDNs can be skipped when looking in the subject. { /*cert_subject=*/{{NID_organizationName, "Acme Corporation"}, {NID_commonName, "a.example"}, {NID_pkcs9_emailAddress, "test@b.example"}, {NID_countryName, "US"}}, /*cert_dns_names=*/{}, /*cert_emails=*/{}, /*valid_dns_names=*/{"a.example"}, /*invalid_dns_names=*/{}, /*valid_emails=*/{"test@b.example"}, /*invalid_emails=*/{}, /*flags=*/0, }, }; size_t i = 0; for (const auto &t : kTests) { SCOPED_TRACE(i++); // Issue a test certificate. bssl::UniquePtr cert = MakeTestCert("Root", "Leaf", key.get(), /*is_ca=*/false); ASSERT_TRUE(cert); if (!t.cert_subject.empty()) { bssl::UniquePtr subject(X509_NAME_new()); ASSERT_TRUE(subject); for (const auto &entry : t.cert_subject) { ASSERT_TRUE(X509_NAME_add_entry_by_NID( subject.get(), entry.first, MBSTRING_ASC, reinterpret_cast(entry.second.data()), entry.second.size(), /*loc=*/-1, /*set=*/0)); } ASSERT_TRUE(X509_set_subject_name(cert.get(), subject.get())); } bssl::UniquePtr sans(sk_GENERAL_NAME_new_null()); ASSERT_TRUE(sans); for (const auto &dns : t.cert_dns_names) { bssl::UniquePtr name(GENERAL_NAME_new()); ASSERT_TRUE(name); name->type = GEN_DNS; name->d.dNSName = ASN1_IA5STRING_new(); ASSERT_TRUE(name->d.dNSName); ASSERT_TRUE(ASN1_STRING_set(name->d.dNSName, dns.data(), dns.size())); ASSERT_TRUE(bssl::PushToStack(sans.get(), std::move(name))); } for (const auto &email : t.cert_emails) { bssl::UniquePtr name(GENERAL_NAME_new()); ASSERT_TRUE(name); name->type = GEN_EMAIL; name->d.rfc822Name = ASN1_IA5STRING_new(); ASSERT_TRUE(name->d.rfc822Name); ASSERT_TRUE( ASN1_STRING_set(name->d.rfc822Name, email.data(), email.size())); ASSERT_TRUE(bssl::PushToStack(sans.get(), std::move(name))); } if (sk_GENERAL_NAME_num(sans.get()) != 0) { ASSERT_TRUE(X509_add1_ext_i2d(cert.get(), NID_subject_alt_name, sans.get(), /*crit=*/0, /*flags=*/0)); } ASSERT_TRUE(X509_sign(cert.get(), key.get(), EVP_sha256())); for (const auto &dns : t.valid_dns_names) { SCOPED_TRACE(dns); EXPECT_EQ(1, X509_check_host(cert.get(), dns.data(), dns.size(), t.flags, /*peername=*/nullptr)); EXPECT_EQ(X509_V_OK, Verify(cert.get(), {root.get()}, /*intermediates=*/{}, /*crls=*/{}, /*flags=*/0, [&](X509_STORE_CTX *ctx) { X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(ctx); ASSERT_TRUE(X509_VERIFY_PARAM_set1_host( param, dns.data(), dns.size())); X509_VERIFY_PARAM_set_hostflags(param, t.flags); })); } for (const auto &dns : t.invalid_dns_names) { SCOPED_TRACE(dns); EXPECT_EQ(0, X509_check_host(cert.get(), dns.data(), dns.size(), t.flags, /*peername=*/nullptr)); EXPECT_EQ(X509_V_ERR_HOSTNAME_MISMATCH, Verify(cert.get(), {root.get()}, /*intermediates=*/{}, /*crls=*/{}, /*flags=*/0, [&](X509_STORE_CTX *ctx) { X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(ctx); ASSERT_TRUE(X509_VERIFY_PARAM_set1_host( param, dns.data(), dns.size())); X509_VERIFY_PARAM_set_hostflags(param, t.flags); })); } for (const auto &email : t.valid_emails) { SCOPED_TRACE(email); EXPECT_EQ( 1, X509_check_email(cert.get(), email.data(), email.size(), t.flags)); EXPECT_EQ(X509_V_OK, Verify(cert.get(), {root.get()}, /*intermediates=*/{}, /*crls=*/{}, /*flags=*/0, [&](X509_STORE_CTX *ctx) { X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(ctx); ASSERT_TRUE(X509_VERIFY_PARAM_set1_email( param, email.data(), email.size())); X509_VERIFY_PARAM_set_hostflags(param, t.flags); })); } for (const auto &email : t.invalid_emails) { SCOPED_TRACE(email); EXPECT_EQ( 0, X509_check_email(cert.get(), email.data(), email.size(), t.flags)); EXPECT_EQ(X509_V_ERR_EMAIL_MISMATCH, Verify(cert.get(), {root.get()}, /*intermediates=*/{}, /*crls=*/{}, /*flags=*/0, [&](X509_STORE_CTX *ctx) { X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(ctx); ASSERT_TRUE(X509_VERIFY_PARAM_set1_email( param, email.data(), email.size())); X509_VERIFY_PARAM_set_hostflags(param, t.flags); })); } } } TEST(X509Test, AddDuplicates) { bssl::UniquePtr store(X509_STORE_new()); bssl::UniquePtr a(CertFromPEM(kCrossSigningRootPEM)); bssl::UniquePtr b(CertFromPEM(kRootCAPEM)); ASSERT_TRUE(store); ASSERT_TRUE(a); ASSERT_TRUE(b); // To begin, add the certs to the store. Subsequent adds will be duplicative. EXPECT_TRUE(X509_STORE_add_cert(store.get(), a.get())); EXPECT_TRUE(X509_STORE_add_cert(store.get(), b.get())); // Half the threads add duplicate certs, the other half take a lock and // look them up to exercise un/locking functions. const size_t kNumThreads = 10; std::vector threads; for (size_t i = 0; i < kNumThreads/2; i++) { threads.emplace_back([&] { // Sleep with some jitter to offset thread execution uint8_t sleep_buf[1]; ASSERT_TRUE(RAND_bytes(sleep_buf, sizeof(sleep_buf))); std::this_thread::sleep_for(std::chrono::microseconds(1 + (sleep_buf[0] % 5))); EXPECT_TRUE(X509_STORE_add_cert(store.get(), a.get())); EXPECT_TRUE(X509_STORE_add_cert(store.get(), b.get())); }); threads.emplace_back([&] { uint8_t sleep_buf[1]; ASSERT_TRUE(RAND_bytes(sleep_buf, sizeof(sleep_buf))); ASSERT_TRUE(X509_STORE_lock(store.get())); // Sleep after taking the lock to cause contention. Sleep longer than the // adder half of threads to ensure we hold the lock while they contend // for it. std::this_thread::sleep_for(std::chrono::microseconds(11 + (sleep_buf[0] % 5))); ASSERT_TRUE(X509_STORE_unlock(store.get())); }); } for (auto &thread : threads) { thread.join(); } EXPECT_EQ(sk_X509_OBJECT_num(X509_STORE_get0_objects(store.get())), 2u); } TEST(X509Test, BytesToHex) { struct { std::vector bytes; const char *hex; } kTests[] = { {{}, ""}, {{0x00}, "00"}, {{0x00, 0x11, 0x22}, "00:11:22"}, {{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, "01:23:45:67:89:AB:CD:EF"}, }; for (const auto &t : kTests) { SCOPED_TRACE(Bytes(t.bytes)); bssl::UniquePtr hex( x509v3_bytes_to_hex(t.bytes.data(), t.bytes.size())); ASSERT_TRUE(hex); EXPECT_STREQ(hex.get(), t.hex); } } TEST(X509Test, NamePrint) { // kTestName is a DER-encoded X.509 that covers many cases. // // SEQUENCE { // SET { // SEQUENCE { // # countryName // OBJECT_IDENTIFIER { 2.5.4.6 } // PrintableString { "US" } // } // } // # Sets may be multi-valued, with different attributes. Try to keep this // # in DER set order, in case we ever enforce this in the parser. // SET { // SEQUENCE { // # stateOrProvinceName // OBJECT_IDENTIFIER { 2.5.4.8 } // PrintableString { "Some State" } // } // SEQUENCE { // # stateOrProvinceName // OBJECT_IDENTIFIER { 2.5.4.8 } // UTF8String { "Some Other State \xe2\x98\x83" } // } // SEQUENCE { // # stateOrProvinceName // OBJECT_IDENTIFIER { 2.5.4.8 } // BMPString { u"Another State \u2603" } // } // SEQUENCE { // # A custom OID // OBJECT_IDENTIFIER { 1.2.840.113554.4.1.72585.2 } // UniversalString { U"\u2603" } // } // } // # Custom OIDs may have non-string values. // SET { // SEQUENCE { // OBJECT_IDENTIFIER { 1.2.840.113554.4.1.72585.3 } // SEQUENCE { INTEGER { 1 } INTEGER { 2 } } // } // } // SET { // SEQUENCE { // # organizationName // OBJECT_IDENTIFIER { 2.5.4.10 } // PrintableString { "Org Name" } // } // } // SET { // SEQUENCE { // # commonName // OBJECT_IDENTIFIER { 2.5.4.3 } // # Embed common delimiter forms to test how well they get escaped. // UTF8String { "Common // Name/CN=A/CN=B,CN=A,CN=B+CN=A+CN=B;CN=A;CN=B\nCN=A\n" } // } // } // SET { // SEQUENCE { // # commonName // OBJECT_IDENTIFIER { 2.5.4.3 } // # Test escaping of leading and trailing spaces. // UTF8String { " spaces " } // } // } static const uint8_t kTestName[] = { 0x30, 0x82, 0x01, 0x00, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x6d, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x20, 0x53, 0x74, 0x61, 0x74, 0x65, 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x14, 0x53, 0x6f, 0x6d, 0x65, 0x20, 0x4f, 0x74, 0x68, 0x65, 0x72, 0x20, 0x53, 0x74, 0x61, 0x74, 0x65, 0x20, 0xe2, 0x98, 0x83, 0x30, 0x25, 0x06, 0x03, 0x55, 0x04, 0x08, 0x1e, 0x1e, 0x00, 0x41, 0x00, 0x6e, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x68, 0x00, 0x65, 0x00, 0x72, 0x00, 0x20, 0x00, 0x53, 0x00, 0x74, 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00, 0x20, 0x26, 0x03, 0x30, 0x14, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x1c, 0x04, 0x00, 0x00, 0x26, 0x03, 0x31, 0x18, 0x30, 0x16, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x03, 0x30, 0x06, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x08, 0x4f, 0x72, 0x67, 0x20, 0x4e, 0x61, 0x6d, 0x65, 0x31, 0x42, 0x30, 0x40, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x39, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x20, 0x4e, 0x61, 0x6d, 0x65, 0x2f, 0x43, 0x4e, 0x3d, 0x41, 0x2f, 0x43, 0x4e, 0x3d, 0x42, 0x2c, 0x43, 0x4e, 0x3d, 0x41, 0x2c, 0x43, 0x4e, 0x3d, 0x42, 0x2b, 0x43, 0x4e, 0x3d, 0x41, 0x2b, 0x43, 0x4e, 0x3d, 0x42, 0x3b, 0x43, 0x4e, 0x3d, 0x41, 0x3b, 0x43, 0x4e, 0x3d, 0x42, 0x0a, 0x43, 0x4e, 0x3d, 0x41, 0x0a, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x08, 0x20, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x20}; const uint8_t *ptr = kTestName; bssl::UniquePtr name( d2i_X509_NAME(nullptr, &ptr, sizeof(kTestName))); ASSERT_TRUE(name); EXPECT_EQ(ptr, kTestName + sizeof(kTestName)); struct { int indent; unsigned long flags; std::string printed; } kTests[] = { // RFC 2253 uses , and + separators and encodes the RDNs in reverse. // OpenSSL's implementation additionally happens to reverse the values // within each RDN. RFC 2253 says any order is permissible. {/*indent=*/0, /*flags=*/XN_FLAG_RFC2253, "CN=\\ spaces\\ ," "CN=Common " "Name/CN=A/CN=B\\,CN=A\\,CN=B\\+CN=A\\+CN=B\\;CN=A\\;CN=B\\0ACN=A\\0A," "O=Org Name," "1.2.840.113554.4.1.72585.3=#3006020101020102," "1.2.840.113554.4.1.72585.2=#1C0400002603+" "ST=Another State \\E2\\98\\83+" "ST=Some Other State \\E2\\98\\83+" "ST=Some State," "C=US"}, {/*indent=*/2, /*flags=*/XN_FLAG_RFC2253, " " "CN=\\ spaces\\ ," "CN=Common " "Name/CN=A/CN=B\\,CN=A\\,CN=B\\+CN=A\\+CN=B\\;CN=A\\;CN=B\\0ACN=A\\0A," "O=Org Name," "1.2.840.113554.4.1.72585.3=#3006020101020102," "1.2.840.113554.4.1.72585.2=#1C0400002603+" "ST=Another State \\E2\\98\\83+" "ST=Some Other State \\E2\\98\\83+" "ST=Some State," "C=US"}, // |XN_FLAG_ONELINE| is an OpenSSL-specific single-line format. It also // omits |XN_FLAG_DUMP_UNKNOWN_FIELDS|, so unknown OIDs that use known // string types will still be decoded. (This may drop important // information if the unknown OID distinguishes between string types.) It // also passes |ASN1_STRFLGS_ESC_QUOTE|. {/*indent=*/0, /*flags=*/XN_FLAG_ONELINE, "C = US, " "ST = Some State + " "ST = Some Other State \\E2\\98\\83 + " "ST = Another State \\E2\\98\\83 + " "1.2.840.113554.4.1.72585.2 = \\E2\\98\\83, " "1.2.840.113554.4.1.72585.3 = #3006020101020102, " "O = Org Name, " "CN = \"Common " "Name/CN=A/CN=B,CN=A,CN=B+CN=A+CN=B;CN=A;CN=B\\0ACN=A\\0A\", " "CN = \" spaces \""}, // |XN_FLAG_MULTILINE| is an OpenSSL-specific multi-line format that tries // to vertically align the equal sizes. The vertical alignment doesn't // quite handle multi-valued RDNs right and uses a non-RFC-2253 escaping. {/*indent=*/0, /*flags=*/XN_FLAG_MULTILINE, "countryName = US\n" "stateOrProvinceName = Some State + " "stateOrProvinceName = Some Other State \\U2603 + " "stateOrProvinceName = Another State \\U2603 + " "1.2.840.113554.4.1.72585.2 = \\U2603\n" "1.2.840.113554.4.1.72585.3 = 0\\06\\02\\01\\01\\02\\01\\02\n" "organizationName = Org Name\n" "commonName = Common " "Name/CN=A/CN=B,CN=A,CN=B+CN=A+CN=B;CN=A;CN=B\\0ACN=A\\0A\n" "commonName = spaces "}, // The multiline format indents every line. {/*indent=*/2, /*flags=*/XN_FLAG_MULTILINE, " countryName = US\n" " stateOrProvinceName = Some State + " "stateOrProvinceName = Some Other State \\U2603 + " "stateOrProvinceName = Another State \\U2603 + " "1.2.840.113554.4.1.72585.2 = \\U2603\n" " 1.2.840.113554.4.1.72585.3 = 0\\06\\02\\01\\01\\02\\01\\02\n" " organizationName = Org Name\n" " commonName = Common " "Name/CN=A/CN=B,CN=A,CN=B+CN=A+CN=B;CN=A;CN=B\\0ACN=A\\0A\n" " commonName = spaces "}, // Callers can also customize the output, wuith both |XN_FLAG_*| and // |ASN1_STRFLGS_*|. |XN_FLAG_SEP_SPLUS_SPC| uses semicolon separators and // |XN_FLAG_FN_OID| forces OIDs. {/*indent=*/0, /*flags=*/XN_FLAG_SEP_SPLUS_SPC | XN_FLAG_FN_OID | ASN1_STRFLGS_RFC2253 | ASN1_STRFLGS_ESC_QUOTE, "2.5.4.6=US; " "2.5.4.8=Some State + " "2.5.4.8=Some Other State \\E2\\98\\83 + " "2.5.4.8=Another State \\E2\\98\\83 + " "1.2.840.113554.4.1.72585.2=\\E2\\98\\83; " "1.2.840.113554.4.1.72585.3=#3006020101020102; " "2.5.4.10=Org Name; " "2.5.4.3=\"Common " "Name/CN=A/CN=B,CN=A,CN=B+CN=A+CN=B;CN=A;CN=B\\0ACN=A\\0A\"; " "2.5.4.3=\" spaces \""}, // |XN_FLAG_COMPAT| matches |X509_NAME_print|, rather than // |X509_NAME_print_ex|. // // TODO(davidben): This works by post-processing the output of // |X509_NAME_oneline|, which uses "/"" separators, and replacing with // ", ". The escaping is ambiguous and the post-processing is buggy, so // some of the trailing slashes are still present and some internal // slashes are mis-converted. {/*indent=*/0, /*flags=*/XN_FLAG_COMPAT, "C=US, " "ST=Some State, " "ST=Some Other State \\xE2\\x98\\x83, " "ST=\\x00A\\x00n\\x00o\\x00t\\x00h\\x00e\\x00r\\x00 " "\\x00S\\x00t\\x00a\\x00t\\x00e\\x00 &\\x03/" "1.2.840.113554.4.1.72585.2=\\x00\\x00&\\x03/" "1.2.840.113554.4.1.72585.3=0\\x06\\x02\\x01\\x01\\x02\\x01\\x02, " "O=Org Name, " "CN=Common Name, " "CN=A, CN=B,CN=A,CN=B+CN=A+CN=B;CN=A;CN=B\\x0ACN=A\\x0A, " "CN= spaces "}, }; for (const auto &t : kTests) { SCOPED_TRACE(t.printed); bssl::UniquePtr bio(BIO_new(BIO_s_mem())); ASSERT_TRUE(bio); int len = X509_NAME_print_ex(bio.get(), name.get(), t.indent, t.flags); ASSERT_GT(len, 0); const uint8_t *printed; size_t printed_len; ASSERT_TRUE(BIO_mem_contents(bio.get(), &printed, &printed_len)); EXPECT_EQ(std::string(printed, printed + printed_len), t.printed); if (t.flags != XN_FLAG_COMPAT) { // TODO(davidben): |XN_FLAG_COMPAT| does not return the length. EXPECT_EQ(static_cast(len), printed_len); // Passing a null |BIO| measures the output instead. len = X509_NAME_print_ex(nullptr, name.get(), t.indent, t.flags); EXPECT_GT(len, 0); EXPECT_EQ(static_cast(len), printed_len); } } // TODO(davidben): This escapes the underlying bytes in the string, but that // is ambiguous without capturing the type. Should this escape like // |ASN1_STRFLGS_UTF8_CONVERT| instead? static const char *kOnelineComponents[] = { "/C=US", "/ST=Some State", "/ST=Some Other State \\xE2\\x98\\x83", ("/ST=\\x00A\\x00n\\x00o\\x00t\\x00h\\x00e\\x00r\\x00 " "\\x00S\\x00t\\x00a\\x00t\\x00e\\x00 &\\x03"), "/1.2.840.113554.4.1.72585.2=\\x00\\x00&\\x03", "/1.2.840.113554.4.1.72585.3=0\\x06\\x02\\x01\\x01\\x02\\x01\\x02", "/O=Org Name", "/CN=Common Name/CN=A/CN=B,CN=A,CN=B+CN=A+CN=B;CN=A;CN=B\\x0ACN=A\\x0A", "/CN= spaces ", }; std::string oneline_expected; for (const auto& component : kOnelineComponents) { oneline_expected += component; } // Given null buffer, |X509_NAME_oneline| allocates a new output. bssl::UniquePtr oneline(X509_NAME_oneline(name.get(), nullptr, 0)); ASSERT_TRUE(oneline); EXPECT_EQ(oneline.get(), oneline_expected); // Otherwise it writes to the specified buffer. Note one extra byte is needed // for the trailing NUL. char buf[1024]; ASSERT_GE(sizeof(buf), oneline_expected.size() + 2); ASSERT_EQ(buf, X509_NAME_oneline(name.get(), buf, oneline_expected.size() + 1)); EXPECT_EQ(buf, oneline_expected); memset(buf, 'a', sizeof(buf)); ASSERT_EQ(buf, X509_NAME_oneline(name.get(), buf, oneline_expected.size() + 2)); EXPECT_EQ(buf, oneline_expected); // If the length is too small, |X509_NAME_oneline| truncates at name // entry boundaries. EXPECT_EQ(nullptr, X509_NAME_oneline(name.get(), buf, 0)); for (size_t len = 1; len < oneline_expected.size(); len++) { SCOPED_TRACE(len); memset(buf, 'a', sizeof(buf)); EXPECT_EQ(buf, X509_NAME_oneline(name.get(), buf, len)); std::string truncated; for (const auto& component : kOnelineComponents) { if (truncated.size() + strlen(component) + 1 > len) { break; } truncated += component; } EXPECT_EQ(buf, truncated); } } // kRareRSAPEM is a certificate with the rare |nid_rsa|. static const char kRareRSAPEM[] = R"( -----BEGIN CERTIFICATE----- MIICVTCCAb6gAwIBAgIJAPuwTC6rEJsMMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX aWRnaXRzIFB0eSBMdGQwHhcNMTQwNDIzMjA1MDQwWhcNMTcwNDIyMjA1MDQwWjBF MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGcMAoGBFUIAQECAgQAA4GNADCBiQKBgQDY K8imMuRi/03z0K1Zi0WnvfFHvwlYeyK9Na6XJYaUoIDAtB92kWdGMdAQhLciHnAj kXLI6W15OoV3gA/ElRZ1xUpxTMhjP6PyY5wqT5r6y8FxbiiFKKAnHmUcrgfVW28t Q+0rkLGMryRtrukXOgXBv7gcrmU7G1jC2a7WqmeI8QIDAQABo1AwTjAdBgNVHQ4E FgQUi3XVrMsIvg4fZbf6Vr5sp3Xaha8wHwYDVR0jBBgwFoAUi3XVrMsIvg4fZbf6 Vr5sp3Xaha8wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQAIZuUICtYv w3cbpCGX6HNCtyI0guOfbytcdwzRkQaCsYNSDrTxrSSWxHwqg3Dl/RlvS+T3Yaua Xkioadstwt7GDP6MwpIpdbjchh0XZd3kjdJWqXSvihUDpRePNjNS2LmJW8GWfB3c F6UVyNK+wcApRY+goREIhyYupAHUexR7FQ== -----END CERTIFICATE----- )"; TEST(X509Test, ITUT_X509_nid_rsa) { bssl::UniquePtr cert(CertFromPEM(kRareRSAPEM)); ASSERT_TRUE(cert); EXPECT_TRUE(X509_get_X509_PUBKEY(cert.get())); bssl::UniquePtr evp_pkey(X509_get_pubkey(cert.get())); EXPECT_TRUE(evp_pkey); bssl::UniquePtr rsa(EVP_PKEY_get1_RSA(evp_pkey.get())); EXPECT_TRUE(rsa); } // kLargeSerialPEM is a certificate with a large serial number. static const char kLargeSerialPEM[] = R"( -----BEGIN CERTIFICATE----- MIICZjCCAc+gAwIBAgIQASNFZ4mrze8BI0VniavN7zANBgkqhkiG9w0BAQsFADA2 MRowGAYDVQQKExFCb3JpbmdTU0wgVEVTVElORzEYMBYGA1UEAxMPSW50ZXJtZWRp YXRlIENBMCAXDTE1MDEwMTAwMDAwMFoYDzIxMDAwMTAxMDAwMDAwWjAyMRowGAYD VQQKExFCb3JpbmdTU0wgVEVTVElORzEUMBIGA1UEAxMLZXhhbXBsZS5jb20wgZ8w DQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMPRTRliCpKEnug6OzI0rJVcQep5p+aT 9sCg+pj+HVyg/DYTwqZ6qJRKhM+MbkhdJuU7FyqlsBeCeM/OjwMjcY0yEB/xJg1i ygfuBztTLuPnHxtSuKwae5MeqSofp3j97sRMnuLcKlHxu8rXoOCAS9BO50uKnPwU Ee1iEVqR92FPAgMBAAGjdzB1MA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggr BgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAZBgNVHQ4EEgQQo3mm9u6v uaVeN4wRgDTidTAbBgNVHSMEFDASgBCMGmiotXbbXVd7H40UsgajMA0GCSqGSIb3 DQEBCwUAA4GBAGP+n4kKGn/8uddYLWTXbUsz+KLuEXNDMyu3vRufLjTpIbP2MCNo 85fhLeC3fzKuGOk+6QGVLOBBcWDrrLqrmqnWdBMPULDo2QoF71a4GVjeJh+ax/tZ PyeGVPUK21TE0LDIxf2a11d1CJw582MgZQIPk4tXk+AcU9EqIceKgECG -----END CERTIFICATE----- )"; TEST(X509Test, Print) { bssl::UniquePtr cert(CertFromPEM(kLargeSerialPEM)); ASSERT_TRUE(cert); bssl::UniquePtr bio(BIO_new(BIO_s_mem())); ASSERT_TRUE(bio); EXPECT_TRUE(X509_print_ex(bio.get(), cert.get(), 0, 0)); // Nothing should be left in the error queue. EXPECT_EQ(0u, ERR_peek_error()); // This output is not guaranteed to be stable, but we assert on it to make // sure something is printed. const uint8_t *data; size_t data_len; ASSERT_TRUE(BIO_mem_contents(bio.get(), &data, &data_len)); std::string print(reinterpret_cast(data), data_len); // Some lines in the X509_print_ex output have trailing whitespace. The raw // string is split and concatenated with " " at those points so that editors // configured to strip trailing whitespace do not break this test. static const char expected_certificate_string[] = R"(Certificate: Data: Version: 3 (0x2) Serial Number: 01:23:45:67:89:ab:cd:ef:01:23:45:67:89:ab:cd:ef Signature Algorithm: sha256WithRSAEncryption Issuer: O=BoringSSL TESTING, CN=Intermediate CA Validity Not Before: Jan 1 00:00:00 2015 GMT Not After : Jan 1 00:00:00 2100 GMT Subject: O=BoringSSL TESTING, CN=example.com Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (1024 bit) Modulus: 00:c3:d1:4d:19:62:0a:92:84:9e:e8:3a:3b:32:34: ac:95:5c:41:ea:79:a7:e6:93:f6:c0:a0:fa:98:fe: 1d:5c:a0:fc:36:13:c2:a6:7a:a8:94:4a:84:cf:8c: 6e:48:5d:26:e5:3b:17:2a:a5:b0:17:82:78:cf:ce: 8f:03:23:71:8d:32:10:1f:f1:26:0d:62:ca:07:ee: 07:3b:53:2e:e3:e7:1f:1b:52:b8:ac:1a:7b:93:1e: a9:2a:1f:a7:78:fd:ee:c4:4c:9e:e2:dc:2a:51:f1: bb:ca:d7:a0:e0:80:4b:d0:4e:e7:4b:8a:9c:fc:14: 11:ed:62:11:5a:91:f7:61:4f Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Key Usage: critical Digital Signature, Key Encipherment X509v3 Extended Key Usage:)" " " R"( TLS Web Server Authentication, TLS Web Client Authentication X509v3 Basic Constraints: critical CA:FALSE X509v3 Subject Key Identifier:)" " " R"( A3:79:A6:F6:EE:AF:B9:A5:5E:37:8C:11:80:34:E2:75 X509v3 Authority Key Identifier:)" " " R"( keyid:8C:1A:68:A8:B5:76:DB:5D:57:7B:1F:8D:14:B2:06:A3 Signature Algorithm: sha256WithRSAEncryption 63:fe:9f:89:0a:1a:7f:fc:b9:d7:58:2d:64:d7:6d:4b:33:f8: a2:ee:11:73:43:33:2b:b7:bd:1b:9f:2e:34:e9:21:b3:f6:30: 23:68:f3:97:e1:2d:e0:b7:7f:32:ae:18:e9:3e:e9:01:95:2c: e0:41:71:60:eb:ac:ba:ab:9a:a9:d6:74:13:0f:50:b0:e8:d9: 0a:05:ef:56:b8:19:58:de:26:1f:9a:c7:fb:59:3f:27:86:54: f5:0a:db:54:c4:d0:b0:c8:c5:fd:9a:d7:57:75:08:9c:39:f3: 63:20:65:02:0f:93:8b:57:93:e0:1c:53:d1:2a:21:c7:8a:80: 40:86 )"; EXPECT_EQ(print, expected_certificate_string); } TEST(X509Test, AddExt) { bssl::UniquePtr x509(X509_new()); ASSERT_TRUE(x509); struct Extension { int nid; bool critical; std::vector data; }; auto expect_extensions = [&](const std::vector &exts) { ASSERT_EQ(static_cast(X509_get_ext_count(x509.get())), exts.size()); for (size_t i = 0; i < exts.size(); i++) { SCOPED_TRACE(i); const X509_EXTENSION *ext = X509_get_ext(x509.get(), static_cast(i)); EXPECT_EQ(OBJ_obj2nid(X509_EXTENSION_get_object(ext)), exts[i].nid); EXPECT_EQ(X509_EXTENSION_get_critical(ext), exts[i].critical ? 1 : 0); const ASN1_OCTET_STRING *data = X509_EXTENSION_get_data(ext); EXPECT_EQ(Bytes(ASN1_STRING_get0_data(data), ASN1_STRING_length(data)), Bytes(exts[i].data)); } }; // Make a few sample extensions. // SEQUENCE {} std::vector basic1_der = {0x30, 0x00}; const uint8_t *inp = basic1_der.data(); bssl::UniquePtr basic1_obj( d2i_BASIC_CONSTRAINTS(nullptr, &inp, basic1_der.size())); EXPECT_EQ(inp, basic1_der.data() + basic1_der.size()); // SEQUENCE { BOOLEAN { TRUE } } std::vector basic2_der = {0x30, 0x03, 0x01, 0x01, 0xff}; inp = basic2_der.data(); bssl::UniquePtr basic2_obj( d2i_BASIC_CONSTRAINTS(nullptr, &inp, basic2_der.size())); EXPECT_EQ(inp, basic2_der.data() + basic2_der.size()); // OCTET_STRING {} std::vector skid1_der = {0x04, 0x00}; inp = skid1_der.data(); bssl::UniquePtr skid1_obj( d2i_ASN1_OCTET_STRING(nullptr, &inp, skid1_der.size())); EXPECT_EQ(inp, skid1_der.data() + skid1_der.size()); // OCTET_STRING { "a" } std::vector skid2_der = {0x04, 0x01, 0x61}; inp = skid2_der.data(); bssl::UniquePtr skid2_obj( d2i_ASN1_OCTET_STRING(nullptr, &inp, skid2_der.size())); EXPECT_EQ(inp, skid2_der.data() + skid2_der.size()); // Initially, the extension list is empty. expect_extensions({}); // Adding extensions works with the default settings. EXPECT_EQ( 1, X509_add1_ext_i2d(x509.get(), NID_basic_constraints, basic1_obj.get(), /*crit=*/1, X509V3_ADD_DEFAULT)); expect_extensions({{NID_basic_constraints, true, basic1_der}}); EXPECT_EQ(1, X509_add1_ext_i2d(x509.get(), NID_subject_key_identifier, skid1_obj.get(), /*crit=*/0, X509V3_ADD_DEFAULT)); expect_extensions({{NID_basic_constraints, true, basic1_der}, {NID_subject_key_identifier, false, skid1_der}}); // By default, we cannot add duplicates. EXPECT_EQ( 0, X509_add1_ext_i2d(x509.get(), NID_basic_constraints, basic2_obj.get(), /*crit=*/0, X509V3_ADD_DEFAULT)); expect_extensions({{NID_basic_constraints, true, basic1_der}, {NID_subject_key_identifier, false, skid1_der}}); // |X509V3_ADD_KEEP_EXISTING| silently keeps the existing extension if already // present. EXPECT_EQ( 1, X509_add1_ext_i2d(x509.get(), NID_basic_constraints, basic2_obj.get(), /*crit=*/0, X509V3_ADD_KEEP_EXISTING)); expect_extensions({{NID_basic_constraints, true, basic1_der}, {NID_subject_key_identifier, false, skid1_der}}); // |X509V3_ADD_REPLACE| replaces it. EXPECT_EQ( 1, X509_add1_ext_i2d(x509.get(), NID_basic_constraints, basic2_obj.get(), /*crit=*/0, X509V3_ADD_REPLACE)); expect_extensions({{NID_basic_constraints, false, basic2_der}, {NID_subject_key_identifier, false, skid1_der}}); // |X509V3_ADD_REPLACE_EXISTING| also replaces matches. EXPECT_EQ(1, X509_add1_ext_i2d(x509.get(), NID_subject_key_identifier, skid2_obj.get(), /*crit=*/1, X509V3_ADD_REPLACE_EXISTING)); expect_extensions({{NID_basic_constraints, false, basic2_der}, {NID_subject_key_identifier, true, skid2_der}}); // |X509V3_ADD_DELETE| ignores the value and deletes the extension. EXPECT_EQ(1, X509_add1_ext_i2d(x509.get(), NID_basic_constraints, nullptr, 0, X509V3_ADD_DELETE)); expect_extensions({{NID_subject_key_identifier, true, skid2_der}}); // Not finding an extension to delete is an error. EXPECT_EQ(0, X509_add1_ext_i2d(x509.get(), NID_basic_constraints, nullptr, 0, X509V3_ADD_DELETE)); expect_extensions({{NID_subject_key_identifier, true, skid2_der}}); // |X509V3_ADD_REPLACE_EXISTING| fails if it cannot find a match. EXPECT_EQ( 0, X509_add1_ext_i2d(x509.get(), NID_basic_constraints, basic1_obj.get(), /*crit=*/1, X509V3_ADD_REPLACE_EXISTING)); expect_extensions({{NID_subject_key_identifier, true, skid2_der}}); // |X509V3_ADD_REPLACE| adds a new extension if not preseent. EXPECT_EQ( 1, X509_add1_ext_i2d(x509.get(), NID_basic_constraints, basic1_obj.get(), /*crit=*/1, X509V3_ADD_REPLACE)); expect_extensions({{NID_subject_key_identifier, true, skid2_der}, {NID_basic_constraints, true, basic1_der}}); // Delete the extension again. EXPECT_EQ(1, X509_add1_ext_i2d(x509.get(), NID_basic_constraints, nullptr, 0, X509V3_ADD_DELETE)); expect_extensions({{NID_subject_key_identifier, true, skid2_der}}); // |X509V3_ADD_KEEP_EXISTING| adds a new extension if not preseent. EXPECT_EQ( 1, X509_add1_ext_i2d(x509.get(), NID_basic_constraints, basic1_obj.get(), /*crit=*/1, X509V3_ADD_KEEP_EXISTING)); expect_extensions({{NID_subject_key_identifier, true, skid2_der}, {NID_basic_constraints, true, basic1_der}}); // Delete the extension again. EXPECT_EQ(1, X509_add1_ext_i2d(x509.get(), NID_basic_constraints, nullptr, 0, X509V3_ADD_DELETE)); expect_extensions({{NID_subject_key_identifier, true, skid2_der}}); // |X509V3_ADD_APPEND| adds a new extension if not present. EXPECT_EQ( 1, X509_add1_ext_i2d(x509.get(), NID_basic_constraints, basic1_obj.get(), /*crit=*/1, X509V3_ADD_APPEND)); expect_extensions({{NID_subject_key_identifier, true, skid2_der}, {NID_basic_constraints, true, basic1_der}}); // |X509V3_ADD_APPEND| keeps adding duplicates (invalid) even if present. EXPECT_EQ( 1, X509_add1_ext_i2d(x509.get(), NID_basic_constraints, basic2_obj.get(), /*crit=*/0, X509V3_ADD_APPEND)); expect_extensions({{NID_subject_key_identifier, true, skid2_der}, {NID_basic_constraints, true, basic1_der}, {NID_basic_constraints, false, basic2_der}}); // |X509V3_ADD_DELETE| only deletes one extension at a time. EXPECT_EQ(1, X509_add1_ext_i2d(x509.get(), NID_basic_constraints, nullptr, 0, X509V3_ADD_DELETE)); expect_extensions({{NID_subject_key_identifier, true, skid2_der}, {NID_basic_constraints, false, basic2_der}}); EXPECT_EQ(1, X509_add1_ext_i2d(x509.get(), NID_basic_constraints, nullptr, 0, X509V3_ADD_DELETE)); expect_extensions({{NID_subject_key_identifier, true, skid2_der}}); } TEST(X509Test, NameEntry) { bssl::UniquePtr name(X509_NAME_new()); ASSERT_TRUE(name); auto check_name = [&](const char *expected_rfc2253) { // Check RDN indices are self-consistent. int num = X509_NAME_entry_count(name.get()); if (num > 0) { // RDN indices must start at zero. EXPECT_EQ(0, X509_NAME_ENTRY_set(X509_NAME_get_entry(name.get(), 0))); } for (int i = 1; i < num; i++) { int prev = X509_NAME_ENTRY_set(X509_NAME_get_entry(name.get(), i - 1)); int current = X509_NAME_ENTRY_set(X509_NAME_get_entry(name.get(), i)); // RDN indices must increase consecutively. EXPECT_TRUE(prev == current || prev + 1 == current) << "Entry " << i << " has RDN index " << current << " which is inconsistent with previous index " << prev; } // Check the name based on the RFC 2253 serialization. Note the RFC 2253 // serialization is in reverse. bssl::UniquePtr bio(BIO_new(BIO_s_mem())); ASSERT_TRUE(bio); EXPECT_GE(X509_NAME_print_ex(bio.get(), name.get(), 0, XN_FLAG_RFC2253), 0); const uint8_t *data; size_t len; ASSERT_TRUE(BIO_mem_contents(bio.get(), &data, &len)); EXPECT_EQ(expected_rfc2253, std::string(data, data + len)); }; check_name(""); // |loc| = -1, |set| = 0 appends as new RDNs. ASSERT_TRUE(X509_NAME_add_entry_by_NID( name.get(), NID_organizationName, MBSTRING_UTF8, reinterpret_cast("Org"), /*len=*/-1, /*loc=*/-1, /*set=*/0)); check_name("O=Org"); // |loc| = -1, |set| = 0 appends as new RDNs. ASSERT_TRUE(X509_NAME_add_entry_by_NID( name.get(), NID_commonName, MBSTRING_UTF8, reinterpret_cast("Name"), /*len=*/-1, /*loc=*/-1, /*set=*/0)); check_name("CN=Name,O=Org"); // Inserting in the middle of the set, but with |set| = 0 inserts a new RDN // and fixes the "set" values as needed. ASSERT_TRUE(X509_NAME_add_entry_by_NID( name.get(), NID_organizationalUnitName, MBSTRING_UTF8, reinterpret_cast("Unit"), /*len=*/-1, /*loc=*/1, /*set=*/0)); check_name("CN=Name,OU=Unit,O=Org"); // |set = -1| adds to the previous entry's RDN. (Although putting O and OU at // the same level makes little sense, the test is written this way to check // the function isn't using attribute types to order things.) ASSERT_TRUE(X509_NAME_add_entry_by_NID( name.get(), NID_organizationName, MBSTRING_UTF8, reinterpret_cast("Org2"), /*len=*/-1, /*loc=*/2, /*set=*/-1)); check_name("CN=Name,O=Org2+OU=Unit,O=Org"); // |set| = 1 adds to the next entry's RDN. ASSERT_TRUE(X509_NAME_add_entry_by_NID( name.get(), NID_commonName, MBSTRING_UTF8, reinterpret_cast("Name2"), /*len=*/-1, /*loc=*/2, /*set=*/-1)); check_name("CN=Name,O=Org2+CN=Name2+OU=Unit,O=Org"); // If there is no previous RDN, |set| = -1 makes a new RDN. ASSERT_TRUE(X509_NAME_add_entry_by_NID( name.get(), NID_countryName, MBSTRING_UTF8, reinterpret_cast("US"), /*len=*/-1, /*loc=*/0, /*set=*/-1)); check_name("CN=Name,O=Org2+CN=Name2+OU=Unit,O=Org,C=US"); // Likewise if there is no next RDN. ASSERT_TRUE(X509_NAME_add_entry_by_NID( name.get(), NID_commonName, MBSTRING_UTF8, reinterpret_cast("Name3"), /*len=*/-1, /*loc=*/-1, /*set=*/1)); check_name("CN=Name3,CN=Name,O=Org2+CN=Name2+OU=Unit,O=Org,C=US"); // If |set| = 0 and we insert in the middle of an existing RDN, it adds an // RDN boundary after the entry but not before. This is a quirk of how the // function is implemented and hopefully not something any caller depends on. ASSERT_TRUE(X509_NAME_add_entry_by_NID( name.get(), NID_commonName, MBSTRING_UTF8, reinterpret_cast("Name4"), /*len=*/-1, /*loc=*/3, /*set=*/0)); check_name("CN=Name3,CN=Name,O=Org2+CN=Name2,CN=Name4+OU=Unit,O=Org,C=US"); // Entries may be deleted. X509_NAME_ENTRY_free(X509_NAME_delete_entry(name.get(), 7)); check_name("CN=Name,O=Org2+CN=Name2,CN=Name4+OU=Unit,O=Org,C=US"); // When deleting the only attribute in an RDN, index invariants should still // hold. X509_NAME_ENTRY_free(X509_NAME_delete_entry(name.get(), 0)); check_name("CN=Name,O=Org2+CN=Name2,CN=Name4+OU=Unit,O=Org"); // Index invariants also hold when deleting attributes from non-singular RDNs. X509_NAME_ENTRY_free(X509_NAME_delete_entry(name.get(), 1)); check_name("CN=Name,O=Org2+CN=Name2,CN=Name4,O=Org"); X509_NAME_ENTRY_free(X509_NAME_delete_entry(name.get(), 1)); check_name("CN=Name,O=Org2+CN=Name2,O=Org"); // Same as above, but delete the second attribute first. X509_NAME_ENTRY_free(X509_NAME_delete_entry(name.get(), 2)); check_name("CN=Name,CN=Name2,O=Org"); X509_NAME_ENTRY_free(X509_NAME_delete_entry(name.get(), 1)); check_name("CN=Name,O=Org"); } // Tests that non-integer types are rejected when passed as an argument to // X509_set_serialNumber(). TEST(X509Test, SetSerialNumberChecksASN1StringType) { bssl::UniquePtr root = CertFromPEM(kRootCAPEM); ASSERT_TRUE(root); // Passing an IA5String to X509_set_serialNumber() should fail. bssl::UniquePtr str(ASN1_IA5STRING_new()); ASSERT_TRUE(str); EXPECT_FALSE(X509_set_serialNumber(root.get(), str.get())); // Passing a negative serial number is allowed. While invalid, we do accept // them and some callers rely in this for tests. bssl::UniquePtr serial(ASN1_INTEGER_new()); ASSERT_TRUE(serial); ASSERT_TRUE(ASN1_INTEGER_set_int64(serial.get(), -1)); ASSERT_TRUE(X509_set_serialNumber(root.get(), serial.get())); int64_t val; ASSERT_TRUE(ASN1_INTEGER_get_int64(&val, X509_get0_serialNumber(root.get()))); EXPECT_EQ(-1, val); } TEST(X509Test, SetSerialNumberCheckEndian) { bssl::UniquePtr root = CertFromPEM(kRootCAPEM); ASSERT_TRUE(root); // Numbers for testing std::vector nums = { 0x0000000000000001LL, 0x0000000000000100LL, 0x0000000000010000LL, 0x0000000001000000LL, 0x0000000100000000LL, 0x0000010000000000LL, 0x0001000000000000LL, -2LL}; for(int64_t num: nums) { bssl::UniquePtr serial(ASN1_INTEGER_new()); ASSERT_TRUE(serial); // Set serial number for cert ASSERT_TRUE(ASN1_INTEGER_set_int64(serial.get(), num)); ASSERT_TRUE(X509_set_serialNumber(root.get(), serial.get())); // Get serial number for cert int64_t val; ASSERT_TRUE(ASN1_INTEGER_get_int64(&val, X509_get0_serialNumber(root.get()))); EXPECT_EQ(num, val); } } TEST(X509Test, Policy) { bssl::UniquePtr oid1( OBJ_txt2obj("1.2.840.113554.4.1.72585.2.1", /*dont_search_names=*/1)); ASSERT_TRUE(oid1); bssl::UniquePtr oid2( OBJ_txt2obj("1.2.840.113554.4.1.72585.2.2", /*dont_search_names=*/1)); ASSERT_TRUE(oid2); bssl::UniquePtr oid3( OBJ_txt2obj("1.2.840.113554.4.1.72585.2.3", /*dont_search_names=*/1)); ASSERT_TRUE(oid3); bssl::UniquePtr root( CertFromPEM(GetTestData("crypto/x509/test/policy_root.pem").c_str())); ASSERT_TRUE(root); bssl::UniquePtr intermediate(CertFromPEM( GetTestData("crypto/x509/test/policy_intermediate.pem").c_str())); ASSERT_TRUE(intermediate); bssl::UniquePtr intermediate_invalid(CertFromPEM( GetTestData("crypto/x509/test/policy_intermediate_invalid.pem").c_str())); ASSERT_TRUE(intermediate_invalid); bssl::UniquePtr intermediate_duplicate(CertFromPEM( GetTestData("crypto/x509/test/policy_intermediate_duplicate.pem") .c_str())); ASSERT_TRUE(intermediate_duplicate); bssl::UniquePtr leaf( CertFromPEM(GetTestData("crypto/x509/test/policy_leaf.pem").c_str())); ASSERT_TRUE(leaf); bssl::UniquePtr leaf_invalid(CertFromPEM( GetTestData("crypto/x509/test/policy_leaf_invalid.pem").c_str())); ASSERT_TRUE(leaf_invalid); bssl::UniquePtr leaf_duplicate(CertFromPEM( GetTestData("crypto/x509/test/policy_leaf_duplicate.pem").c_str())); ASSERT_TRUE(leaf_duplicate); // By default, OpenSSL does not check policies, so even syntax errors in the // certificatePolicies extension go unnoticed. (This is probably not // important.) EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {intermediate.get()}, /*crls=*/{})); EXPECT_EQ(X509_V_OK, Verify(leaf_invalid.get(), {root.get()}, {intermediate.get()}, /*crls=*/{})); auto set_policies = [](X509_STORE_CTX *ctx, std::vector oids) { X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(ctx); for (const ASN1_OBJECT *oid : oids) { bssl::UniquePtr copy(OBJ_dup(oid)); ASSERT_TRUE(copy); ASSERT_TRUE(X509_VERIFY_PARAM_add0_policy(param, copy.get())); copy.release(); // |X509_VERIFY_PARAM_add0_policy| takes ownership on // success. } }; // The chain is good for |oid1| and |oid2|, but not |oid3|. EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {intermediate.get()}, /*crls=*/{}, X509_V_FLAG_EXPLICIT_POLICY, [&](X509_STORE_CTX *ctx) { set_policies(ctx, {oid1.get()}); })); EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {intermediate.get()}, /*crls=*/{}, X509_V_FLAG_EXPLICIT_POLICY, [&](X509_STORE_CTX *ctx) { set_policies(ctx, {oid2.get()}); })); EXPECT_EQ(X509_V_ERR_NO_EXPLICIT_POLICY, Verify(leaf.get(), {root.get()}, {intermediate.get()}, /*crls=*/{}, X509_V_FLAG_EXPLICIT_POLICY, [&](X509_STORE_CTX *ctx) { set_policies(ctx, {oid3.get()}); })); EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {intermediate.get()}, /*crls=*/{}, X509_V_FLAG_EXPLICIT_POLICY, [&](X509_STORE_CTX *ctx) { set_policies(ctx, {oid1.get(), oid2.get()}); })); EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {intermediate.get()}, /*crls=*/{}, X509_V_FLAG_EXPLICIT_POLICY, [&](X509_STORE_CTX *ctx) { set_policies(ctx, {oid1.get(), oid3.get()}); })); // The policy extension cannot be parsed. EXPECT_EQ(X509_V_ERR_INVALID_POLICY_EXTENSION, Verify(leaf.get(), {root.get()}, {intermediate_invalid.get()}, /*crls=*/{}, X509_V_FLAG_EXPLICIT_POLICY, [&](X509_STORE_CTX *ctx) { set_policies(ctx, {oid1.get()}); })); EXPECT_EQ(X509_V_ERR_INVALID_POLICY_EXTENSION, Verify(leaf_invalid.get(), {root.get()}, {intermediate.get()}, /*crls=*/{}, X509_V_FLAG_EXPLICIT_POLICY, [&](X509_STORE_CTX *ctx) { set_policies(ctx, {oid1.get()}); })); // There is a duplicate policy in the policy extension. EXPECT_EQ(X509_V_ERR_INVALID_POLICY_EXTENSION, Verify(leaf.get(), {root.get()}, {intermediate_duplicate.get()}, /*crls=*/{}, X509_V_FLAG_EXPLICIT_POLICY, [&](X509_STORE_CTX *ctx) { set_policies(ctx, {oid1.get()}); })); // The policy extension in the leaf cannot be parsed. EXPECT_EQ(X509_V_ERR_INVALID_POLICY_EXTENSION, Verify(leaf_duplicate.get(), {root.get()}, {intermediate.get()}, /*crls=*/{}, X509_V_FLAG_EXPLICIT_POLICY, [&](X509_STORE_CTX *ctx) { set_policies(ctx, {oid1.get()}); })); // With just a trust anchor, policy checking silently succeeds. EXPECT_EQ(X509_V_OK, Verify(root.get(), {root.get()}, {}, /*crls=*/{}, X509_V_FLAG_EXPLICIT_POLICY, [&](X509_STORE_CTX *ctx) { set_policies(ctx, {oid1.get()}); })); } #if defined(OPENSSL_THREADS) // A similar test to the above, but ensures the various bits of intermediate // state are computed safely. TEST(X509Test, PolicyThreads) { const size_t kNumThreads = 10; bssl::UniquePtr oid1( OBJ_txt2obj("1.2.840.113554.4.1.72585.2.1", /*dont_search_names=*/1)); ASSERT_TRUE(oid1); bssl::UniquePtr oid2( OBJ_txt2obj("1.2.840.113554.4.1.72585.2.2", /*dont_search_names=*/1)); ASSERT_TRUE(oid2); bssl::UniquePtr oid3( OBJ_txt2obj("1.2.840.113554.4.1.72585.2.3", /*dont_search_names=*/1)); ASSERT_TRUE(oid3); auto set_policies = [](X509_STORE_CTX *ctx, std::vector oids) { X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(ctx); for (const ASN1_OBJECT *oid : oids) { bssl::UniquePtr copy(OBJ_dup(oid)); ASSERT_TRUE(copy); ASSERT_TRUE(X509_VERIFY_PARAM_add0_policy(param, copy.get())); copy.release(); // |X509_VERIFY_PARAM_add0_policy| takes ownership on // success. } }; { bssl::UniquePtr root( CertFromPEM(GetTestData("crypto/x509/test/policy_root.pem").c_str())); ASSERT_TRUE(root); bssl::UniquePtr intermediate(CertFromPEM( GetTestData("crypto/x509/test/policy_intermediate.pem").c_str())); ASSERT_TRUE(intermediate); bssl::UniquePtr leaf( CertFromPEM(GetTestData("crypto/x509/test/policy_leaf.pem").c_str())); ASSERT_TRUE(leaf); std::vector threads; for (size_t i = 0; i < kNumThreads; i++) { threads.emplace_back([&] { EXPECT_EQ( X509_V_OK, Verify(leaf.get(), {root.get()}, {intermediate.get()}, /*crls=*/{}, X509_V_FLAG_EXPLICIT_POLICY, [&](X509_STORE_CTX *ctx) { set_policies(ctx, {oid1.get()}); })); }); } for (auto &thread : threads) { thread.join(); } } { bssl::UniquePtr root( CertFromPEM(GetTestData("crypto/x509/test/policy_root.pem").c_str())); ASSERT_TRUE(root); bssl::UniquePtr intermediate(CertFromPEM( GetTestData("crypto/x509/test/policy_intermediate.pem").c_str())); ASSERT_TRUE(intermediate); bssl::UniquePtr leaf_invalid(CertFromPEM( GetTestData("crypto/x509/test/policy_leaf_invalid.pem").c_str())); ASSERT_TRUE(leaf_invalid); std::vector threads; for (size_t i = 0; i < kNumThreads; i++) { threads.emplace_back([&] { EXPECT_EQ(X509_V_ERR_INVALID_POLICY_EXTENSION, Verify(leaf_invalid.get(), {root.get()}, {intermediate.get()}, /*crls=*/{}, X509_V_FLAG_EXPLICIT_POLICY, [&](X509_STORE_CTX *ctx) { set_policies(ctx, {oid1.get()}); })); }); } for (auto &thread : threads) { thread.join(); } } } #endif // OPENSSL_THREADS TEST(X509Test, ExtensionFromConf) { static const char kTestOID[] = "1.2.840.113554.4.1.72585.2"; const struct { const char *name; std::string value; // conf is the serialized confdb, or nullptr if none is to be provided. const char *conf; // expected is the resulting extension, encoded in DER, or the empty string // if an error is expected. std::vector expected; } kTests[] = { // Many extensions have built-in syntax. {"basicConstraints", "critical,CA:true", nullptr, {0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff}}, {"basicConstraints", "critical,CA:true,pathlen:1", nullptr, {0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x01}}, // key:value tuples can be repeated and just override the previous value. {"basicConstraints", "critical,CA:true,pathlen:100,pathlen:1", nullptr, {0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x01}}, // Extension contents may be referenced from a config section. {"basicConstraints", "critical,@section", "[section]\nCA = true\n", {0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff}}, // If no config is provided, this should fail. {"basicConstraints", "critical,@section", nullptr, {}}, // issuingDistributionPoint takes a list of name:value pairs. Omitting the // value is not allowed. {"issuingDistributionPoint", "fullname", nullptr, {}}, {"issuingDistributionPoint", "relativename:name", "[name]\nCN=Hello\n", {0x30, 0x1b, 0x06, 0x03, 0x55, 0x1d, 0x1c, 0x04, 0x14, 0x30, 0x12, 0xa0, 0x10, 0xa1, 0x0e, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f}}, // relativename referencing a section which doesn't exist. {"issuingDistributionPoint", "relativename:wrong_section_name", "[name]\nCN=Hello\n", {}}, // relativename must be a single RDN. By default, the section-based name // syntax puts each attribute into its own RDN. {"issuingDistributionPoint", "relativename:name", "[name]\nCN=Hello\nC=US\n", {}}, // A single RDN with multiple attributes is allowed. {"issuingDistributionPoint", "relativename:name", "[name]\nCN=Hello\n+C=US\n", {0x30, 0x26, 0x06, 0x03, 0x55, 0x1d, 0x1c, 0x04, 0x1f, 0x30, 0x1d, 0xa0, 0x1b, 0xa1, 0x19, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f}}, // Duplicate reason keys are an error. Reaching this case is interesting. // The value can a string like "key:value,key:value", or it can be // "@section" and reference a config section. If using a string, duplicate // keys are possible, but then it is impossible to put commas in the // value, as onlysomereasons expects. If using a section reference, it is // impossible to have a duplicate key because the config file parser // overrides the old value. {"issuingDistributionPoint", "onlysomereasons:keyCompromise", nullptr, {0x30, 0x0d, 0x06, 0x03, 0x55, 0x1d, 0x1c, 0x04, 0x06, 0x30, 0x04, 0x83, 0x02, 0x06, 0x40}}, {"issuingDistributionPoint", "onlysomereasons:keyCompromise,onlysomereasons:CACompromise\n", nullptr, {}}, // subjectAltName has a series of string-based inputs for each name type. {"subjectAltName", "email:foo@example.com, URI:https://example.com, DNS:example.com, " "RID:1.2.3.4, IP:127.0.0.1, IP:::1, dirName:section, " "otherName:1.2.3.4;BOOLEAN:TRUE", "[section]\nCN=Test\n", {0x30, 0x78, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x71, 0x30, 0x6f, 0x81, 0x0f, 0x66, 0x6f, 0x6f, 0x40, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x86, 0x13, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x82, 0x0b, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x88, 0x03, 0x2a, 0x03, 0x04, 0x87, 0x04, 0x7f, 0x00, 0x00, 0x01, 0x87, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xa4, 0x11, 0x30, 0x0f, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x04, 0x54, 0x65, 0x73, 0x74, 0xa0, 0x0a, 0x06, 0x03, 0x2a, 0x03, 0x04, 0xa0, 0x03, 0x01, 0x01, 0xff}}, // Syntax errors in each case, where they exist. (The string types just // copy the string in as-is.) {"subjectAltName", "RID:not_an_oid", nullptr, {}}, {"subjectAltName", "IP:not_an_ip", nullptr, {}}, {"subjectAltName", "dirName:no_conf_db", nullptr, {}}, {"subjectAltName", "dirName:missing_section", "[section]\nCN=Test\n", {}}, {"subjectAltName", "otherName:missing_semicolon", nullptr, {}}, {"subjectAltName", "otherName:1.2.3.4", nullptr, {}}, {"subjectAltName", "otherName:invalid_oid;BOOLEAN:TRUE", nullptr, {}}, {"subjectAltName", "otherName:1.2.3.4;invalid_value", nullptr, {}}, {"policyMappings", "1.1.1.1:2.2.2.2", nullptr, {0x30, 0x15, 0x06, 0x03, 0x55, 0x1d, 0x21, 0x04, 0x0e, 0x30, 0x0c, 0x30, 0x0a, 0x06, 0x03, 0x29, 0x01, 0x01, 0x06, 0x03, 0x52, 0x02, 0x02}}, {"policyMappings", "invalid_oid:2.2.2.2", nullptr, {}}, {"policyMappings", "1.1.1.1:invalid_oid", nullptr, {}}, // The "DER:" prefix just specifies an arbitrary byte string. Colons // separators are ignored. {kTestOID, "DER:0001020304", nullptr, {0x30, 0x15, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x05, 0x00, 0x01, 0x02, 0x03, 0x04}}, {kTestOID, "DER:00:01:02:03:04", nullptr, {0x30, 0x15, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x05, 0x00, 0x01, 0x02, 0x03, 0x04}}, {kTestOID, "DER:invalid hex", nullptr, {}}, // The "ASN1:" prefix implements a complex language for describing ASN.1 // structures. See // https://www.openssl.org/docs/man1.1.1/man3/ASN1_generate_nconf.html {kTestOID, "ASN1:invalid", nullptr, {}}, {kTestOID, "ASN1:BOOLEAN:TRUE", nullptr, {0x30, 0x13, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x03, 0x01, 0x01, 0xff}}, {kTestOID, "ASN1:BOOL:yes", nullptr, {0x30, 0x13, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x03, 0x01, 0x01, 0xff}}, {kTestOID, "ASN1:BOOLEAN:NO", nullptr, {0x30, 0x13, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x03, 0x01, 0x01, 0x00}}, {kTestOID, "ASN1:BOOLEAN", // Missing value nullptr, {}}, {kTestOID, "ASN1:BOOLEAN:invalid", nullptr, {}}, {kTestOID, "ASN1:BOOLEAN:TRUE,invalid", nullptr, {}}, {kTestOID, "ASN1:NULL", nullptr, {0x30, 0x12, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x02, 0x05, 0x00}}, {kTestOID, "ASN1:NULL,invalid", nullptr, {}}, {kTestOID, "ASN1:NULL:invalid", nullptr, {}}, // Missing value. {kTestOID, "ASN1:INTEGER", nullptr, {}}, {kTestOID, "ASN1:INTEGER:", nullptr, {}}, {kTestOID, "ASN1:INTEGER,invalid", nullptr, {}}, // INTEGER may be decimal or hexadecimal. {kTestOID, "ASN1:INT:-0x10", nullptr, {0x30, 0x13, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x03, 0x02, 0x01, 0xf0}}, {kTestOID, "ASN1:INT:-10", nullptr, {0x30, 0x13, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x03, 0x02, 0x01, 0xf6}}, {kTestOID, "ASN1:INT:0", nullptr, {0x30, 0x13, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x03, 0x02, 0x01, 0x00}}, {kTestOID, "ASN1:INTEGER:10", nullptr, {0x30, 0x13, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x03, 0x02, 0x01, 0x0a}}, {kTestOID, "ASN1:INTEGER:0x10", nullptr, {0x30, 0x13, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x03, 0x02, 0x01, 0x10}}, {kTestOID, "ASN1:ENUM:0", nullptr, {0x30, 0x13, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x03, 0x0a, 0x01, 0x00}}, {kTestOID, "ASN1:ENUMERATED:0", nullptr, {0x30, 0x13, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x03, 0x0a, 0x01, 0x00}}, // OIDs may be spelled out or specified by name. {kTestOID, "ASN1:OBJECT:invalid", nullptr, {}}, {kTestOID, "ASN1:OBJECT:basicConstraints", nullptr, {0x30, 0x15, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x05, 0x06, 0x03, 0x55, 0x1d, 0x13}}, {kTestOID, "ASN1:OBJECT:2.5.29.19", nullptr, {0x30, 0x15, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x05, 0x06, 0x03, 0x55, 0x1d, 0x13}}, {kTestOID, "ASN1:OID:2.5.29.19", nullptr, {0x30, 0x15, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x05, 0x06, 0x03, 0x55, 0x1d, 0x13}}, {kTestOID, "ASN1:UTC:invalid", nullptr, {}}, {kTestOID, "ASN1:UTC:20001231235959Z", nullptr, {}}, {kTestOID, "ASN1:UTCTIME:invalid", nullptr, {}}, {kTestOID, "ASN1:UTC:001231235959Z", nullptr, {0x30, 0x1f, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x0f, 0x17, 0x0d, 0x30, 0x30, 0x31, 0x32, 0x33, 0x31, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a}}, {kTestOID, "ASN1:UTCTIME:001231235959Z", nullptr, {0x30, 0x1f, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x0f, 0x17, 0x0d, 0x30, 0x30, 0x31, 0x32, 0x33, 0x31, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a}}, {kTestOID, "ASN1:GENTIME:invalid", nullptr, {}}, {kTestOID, "ASN1:GENTIME:001231235959Z", nullptr, {}}, {kTestOID, "ASN1:GENERALIZEDTIME:invalid", nullptr, {}}, {kTestOID, "ASN1:GENTIME:20001231235959Z", nullptr, {0x30, 0x21, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x11, 0x18, 0x0f, 0x32, 0x30, 0x30, 0x30, 0x31, 0x32, 0x33, 0x31, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a}}, {kTestOID, "ASN1:GENERALIZEDTIME:20001231235959Z", nullptr, {0x30, 0x21, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x11, 0x18, 0x0f, 0x32, 0x30, 0x30, 0x30, 0x31, 0x32, 0x33, 0x31, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a}}, // The default input format for string types is ASCII, which is then // converted into the target string type. {kTestOID, "ASN1:UTF8:hello", nullptr, {0x30, 0x17, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x07, 0x0c, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f}}, {kTestOID, "ASN1:UTF8String:hello", nullptr, {0x30, 0x17, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x07, 0x0c, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f}}, {kTestOID, "ASN1:UNIV:hello", nullptr, {0x30, 0x26, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x16, 0x1c, 0x14, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x6f}}, {kTestOID, "ASN1:UNIVERSALSTRING:hello", nullptr, {0x30, 0x26, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x16, 0x1c, 0x14, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x6f}}, {kTestOID, "ASN1:BMP:hello", nullptr, {0x30, 0x1c, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x0c, 0x1e, 0x0a, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x6f}}, {kTestOID, "ASN1:BMPSTRING:hello", nullptr, {0x30, 0x1c, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x0c, 0x1e, 0x0a, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x6f}}, {kTestOID, "ASN1:IA5:hello", nullptr, {0x30, 0x17, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x07, 0x16, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f}}, {kTestOID, "ASN1:IA5STRING:hello", nullptr, {0x30, 0x17, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x07, 0x16, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f}}, {kTestOID, "ASN1:PRINTABLE:hello", nullptr, {0x30, 0x17, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x07, 0x13, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f}}, {kTestOID, "ASN1:PRINTABLESTRING:hello", nullptr, {0x30, 0x17, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x07, 0x13, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f}}, {kTestOID, "ASN1:T61:hello", nullptr, {0x30, 0x17, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x07, 0x14, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f}}, {kTestOID, "ASN1:T61STRING:hello", nullptr, {0x30, 0x17, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x07, 0x14, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f}}, {kTestOID, "ASN1:TELETEXSTRING:hello", nullptr, {0x30, 0x17, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x07, 0x14, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f}}, // FORMAT:UTF8 switches the input format to UTF-8. This should be // converted to the destination string, or rejected if invalid. {kTestOID, "ASN1:FORMAT:UTF8,UTF8:\xe2\x98\x83", nullptr, {0x30, 0x15, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x05, 0x0c, 0x03, 0xe2, 0x98, 0x83}}, {kTestOID, "ASN1:FORMAT:UTF8,UNIV:\xe2\x98\x83", nullptr, {0x30, 0x16, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x06, 0x1c, 0x04, 0x00, 0x00, 0x26, 0x03}}, {kTestOID, "ASN1:FORMAT:UTF8,BMP:\xe2\x98\x83", nullptr, {0x30, 0x14, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x04, 0x1e, 0x02, 0x26, 0x03}}, {kTestOID, "ASN1:FORMAT:UTF8,IA5:\xe2\x98\x83", nullptr, {}}, {kTestOID, "ASN1:FORMAT:UTF8,IA5:hello", nullptr, {0x30, 0x17, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x07, 0x16, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f}}, {kTestOID, "ASN1:FORMAT:UTF8,PRINTABLE:\xe2\x98\x83", nullptr, {}}, {kTestOID, "ASN1:FORMAT:UTF8,PRINTABLE:hello", nullptr, {0x30, 0x17, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x07, 0x13, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f}}, {kTestOID, "ASN1:FORMAT:UTF8,T61:\xe2\x98\x83", nullptr, {}}, {kTestOID, "ASN1:FORMAT:UTF8,T61:\xc3\xb7", nullptr, {0x30, 0x13, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x03, 0x14, 0x01, 0xf7}}, // Invalid UTF-8. {kTestOID, "ASN1:FORMAT:UTF8,UTF8:\xff", nullptr, {}}, // We don't support these string types. {kTestOID, "ASN1:NUMERIC:0", nullptr, {}}, {kTestOID, "ASN1:NUMERICSTRING:0", nullptr, {}}, {kTestOID, "ASN1:VISIBLE:hello", nullptr, {}}, {kTestOID, "ASN1:VISIBLESTRING:hello", nullptr, {}}, {kTestOID, "ASN1:GeneralString:hello", nullptr, {}}, // OCTET STRING and BIT STRING also default to ASCII, but also accept HEX. // BIT STRING interprets OCTET STRING formats by having zero unused bits. {kTestOID, "ASN1:OCT:hello", nullptr, {0x30, 0x17, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x07, 0x04, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f}}, {kTestOID, "ASN1:OCTETSTRING:hello", nullptr, {0x30, 0x17, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x07, 0x04, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f}}, {kTestOID, "ASN1:FORMAT:HEX,OCT:0123abcd", nullptr, {0x30, 0x16, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x06, 0x04, 0x04, 0x01, 0x23, 0xab, 0xcd}}, {kTestOID, "ASN1:BITSTR:hello", nullptr, {0x30, 0x18, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x08, 0x03, 0x06, 0x00, 0x68, 0x65, 0x6c, 0x6c, 0x6f}}, {kTestOID, "ASN1:BITSTRING:hello", nullptr, {0x30, 0x18, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x08, 0x03, 0x06, 0x00, 0x68, 0x65, 0x6c, 0x6c, 0x6f}}, {kTestOID, "ASN1:FORMAT:HEX,BITSTR:0123abcd", nullptr, {0x30, 0x17, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x07, 0x03, 0x05, 0x00, 0x01, 0x23, 0xab, 0xcd}}, {kTestOID, "ASN1:FORMAT:HEX,OCT:invalid hex", nullptr, {}}, // BIT STRING additionally supports a BITLIST type, which specifies a // list of bits to set. {kTestOID, "ASN1:FORMAT:BITLIST,BITSTR:1,5", nullptr, {0x30, 0x14, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x04, 0x03, 0x02, 0x02, 0x44}}, {kTestOID, "ASN1:FORMAT:BITLIST,BITSTR:1,invalid,5", nullptr, {}}, // Negative bit inidices are not allowed. {kTestOID, "ASN1:FORMAT:BITLIST,BITSTR:-1", nullptr, {}}, // We cap bit indices at 256. {kTestOID, "ASN1:FORMAT:BITLIST,BITSTR:257", nullptr, {}}, {kTestOID, "ASN1:FORMAT:BITLIST,BITSTR:256", nullptr, {0x30, 0x34, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x24, 0x03, 0x22, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80}}, // Unsupported formats for string types. {kTestOID, "ASN1:FORMAT:BITLIST,IA5:abcd", nullptr, {}}, {kTestOID, "ASN1:FORMAT:BITLIST,UTF8:abcd", nullptr, {}}, {kTestOID, "ASN1:FORMAT:BITLIST,OCT:abcd", nullptr, {}}, {kTestOID, "ASN1:FORMAT:BITLIST,UTC:abcd", nullptr, {}}, {kTestOID, "ASN1:FORMAT:HEX,IA5:abcd", nullptr, {}}, {kTestOID, "ASN1:FORMAT:HEX,UTF8:abcd", nullptr, {}}, {kTestOID, "ASN1:FORMAT:HEX,UTC:abcd", nullptr, {}}, {kTestOID, "ASN1:FORMAT:UTF8,OCT:abcd", nullptr, {}}, {kTestOID, "ASN1:FORMAT:UTF8,UTC:abcd", nullptr, {}}, // Invalid format type. {kTestOID, "ASN1:FORMAT:invalid,IA5:abcd", nullptr, {}}, // SEQUENCE and SET encode empty values when there is no value. {kTestOID, "ASN1:SEQ", nullptr, {0x30, 0x12, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x02, 0x30, 0x00}}, {kTestOID, "ASN1:SET", nullptr, {0x30, 0x12, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x02, 0x31, 0x00}}, {kTestOID, "ASN1:SEQUENCE", nullptr, {0x30, 0x12, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x02, 0x30, 0x00}}, // Otherwise, they require a corresponding section in the config database // to encode values. This can be nested recursively. {kTestOID, "ASN1:SEQ:missing_confdb", nullptr, {}}, {kTestOID, "ASN1:SET:missing_confdb", nullptr, {}}, {kTestOID, "ASN1:SEQ:seq", R"( [seq] val1 = NULL val2 = IA5:a val3 = SET:set [set] # Config names do not matter, only the order. val4 = INT:1 val3 = INT:2 val2 = SEQ:empty val1 = INT:3 [empty] )", {0x30, 0x24, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x14, 0x30, 0x12, 0x05, 0x00, 0x16, 0x01, 0x61, 0x31, 0x0b, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x01, 0x03, 0x30, 0x00}}, // There is a recursion limit to stop infinite recursion. {kTestOID, "ASN1:SEQ:seq1", R"( [seq1] val = SEQ:seq2 [seq2] val = SEQ:seq1 )", {}}, // Various modifiers wrap with explicit tagging or universal types. {kTestOID, "ASN1:EXP:0,EXP:16U,EXP:100A,EXP:1000C,OCTWRAP,SEQWRAP,SETWRAP,BITWRAP," "NULL", nullptr, {0x30, 0x26, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x16, 0xa0, 0x14, 0x30, 0x12, 0x7f, 0x64, 0x0f, 0xbf, 0x87, 0x68, 0x0b, 0x04, 0x09, 0x30, 0x07, 0x31, 0x05, 0x03, 0x03, 0x00, 0x05, 0x00}}, // Invalid tag numbers. {kTestOID, "ASN1:EXP:-1,NULL", nullptr, {}}, {kTestOID, "ASN1:EXP:1?,NULL", nullptr, {}}, // Fits in |uint32_t| but exceeds |CBS_ASN1_TAG_NUMBER_MASK|, the largest // tag number we support. {kTestOID, "ASN1:EXP:536870912,NULL", nullptr, {}}, // Implicit tagging may also be applied to the underlying type, or the // wrapping modifiers. {kTestOID, "ASN1:IMP:1A,OCTWRAP,IMP:10,SEQWRAP,IMP:100,SETWRAP,IMP:1000,BITWRAP," "IMP:10000,NULL", nullptr, {0x30, 0x20, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x10, 0x41, 0x0e, 0xaa, 0x0c, 0xbf, 0x64, 0x09, 0x9f, 0x87, 0x68, 0x05, 0x00, 0x9f, 0xce, 0x10, 0x00}}, // Implicit tagging may not be applied to explicit tagging or itself. // There's no rule against this in ASN.1, but OpenSSL does not allow it // here. {kTestOID, "ASN1:IMP:1,EXP:1,NULL", nullptr, {}}, {kTestOID, "ASN1:IMP:1,IMP:1,NULL", nullptr, {}}, // [UNIVERSAL 0] is reserved. {kTestOID, "ASN1:0U,NULL", nullptr, {}}, // Leading and trailing spaces on name:value pairs are removed. However, // while these pairs are delimited by commas, a type will consumes // everything after it, including commas, and spaces. So this is the // string " a, b ". {kTestOID, "ASN1: EXP:0 , IA5: a, b ", nullptr, {0x30, 0x1a, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x0a, 0xa0, 0x08, 0x16, 0x06, 0x20, 0x61, 0x2c, 0x20, 0x62, 0x20}}, // Modifiers without a final type. {kTestOID, "ASN1:EXP:1", nullptr, {}}, // Put it all together to describe a test Ed25519 key (wrapped inside an // X.509 extension). {kTestOID, "ASN1:SEQUENCE:pkcs8", R"( [pkcs8] vers = INT:0 alg = SEQWRAP,OID:1.3.101.112 key = FORMAT:HEX,OCTWRAP,OCT:9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60 )", {0x30, 0x40, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x30, 0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x04, 0x22, 0x04, 0x20, 0x9d, 0x61, 0xb1, 0x9d, 0xef, 0xfd, 0x5a, 0x60, 0xba, 0x84, 0x4a, 0xf4, 0x92, 0xec, 0x2c, 0xc4, 0x44, 0x49, 0xc5, 0x69, 0x7b, 0x32, 0x69, 0x19, 0x70, 0x3b, 0xac, 0x03, 0x1c, 0xae, 0x7f, 0x60}}, // Sections can be referenced multiple times. {kTestOID, "ASN1:SEQUENCE:seq1", R"( [seq1] val1 = SEQUENCE:seq2 val2 = SEQUENCE:seq2 [seq2] val1 = INT:1 val2 = INT:2 )", {0x30, 0x22, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x12, 0x30, 0x10, 0x30, 0x06, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02, 0x30, 0x06, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02}}, // But we cap this before it blows up exponentially. {kTestOID, "ASN1:SEQ:seq1", R"( [seq1] val1 = SEQ:seq2 val2 = SEQ:seq2 [seq2] val1 = SEQ:seq3 val2 = SEQ:seq3 [seq3] val1 = SEQ:seq4 val2 = SEQ:seq4 [seq4] val1 = SEQ:seq5 val2 = SEQ:seq5 [seq5] val1 = SEQ:seq6 val2 = SEQ:seq6 [seq6] val1 = SEQ:seq7 val2 = SEQ:seq7 [seq7] val1 = SEQ:seq8 val2 = SEQ:seq8 [seq8] val1 = SEQ:seq9 val2 = SEQ:seq9 [seq9] val1 = SEQ:seq10 val2 = SEQ:seq10 [seq10] val1 = SEQ:seq11 val2 = SEQ:seq11 [seq11] val1 = SEQ:seq12 val2 = SEQ:seq12 [seq12] val1 = IA5:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA val2 = IA5:BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB )", {}}, // Integer sizes are capped to mitigate quadratic behavior. {kTestOID, "ASN1:INT:" + std::string(16384, '9'), nullptr, {}}, }; for (const auto &t : kTests) { SCOPED_TRACE(t.name); SCOPED_TRACE(t.value); SCOPED_TRACE(t.conf); bssl::UniquePtr conf; if (t.conf != nullptr) { conf.reset(NCONF_new(nullptr)); ASSERT_TRUE(conf); bssl::UniquePtr bio(BIO_new_mem_buf(t.conf, strlen(t.conf))); ASSERT_TRUE(bio); long error_line; ASSERT_TRUE(NCONF_load_bio(conf.get(), bio.get(), &error_line)) << "Failed to load config at line " << error_line; } bssl::UniquePtr ext( X509V3_EXT_nconf(conf.get(), nullptr, t.name, t.value.c_str())); if (t.expected.empty()) { EXPECT_FALSE(ext); } else { ASSERT_TRUE(ext); uint8_t *der = nullptr; int len = i2d_X509_EXTENSION(ext.get(), &der); ASSERT_GE(len, 0); bssl::UniquePtr free_der(der); EXPECT_EQ(Bytes(t.expected), Bytes(der, len)); } // Repeat the test with an explicit |X509V3_CTX|. X509V3_CTX ctx; X509V3_set_ctx(&ctx, nullptr, nullptr, nullptr, nullptr, 0); X509V3_set_nconf(&ctx, conf.get()); ext.reset(X509V3_EXT_nconf(conf.get(), &ctx, t.name, t.value.c_str())); if (t.expected.empty()) { EXPECT_FALSE(ext); } else { ASSERT_TRUE(ext); uint8_t *der = nullptr; int len = i2d_X509_EXTENSION(ext.get(), &der); ASSERT_GE(len, 0); bssl::UniquePtr free_der(der); EXPECT_EQ(Bytes(t.expected), Bytes(der, len)); } } } TEST(X509Test, AddUnserializableExtension) { bssl::UniquePtr key = PrivateKeyFromPEM(kP256Key); ASSERT_TRUE(key); bssl::UniquePtr x509 = MakeTestCert("Issuer", "Subject", key.get(), /*is_ca=*/true); ASSERT_TRUE(x509); bssl::UniquePtr ext(X509_EXTENSION_new()); ASSERT_TRUE(X509_EXTENSION_set_object(ext.get(), OBJ_get_undef())); EXPECT_FALSE(X509_add_ext(x509.get(), ext.get(), /*loc=*/-1)); } // Test that, when constructing an |X509_NAME|, names are sorted by DER order. TEST(X509Test, SortRDN) { bssl::UniquePtr name(X509_NAME_new()); ASSERT_TRUE(name); auto append_entry_new_rdn = [&](const char *str) { return X509_NAME_add_entry_by_NID(name.get(), NID_commonName, MBSTRING_ASC, reinterpret_cast(str), strlen(str), /*loc=*/-1, /*set=*/0); }; auto append_entry_prev_rdn = [&](const char *str) { return X509_NAME_add_entry_by_NID(name.get(), NID_commonName, MBSTRING_ASC, reinterpret_cast(str), strlen(str), /*loc=*/-1, /*set=*/-1); }; // This is the sort order to expect. ASSERT_TRUE(append_entry_new_rdn("A")); ASSERT_TRUE(append_entry_prev_rdn("B")); ASSERT_TRUE(append_entry_prev_rdn("AA")); ASSERT_TRUE(append_entry_prev_rdn("AB")); // The same RDN, with entries added in a different order. ASSERT_TRUE(append_entry_new_rdn("AB")); ASSERT_TRUE(append_entry_prev_rdn("AA")); ASSERT_TRUE(append_entry_prev_rdn("B")); ASSERT_TRUE(append_entry_prev_rdn("A")); // The same RDN, with entries added in a different order. ASSERT_TRUE(append_entry_new_rdn("A")); ASSERT_TRUE(append_entry_prev_rdn("AA")); ASSERT_TRUE(append_entry_prev_rdn("B")); ASSERT_TRUE(append_entry_prev_rdn("AB")); uint8_t *der = nullptr; int der_len = i2d_X509_NAME(name.get(), &der); ASSERT_GT(der_len, 0); bssl::UniquePtr free_der(der); // SEQUENCE { // SET { // SEQUENCE { // # commonName // OBJECT_IDENTIFIER { 2.5.4.3 } // UTF8String { "A" } // } // SEQUENCE { // # commonName // OBJECT_IDENTIFIER { 2.5.4.3 } // UTF8String { "B" } // } // SEQUENCE { // # commonName // OBJECT_IDENTIFIER { 2.5.4.3 } // UTF8String { "AA" } // } // SEQUENCE { // # commonName // OBJECT_IDENTIFIER { 2.5.4.3 } // UTF8String { "AB" } // } // } // ...two more copies of the above SET... // } static uint8_t kExpected[] = { 0x30, 0x81, 0x84, 0x31, 0x2a, 0x30, 0x08, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x01, 0x41, 0x30, 0x08, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x01, 0x42, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x02, 0x41, 0x41, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x02, 0x41, 0x42, 0x31, 0x2a, 0x30, 0x08, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x01, 0x41, 0x30, 0x08, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x01, 0x42, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x02, 0x41, 0x41, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x02, 0x41, 0x42, 0x31, 0x2a, 0x30, 0x08, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x01, 0x41, 0x30, 0x08, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x01, 0x42, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x02, 0x41, 0x41, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x02, 0x41, 0x42}; EXPECT_EQ(Bytes(kExpected), Bytes(der, der_len)); } TEST(X509Test, TestDecode) { bssl::UniquePtr cert(CertFromPEM(kExamplePSSCert)); ASSERT_TRUE(cert); // |X509_get0_pubkey| directly returns a reference to the decoded |pkey|, so // it can't be freed. EVP_PKEY *pkey1 = X509_get0_pubkey(cert.get()); ASSERT_TRUE(pkey1); ASSERT_TRUE(X509_verify(cert.get(), pkey1)); // |X509_get_pubkey| returns the decoded |pkey| with its reference count // updated, so we must free it. bssl::UniquePtr pkey2(X509_get_pubkey(cert.get())); ASSERT_TRUE(pkey2); ASSERT_TRUE(X509_verify(cert.get(), pkey2.get())); } TEST(X509Test, X509_OBJECT_heap) { X509_OBJECT *x509_object = X509_OBJECT_new(); ASSERT_TRUE(x509_object); X509_OBJECT_free(x509_object); } TEST(X509Test, NameAttributeValues) { // 1.2.840.113554.4.1.72585.0. We use an unrecognized OID because using an // arbitrary ASN.1 type as the value for commonName is invalid. Our parser // does not check this, but best to avoid unrelated errors in tests, in case // we decide to later. static const uint8_t kOID[] = {0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x00}; static const char kOIDText[] = "1.2.840.113554.4.1.72585.0"; auto encode_single_attribute_name = [](CBS_ASN1_TAG tag, const std::string &contents) -> std::vector { bssl::ScopedCBB cbb; CBB seq, rdn, attr, attr_type, attr_value; if (!CBB_init(cbb.get(), 128) || !CBB_add_asn1(cbb.get(), &seq, CBS_ASN1_SEQUENCE) || !CBB_add_asn1(&seq, &rdn, CBS_ASN1_SET) || !CBB_add_asn1(&rdn, &attr, CBS_ASN1_SEQUENCE) || !CBB_add_asn1(&attr, &attr_type, CBS_ASN1_OBJECT) || !CBB_add_bytes(&attr_type, kOID, sizeof(kOID)) || !CBB_add_asn1(&attr, &attr_value, tag) || !CBB_add_bytes(&attr_value, reinterpret_cast(contents.data()), contents.size()) || !CBB_flush(cbb.get())) { ADD_FAILURE() << "Could not encode name"; return {}; }; return std::vector(CBB_data(cbb.get()), CBB_data(cbb.get()) + CBB_len(cbb.get())); }; const struct { CBS_ASN1_TAG der_tag; std::string der_contents; int str_type; std::string str_contents; } kTests[] = { // String types are parsed as string types. {CBS_ASN1_BITSTRING, std::string("\0", 1), V_ASN1_BIT_STRING, ""}, {CBS_ASN1_UTF8STRING, "abc", V_ASN1_UTF8STRING, "abc"}, {CBS_ASN1_NUMERICSTRING, "123", V_ASN1_NUMERICSTRING, "123"}, {CBS_ASN1_PRINTABLESTRING, "abc", V_ASN1_PRINTABLESTRING, "abc"}, {CBS_ASN1_T61STRING, "abc", V_ASN1_T61STRING, "abc"}, {CBS_ASN1_IA5STRING, "abc", V_ASN1_IA5STRING, "abc"}, {CBS_ASN1_UNIVERSALSTRING, std::string("\0\0\0a", 4), V_ASN1_UNIVERSALSTRING, std::string("\0\0\0a", 4)}, {CBS_ASN1_BMPSTRING, std::string("\0a", 2), V_ASN1_BMPSTRING, std::string("\0a", 2)}, // ENUMERATED is supported but, currently, INTEGER is not. {CBS_ASN1_ENUMERATED, "\x01", V_ASN1_ENUMERATED, "\x01"}, // Test negative values. These are interesting because, when encoding, the // ASN.1 type must be determined from the string type, but the string type // has an extra |V_ASN1_NEG| bit. {CBS_ASN1_ENUMERATED, "\xff", V_ASN1_NEG_ENUMERATED, "\x01"}, // SEQUENCE is supported but, currently, SET is not. Note the // |ASN1_STRING| representation will include the tag and length. {CBS_ASN1_SEQUENCE, "", V_ASN1_SEQUENCE, std::string("\x30\x00", 2)}, // These types are not actually supported by the library but, // historically, we would parse them, and not other unsupported types, due // to quirks of |ASN1_tag2bit|. {7, "", V_ASN1_OBJECT_DESCRIPTOR, ""}, {8, "", V_ASN1_EXTERNAL, ""}, {9, "", V_ASN1_REAL, ""}, {11, "", 11 /* EMBEDDED PDV */, ""}, {13, "", 13 /* RELATIVE-OID */, ""}, {14, "", 14 /* TIME */, ""}, {15, "", 15 /* not a type; reserved value */, ""}, {29, "", 29 /* CHARACTER STRING */, ""}, // TODO(crbug.com/boringssl/412): Attribute values are an ANY DEFINED BY // type, so we actually shoudl be accepting all ASN.1 types. We currently // do not and only accept the above types. Extend this test when we fix // this. }; for (const auto &t : kTests) { SCOPED_TRACE(t.der_tag); SCOPED_TRACE(Bytes(t.der_contents)); // Construct an X.509 name containing a single RDN with a single attribute: // kOID with the specified value. auto encoded = encode_single_attribute_name(t.der_tag, t.der_contents); ASSERT_FALSE(encoded.empty()); SCOPED_TRACE(Bytes(encoded)); // The input should parse. const uint8_t *inp = encoded.data(); bssl::UniquePtr name( d2i_X509_NAME(nullptr, &inp, encoded.size())); ASSERT_TRUE(name); EXPECT_EQ(inp, encoded.data() + encoded.size()) << "input was not fully consumed"; // Check there is a single attribute with the expected in-memory // representation. ASSERT_EQ(1, X509_NAME_entry_count(name.get())); const X509_NAME_ENTRY *entry = X509_NAME_get_entry(name.get(), 0); const ASN1_OBJECT *obj = X509_NAME_ENTRY_get_object(entry); EXPECT_EQ(Bytes(OBJ_get0_data(obj), OBJ_length(obj)), Bytes(kOID)); const ASN1_STRING *value = X509_NAME_ENTRY_get_data(entry); EXPECT_EQ(ASN1_STRING_type(value), t.str_type); EXPECT_EQ(Bytes(ASN1_STRING_get0_data(value), ASN1_STRING_length(value)), Bytes(t.str_contents)); // The name should re-encode with the same input. uint8_t *der = nullptr; int der_len = i2d_X509_NAME(name.get(), &der); ASSERT_GE(der_len, 0); bssl::UniquePtr free_der(der); EXPECT_EQ(Bytes(der, der_len), Bytes(encoded)); // X509_NAME internally caches its encoding, which means the check above // does not fully test re-encoding. Repeat the test by constructing an // |X509_NAME| from the string representation. name.reset(X509_NAME_new()); ASSERT_TRUE(name); ASSERT_TRUE(X509_NAME_add_entry_by_txt( name.get(), kOIDText, t.str_type, reinterpret_cast(t.str_contents.data()), t.str_contents.size(), /*loc=*/-1, /*set=*/0)); // The name should re-encode with the same input. der = nullptr; der_len = i2d_X509_NAME(name.get(), &der); ASSERT_GE(der_len, 0); free_der.reset(der); EXPECT_EQ(Bytes(der, der_len), Bytes(encoded)); } const struct { CBS_ASN1_TAG der_tag; std::string der_contents; } kInvalidTests[] = { // Errors in supported universal types should be handled. {CBS_ASN1_NULL, "not null"}, {CBS_ASN1_BOOLEAN, "not bool"}, {CBS_ASN1_OBJECT, ""}, {CBS_ASN1_INTEGER, std::string("\0\0", 2)}, {CBS_ASN1_ENUMERATED, std::string("\0\0", 2)}, {CBS_ASN1_BITSTRING, ""}, {CBS_ASN1_UTF8STRING, "not utf-8 \xff"}, {CBS_ASN1_BMPSTRING, "not utf-16 "}, {CBS_ASN1_UNIVERSALSTRING, "not utf-32"}, {CBS_ASN1_UTCTIME, "not utctime"}, {CBS_ASN1_GENERALIZEDTIME, "not generalizedtime"}, {CBS_ASN1_SEQUENCE & ~CBS_ASN1_CONSTRUCTED, ""}, // TODO(crbug.com/boringssl/412): The following inputs should parse, but // are currently rejected because they cannot be represented in // |ASN1_PRINTABLE|, either because they don't fit in |ASN1_STRING| or // simply in the |B_ASN1_PRINTABLE| bitmask. {CBS_ASN1_NULL, ""}, {CBS_ASN1_BOOLEAN, std::string("\x00", 1)}, {CBS_ASN1_BOOLEAN, "\xff"}, {CBS_ASN1_OBJECT, "\x01\x02\x03\x04"}, {CBS_ASN1_INTEGER, "\x01"}, {CBS_ASN1_INTEGER, "\xff"}, {CBS_ASN1_OCTETSTRING, ""}, {CBS_ASN1_UTCTIME, "700101000000Z"}, {CBS_ASN1_GENERALIZEDTIME, "19700101000000Z"}, {CBS_ASN1_SET, ""}, {CBS_ASN1_APPLICATION | CBS_ASN1_CONSTRUCTED | 42, ""}, {CBS_ASN1_APPLICATION | 42, ""}, }; for (const auto &t : kInvalidTests) { SCOPED_TRACE(t.der_tag); SCOPED_TRACE(Bytes(t.der_contents)); // Construct an X.509 name containing a single RDN with a single attribute: // kOID with the specified value. auto encoded = encode_single_attribute_name(t.der_tag, t.der_contents); ASSERT_FALSE(encoded.empty()); SCOPED_TRACE(Bytes(encoded)); // The input should not parse. const uint8_t *inp = encoded.data(); bssl::UniquePtr name( d2i_X509_NAME(nullptr, &inp, encoded.size())); EXPECT_FALSE(name); } } TEST(X509Test, GetTextByOBJ) { struct OBJTestCase { const char *content; int content_type; int len; int expected_result; const char *expected_string; } kTests[] = { {"", V_ASN1_UTF8STRING, 0, 0, ""}, {"derp", V_ASN1_UTF8STRING, 4, 4, "derp"}, {"\x30\x00", // Empty sequence can not be converted to UTF-8 V_ASN1_SEQUENCE, 2, -1, ""}, { "der\0p", V_ASN1_TELETEXSTRING, 5, -1, "", }, { "0123456789ABCDEF", V_ASN1_IA5STRING, 16, 16, "0123456789ABCDEF", }, { "\x07\xff", V_ASN1_BMPSTRING, 2, 2, "\xdf\xbf", }, { "\x00\xc3\x00\xaf", V_ASN1_BMPSTRING, 4, 4, "\xc3\x83\xc2\xaf", }, }; for (const auto &test : kTests) { bssl::UniquePtr name(X509_NAME_new()); ASSERT_TRUE(name); ASSERT_TRUE(X509_NAME_add_entry_by_NID( name.get(), NID_commonName, test.content_type, reinterpret_cast(test.content), test.len, /*loc=*/-1, /*set=*/0)); char text[256] = {}; EXPECT_EQ(test.expected_result, X509_NAME_get_text_by_NID(name.get(), NID_commonName, text, sizeof(text))); EXPECT_STREQ(text, test.expected_string); if (test.expected_result > 0) { // Test truncation. The function writes a trailing NUL byte so the // buffer needs to be one bigger than the expected result. char small[2] = "a"; EXPECT_EQ( -1, X509_NAME_get_text_by_NID(name.get(), NID_commonName, small, 1)); // The buffer should be unmodified by truncation failure. EXPECT_STREQ(small, "a"); } } } TEST(X509Test, GetSigInfo) { bssl::UniquePtr cert(CertFromPEM(kLeafPEM)); ASSERT_TRUE(cert); int digest_nid, pubkey_nid, sec_bits; uint32_t flags; EXPECT_TRUE(X509_get_signature_info(cert.get(), &digest_nid, &pubkey_nid, &sec_bits, &flags)); EXPECT_EQ(digest_nid, NID_sha256); EXPECT_EQ(pubkey_nid, NID_rsaEncryption); EXPECT_EQ(sec_bits, (int)EVP_MD_size(EVP_sha256()) * 4); EXPECT_TRUE(flags & (X509_SIG_INFO_VALID | X509_SIG_INFO_TLS)); cert = CertFromPEM(kEd25519Cert); EXPECT_TRUE(X509_get_signature_info(cert.get(), &digest_nid, &pubkey_nid, &sec_bits, &flags)); EXPECT_EQ(digest_nid, NID_undef); EXPECT_EQ(pubkey_nid, NID_ED25519); EXPECT_EQ(sec_bits, -1); EXPECT_TRUE(flags & X509_SIG_INFO_VALID); cert = CertFromPEM(kExampleRsassaPssCert); EXPECT_TRUE(X509_get_signature_info(cert.get(), &digest_nid, &pubkey_nid, &sec_bits, &flags)); EXPECT_EQ(digest_nid, NID_undef); EXPECT_EQ(pubkey_nid, NID_rsaEncryption); EXPECT_EQ(sec_bits, -1); EXPECT_TRUE(flags & X509_SIG_INFO_VALID); } TEST(X509Test, ExternalData) { // Create a |X509_STORE| object bssl::UniquePtr store(X509_STORE_new()); int store_index = X509_STORE_get_ex_new_index(0, nullptr, nullptr, nullptr, CustomDataFree); ASSERT_GT(store_index, 0); // Associate custom data with the |X509_STORE| using |X509_STORE_set_ex_data| // and set an arbitrary number. auto *custom_data = static_cast(malloc(sizeof(CustomData))); ASSERT_TRUE(custom_data); custom_data->custom_data = 123; ASSERT_TRUE(X509_STORE_set_ex_data(store.get(), store_index, custom_data)); // Retrieve the custom data using |X509_STORE_get_ex_data|. auto *retrieved_data = static_cast( X509_STORE_get_ex_data(store.get(), store_index)); ASSERT_TRUE(retrieved_data); EXPECT_EQ(retrieved_data->custom_data, 123); } TEST(X509Test, ParamInheritance) { // |X509_VERIFY_PARAM_inherit| with both unset. { bssl::UniquePtr dest(X509_VERIFY_PARAM_new()); ASSERT_TRUE(dest); bssl::UniquePtr src(X509_VERIFY_PARAM_new()); ASSERT_TRUE(src); ASSERT_TRUE(X509_VERIFY_PARAM_inherit(dest.get(), src.get())); EXPECT_EQ(X509_VERIFY_PARAM_get_depth(dest.get()), -1); } // |X509_VERIFY_PARAM_inherit| with source set. { bssl::UniquePtr dest(X509_VERIFY_PARAM_new()); ASSERT_TRUE(dest); bssl::UniquePtr src(X509_VERIFY_PARAM_new()); ASSERT_TRUE(src); X509_VERIFY_PARAM_set_depth(src.get(), 5); ASSERT_TRUE(X509_VERIFY_PARAM_inherit(dest.get(), src.get())); EXPECT_EQ(X509_VERIFY_PARAM_get_depth(dest.get()), 5); } // |X509_VERIFY_PARAM_inherit| with destination set. { bssl::UniquePtr dest(X509_VERIFY_PARAM_new()); ASSERT_TRUE(dest); bssl::UniquePtr src(X509_VERIFY_PARAM_new()); ASSERT_TRUE(src); X509_VERIFY_PARAM_set_depth(dest.get(), 5); ASSERT_TRUE(X509_VERIFY_PARAM_inherit(dest.get(), src.get())); EXPECT_EQ(X509_VERIFY_PARAM_get_depth(dest.get()), 5); } // |X509_VERIFY_PARAM_inherit| with both set. { bssl::UniquePtr dest(X509_VERIFY_PARAM_new()); ASSERT_TRUE(dest); bssl::UniquePtr src(X509_VERIFY_PARAM_new()); ASSERT_TRUE(src); X509_VERIFY_PARAM_set_depth(dest.get(), 5); X509_VERIFY_PARAM_set_depth(src.get(), 10); ASSERT_TRUE(X509_VERIFY_PARAM_inherit(dest.get(), src.get())); // The existing value is used. EXPECT_EQ(X509_VERIFY_PARAM_get_depth(dest.get()), 5); } // |X509_VERIFY_PARAM_set1| with both unset. { bssl::UniquePtr dest(X509_VERIFY_PARAM_new()); ASSERT_TRUE(dest); bssl::UniquePtr src(X509_VERIFY_PARAM_new()); ASSERT_TRUE(src); ASSERT_TRUE(X509_VERIFY_PARAM_set1(dest.get(), src.get())); EXPECT_EQ(X509_VERIFY_PARAM_get_depth(dest.get()), -1); } // |X509_VERIFY_PARAM_set1| with source set. { bssl::UniquePtr dest(X509_VERIFY_PARAM_new()); ASSERT_TRUE(dest); bssl::UniquePtr src(X509_VERIFY_PARAM_new()); ASSERT_TRUE(src); X509_VERIFY_PARAM_set_depth(src.get(), 5); ASSERT_TRUE(X509_VERIFY_PARAM_set1(dest.get(), src.get())); EXPECT_EQ(X509_VERIFY_PARAM_get_depth(dest.get()), 5); } // |X509_VERIFY_PARAM_set1| with destination set. { bssl::UniquePtr dest(X509_VERIFY_PARAM_new()); ASSERT_TRUE(dest); bssl::UniquePtr src(X509_VERIFY_PARAM_new()); ASSERT_TRUE(src); X509_VERIFY_PARAM_set_depth(dest.get(), 5); ASSERT_TRUE(X509_VERIFY_PARAM_set1(dest.get(), src.get())); EXPECT_EQ(X509_VERIFY_PARAM_get_depth(dest.get()), 5); } // |X509_VERIFY_PARAM_set1| with both set. { bssl::UniquePtr dest(X509_VERIFY_PARAM_new()); ASSERT_TRUE(dest); bssl::UniquePtr src(X509_VERIFY_PARAM_new()); ASSERT_TRUE(src); X509_VERIFY_PARAM_set_depth(dest.get(), 5); X509_VERIFY_PARAM_set_depth(src.get(), 10); ASSERT_TRUE(X509_VERIFY_PARAM_set1(dest.get(), src.get())); // The new value is used. EXPECT_EQ(X509_VERIFY_PARAM_get_depth(dest.get()), 10); } } TEST(X509Test, PublicKeyCache) { bssl::UniquePtr key = PrivateKeyFromPEM(kP256Key); ASSERT_TRUE(key); X509_PUBKEY *pub = nullptr; ASSERT_TRUE(X509_PUBKEY_set(&pub, key.get())); bssl::UniquePtr free_pub(pub); bssl::UniquePtr key2(X509_PUBKEY_get(pub)); ASSERT_TRUE(key2); EXPECT_EQ(1, EVP_PKEY_cmp(key.get(), key2.get())); // Replace |pub| with different (garbage) values. ASSERT_TRUE(X509_PUBKEY_set0_param(pub, OBJ_nid2obj(NID_subject_alt_name), V_ASN1_NULL, nullptr, nullptr, 0)); // The cached key should no longer be returned. key2.reset(X509_PUBKEY_get(pub)); EXPECT_FALSE(key2); } TEST(X509Test, SPKIPrint) { bssl::UniquePtr bio(BIO_new(BIO_s_mem())); ASSERT_TRUE(bio); bssl::UniquePtr spki(NETSCAPE_SPKI_new()); ASSERT_TRUE(spki); bssl::UniquePtr key = PrivateKeyFromPEM(kP256Key); EXPECT_TRUE(NETSCAPE_SPKI_set_pubkey(spki.get(), key.get())); EXPECT_TRUE(NETSCAPE_SPKI_sign(spki.get(), key.get(), EVP_sha256())); std::string challenge = "challenge string"; ASSERT_TRUE(ASN1_STRING_set(spki.get()->spkac->challenge, challenge.data(), challenge.size())); EXPECT_TRUE(NETSCAPE_SPKI_print(bio.get(), spki.get())); // The contents of the signature is printed last but it's randomized, // so we only check the expected output before that. static const char expected_certificate_string[] = R"(Netscape SPKI: Public Key Algorithm: id-ecPublicKey Public-Key: (P-256) pub: 04:e6:2b:69:e2:bf:65:9f:97:be:2f:1e:0d:94:8a: 4c:d5:97:6b:b7:a9:1e:0d:46:fb:dd:a9:a9:1e:9d: dc:ba:5a:01:e7:d6:97:a8:0a:18:f9:c3:c4:a3:1e: 56:e2:7c:83:48:db:16:1a:1c:f5:1d:7e:f1:94:2d: 4b:cf:72:22:c1 Challenge String: challenge string Signature Algorithm: ecdsa-with-SHA256 )"; const uint8_t *data; size_t data_len; ASSERT_TRUE(BIO_mem_contents(bio.get(), &data, &data_len)); ASSERT_GT(data_len, strlen(expected_certificate_string)); std::string print(reinterpret_cast(data), strlen(expected_certificate_string)); EXPECT_EQ(print, expected_certificate_string); } // Tests some unusual behavior in |X509_STORE_CTX_set_purpose| and // |X509_STORE_CTX_set_trust|. TEST(X509Test, ContextTrustAndPurpose) { bssl::UniquePtr store(X509_STORE_new()); ASSERT_TRUE(store); bssl::UniquePtr leaf(CertFromPEM(kLeafPEM)); ASSERT_TRUE(leaf); bssl::UniquePtr ctx(X509_STORE_CTX_new()); ASSERT_TRUE(ctx); ASSERT_TRUE(X509_STORE_CTX_init(ctx.get(), store.get(), leaf.get(), nullptr)); // Initially, neither parameter is set. EXPECT_EQ(ctx->param->purpose, 0); EXPECT_EQ(ctx->param->trust, 0); // Invalid purpose and trust types fail. EXPECT_FALSE(X509_STORE_CTX_set_purpose(ctx.get(), 999)); EXPECT_FALSE(X509_STORE_CTX_set_trust(ctx.get(), 999)); // It is not possible to set |X509_PURPOSE_ANY| with this API, because there // is no corresponding trust. EXPECT_FALSE(X509_STORE_CTX_set_purpose(ctx.get(), X509_PURPOSE_ANY)); // Setting a purpose also sets the corresponding trust. ASSERT_TRUE(X509_STORE_CTX_set_purpose(ctx.get(), X509_PURPOSE_SSL_SERVER)); EXPECT_EQ(ctx->param->purpose, X509_PURPOSE_SSL_SERVER); EXPECT_EQ(ctx->param->trust, X509_TRUST_SSL_SERVER); // Once set, the functions silently do nothing. ASSERT_TRUE(X509_STORE_CTX_set_purpose(ctx.get(), X509_PURPOSE_SSL_CLIENT)); ASSERT_TRUE(X509_STORE_CTX_set_trust(ctx.get(), X509_TRUST_SSL_CLIENT)); EXPECT_EQ(ctx->param->purpose, X509_PURPOSE_SSL_SERVER); EXPECT_EQ(ctx->param->trust, X509_TRUST_SSL_SERVER); // Start over. ctx.reset(X509_STORE_CTX_new()); ASSERT_TRUE(ctx); ASSERT_TRUE(X509_STORE_CTX_init(ctx.get(), store.get(), leaf.get(), nullptr)); EXPECT_EQ(ctx->param->purpose, 0); EXPECT_EQ(ctx->param->trust, 0); // Setting trust leaves purpose unset. ASSERT_TRUE(X509_STORE_CTX_set_trust(ctx.get(), X509_TRUST_SSL_SERVER)); EXPECT_EQ(ctx->param->purpose, 0); EXPECT_EQ(ctx->param->trust, X509_TRUST_SSL_SERVER); // If trust is set, but not purpose, |X509_STORE_CTX_set_purpose| only sets // purpose. ASSERT_TRUE(X509_STORE_CTX_set_purpose(ctx.get(), X509_PURPOSE_SSL_CLIENT)); EXPECT_EQ(ctx->param->purpose, X509_PURPOSE_SSL_CLIENT); EXPECT_EQ(ctx->param->trust, X509_TRUST_SSL_SERVER); // Start over. ctx.reset(X509_STORE_CTX_new()); ASSERT_TRUE(ctx); ASSERT_TRUE(X509_STORE_CTX_init(ctx.get(), store.get(), leaf.get(), nullptr)); EXPECT_EQ(ctx->param->purpose, 0); EXPECT_EQ(ctx->param->trust, 0); // If purpose is set, but not trust, |X509_STORE_CTX_set_purpose| only sets // trust. ASSERT_TRUE(X509_VERIFY_PARAM_set_purpose( X509_STORE_CTX_get0_param(ctx.get()), X509_PURPOSE_SSL_CLIENT)); EXPECT_EQ(ctx->param->purpose, X509_PURPOSE_SSL_CLIENT); EXPECT_EQ(ctx->param->trust, 0); ASSERT_TRUE(X509_STORE_CTX_set_purpose(ctx.get(), X509_PURPOSE_SSL_SERVER)); EXPECT_EQ(ctx->param->purpose, X509_PURPOSE_SSL_CLIENT); EXPECT_EQ(ctx->param->trust, X509_TRUST_SSL_SERVER); } TEST(X509Test, Purpose) { bssl::UniquePtr key = PrivateKeyFromPEM(kP256Key); ASSERT_TRUE(key); struct { int purpose; int eku_nid; std::vector key_usages; } kTests[] = { {X509_PURPOSE_SSL_CLIENT, NID_client_auth, {KeyUsage::kDigitalSignature, KeyUsage::kKeyAgreement}}, {X509_PURPOSE_SSL_SERVER, NID_server_auth, {KeyUsage::kDigitalSignature, KeyUsage::kKeyAgreement, KeyUsage::kKeyEncipherment}}, {X509_PURPOSE_NS_SSL_SERVER, NID_server_auth, {KeyUsage::kKeyEncipherment}}, {X509_PURPOSE_SMIME_SIGN, NID_email_protect, {KeyUsage::kDigitalSignature, KeyUsage::kNonRepudiation}}, {X509_PURPOSE_SMIME_ENCRYPT, NID_email_protect, {KeyUsage::kKeyEncipherment}}, {X509_PURPOSE_CRL_SIGN, NID_undef, {KeyUsage::kCRLSign}}, }; for (const auto &t : kTests) { SCOPED_TRACE(t.purpose); auto configure_callback = [&](X509_STORE_CTX *ctx) { X509_STORE_CTX_set_purpose(ctx, t.purpose); }; // An unconstrained cert chain is valid. bssl::UniquePtr root = MakeTestCert("Root", "Root", key.get(), /*is_ca=*/true); ASSERT_TRUE(root); ASSERT_TRUE(X509_sign(root.get(), key.get(), EVP_sha256())); bssl::UniquePtr intermediate = MakeTestCert("Root", "Intermediate", key.get(), /*is_ca=*/true); ASSERT_TRUE(intermediate); ASSERT_TRUE(X509_sign(intermediate.get(), key.get(), EVP_sha256())); bssl::UniquePtr leaf = MakeTestCert("Intermediate", "Leaf", key.get(), /*is_ca=*/false); ASSERT_TRUE(leaf); ASSERT_TRUE(X509_sign(leaf.get(), key.get(), EVP_sha256())); EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {intermediate.get()}, {}, 0, configure_callback)); // A leaf and intermediate with compatible constraints is valid. intermediate = MakeTestCert("Root", "Intermediate", key.get(), /*is_ca=*/true); ASSERT_TRUE(intermediate); ASSERT_TRUE(AddKeyUsage(intermediate.get(), {KeyUsage::kKeyCertSign})); if (t.eku_nid != NID_undef) { ASSERT_TRUE(AddExtendedKeyUsage(intermediate.get(), {t.eku_nid})); } ASSERT_TRUE(X509_sign(intermediate.get(), key.get(), EVP_sha256())); leaf = MakeTestCert("Intermediate", "Leaf", key.get(), /*is_ca=*/false); ASSERT_TRUE(leaf); if (t.eku_nid != NID_undef) { ASSERT_TRUE(AddExtendedKeyUsage(leaf.get(), {t.eku_nid})); } ASSERT_TRUE(AddKeyUsage(leaf.get(), t.key_usages)); ASSERT_TRUE(X509_sign(leaf.get(), key.get(), EVP_sha256())); EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {intermediate.get()}, {}, 0, configure_callback)); // Each key usage asserted individually is valid. for (KeyUsage usage : t.key_usages) { SCOPED_TRACE(static_cast(usage)); leaf = MakeTestCert("Intermediate", "Leaf", key.get(), /*is_ca=*/false); ASSERT_TRUE(leaf); if (t.eku_nid != NID_undef) { ASSERT_TRUE(AddExtendedKeyUsage(leaf.get(), {t.eku_nid})); } ASSERT_TRUE(AddKeyUsage(leaf.get(), {usage})); ASSERT_TRUE(X509_sign(leaf.get(), key.get(), EVP_sha256())); EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {intermediate.get()}, {}, 0, configure_callback)); } // A leaf with the wrong EKU is invalid. if (t.eku_nid != NID_undef) { leaf = MakeTestCert("Intermediate", "Leaf", key.get(), /*is_ca=*/false); ASSERT_TRUE(leaf); ASSERT_TRUE(AddExtendedKeyUsage(leaf.get(), {NID_rsaEncryption})); ASSERT_TRUE(AddKeyUsage(leaf.get(), t.key_usages)); ASSERT_TRUE(X509_sign(leaf.get(), key.get(), EVP_sha256())); EXPECT_EQ(X509_V_ERR_INVALID_PURPOSE, Verify(leaf.get(), {root.get()}, {intermediate.get()}, {}, 0, configure_callback)); } // A leaf without any of the requested key usages is invalid. std::vector usages; for (int i = 0; i < 10; i++) { auto k = static_cast(i); if (std::find(t.key_usages.begin(), t.key_usages.end(), k) == t.key_usages.end()) { usages.push_back(k); } } leaf = MakeTestCert("Intermediate", "Leaf", key.get(), /*is_ca=*/false); ASSERT_TRUE(leaf); if (t.eku_nid != NID_undef) { ASSERT_TRUE(AddExtendedKeyUsage(leaf.get(), {t.eku_nid})); } ASSERT_TRUE(AddKeyUsage(leaf.get(), usages)); ASSERT_TRUE(X509_sign(leaf.get(), key.get(), EVP_sha256())); EXPECT_EQ(X509_V_ERR_INVALID_PURPOSE, Verify(leaf.get(), {root.get()}, {intermediate.get()}, {}, 0, configure_callback)); // Extra EKUs and key usages are fine. usages.clear(); for (int i = 0; i < 10; i++) { usages.push_back(static_cast(i)); } leaf = MakeTestCert("Intermediate", "Leaf", key.get(), /*is_ca=*/false); ASSERT_TRUE(leaf); if (t.eku_nid != NID_undef) { ASSERT_TRUE( AddExtendedKeyUsage(leaf.get(), {t.eku_nid, NID_rsaEncryption})); } ASSERT_TRUE(AddKeyUsage(leaf.get(), usages)); ASSERT_TRUE(X509_sign(leaf.get(), key.get(), EVP_sha256())); EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {intermediate.get()}, {}, 0, configure_callback)); // anyExtendedKeyUsage is not allowed in place of a concrete EKU. if (t.eku_nid != NID_undef) { leaf = MakeTestCert("Intermediate", "Leaf", key.get(), /*is_ca=*/false); ASSERT_TRUE(leaf); ASSERT_TRUE(AddExtendedKeyUsage(leaf.get(), {NID_anyExtendedKeyUsage})); ASSERT_TRUE(AddKeyUsage(leaf.get(), t.key_usages)); ASSERT_TRUE(X509_sign(leaf.get(), key.get(), EVP_sha256())); EXPECT_EQ(X509_V_ERR_INVALID_PURPOSE, Verify(leaf.get(), {root.get()}, {intermediate.get()}, {}, 0, configure_callback)); } // Restore |leaf| to a valid option. leaf = MakeTestCert("Intermediate", "Leaf", key.get(), /*is_ca=*/false); ASSERT_TRUE(leaf); ASSERT_TRUE(X509_sign(leaf.get(), key.get(), EVP_sha256())); // The intermediate must have the keyCertSign bit. This bit is checked in // multiple places. The first place that fails is in looking for candidate // issuers. intermediate = MakeTestCert("Root", "Intermediate", key.get(), /*is_ca=*/true); ASSERT_TRUE(intermediate); ASSERT_TRUE(AddKeyUsage(intermediate.get(), {KeyUsage::kDigitalSignature})); if (t.eku_nid != NID_undef) { ASSERT_TRUE(AddExtendedKeyUsage(intermediate.get(), {t.eku_nid})); } ASSERT_TRUE(X509_sign(intermediate.get(), key.get(), EVP_sha256())); EXPECT_EQ(X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, Verify(leaf.get(), {root.get()}, {intermediate.get()}, {}, 0, configure_callback)); // The intermediate must have the EKU asserted. if (t.eku_nid != NID_undef) { intermediate = MakeTestCert("Root", "Intermediate", key.get(), /*is_ca=*/true); ASSERT_TRUE(intermediate); ASSERT_TRUE(AddKeyUsage(intermediate.get(), {KeyUsage::kKeyCertSign})); ASSERT_TRUE(AddExtendedKeyUsage(intermediate.get(), {NID_rsaEncryption})); ASSERT_TRUE(X509_sign(intermediate.get(), key.get(), EVP_sha256())); EXPECT_EQ(X509_V_ERR_INVALID_PURPOSE, Verify(leaf.get(), {root.get()}, {intermediate.get()}, {}, 0, configure_callback)); } } } TEST(X509Test, Trust) { struct Certs { bssl::UniquePtr normal = nullptr; bssl::UniquePtr trusted_server = nullptr, distrusted_server = nullptr; bssl::UniquePtr trusted_any = nullptr, distrusted_any = nullptr; }; auto certs_from_pem = [](const char *pem) -> Certs { Certs certs; certs.normal = CertFromPEM(pem); certs.trusted_server = CertFromPEM(pem); certs.distrusted_server = CertFromPEM(pem); certs.trusted_any = CertFromPEM(pem); certs.distrusted_any = CertFromPEM(pem); if (certs.normal == nullptr || certs.trusted_server == nullptr || certs.distrusted_server == nullptr || certs.trusted_any == nullptr || certs.distrusted_any == nullptr || !X509_add1_trust_object(certs.trusted_server.get(), OBJ_nid2obj(NID_server_auth)) || !X509_add1_reject_object(certs.distrusted_server.get(), OBJ_nid2obj(NID_server_auth)) || !X509_add1_trust_object(certs.trusted_any.get(), OBJ_nid2obj(NID_anyExtendedKeyUsage)) || !X509_add1_reject_object(certs.distrusted_any.get(), OBJ_nid2obj(NID_anyExtendedKeyUsage))) { return Certs{}; } return certs; }; Certs root = certs_from_pem(kRootCAPEM); Certs intermediate = certs_from_pem(kIntermediatePEM); Certs leaf = certs_from_pem(kLeafPEM); ASSERT_TRUE(root.normal); ASSERT_TRUE(intermediate.normal); ASSERT_TRUE(leaf.normal); // By default, trust is determined by a combination of self-signedness and // NID_anyExtendedKeyUsage. EXPECT_EQ(X509_V_OK, Verify(leaf.normal.get(), {root.normal.get()}, {intermediate.normal.get()}, {})); EXPECT_EQ(X509_V_OK, Verify(leaf.normal.get(), {root.trusted_any.get()}, {intermediate.normal.get()}, {})); EXPECT_EQ(X509_V_ERR_CERT_REJECTED, Verify(leaf.normal.get(), {root.distrusted_any.get()}, {intermediate.normal.get()}, {})); // Intermediate certificates are not self-signed, so must have an // NID_anyExtendedKeyUsage trust setting. EXPECT_EQ(X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT, Verify(leaf.normal.get(), {intermediate.normal.get()}, {}, {})); EXPECT_EQ(X509_V_OK, Verify(leaf.normal.get(), {intermediate.trusted_any.get()}, {}, {})); EXPECT_EQ( X509_V_ERR_CERT_REJECTED, Verify(leaf.normal.get(), {intermediate.distrusted_any.get()}, {}, {})); // If a certificate has trust settings, but only for a different OID, the // self-signed rule still takes effect. EXPECT_EQ(X509_V_OK, Verify(leaf.normal.get(), {root.trusted_server.get()}, {intermediate.normal.get()}, {})); EXPECT_EQ(X509_V_OK, Verify(leaf.normal.get(), {root.distrusted_server.get()}, {intermediate.normal.get()}, {})); EXPECT_EQ( X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT, Verify(leaf.normal.get(), {intermediate.trusted_server.get()}, {}, {})); // |X509_TRUST_SSL_SERVER| should instead look at self-signedness and // |NID_server_auth|. auto set_server_trust = [](X509_STORE_CTX *ctx) { X509_STORE_CTX_set_trust(ctx, X509_TRUST_SSL_SERVER); }; EXPECT_EQ(X509_V_OK, Verify(leaf.normal.get(), {root.normal.get()}, {intermediate.normal.get()}, {}, /*flags=*/0, set_server_trust)); EXPECT_EQ(X509_V_OK, Verify(leaf.normal.get(), {root.trusted_server.get()}, {intermediate.normal.get()}, {}, /*flags=*/0, set_server_trust)); EXPECT_EQ( X509_V_ERR_CERT_REJECTED, Verify(leaf.normal.get(), {root.distrusted_server.get()}, {intermediate.normal.get()}, {}, /*flags=*/0, set_server_trust)); EXPECT_EQ(X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT, Verify(leaf.normal.get(), {intermediate.normal.get()}, {}, {}, /*flags=*/0, set_server_trust)); EXPECT_EQ(X509_V_OK, Verify(leaf.normal.get(), {intermediate.trusted_server.get()}, {}, {}, /*flags=*/0, set_server_trust)); EXPECT_EQ(X509_V_ERR_CERT_REJECTED, Verify(leaf.normal.get(), {intermediate.distrusted_server.get()}, {}, {}, /*flags=*/0, set_server_trust)); // NID_anyExtendedKeyUsage is just an unrelated OID to X509_TRUST_SSL_SERVER. // Unlike the default behavior, once a certificate has explicit trust settings // for any OID, the self-signed check is disabled. EXPECT_EQ( X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT, Verify(leaf.normal.get(), {root.trusted_any.get()}, {intermediate.normal.get()}, {}, /*flags=*/0, set_server_trust)); // Trust settings on a certificate are ignored if the leaf did not come from // |X509_STORE|. This is important because trust settings may be serialized // via |d2i_X509_AUX|. It is often not obvious which functions may trigger // this, so callers may inadvertently run with attacker-supplied trust // settings on untrusted certificates. EXPECT_EQ(X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, Verify(leaf.trusted_server.get(), /*roots=*/{}, /*intermediates=*/{intermediate.trusted_server.get()}, {}, /*flags=*/0, set_server_trust)); EXPECT_EQ( X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN, Verify(leaf.trusted_server.get(), /*roots=*/{}, /*intermediates=*/ {intermediate.trusted_server.get(), root.trusted_server.get()}, {}, /*flags=*/0, set_server_trust)); // Likewise, distrusts only take effect from |X509_STORE|. EXPECT_EQ(X509_V_OK, Verify(leaf.distrusted_server.get(), {root.normal.get()}, {intermediate.normal.get()}, {}, /*flags=*/0, set_server_trust)); } TEST(X509Test, ParseIPAddress) { const struct { const char *inp; // out is the expected output, or an empty vector if the parser is expected // to fail. std::vector out; } kIPTests[] = { // Valid IPv4 addresses. {"127.0.0.1", {127, 0, 0, 1}}, {"1.2.3.4", {1, 2, 3, 4}}, {"1.2.3.255", {1, 2, 3, 255}}, {"255.255.255.255", {255, 255, 255, 255}}, // Valid IPv6 addresses {"::", {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, {"::1", {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}}, {"::01", {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}}, {"::001", {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}}, {"::0001", {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}}, {"ffff::", {0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, {"1::2", {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2}}, {"1:1:1:1:1:1:1:1", {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}, {"2001:db8::ff00:42:8329", {0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x42, 0x83, 0x29}}, {"1234::1.2.3.4", {0x12, 0x34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4}}, {"::1.2.3.4", {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4}}, {"ffff:ffff:ffff:ffff:ffff:ffff:1.2.3.4", {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 1, 2, 3, 4}}, // Too few IPv4 components. {"1", {}}, {"1.", {}}, {"1.2", {}}, {"1.2.", {}}, {"1.2.3", {}}, {"1.2.3.", {}}, // Invalid embedded IPv4 address. {"::1.2.3", {}}, // Too many components. {"1.2.3.4.5", {}}, {"1:2:3:4:5:6:7:8:9", {}}, {"1:2:3:4:5::6:7:8:9", {}}, // IPv4 literals take the place of two IPv6 components. {"1:2:3:4:5:6:7:1.2.3.4", {}}, // '::' should have fewer than 16 components or it is redundant. {"1:2:3:4:5:6:7::8", {}}, // Embedded IPv4 addresses must be at the end. {"::1.2.3.4:1", {}}, // Stray whitespace or other invalid characters. {"1.2.3.4 ", {}}, {"1.2.3 .4", {}}, {"1.2.3. 4", {}}, {" 1.2.3.4", {}}, {"1.2.3.4.", {}}, {"1.2.3.+4", {}}, {"1.2.3.-4", {}}, {"1.2.3.4.example.test", {}}, {"::1 ", {}}, {" ::1", {}}, {":: 1", {}}, {": :1", {}}, {"1.2.3.nope", {}}, {"::nope", {}}, // Components too large. {"1.2.3.256", {}}, // Overflows when adding {"1.2.3.260", {}}, // Overflows when multiplying by 10 {"1.2.3.999999999999999999999999999999999999999999", {}}, {"::fffff", {}}, // Although not an overflow, more than four hex digits is an error. {"::00000", {}}, // Too many colons. {":::", {}}, {"1:::", {}}, {":::2", {}}, {"1:::2", {}}, // Only one group of zeros may be elided. {"1::2::3", {}}, // We only support decimal. {"1.2.3.01", {}}, {"1.2.3.0x1", {}}, // Random garbage. {"example.test", {}}, {"", {}}, }; for (const auto &t : kIPTests) { SCOPED_TRACE(t.inp); bssl::UniquePtr oct(a2i_IPADDRESS(t.inp)); if (t.out.empty()) { EXPECT_FALSE(oct); } else { ASSERT_TRUE(oct); EXPECT_EQ(Bytes(t.out), Bytes(ASN1_STRING_get0_data(oct.get()), ASN1_STRING_length(oct.get()))); } } } // A brief validation against the |oids| expected to be done by the consumer. // This example simulates the consumer checking that the certificate has the // correct number of unknown extensions and there aren't any duplicates. static int verify_crit_oids_callback(X509_STORE_CTX *ctx, X509 *x509, STACK_OF(ASN1_OBJECT) *oids) { if (oids == nullptr) { return 0; // Fail if no OIDs provided } size_t known_oid_count = sk_ASN1_OBJECT_num(oids); size_t unknown_ext_count = 0; int last_pos = X509_get_ext_by_critical(x509, 1, -1); while (last_pos >= 0) { const X509_EXTENSION *ext = X509_get_ext(x509, last_pos); if (!X509_supported_extension(ext)) { unknown_ext_count++; } last_pos = X509_get_ext_by_critical(x509, 1, last_pos); } return known_oid_count == unknown_ext_count; } // Helper function to set up the basic verification context static void SetupVerificationContext( X509_STORE_CTX *ctx, const std::vector &custom_oids = {}, bool set_callback = false) { X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(ctx); X509_VERIFY_PARAM_set_time_posix(param, 1745884800); // Apr 28, 2025 for (const auto &oid : custom_oids) { ASSERT_TRUE(X509_STORE_CTX_add_custom_crit_oid(ctx, oid)); } if (set_callback) { X509_STORE_CTX_set_verify_crit_oids(ctx, verify_crit_oids_callback); } } TEST(X509Test, X509CustomExtensions) { bssl::UniquePtr cert(CertFromPEM(kX509CustomExtensionsCert)); ASSERT_TRUE(cert); bssl::UniquePtr ca(CertFromPEM(kX509CustomExtensionsCA)); ASSERT_TRUE(ca); // Check that the cert has been marked as |EXFLAG_CRITICAL|. EXPECT_TRUE(X509_get_extension_flags(cert.get()) & EXFLAG_CRITICAL); bssl::UniquePtr custom_oid(OBJ_txt2obj("1.3.187.25204.5", 1)); ASSERT_TRUE(custom_oid); // A typical call to |X509_verify_cert| without any set up would fail due to // the unknown critical extensions. auto typical_setup = [&](X509_STORE_CTX *ctx) { SetupVerificationContext(ctx, {}, false); }; EXPECT_EQ(X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION, Verify(cert.get(), {ca.get()}, {}, {}, /*flags=*/0, typical_setup)); // Unknown critical certificate extensions aren't enabled without the // callback. auto set_custom_ext_with_no_callback = [&](X509_STORE_CTX *ctx) { SetupVerificationContext(ctx, {custom_oid.get()}, false); }; EXPECT_EQ(X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION, Verify(cert.get(), {ca.get()}, {}, {}, /*flags=*/0, set_custom_ext_with_no_callback)); // Unknown critical certificate extensions aren't enabled, when only the // callback is enabled, but no custom oids are set. auto set_no_custom_ext_with_callback = [&](X509_STORE_CTX *ctx) { SetupVerificationContext(ctx, {}, true); }; EXPECT_EQ(X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION, Verify(cert.get(), {ca.get()}, {}, {}, /*flags=*/0, set_no_custom_ext_with_callback)); // This correctly sets up |ctx| with a custom critical extension and the // |verify_crit_oids| callback. auto set_custom_ext_with_callback = [&](X509_STORE_CTX *ctx) { SetupVerificationContext(ctx, {custom_oid.get()}, true); }; EXPECT_EQ(X509_V_OK, Verify(cert.get(), {ca.get()}, {}, {}, /*flags=*/0, set_custom_ext_with_callback)); // Check that |EXFLAG_CRITICAL| is preserved after validation. EXPECT_TRUE(X509_get_extension_flags(cert.get()) & EXFLAG_CRITICAL); // Check that verification is unsuccessful with the same cert without // the callback. EXPECT_EQ(X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION, Verify(cert.get(), {ca.get()}, {}, {}, /*flags=*/0, set_no_custom_ext_with_callback)); EXPECT_EQ(X509_V_OK, Verify(cert.get(), {ca.get()}, {}, {}, /*flags=*/0, set_custom_ext_with_callback)); } TEST(X509Test, X509MultipleCustomExtensions) { bssl::UniquePtr cert(CertFromPEM(kX509MultipleCustomExtensionsCert)); ASSERT_TRUE(cert); bssl::UniquePtr ca(CertFromPEM(kX509MultipleCustomExtensionsCA)); ASSERT_TRUE(ca); // Check that the cert has been marked as |EXFLAG_CRITICAL|. EXPECT_TRUE(X509_get_extension_flags(cert.get()) & EXFLAG_CRITICAL); bssl::UniquePtr custom_oid(OBJ_txt2obj("1.3.187.25204.5", 1)); ASSERT_TRUE(custom_oid); bssl::UniquePtr custom_oid2(OBJ_txt2obj("1.3.187.25204.6", 1)); ASSERT_TRUE(custom_oid2); // The result should be |X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION| since only // one custom critical extension was set. Both extensions are needed since the // cert contains two unknown extensions. auto set_single_custom_ext = [&](X509_STORE_CTX *ctx) { SetupVerificationContext(ctx, {custom_oid.get()}, true); }; EXPECT_EQ(X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION, Verify(cert.get(), {ca.get()}, {}, {}, /*flags=*/0, set_single_custom_ext)); auto set_other_custom_ext = [&](X509_STORE_CTX *ctx) { SetupVerificationContext(ctx, {custom_oid2.get()}, true); }; EXPECT_EQ(X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION, Verify(cert.get(), {ca.get()}, {}, {}, /*flags=*/0, set_other_custom_ext)); // Verification should not pass if all custom critical extensions are set, but // the |verify_crit_oids| callback is not configured. auto only_custom_exts_set = [&](X509_STORE_CTX *ctx) { SetupVerificationContext(ctx, {custom_oid.get(), custom_oid2.get()}, false); }; EXPECT_EQ(X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION, Verify(cert.get(), {ca.get()}, {}, {}, /*flags=*/0, only_custom_exts_set)); // Verification should only pass if all custom critical extensions are set, and // the |verify_crit_oids| callback is configured. auto set_custom_exts_with_callback = [&](X509_STORE_CTX *ctx) { SetupVerificationContext(ctx, {custom_oid.get(), custom_oid2.get()}, true); }; EXPECT_EQ(X509_V_OK, Verify(cert.get(), {ca.get()}, {}, {}, /*flags=*/0, set_custom_exts_with_callback)); // Check that |EXFLAG_CRITICAL| is preserved after validation. EXPECT_TRUE(X509_get_extension_flags(cert.get()) & EXFLAG_CRITICAL); } // Test that |X509_STORE_CTX_add_custom_crit_oid| does not leak memory. Under // ASAN/LSAN, this test will catch leaks on both the success path and the // cleanup path in |X509_STORE_CTX_cleanup|. TEST(X509Test, AddCustomCritOidNoLeak) { bssl::UniquePtr ctx(X509_STORE_CTX_new()); ASSERT_TRUE(ctx); bssl::UniquePtr store(X509_STORE_new()); ASSERT_TRUE(store); // |X509_STORE_CTX_init| must be called before adding custom OIDs. ASSERT_TRUE(X509_STORE_CTX_init(ctx.get(), store.get(), nullptr, nullptr)); // Add several OIDs. Each call duplicates the object internally. bssl::UniquePtr oid1(OBJ_txt2obj("1.2.3.4.5", 1)); ASSERT_TRUE(oid1); bssl::UniquePtr oid2(OBJ_txt2obj("1.2.3.4.6", 1)); ASSERT_TRUE(oid2); EXPECT_TRUE(X509_STORE_CTX_add_custom_crit_oid(ctx.get(), oid1.get())); EXPECT_TRUE(X509_STORE_CTX_add_custom_crit_oid(ctx.get(), oid2.get())); // |X509_STORE_CTX_cleanup| (called by the destructor) must free all // duplicated OIDs and the stack itself without leaking. } TEST(X509Test, StoreVerifyCallback) { bssl::UniquePtr store(X509_STORE_new()); ASSERT_TRUE(store); // Initially verify callback should be null EXPECT_EQ(nullptr, X509_STORE_get_verify_cb(store.get())); // Store the callback pointer for comparison X509_STORE_CTX_verify_cb verify_cb = [](int ok, X509_STORE_CTX *ctx) -> int { return 1; }; // Set a custom verify callback X509_STORE_set_verify_cb(store.get(), verify_cb); // Verify callback should now be set and match the stored pointer EXPECT_EQ(verify_cb, X509_STORE_get_verify_cb(store.get())); } TEST(X509Test, StoreLookupCRLs) { bssl::UniquePtr store(X509_STORE_new()); ASSERT_TRUE(store); // Initially lookup_crls callback should be null EXPECT_EQ(nullptr, X509_STORE_get_lookup_crls(store.get())); X509_STORE_CTX_lookup_crls_fn lookup_crls = [](X509_STORE_CTX *ctx, X509_NAME *nm) { return sk_X509_CRL_new_null(); }; // Set the custom lookup_crls callback X509_STORE_set_lookup_crls(store.get(), lookup_crls); // Lookup_crls callback should now be set and match the stored pointer EXPECT_EQ(lookup_crls, X509_STORE_get_lookup_crls(store.get())); }