|  | // Copyright 2016 The BoringSSL Authors | 
|  | // | 
|  | // Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | // you may not use this file except in compliance with the License. | 
|  | // You may obtain a copy of the License at | 
|  | // | 
|  | //     https://www.apache.org/licenses/LICENSE-2.0 | 
|  | // | 
|  | // Unless required by applicable law or agreed to in writing, software | 
|  | // distributed under the License is distributed on an "AS IS" BASIS, | 
|  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | // See the License for the specific language governing permissions and | 
|  | // limitations under the License. | 
|  |  | 
|  | #include <limits.h> | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <functional> | 
|  | #include <string> | 
|  | #include <vector> | 
|  |  | 
|  | #include <gtest/gtest.h> | 
|  |  | 
|  | #include <openssl/asn1.h> | 
|  | #include <openssl/bio.h> | 
|  | #include <openssl/bytestring.h> | 
|  | #include <openssl/conf.h> | 
|  | #include <openssl/crypto.h> | 
|  | #include <openssl/curve25519.h> | 
|  | #include <openssl/digest.h> | 
|  | #include <openssl/err.h> | 
|  | #include <openssl/nid.h> | 
|  | #include <openssl/pem.h> | 
|  | #include <openssl/pool.h> | 
|  | #include <openssl/x509.h> | 
|  |  | 
|  | #include "../internal.h" | 
|  | #include "../test/file_util.h" | 
|  | #include "../test/test_data.h" | 
|  | #include "../test/test_util.h" | 
|  | #include "internal.h" | 
|  |  | 
|  | #if defined(OPENSSL_THREADS) | 
|  | #include <thread> | 
|  | #endif | 
|  |  | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | 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----- | 
|  | )"; | 
|  |  | 
|  | // 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----- | 
|  | )"; | 
|  |  | 
|  | // kCRLTestRoot is a test root certificate. It has private key: | 
|  | // | 
|  | //     -----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----- | 
|  | )"; | 
|  |  | 
|  | // 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. | 
|  |  | 
|  | // clang-format off | 
|  |  | 
|  | // 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}) | 
|  | // } | 
|  |  | 
|  | // clang-format on | 
|  |  | 
|  | // 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----- | 
|  | )"; | 
|  |  | 
|  | // kConstrainedIntermediate is an intermediate signed by kSANTypesRoot, with | 
|  | // permitted DNS names of permitted1.test and foo.permitted2.test and an | 
|  | // excluded DNS name of excluded.permitted1.test. Its private key is: | 
|  | // | 
|  | // -----BEGIN PRIVATE KEY----- | 
|  | // MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgTXUM4tJWM7OzATty | 
|  | // JhNOfIv/d8heWFBeKOfMR+RfaROhRANCAASbbbWYiN6mn+BCpg4XNpibOH0D/DN4 | 
|  | // kZ5C/Ml2YVomC9T83OKk2CzB8fPAabPb4P4Vv+fIabpEfjWS5nzKLY1y | 
|  | // -----END PRIVATE KEY----- | 
|  | static const char kConstrainedIntermediate[] = R"( | 
|  | -----BEGIN CERTIFICATE----- | 
|  | MIICDjCCAXegAwIBAgIBAjANBgkqhkiG9w0BAQsFADArMRcwFQYDVQQKEw5Cb3Jp | 
|  | bmdTU0wgVGVzdDEQMA4GA1UEAxMHUm9vdCBDQTAgFw0wMDAxMDEwMDAwMDBaGA8y | 
|  | MDk5MDEwMTAwMDAwMFowKDEmMCQGA1UEAxMdTmFtZSBDb25zdHJhaW50cyBJbnRl | 
|  | cm1lZGlhdGUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASbbbWYiN6mn+BCpg4X | 
|  | NpibOH0D/DN4kZ5C/Ml2YVomC9T83OKk2CzB8fPAabPb4P4Vv+fIabpEfjWS5nzK | 
|  | LY1yo4GJMIGGMA8GA1UdEwEB/wQFMAMBAf8wGwYDVR0jBBQwEoAQQDfXAftAL7gc | 
|  | flQEJ4xZATBWBgNVHR4BAf8ETDBKoCowEYIPcGVybWl0dGVkMS50ZXN0MBWCE2Zv | 
|  | by5wZXJtaXR0ZWQyLnRlc3ShHDAaghhleGNsdWRlZC5wZXJtaXR0ZWQxLnRlc3Qw | 
|  | DQYJKoZIhvcNAQELBQADgYEAFq1Ka05hiKREwRpSceQPzIIH4B5a5IVBg5/EvmQI | 
|  | 9V0fXyAE1GmahPt70sIBxIgzNTEaY8P/IoOuCdlZWe0msmyEO3S6YSAzOWR5Van6 | 
|  | cXmFM1uMd95TlkxUMRdV+jKJTvG6R/BM2zltaV7Xt662k5HtzT5Svw0rZlFaggZz | 
|  | UyM= | 
|  | -----END CERTIFICATE----- | 
|  | )"; | 
|  |  | 
|  | // kCommonNamePermittedLeaf is a leaf certificate signed by | 
|  | // kConstrainedIntermediate. Its common name is permitted by the name | 
|  | // constraints. | 
|  | static const char kCommonNamePermittedLeaf[] = R"( | 
|  | -----BEGIN CERTIFICATE----- | 
|  | MIIBaDCCAQ2gAwIBAgIBAzAKBggqhkjOPQQDAjAoMSYwJAYDVQQDEx1OYW1lIENv | 
|  | bnN0cmFpbnRzIEludGVybWVkaWF0ZTAgFw0wMDAxMDEwMDAwMDBaGA8yMDk5MDEw | 
|  | MTAwMDAwMFowPjEeMBwGA1UEChMVQ29tbW9uIG5hbWUgcGVybWl0dGVkMRwwGgYD | 
|  | VQQDExNmb28ucGVybWl0dGVkMS50ZXN0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD | 
|  | QgAENX5Ycs8q8MRzPYUz6DqLHhJR3wcmniFRgkiEa7MxE/mRe00y0VGwH7xi7Aoc | 
|  | emXPrtD4JwN5bssbcxWGAKYYzaMQMA4wDAYDVR0TAQH/BAIwADAKBggqhkjOPQQD | 
|  | AgNJADBGAiEAtsnWuRQXtw2xbieC78Y8SVEtTjcZUx8uZyQe1GPLfGICIQDR4fNY | 
|  | yg3PC94ydPNQZVsFxAne32CbonWWsokalTFpUQ== | 
|  | -----END CERTIFICATE----- | 
|  | )"; | 
|  | static const char kCommonNamePermitted[] = "foo.permitted1.test"; | 
|  |  | 
|  | // kCommonNameNotPermittedLeaf is a leaf certificate signed by | 
|  | // kConstrainedIntermediate. Its common name is not permitted by the name | 
|  | // constraints. | 
|  | static const char kCommonNameNotPermittedLeaf[] = R"( | 
|  | -----BEGIN CERTIFICATE----- | 
|  | MIIBazCCARCgAwIBAgIBBDAKBggqhkjOPQQDAjAoMSYwJAYDVQQDEx1OYW1lIENv | 
|  | bnN0cmFpbnRzIEludGVybWVkaWF0ZTAgFw0wMDAxMDEwMDAwMDBaGA8yMDk5MDEw | 
|  | MTAwMDAwMFowQTEiMCAGA1UEChMZQ29tbW9uIG5hbWUgbm90IHBlcm1pdHRlZDEb | 
|  | MBkGA1UEAxMSbm90LXBlcm1pdHRlZC50ZXN0MFkwEwYHKoZIzj0CAQYIKoZIzj0D | 
|  | AQcDQgAEzfghKuWf0JoXb0Drp09C3yXMSQQ1byt+AUaymvsHOWsxQ9v1Q+vkF/IM | 
|  | HRqGTk2TyxrB2iClVEn/Uu+YtYox1KMQMA4wDAYDVR0TAQH/BAIwADAKBggqhkjO | 
|  | PQQDAgNJADBGAiEAxaUslxmoWL1tIvnDz7gDkto/HcmdU0jHVuUQLXcCG8wCIQCN | 
|  | 5xZjitlCQU8UB5qSu9wH4B+0JcVO3Ss4Az76HEJWMw== | 
|  | -----END CERTIFICATE----- | 
|  | )"; | 
|  | static const char kCommonNameNotPermitted[] = "not-permitted.test"; | 
|  |  | 
|  | // kCommonNameNotPermittedWithSANsLeaf is a leaf certificate signed by | 
|  | // kConstrainedIntermediate. Its common name is not permitted by the name | 
|  | // constraints but it has a SAN list. | 
|  | static const char kCommonNameNotPermittedWithSANsLeaf[] = R"( | 
|  | -----BEGIN CERTIFICATE----- | 
|  | MIIBqTCCAU+gAwIBAgIBBjAKBggqhkjOPQQDAjAoMSYwJAYDVQQDEx1OYW1lIENv | 
|  | bnN0cmFpbnRzIEludGVybWVkaWF0ZTAgFw0wMDAxMDEwMDAwMDBaGA8yMDk5MDEw | 
|  | MTAwMDAwMFowSzEsMCoGA1UEChMjQ29tbW9uIG5hbWUgbm90IHBlcm1pdHRlZCB3 | 
|  | aXRoIFNBTlMxGzAZBgNVBAMTEm5vdC1wZXJtaXR0ZWQudGVzdDBZMBMGByqGSM49 | 
|  | AgEGCCqGSM49AwEHA0IABKsn9wOApXFHrqhLdQgbFSeaSoAIbxgO0zVSRZUb5naR | 
|  | 93zoL3MFOvZEF8xiEqh7le+l3XuUig0fwqpcsZzRNJajRTBDMAwGA1UdEwEB/wQC | 
|  | MAAwMwYDVR0RBCwwKoITZm9vLnBlcm1pdHRlZDEudGVzdIITZm9vLnBlcm1pdHRl | 
|  | ZDIudGVzdDAKBggqhkjOPQQDAgNIADBFAiACk+1f184KkKAXuntmrz+Ygcq8MiZl | 
|  | 4delx44FtcNaegIhAIA5nYfzxNcTXxDo3U+x1vSLH6Y7faLvHiFySp7O//q+ | 
|  | -----END CERTIFICATE----- | 
|  | )"; | 
|  | static const char kCommonNameNotPermittedWithSANs[] = "not-permitted.test"; | 
|  |  | 
|  | // kCommonNameNotDNSLeaf is a leaf certificate signed by | 
|  | // kConstrainedIntermediate. Its common name is not a DNS name. | 
|  | static const char kCommonNameNotDNSLeaf[] = R"( | 
|  | -----BEGIN CERTIFICATE----- | 
|  | MIIBYTCCAQagAwIBAgIBCDAKBggqhkjOPQQDAjAoMSYwJAYDVQQDEx1OYW1lIENv | 
|  | bnN0cmFpbnRzIEludGVybWVkaWF0ZTAgFw0wMDAxMDEwMDAwMDBaGA8yMDk5MDEw | 
|  | MTAwMDAwMFowNzEcMBoGA1UEChMTQ29tbW9uIG5hbWUgbm90IEROUzEXMBUGA1UE | 
|  | AxMOTm90IGEgRE5TIG5hbWUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASnueyc | 
|  | Zxtnw5ke2J2T0/LwAK37auQP/RSFd9mem+BJVbgviawtAlignJmafp7Zw4/GdYEJ | 
|  | Vm8qlriOJtluvXGcoxAwDjAMBgNVHRMBAf8EAjAAMAoGCCqGSM49BAMCA0kAMEYC | 
|  | IQChUAmVNI39VHe0zemRE09VDcSEgOxr1nTvjLcg/Q8pVQIhAJYZnJI0YZAi05QH | 
|  | RHNlAkTK2TnUaVn3fGSylaLiFS1r | 
|  | -----END CERTIFICATE----- | 
|  | )"; | 
|  | static const char kCommonNameNotDNS[] = "Not a DNS name"; | 
|  |  | 
|  | // 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----- | 
|  | )"; | 
|  |  | 
|  | // CertFromPEM parses the given, NUL-terminated PEM block and returns an | 
|  | // |X509*|. | 
|  | static bssl::UniquePtr<X509> CertFromPEM(const char *pem) { | 
|  | bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(pem, strlen(pem))); | 
|  | return bssl::UniquePtr<X509>( | 
|  | PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr)); | 
|  | } | 
|  |  | 
|  | // CRLFromPEM parses the given, NUL-terminated PEM block and returns an | 
|  | // |X509_CRL*|. | 
|  | static bssl::UniquePtr<X509_CRL> CRLFromPEM(const char *pem) { | 
|  | bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(pem, strlen(pem))); | 
|  | return bssl::UniquePtr<X509_CRL>( | 
|  | 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<X509_REQ> CSRFromPEM(const char *pem) { | 
|  | bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(pem, strlen(pem))); | 
|  | return bssl::UniquePtr<X509_REQ>( | 
|  | 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<EVP_PKEY> PrivateKeyFromPEM(const char *pem) { | 
|  | bssl::UniquePtr<BIO> bio( | 
|  | BIO_new_mem_buf(const_cast<char *>(pem), strlen(pem))); | 
|  | return bssl::UniquePtr<EVP_PKEY>( | 
|  | PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr)); | 
|  | } | 
|  |  | 
|  | // CertsToStack converts a vector of |X509*| to an OpenSSL STACK_OF(X509), | 
|  | // bumping the reference counts for each certificate in question. | 
|  | static bssl::UniquePtr<STACK_OF(X509)> CertsToStack( | 
|  | const std::vector<X509 *> &certs) { | 
|  | bssl::UniquePtr<STACK_OF(X509)> stack(sk_X509_new_null()); | 
|  | if (!stack) { | 
|  | return nullptr; | 
|  | } | 
|  | for (auto cert : certs) { | 
|  | if (!bssl::PushToStack(stack.get(), bssl::UpRef(cert))) { | 
|  | return nullptr; | 
|  | } | 
|  | } | 
|  |  | 
|  | return stack; | 
|  | } | 
|  |  | 
|  | // CRLsToStack converts a vector of |X509_CRL*| to an OpenSSL | 
|  | // STACK_OF(X509_CRL), bumping the reference counts for each CRL in question. | 
|  | static bssl::UniquePtr<STACK_OF(X509_CRL)> CRLsToStack( | 
|  | const std::vector<X509_CRL *> &crls) { | 
|  | bssl::UniquePtr<STACK_OF(X509_CRL)> stack(sk_X509_CRL_new_null()); | 
|  | if (!stack) { | 
|  | return nullptr; | 
|  | } | 
|  | for (auto crl : crls) { | 
|  | if (!bssl::PushToStack(stack.get(), bssl::UpRef(crl))) { | 
|  | return nullptr; | 
|  | } | 
|  | } | 
|  |  | 
|  | return stack; | 
|  | } | 
|  |  | 
|  | static const int64_t kReferenceTime = 1474934400 /* Sep 27th, 2016 */; | 
|  |  | 
|  | static int Verify( | 
|  | X509 *leaf, const std::vector<X509 *> &roots, | 
|  | const std::vector<X509 *> &intermediates, | 
|  | const std::vector<X509_CRL *> &crls, unsigned long flags = 0, | 
|  | std::function<void(X509_STORE_CTX *)> configure_callback = nullptr) { | 
|  | bssl::UniquePtr<STACK_OF(X509)> roots_stack(CertsToStack(roots)); | 
|  | bssl::UniquePtr<STACK_OF(X509)> intermediates_stack( | 
|  | CertsToStack(intermediates)); | 
|  | bssl::UniquePtr<STACK_OF(X509_CRL)> crls_stack(CRLsToStack(crls)); | 
|  |  | 
|  | if (!roots_stack ||          // | 
|  | !intermediates_stack ||  // | 
|  | !crls_stack) { | 
|  | return X509_V_ERR_UNSPECIFIED; | 
|  | } | 
|  |  | 
|  | bssl::UniquePtr<X509_STORE_CTX> ctx(X509_STORE_CTX_new()); | 
|  | bssl::UniquePtr<X509_STORE> store(X509_STORE_new()); | 
|  | if (!ctx ||  // | 
|  | !store) { | 
|  | return X509_V_ERR_UNSPECIFIED; | 
|  | } | 
|  |  | 
|  | if (!X509_STORE_CTX_init(ctx.get(), store.get(), leaf, | 
|  | intermediates_stack.get())) { | 
|  | return X509_V_ERR_UNSPECIFIED; | 
|  | } | 
|  |  | 
|  | X509_STORE_CTX_set0_trusted_stack(ctx.get(), roots_stack.get()); | 
|  | X509_STORE_CTX_set0_crls(ctx.get(), crls_stack.get()); | 
|  |  | 
|  | X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(ctx.get()); | 
|  | X509_VERIFY_PARAM_set_time_posix(param, kReferenceTime); | 
|  | if (configure_callback) { | 
|  | configure_callback(ctx.get()); | 
|  | } | 
|  | if (flags) { | 
|  | X509_VERIFY_PARAM_set_flags(param, flags); | 
|  | } | 
|  |  | 
|  | ERR_clear_error(); | 
|  | if (X509_verify_cert(ctx.get()) != 1) { | 
|  | return X509_STORE_CTX_get_error(ctx.get()); | 
|  | } | 
|  |  | 
|  | return X509_V_OK; | 
|  | } | 
|  |  | 
|  | TEST(X509Test, TestVerify) { | 
|  | //  cross_signing_root | 
|  | //         | | 
|  | //   root_cross_signed    root | 
|  | //              \         / | 
|  | //             intermediate | 
|  | //                |     | | 
|  | //              leaf  leaf_no_key_usage | 
|  | //                      | | 
|  | //                    forgery | 
|  | bssl::UniquePtr<X509> cross_signing_root(CertFromPEM(kCrossSigningRootPEM)); | 
|  | bssl::UniquePtr<X509> root(CertFromPEM(kRootCAPEM)); | 
|  | bssl::UniquePtr<X509> root_cross_signed(CertFromPEM(kRootCrossSignedPEM)); | 
|  | bssl::UniquePtr<X509> intermediate(CertFromPEM(kIntermediatePEM)); | 
|  | bssl::UniquePtr<X509> intermediate_self_signed( | 
|  | CertFromPEM(kIntermediateSelfSignedPEM)); | 
|  | bssl::UniquePtr<X509> leaf(CertFromPEM(kLeafPEM)); | 
|  | bssl::UniquePtr<X509> leaf_no_key_usage(CertFromPEM(kLeafNoKeyUsagePEM)); | 
|  | bssl::UniquePtr<X509> 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)); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | #if defined(OPENSSL_THREADS) | 
|  | // Verifying the same |X509| objects on two threads should be safe. | 
|  | TEST(X509Test, VerifyThreads) { | 
|  | bssl::UniquePtr<X509> root(CertFromPEM(kRootCAPEM)); | 
|  | bssl::UniquePtr<X509> intermediate(CertFromPEM(kIntermediatePEM)); | 
|  | bssl::UniquePtr<X509> leaf(CertFromPEM(kLeafPEM)); | 
|  | ASSERT_TRUE(root); | 
|  | ASSERT_TRUE(intermediate); | 
|  | ASSERT_TRUE(leaf); | 
|  |  | 
|  | const size_t kNumThreads = 10; | 
|  | std::vector<std::thread> 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<X509> root(CertFromPEM(kCRLTestRoot)); | 
|  | bssl::UniquePtr<X509> leaf(CertFromPEM(kCRLTestLeaf)); | 
|  | bssl::UniquePtr<X509_CRL> basic_crl(CRLFromPEM(kBasicCRL)); | 
|  | bssl::UniquePtr<X509_CRL> 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<std::thread> 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<X509> root(CertFromPEM(kRootCAPEM)); | 
|  | bssl::UniquePtr<X509> intermediate(CertFromPEM(kIntermediatePEM)); | 
|  | bssl::UniquePtr<X509> leaf(CertFromPEM(kLeafPEM)); | 
|  | ASSERT_TRUE(root); | 
|  | ASSERT_TRUE(intermediate); | 
|  | ASSERT_TRUE(leaf); | 
|  |  | 
|  | bssl::UniquePtr<STACK_OF(X509)> intermediates = | 
|  | CertsToStack({intermediate.get()}); | 
|  | ASSERT_TRUE(intermediates); | 
|  |  | 
|  | // Some unrelated certificates. | 
|  | bssl::UniquePtr<X509> other1(CertFromPEM(kCRLTestRoot)); | 
|  | bssl::UniquePtr<X509> other2(CertFromPEM(kCRLTestLeaf)); | 
|  | ASSERT_TRUE(other1); | 
|  | ASSERT_TRUE(other2); | 
|  |  | 
|  | bssl::UniquePtr<X509_STORE> store(X509_STORE_new()); | 
|  | ASSERT_TRUE(store); | 
|  | ASSERT_TRUE(X509_STORE_add_cert(store.get(), root.get())); | 
|  |  | 
|  | const size_t kNumThreads = 10; | 
|  | std::vector<std::thread> threads; | 
|  | for (size_t i = 0; i < kNumThreads; i++) { | 
|  | threads.emplace_back([&] { | 
|  | bssl::UniquePtr<X509_STORE_CTX> 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())); }); | 
|  | threads.emplace_back([&] { | 
|  | bssl::UniquePtr<STACK_OF(X509_OBJECT)> objs( | 
|  | X509_STORE_get1_objects(store.get())); | 
|  | ASSERT_TRUE(objs); | 
|  | }); | 
|  | } | 
|  | 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<X509> leaf(CertFromPEM(kSANTypesLeaf)); | 
|  | bssl::UniquePtr<X509> root(CertFromPEM(kSANTypesRoot)); | 
|  | ASSERT_TRUE(leaf); | 
|  | ASSERT_TRUE(root); | 
|  |  | 
|  | std::vector<X509_CRL *> 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<X509Test> 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)); | 
|  | })); | 
|  |  | 
|  | // Passing zero as the length, unlike OpenSSL, should trigger an error and | 
|  | // should 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, test.correct_value, 0)); | 
|  | })); | 
|  |  | 
|  | // Passing an empty value should be an error when setting and should 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, 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)); | 
|  | })); | 
|  | } | 
|  |  | 
|  | // 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<X509> 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)); | 
|  |  | 
|  | // OpenSSL supports passing zero as the length for host and email. We do not | 
|  | // and it should always fail. | 
|  | EXPECT_NE(1, X509_check_host(leaf.get(), kHostname, 0, 0, nullptr)); | 
|  | EXPECT_NE(1, X509_check_host(leaf.get(), kWrongHostname, 0, 0, nullptr)); | 
|  |  | 
|  | EXPECT_NE(1, X509_check_email(leaf.get(), kEmail, 0, 0)); | 
|  | EXPECT_NE(1, X509_check_email(leaf.get(), kWrongEmail, 0, 0)); | 
|  |  | 
|  | 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, TestCRL) { | 
|  | bssl::UniquePtr<X509> root(CertFromPEM(kCRLTestRoot)); | 
|  | bssl::UniquePtr<X509> leaf(CertFromPEM(kCRLTestLeaf)); | 
|  | bssl::UniquePtr<X509_CRL> basic_crl(CRLFromPEM(kBasicCRL)); | 
|  | bssl::UniquePtr<X509_CRL> revoked_crl(CRLFromPEM(kRevokedCRL)); | 
|  | bssl::UniquePtr<X509_CRL> bad_issuer_crl(CRLFromPEM(kBadIssuerCRL)); | 
|  | bssl::UniquePtr<X509_CRL> known_critical_crl(CRLFromPEM(kKnownCriticalCRL)); | 
|  | bssl::UniquePtr<X509_CRL> unknown_critical_crl( | 
|  | CRLFromPEM(kUnknownCriticalCRL)); | 
|  | bssl::UniquePtr<X509_CRL> unknown_critical_crl2( | 
|  | CRLFromPEM(kUnknownCriticalCRL2)); | 
|  | bssl::UniquePtr<X509_CRL> algorithm_mismatch_crl( | 
|  | CRLFromPEM(kAlgorithmMismatchCRL)); | 
|  | bssl::UniquePtr<X509_CRL> 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<X509_CRL *> 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)); | 
|  | } | 
|  |  | 
|  | TEST(X509Test, ManyNamesAndConstraints) { | 
|  | bssl::UniquePtr<X509> many_constraints(CertFromPEM( | 
|  | GetTestData("crypto/x509/test/many_constraints.pem").c_str())); | 
|  | ASSERT_TRUE(many_constraints); | 
|  | bssl::UniquePtr<X509> many_names1( | 
|  | CertFromPEM(GetTestData("crypto/x509/test/many_names1.pem").c_str())); | 
|  | ASSERT_TRUE(many_names1); | 
|  | bssl::UniquePtr<X509> many_names2( | 
|  | CertFromPEM(GetTestData("crypto/x509/test/many_names2.pem").c_str())); | 
|  | ASSERT_TRUE(many_names2); | 
|  | bssl::UniquePtr<X509> many_names3( | 
|  | CertFromPEM(GetTestData("crypto/x509/test/many_names3.pem").c_str())); | 
|  | ASSERT_TRUE(many_names3); | 
|  | bssl::UniquePtr<X509> some_names1( | 
|  | CertFromPEM(GetTestData("crypto/x509/test/some_names1.pem").c_str())); | 
|  | ASSERT_TRUE(some_names1); | 
|  | bssl::UniquePtr<X509> some_names2( | 
|  | CertFromPEM(GetTestData("crypto/x509/test/some_names2.pem").c_str())); | 
|  | ASSERT_TRUE(some_names2); | 
|  | bssl::UniquePtr<X509> 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<GENERAL_NAME> 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<ASN1_IA5STRING> str(ASN1_IA5STRING_new()); | 
|  | bssl::UniquePtr<GENERAL_NAME> 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 bssl::UniquePtr<X509_NAME> MakeTestName(const char *common_name) { | 
|  | bssl::UniquePtr<X509_NAME> name(X509_NAME_new()); | 
|  | if (name == nullptr || | 
|  | !X509_NAME_add_entry_by_txt( | 
|  | name.get(), "CN", MBSTRING_UTF8, | 
|  | reinterpret_cast<const uint8_t *>(common_name), -1, -1, 0)) { | 
|  | return nullptr; | 
|  | } | 
|  | return name; | 
|  | } | 
|  |  | 
|  | static bssl::UniquePtr<X509> MakeTestCert(const char *issuer, | 
|  | const char *subject, EVP_PKEY *key, | 
|  | bool is_ca) { | 
|  | bssl::UniquePtr<X509_NAME> issuer_name = MakeTestName(issuer); | 
|  | bssl::UniquePtr<X509_NAME> subject_name = MakeTestName(subject); | 
|  | bssl::UniquePtr<X509> cert(X509_new()); | 
|  | if (issuer_name == nullptr || subject_name == nullptr || cert == nullptr || | 
|  | !X509_set_version(cert.get(), X509_VERSION_3) || | 
|  | !X509_set_issuer_name(cert.get(), issuer_name.get()) || | 
|  | !X509_set_subject_name(cert.get(), subject_name.get()) || | 
|  | !X509_set_pubkey(cert.get(), key) || | 
|  | !ASN1_TIME_adj(X509_getm_notBefore(cert.get()), kReferenceTime, -1, 0) || | 
|  | !ASN1_TIME_adj(X509_getm_notAfter(cert.get()), kReferenceTime, 1, 0)) { | 
|  | return nullptr; | 
|  | } | 
|  | bssl::UniquePtr<BASIC_CONSTRAINTS> bc(BASIC_CONSTRAINTS_new()); | 
|  | if (!bc) { | 
|  | return nullptr; | 
|  | } | 
|  | bc->ca = is_ca ? ASN1_BOOLEAN_TRUE : ASN1_BOOLEAN_FALSE; | 
|  | if (!X509_add1_ext_i2d(cert.get(), NID_basic_constraints, bc.get(), | 
|  | /*crit=*/1, /*flags=*/0)) { | 
|  | return nullptr; | 
|  | } | 
|  | return cert; | 
|  | } | 
|  |  | 
|  | static bool AddExtendedKeyUsage(X509 *x509, const std::vector<int> &eku_nids) { | 
|  | bssl::UniquePtr<STACK_OF(ASN1_OBJECT)> 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<KeyUsage> usages) { | 
|  | bssl::UniquePtr<ASN1_BIT_STRING> str(ASN1_BIT_STRING_new()); | 
|  | if (str == nullptr) { | 
|  | return false; | 
|  | } | 
|  | for (KeyUsage usage : usages) { | 
|  | if (!ASN1_BIT_STRING_set_bit(str.get(), static_cast<int>(usage), 1)) { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | return X509_add1_ext_i2d(x509, NID_key_usage, str.get(), /*crit=*/1, | 
|  | /*flags=*/0); | 
|  | } | 
|  |  | 
|  | static bool AddSubjectKeyIdentifier(X509 *x509, | 
|  | bssl::Span<const uint8_t> key_id) { | 
|  | bssl::UniquePtr<ASN1_OCTET_STRING> oct(ASN1_OCTET_STRING_new()); | 
|  | return oct != nullptr && | 
|  | ASN1_STRING_set(oct.get(), key_id.data(), key_id.size()) && | 
|  | X509_add1_ext_i2d(x509, NID_subject_key_identifier, oct.get(), | 
|  | /*crit=*/0, /*flags=*/0); | 
|  | } | 
|  |  | 
|  | static bool AddAuthorityKeyIdentifier(X509 *x509, | 
|  | bssl::Span<const uint8_t> key_id) { | 
|  | bssl::UniquePtr<AUTHORITY_KEYID> akid(AUTHORITY_KEYID_new()); | 
|  | if (akid == nullptr) { | 
|  | return false; | 
|  | } | 
|  | akid->keyid = ASN1_OCTET_STRING_new(); | 
|  | if (akid->keyid == nullptr || | 
|  | !ASN1_STRING_set(akid->keyid, key_id.data(), key_id.size()) || | 
|  | !X509_add1_ext_i2d(x509, NID_authority_key_identifier, akid.get(), | 
|  | /*crit=*/0, /*flags=*/0)) { | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | static bssl::UniquePtr<X509_CRL> MakeTestCRL(const char *issuer, | 
|  | int this_update_offset_day, | 
|  | int next_update_offset_day) { | 
|  | bssl::UniquePtr<X509_NAME> issuer_name = MakeTestName(issuer); | 
|  | bssl::UniquePtr<X509_CRL> crl(X509_CRL_new()); | 
|  | bssl::UniquePtr<ASN1_TIME> this_update(ASN1_TIME_adj( | 
|  | nullptr, kReferenceTime, this_update_offset_day, /*offset_sec=*/0)); | 
|  | bssl::UniquePtr<ASN1_TIME> next_update(ASN1_TIME_adj( | 
|  | nullptr, kReferenceTime, next_update_offset_day, /*offset_sec=*/0)); | 
|  | if (crl == nullptr || issuer_name == nullptr || this_update == nullptr || | 
|  | next_update == nullptr || | 
|  | !X509_CRL_set_version(crl.get(), X509_CRL_VERSION_2) || | 
|  | !X509_CRL_set_issuer_name(crl.get(), issuer_name.get()) || | 
|  | // OpenSSL's API is named incorrectly. The field is called thisUpdate. | 
|  | !X509_CRL_set1_lastUpdate(crl.get(), this_update.get()) || | 
|  | !X509_CRL_set1_nextUpdate(crl.get(), next_update.get())) { | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | return crl; | 
|  | } | 
|  |  | 
|  | static bool AddRevokedSerialU64(X509_CRL *crl, uint64_t serial, | 
|  | int offset_day) { | 
|  | bssl::UniquePtr<X509_REVOKED> rev(X509_REVOKED_new()); | 
|  | bssl::UniquePtr<ASN1_INTEGER> serial_asn1(ASN1_INTEGER_new()); | 
|  | bssl::UniquePtr<ASN1_TIME> rev_date( | 
|  | ASN1_TIME_adj(nullptr, kReferenceTime, offset_day, /*offset_sec=*/0)); | 
|  | if (rev == nullptr || serial_asn1 == nullptr || rev_date == nullptr || | 
|  | !ASN1_INTEGER_set_uint64(serial_asn1.get(), serial) || | 
|  | !X509_REVOKED_set_serialNumber(rev.get(), serial_asn1.get()) || | 
|  | !X509_REVOKED_set_revocationDate(rev.get(), rev_date.get()) || | 
|  | !X509_CRL_add0_revoked(crl, rev.get())) { | 
|  | return false; | 
|  | } | 
|  | rev.release();  // X509_CRL_add0_revoked takes ownership on success. | 
|  | return true; | 
|  | } | 
|  |  | 
|  | static bool AddAuthorityKeyIdentifier(X509_CRL *crl, | 
|  | bssl::Span<const uint8_t> key_id) { | 
|  | bssl::UniquePtr<AUTHORITY_KEYID> akid(AUTHORITY_KEYID_new()); | 
|  | if (akid == nullptr) { | 
|  | return false; | 
|  | } | 
|  | akid->keyid = ASN1_OCTET_STRING_new(); | 
|  | if (akid->keyid == nullptr || | 
|  | !ASN1_STRING_set(akid->keyid, key_id.data(), key_id.size()) || | 
|  | !X509_CRL_add1_ext_i2d(crl, NID_authority_key_identifier, akid.get(), | 
|  | /*crit=*/0, /*flags=*/0)) { | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | TEST(X509Test, NameConstraints) { | 
|  | bssl::UniquePtr<EVP_PKEY> key = PrivateKeyFromPEM(kP256Key); | 
|  | ASSERT_TRUE(key); | 
|  |  | 
|  | const struct { | 
|  | int type; | 
|  | std::string name; | 
|  | std::string constraint; | 
|  | int result; | 
|  | } kTests[] = { | 
|  | // Empty string matches everything. | 
|  | {GEN_DNS, "foo.example.com", "", X509_V_OK}, | 
|  | // Name constraints match the entire subtree. | 
|  | {GEN_DNS, "foo.example.com", "example.com", X509_V_OK}, | 
|  | {GEN_DNS, "foo.example.com", "EXAMPLE.COM", X509_V_OK}, | 
|  | {GEN_DNS, "foo.example.com", "xample.com", | 
|  | X509_V_ERR_PERMITTED_VIOLATION}, | 
|  | {GEN_DNS, "foo.example.com", "unrelated.much.longer.name.example", | 
|  | X509_V_ERR_PERMITTED_VIOLATION}, | 
|  | // A leading dot means at least one component must be added. | 
|  | {GEN_DNS, "foo.example.com", ".example.com", X509_V_OK}, | 
|  | {GEN_DNS, "foo.example.com", "foo.example.com", X509_V_OK}, | 
|  | {GEN_DNS, "foo.example.com", ".foo.example.com", | 
|  | X509_V_ERR_PERMITTED_VIOLATION}, | 
|  | {GEN_DNS, "foo.example.com", ".xample.com", | 
|  | X509_V_ERR_PERMITTED_VIOLATION}, | 
|  | {GEN_DNS, "foo.example.com", ".unrelated.much.longer.name.example", | 
|  | X509_V_ERR_PERMITTED_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}, | 
|  |  | 
|  | // Names must be emails. | 
|  | {GEN_EMAIL, "not-an-email.example", "not-an-email.example", | 
|  | 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}, | 
|  | {GEN_EMAIL, "foo@bar.example.com", ".EXAMPLE.COM", X509_V_OK}, | 
|  | {GEN_EMAIL, "foo@bar.example.com", ".bar.example.com", | 
|  | X509_V_ERR_PERMITTED_VIOLATION}, | 
|  | // Without a leading dot, the host must match exactly. | 
|  | {GEN_EMAIL, "foo@example.com", "example.com", X509_V_OK}, | 
|  | {GEN_EMAIL, "foo@example.com", "EXAMPLE.COM", X509_V_OK}, | 
|  | {GEN_EMAIL, "foo@bar.example.com", "example.com", | 
|  | X509_V_ERR_PERMITTED_VIOLATION}, | 
|  | // 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}, | 
|  | {GEN_EMAIL, "foo@example.com", "foo@EXAMPLE.COM", X509_V_OK}, | 
|  | {GEN_EMAIL, "foo@example.com", "FOO@example.com", | 
|  | X509_V_ERR_PERMITTED_VIOLATION}, | 
|  | {GEN_EMAIL, "foo@example.com", "bar@example.com", | 
|  | X509_V_ERR_PERMITTED_VIOLATION}, | 
|  | // OpenSSL ignores a stray leading @. | 
|  | {GEN_EMAIL, "foo@example.com", "@example.com", X509_V_OK}, | 
|  | {GEN_EMAIL, "foo@example.com", "@EXAMPLE.COM", X509_V_OK}, | 
|  | {GEN_EMAIL, "foo@bar.example.com", "@example.com", | 
|  | X509_V_ERR_PERMITTED_VIOLATION}, | 
|  |  | 
|  | // Basic syntax check. | 
|  | {GEN_URI, "not-a-url", "not-a-url", X509_V_ERR_UNSUPPORTED_NAME_SYNTAX}, | 
|  | {GEN_URI, "foo:not-a-url", "not-a-url", | 
|  | X509_V_ERR_UNSUPPORTED_NAME_SYNTAX}, | 
|  | {GEN_URI, "foo:/not-a-url", "not-a-url", | 
|  | X509_V_ERR_UNSUPPORTED_NAME_SYNTAX}, | 
|  | {GEN_URI, "foo:///not-a-url", "not-a-url", | 
|  | X509_V_ERR_UNSUPPORTED_NAME_SYNTAX}, | 
|  | {GEN_URI, "foo://:not-a-url", "not-a-url", | 
|  | X509_V_ERR_UNSUPPORTED_NAME_SYNTAX}, | 
|  | {GEN_URI, "foo://", "not-a-url", X509_V_ERR_UNSUPPORTED_NAME_SYNTAX}, | 
|  | // Hosts are an exact match. | 
|  | {GEN_URI, "foo://example.com", "example.com", X509_V_OK}, | 
|  | {GEN_URI, "foo://example.com:443", "example.com", X509_V_OK}, | 
|  | {GEN_URI, "foo://example.com/whatever", "example.com", X509_V_OK}, | 
|  | {GEN_URI, "foo://bar.example.com", "example.com", | 
|  | X509_V_ERR_PERMITTED_VIOLATION}, | 
|  | {GEN_URI, "foo://bar.example.com:443", "example.com", | 
|  | X509_V_ERR_PERMITTED_VIOLATION}, | 
|  | {GEN_URI, "foo://bar.example.com/whatever", "example.com", | 
|  | X509_V_ERR_PERMITTED_VIOLATION}, | 
|  | {GEN_URI, "foo://bar.example.com", "xample.com", | 
|  | X509_V_ERR_PERMITTED_VIOLATION}, | 
|  | {GEN_URI, "foo://bar.example.com:443", "xample.com", | 
|  | X509_V_ERR_PERMITTED_VIOLATION}, | 
|  | {GEN_URI, "foo://bar.example.com/whatever", "xample.com", | 
|  | X509_V_ERR_PERMITTED_VIOLATION}, | 
|  | {GEN_URI, "foo://example.com", "some-other-name.example", | 
|  | X509_V_ERR_PERMITTED_VIOLATION}, | 
|  | {GEN_URI, "foo://example.com:443", "some-other-name.example", | 
|  | X509_V_ERR_PERMITTED_VIOLATION}, | 
|  | {GEN_URI, "foo://example.com/whatever", "some-other-name.example", | 
|  | X509_V_ERR_PERMITTED_VIOLATION}, | 
|  | // A leading dot allows components to be added. | 
|  | {GEN_URI, "foo://example.com", ".example.com", | 
|  | X509_V_ERR_PERMITTED_VIOLATION}, | 
|  | {GEN_URI, "foo://example.com:443", ".example.com", | 
|  | X509_V_ERR_PERMITTED_VIOLATION}, | 
|  | {GEN_URI, "foo://example.com/whatever", ".example.com", | 
|  | X509_V_ERR_PERMITTED_VIOLATION}, | 
|  | {GEN_URI, "foo://bar.example.com", ".example.com", X509_V_OK}, | 
|  | {GEN_URI, "foo://bar.example.com:443", ".example.com", X509_V_OK}, | 
|  | {GEN_URI, "foo://bar.example.com/whatever", ".example.com", X509_V_OK}, | 
|  | {GEN_URI, "foo://example.com", ".some-other-name.example", | 
|  | X509_V_ERR_PERMITTED_VIOLATION}, | 
|  | {GEN_URI, "foo://example.com:443", ".some-other-name.example", | 
|  | X509_V_ERR_PERMITTED_VIOLATION}, | 
|  | {GEN_URI, "foo://example.com/whatever", ".some-other-name.example", | 
|  | X509_V_ERR_PERMITTED_VIOLATION}, | 
|  | {GEN_URI, "foo://example.com", ".xample.com", | 
|  | X509_V_ERR_PERMITTED_VIOLATION}, | 
|  | {GEN_URI, "foo://example.com:443", ".xample.com", | 
|  | X509_V_ERR_PERMITTED_VIOLATION}, | 
|  | {GEN_URI, "foo://example.com/whatever", ".xample.com", | 
|  | X509_V_ERR_PERMITTED_VIOLATION}, | 
|  | }; | 
|  | for (const auto &t : kTests) { | 
|  | SCOPED_TRACE(t.type); | 
|  | SCOPED_TRACE(t.name); | 
|  | SCOPED_TRACE(t.constraint); | 
|  |  | 
|  | bssl::UniquePtr<GENERAL_NAME> name = MakeGeneralName(t.type, t.name); | 
|  | ASSERT_TRUE(name); | 
|  | bssl::UniquePtr<GENERAL_NAMES> names(GENERAL_NAMES_new()); | 
|  | ASSERT_TRUE(names); | 
|  | ASSERT_TRUE(bssl::PushToStack(names.get(), std::move(name))); | 
|  |  | 
|  | bssl::UniquePtr<NAME_CONSTRAINTS> nc(NAME_CONSTRAINTS_new()); | 
|  | ASSERT_TRUE(nc); | 
|  | nc->permittedSubtrees = sk_GENERAL_SUBTREE_new_null(); | 
|  | ASSERT_TRUE(nc->permittedSubtrees); | 
|  | bssl::UniquePtr<GENERAL_SUBTREE> subtree(GENERAL_SUBTREE_new()); | 
|  | ASSERT_TRUE(subtree); | 
|  | GENERAL_NAME_free(subtree->base); | 
|  | subtree->base = MakeGeneralName(t.type, t.constraint).release(); | 
|  | ASSERT_TRUE(subtree->base); | 
|  | ASSERT_TRUE(bssl::PushToStack(nc->permittedSubtrees, std::move(subtree))); | 
|  |  | 
|  | bssl::UniquePtr<X509> 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<X509> 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 ret = Verify(leaf.get(), {root.get()}, {}, {}, 0); | 
|  | EXPECT_EQ(t.result, ret) << X509_verify_cert_error_string(ret); | 
|  | } | 
|  | } | 
|  |  | 
|  | 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<GENERAL_NAME> gen = MakeGeneralName(GEN_DNS, "example.com"); | 
|  | ASSERT_TRUE(gen); | 
|  | bssl::UniquePtr<STACK_OF(CONF_VALUE)> 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) { | 
|  | static const char *kGoodCerts[] = { | 
|  | "crypto/x509/test/pss_sha256.pem", | 
|  | "crypto/x509/test/pss_sha384.pem", | 
|  | "crypto/x509/test/pss_sha512.pem", | 
|  | // We accept inputs with and without explicit NULLs. See RFC 4055, | 
|  | // section 2.1. | 
|  | "crypto/x509/test/pss_sha256_omit_nulls.pem", | 
|  | // Although invalid, we tolerate an explicit trailerField value. See the | 
|  | // certificates in cl/362617931. | 
|  | "crypto/x509/test/pss_sha256_explicit_trailer.pem", | 
|  | }; | 
|  | for (const char *path : kGoodCerts) { | 
|  | SCOPED_TRACE(path); | 
|  | bssl::UniquePtr<X509> cert = CertFromPEM(GetTestData(path).c_str()); | 
|  | ASSERT_TRUE(cert); | 
|  | bssl::UniquePtr<EVP_PKEY> pkey(X509_get_pubkey(cert.get())); | 
|  | ASSERT_TRUE(pkey); | 
|  | EXPECT_TRUE(X509_verify(cert.get(), pkey.get())); | 
|  | } | 
|  |  | 
|  | static const char *kBadCerts[] = { | 
|  | "crypto/x509/test/pss_sha1_explicit.pem", | 
|  | "crypto/x509/test/pss_sha1_mgf1_syntax_error.pem", | 
|  | "crypto/x509/test/pss_sha1.pem", | 
|  | "crypto/x509/test/pss_sha224.pem", | 
|  | "crypto/x509/test/pss_sha256_mgf1_sha384.pem", | 
|  | "crypto/x509/test/pss_sha256_mgf1_syntax_error.pem", | 
|  | "crypto/x509/test/pss_sha256_salt_overflow.pem", | 
|  | "crypto/x509/test/pss_sha256_salt31.pem", | 
|  | "crypto/x509/test/pss_sha256_unknown_mgf.pem", | 
|  | "crypto/x509/test/pss_sha256_wrong_trailer.pem", | 
|  | }; | 
|  | for (const char *path : kBadCerts) { | 
|  | SCOPED_TRACE(path); | 
|  | bssl::UniquePtr<X509> cert = CertFromPEM(GetTestData(path).c_str()); | 
|  | ASSERT_TRUE(cert); | 
|  | bssl::UniquePtr<EVP_PKEY> pkey(X509_get_pubkey(cert.get())); | 
|  | ASSERT_TRUE(pkey); | 
|  | EXPECT_FALSE(X509_verify(cert.get(), pkey.get())); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST(X509Test, TestPSSBadParameters) { | 
|  | bssl::UniquePtr<X509> cert(CertFromPEM(kBadPSSCertPEM)); | 
|  | ASSERT_TRUE(cert); | 
|  |  | 
|  | bssl::UniquePtr<EVP_PKEY> 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<X509> cert(CertFromPEM(kEd25519Cert)); | 
|  | ASSERT_TRUE(cert); | 
|  |  | 
|  | bssl::UniquePtr<EVP_PKEY> pkey(X509_get_pubkey(cert.get())); | 
|  | ASSERT_TRUE(pkey); | 
|  |  | 
|  | ASSERT_TRUE(X509_verify(cert.get(), pkey.get())); | 
|  | } | 
|  |  | 
|  | TEST(X509Test, TestEd25519BadParameters) { | 
|  | bssl::UniquePtr<X509> cert(CertFromPEM(kEd25519CertNull)); | 
|  | ASSERT_TRUE(cert); | 
|  |  | 
|  | bssl::UniquePtr<EVP_PKEY> 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<X509> cert(CertFromPEM(kX25519Cert)); | 
|  | ASSERT_TRUE(cert); | 
|  |  | 
|  | bssl::UniquePtr<EVP_PKEY> 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<X509> ReencodeCertificate(X509 *cert) { | 
|  | uint8_t *der = nullptr; | 
|  | int len = i2d_X509(cert, &der); | 
|  | bssl::UniquePtr<uint8_t> free_der(der); | 
|  | if (len <= 0) { | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | const uint8_t *inp = der; | 
|  | return bssl::UniquePtr<X509>(d2i_X509(nullptr, &inp, len)); | 
|  | } | 
|  |  | 
|  | static bssl::UniquePtr<X509_CRL> ReencodeCRL(X509_CRL *crl) { | 
|  | uint8_t *der = nullptr; | 
|  | int len = i2d_X509_CRL(crl, &der); | 
|  | bssl::UniquePtr<uint8_t> free_der(der); | 
|  | if (len <= 0) { | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | const uint8_t *inp = der; | 
|  | return bssl::UniquePtr<X509_CRL>(d2i_X509_CRL(nullptr, &inp, len)); | 
|  | } | 
|  |  | 
|  | static bssl::UniquePtr<X509_REQ> ReencodeCSR(X509_REQ *req) { | 
|  | uint8_t *der = nullptr; | 
|  | int len = i2d_X509_REQ(req, &der); | 
|  | bssl::UniquePtr<uint8_t> free_der(der); | 
|  | if (len <= 0) { | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | const uint8_t *inp = der; | 
|  | return bssl::UniquePtr<X509_REQ>(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<X509> 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<X509> copy = ReencodeCertificate(cert.get()); | 
|  | return copy && X509_verify(copy.get(), pkey); | 
|  | } | 
|  |  | 
|  | TEST(X509Test, RSASign) { | 
|  | bssl::UniquePtr<EVP_PKEY> 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())); | 
|  |  | 
|  | // RSA-PSS with salt length matching hash length should work when passing in | 
|  | // -1 or the value explicitly. | 
|  | 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_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())); | 
|  |  | 
|  | // RSA-PSS with SHA-1 is not supported. | 
|  | md_ctx.Reset(); | 
|  | ASSERT_TRUE(EVP_DigestSignInit(md_ctx.get(), &pkey_ctx, EVP_sha1(), 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)); | 
|  | bssl::UniquePtr<X509> cert = CertFromPEM(kLeafPEM); | 
|  | ASSERT_TRUE(cert); | 
|  | EXPECT_FALSE(X509_sign_ctx(cert.get(), md_ctx.get())); | 
|  |  | 
|  | // RSA-PSS with mismatched hashes is not supported. | 
|  | 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(EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, EVP_sha512())); | 
|  | cert = CertFromPEM(kLeafPEM); | 
|  | ASSERT_TRUE(cert); | 
|  | EXPECT_FALSE(X509_sign_ctx(cert.get(), md_ctx.get())); | 
|  |  | 
|  | // RSA-PSS with the wrong salt length is not supported. | 
|  | 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, 33)); | 
|  | cert = CertFromPEM(kLeafPEM); | 
|  | ASSERT_TRUE(cert); | 
|  | EXPECT_FALSE(X509_sign_ctx(cert.get(), md_ctx.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<EVP_PKEY> pkey(PrivateKeyFromPEM(kRSAKey)); | 
|  | ASSERT_TRUE(pkey); | 
|  | bssl::UniquePtr<X509_ALGOR> 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<X509> 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<const uint8_t *>("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<uint8_t> 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<uint8_t> 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<X509> 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<EVP_PKEY> pkey(PrivateKeyFromPEM(kRSAKey)); | 
|  | ASSERT_TRUE(pkey); | 
|  | bssl::UniquePtr<X509_ALGOR> 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<X509_CRL> 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<ASN1_TIME> 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<X509_NAME> issuer(X509_NAME_new()); | 
|  | ASSERT_TRUE(issuer); | 
|  | ASSERT_TRUE(X509_NAME_add_entry_by_txt( | 
|  | issuer.get(), "CN", MBSTRING_ASC, | 
|  | reinterpret_cast<const uint8_t *>("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<uint8_t> 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<uint8_t> 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<X509_CRL> 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<EVP_PKEY> pkey(PrivateKeyFromPEM(kRSAKey)); | 
|  | ASSERT_TRUE(pkey); | 
|  | bssl::UniquePtr<X509_ALGOR> 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<X509_REQ> csr; | 
|  | if (new_csr) { | 
|  | csr.reset(X509_REQ_new()); | 
|  | ASSERT_TRUE(csr); | 
|  | bssl::UniquePtr<X509_NAME> subject(X509_NAME_new()); | 
|  | ASSERT_TRUE(subject); | 
|  | ASSERT_TRUE(X509_NAME_add_entry_by_txt( | 
|  | subject.get(), "CN", MBSTRING_ASC, | 
|  | reinterpret_cast<const uint8_t *>("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<uint8_t> 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<uint8_t> 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<X509_REQ> 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<EVP_PKEY> copy_pubkey(X509_REQ_get_pubkey(copy.get())); | 
|  | ASSERT_TRUE(copy_pubkey); | 
|  | EXPECT_EQ(1, EVP_PKEY_cmp(pkey.get(), copy_pubkey.get())); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST(X509Test, Ed25519Sign) { | 
|  | uint8_t pub_bytes[32], priv_bytes[64]; | 
|  | ED25519_keypair(pub_bytes, priv_bytes); | 
|  |  | 
|  | bssl::UniquePtr<EVP_PKEY> pub( | 
|  | EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, nullptr, pub_bytes, 32)); | 
|  | ASSERT_TRUE(pub); | 
|  | bssl::UniquePtr<EVP_PKEY> 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())); | 
|  | } | 
|  |  | 
|  | static bool PEMToDER(bssl::UniquePtr<uint8_t> *out, size_t *out_len, | 
|  | const char *pem) { | 
|  | bssl::UniquePtr<BIO> 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<uint8_t> data; | 
|  | ASSERT_TRUE(PEMToDER(&data, &data_len, kRootCAPEM)); | 
|  |  | 
|  | bssl::UniquePtr<CRYPTO_BUFFER> buf( | 
|  | CRYPTO_BUFFER_new(data.get(), data_len, nullptr)); | 
|  | ASSERT_TRUE(buf); | 
|  | bssl::UniquePtr<X509> 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<uint8_t> data; | 
|  | ASSERT_TRUE(PEMToDER(&data, &data_len, kRootCAPEM)); | 
|  |  | 
|  | auto trailing_data = std::make_unique<uint8_t[]>(data_len + 1); | 
|  | OPENSSL_memcpy(trailing_data.get(), data.get(), data_len); | 
|  |  | 
|  | bssl::UniquePtr<CRYPTO_BUFFER> buf_trailing_data( | 
|  | CRYPTO_BUFFER_new(trailing_data.get(), data_len + 1, nullptr)); | 
|  | ASSERT_TRUE(buf_trailing_data); | 
|  |  | 
|  | bssl::UniquePtr<X509> 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<uint8_t> data; | 
|  | ASSERT_TRUE(PEMToDER(&data, &data_len, kRootCAPEM)); | 
|  |  | 
|  | bssl::UniquePtr<CRYPTO_BUFFER> buf( | 
|  | CRYPTO_BUFFER_new(data.get(), data_len, nullptr)); | 
|  | ASSERT_TRUE(buf); | 
|  |  | 
|  | bssl::UniquePtr<X509> root(X509_parse_from_buffer(buf.get())); | 
|  | ASSERT_TRUE(root); | 
|  |  | 
|  | bssl::UniquePtr<ASN1_INTEGER> 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<long>(data_len), i2d_X509(root.get(), nullptr)); | 
|  |  | 
|  | // Re-encode the TBSCertificate. | 
|  | i2d_re_X509_tbs(root.get(), nullptr); | 
|  |  | 
|  | ASSERT_NE(static_cast<long>(data_len), i2d_X509(root.get(), nullptr)); | 
|  | } | 
|  |  | 
|  | TEST(X509Test, TestFromBufferReused) { | 
|  | size_t data_len; | 
|  | bssl::UniquePtr<uint8_t> data; | 
|  | ASSERT_TRUE(PEMToDER(&data, &data_len, kRootCAPEM)); | 
|  |  | 
|  | bssl::UniquePtr<CRYPTO_BUFFER> buf( | 
|  | CRYPTO_BUFFER_new(data.get(), data_len, nullptr)); | 
|  | ASSERT_TRUE(buf); | 
|  |  | 
|  | bssl::UniquePtr<X509> root(X509_parse_from_buffer(buf.get())); | 
|  | ASSERT_TRUE(root); | 
|  |  | 
|  | size_t data2_len; | 
|  | bssl::UniquePtr<uint8_t> data2; | 
|  | ASSERT_TRUE(PEMToDER(&data2, &data2_len, kLeafPEM)); | 
|  | EXPECT_TRUE(buffers_alias(root->cert_info->enc.enc, root->cert_info->enc.len, | 
|  | CRYPTO_BUFFER_data(buf.get()), | 
|  | CRYPTO_BUFFER_len(buf.get()))); | 
|  |  | 
|  | // Historically, this function tested the interaction betweeen | 
|  | // |X509_parse_from_buffer| and object reuse. We no longer support object | 
|  | // reuse, so |d2i_X509| will replace |raw| with a new object. However, we | 
|  | // retain this test to verify that releasing objects from |d2i_X509| works | 
|  | // correctly. | 
|  | X509 *raw = root.release(); | 
|  | const uint8_t *inp = data2.get(); | 
|  | X509 *ret = d2i_X509(&raw, &inp, data2_len); | 
|  | root.reset(raw); | 
|  |  | 
|  | ASSERT_EQ(root.get(), ret); | 
|  | ASSERT_EQ(nullptr, root->cert_info->enc.buf); | 
|  | EXPECT_FALSE(buffers_alias(root->cert_info->enc.enc, root->cert_info->enc.len, | 
|  | CRYPTO_BUFFER_data(buf.get()), | 
|  | CRYPTO_BUFFER_len(buf.get()))); | 
|  |  | 
|  | // 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<uint8_t> i2d_storage(i2d); | 
|  |  | 
|  | ASSERT_TRUE(PEMToDER(&data2, &data2_len, kLeafPEM)); | 
|  |  | 
|  | ASSERT_EQ(static_cast<long>(data2_len), i2d_len); | 
|  | ASSERT_EQ(0, OPENSSL_memcmp(data2.get(), i2d, i2d_len)); | 
|  | ASSERT_EQ(nullptr, root->cert_info->enc.buf); | 
|  | } | 
|  |  | 
|  | TEST(X509Test, TestFailedParseFromBuffer) { | 
|  | static const uint8_t kNonsense[] = {1, 2, 3, 4, 5}; | 
|  |  | 
|  | bssl::UniquePtr<CRYPTO_BUFFER> buf( | 
|  | CRYPTO_BUFFER_new(kNonsense, sizeof(kNonsense), nullptr)); | 
|  | ASSERT_TRUE(buf); | 
|  |  | 
|  | bssl::UniquePtr<X509> 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<uint8_t> data; | 
|  | ASSERT_TRUE(PEMToDER(&data, &data_len, kRootCAPEM)); | 
|  |  | 
|  | auto data_with_trailing_byte = std::make_unique<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<CRYPTO_BUFFER> buf_with_trailing_byte( | 
|  | CRYPTO_BUFFER_new(data_with_trailing_byte.get(), data_len + 1, nullptr)); | 
|  | ASSERT_TRUE(buf_with_trailing_byte); | 
|  |  | 
|  | bssl::UniquePtr<X509> 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<ASN1_UTCTIME> tm(ASN1_UTCTIME_new()); | 
|  | ASSERT_TRUE(tm); | 
|  | bssl::UniquePtr<BIO> 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, bssl::BytesAsStringView(bssl::Span(contents, len))); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST(X509Test, PrettyPrintIntegers) { | 
|  | static const char *kTests[] = { | 
|  | // Small numbers are pretty-printed in decimal. | 
|  | "0", | 
|  | "-1", | 
|  | "1", | 
|  | "42", | 
|  | "-42", | 
|  | "256", | 
|  | "-256", | 
|  | // Large numbers are pretty-printed in hex to avoid taking quadratic time. | 
|  | "0x0123456789", | 
|  | "-0x0123456789", | 
|  | }; | 
|  | for (const char *in : kTests) { | 
|  | SCOPED_TRACE(in); | 
|  | BIGNUM *bn = nullptr; | 
|  | ASSERT_TRUE(BN_asc2bn(&bn, in)); | 
|  | bssl::UniquePtr<BIGNUM> free_bn(bn); | 
|  |  | 
|  | { | 
|  | bssl::UniquePtr<ASN1_INTEGER> asn1(BN_to_ASN1_INTEGER(bn, nullptr)); | 
|  | ASSERT_TRUE(asn1); | 
|  | bssl::UniquePtr<char> out(i2s_ASN1_INTEGER(nullptr, asn1.get())); | 
|  | ASSERT_TRUE(out.get()); | 
|  | EXPECT_STREQ(in, out.get()); | 
|  | } | 
|  |  | 
|  | { | 
|  | bssl::UniquePtr<ASN1_ENUMERATED> asn1(BN_to_ASN1_ENUMERATED(bn, nullptr)); | 
|  | ASSERT_TRUE(asn1); | 
|  | bssl::UniquePtr<char> out(i2s_ASN1_ENUMERATED(nullptr, asn1.get())); | 
|  | ASSERT_TRUE(out.get()); | 
|  | EXPECT_STREQ(in, out.get()); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST(X509Test, X509AlgorSetMd) { | 
|  | bssl::UniquePtr<X509_ALGOR> 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<X509_NAME> name(X509_NAME_new()); | 
|  | ASSERT_TRUE(name); | 
|  | EXPECT_TRUE(X509_NAME_add_entry_by_txt( | 
|  | name.get(), "C", MBSTRING_ASC, reinterpret_cast<const uint8_t *>("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<const uint8_t *>("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<const uint8_t *>("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<const uint8_t *>("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<uint8_t> 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<X509_NAME> 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<X509> root(CertFromPEM(kSANTypesRoot)); | 
|  | bssl::UniquePtr<X509> intermediate( | 
|  | CertFromPEM(kNoBasicConstraintsCertSignIntermediate)); | 
|  | bssl::UniquePtr<X509> 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<X509> root(CertFromPEM(kSANTypesRoot)); | 
|  | bssl::UniquePtr<X509> intermediate( | 
|  | CertFromPEM(kNoBasicConstraintsNetscapeCAIntermediate)); | 
|  | bssl::UniquePtr<X509> 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<X509> cert(CertFromPEM(kSelfSignedMismatchAlgorithms)); | 
|  | ASSERT_TRUE(cert); | 
|  |  | 
|  | bssl::UniquePtr<EVP_PKEY> 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)); | 
|  | } | 
|  |  | 
|  | // TODO(crbug.com/387737061): Test that this function can decrypt certificates | 
|  | // and CRLs, even though it leaves encrypted private keys alone. | 
|  | 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); | 
|  |  | 
|  | bssl::UniquePtr<EVP_PKEY> placeholder_key(EVP_PKEY_new()); | 
|  | ASSERT_TRUE(placeholder_key); | 
|  |  | 
|  | std::string unknown = | 
|  | "-----BEGIN UNKNOWN-----\n" | 
|  | "AAAA\n" | 
|  | "-----END UNKNOWN-----\n"; | 
|  |  | 
|  | std::string invalid = | 
|  | "-----BEGIN CERTIFICATE-----\n" | 
|  | "AAAA\n" | 
|  | "-----END CERTIFICATE-----\n"; | 
|  |  | 
|  | std::string encrypted_key = R"(-----BEGIN EC PRIVATE KEY----- | 
|  | Proc-Type: 4,ENCRYPTED | 
|  | DEK-Info: AES-128-CBC,B3B2988AECAE6EAB0D043105994C1123 | 
|  |  | 
|  | RK7DUIGDHWTFh2rpTX+dR88hUyC1PyDlIULiNCkuWFwHrJbc1gM6hMVOKmU196XC | 
|  | iITrIKmilFm9CPD6Tpfk/NhI/QPxyJlk1geIkxpvUZ2FCeMuYI1To14oYOUKv14q | 
|  | wr6JtaX2G+pOmwcSPymZC4u2TncAP7KHgS8UGcMw8CE= | 
|  | -----END EC PRIVATE KEY----- | 
|  | )"; | 
|  |  | 
|  | // 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 + | 
|  | // Encrypted private keys are not decrypted (decryption failures would be | 
|  | // fatal) and just returned as placeholder. | 
|  | crl + cert + encrypted_key + | 
|  | // Placeholder keys are still keys, so a new key starts a new entry. | 
|  | rsa; | 
|  |  | 
|  | 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()}, | 
|  | {cert_obj.get(), placeholder_key.get(), crl_obj.get()}, | 
|  | {nullptr, rsa_obj.get(), nullptr}, | 
|  | }; | 
|  |  | 
|  | 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); | 
|  | if (EVP_PKEY_id(expected->key) == EVP_PKEY_NONE) { | 
|  | // Expect a placeholder key. | 
|  | EXPECT_FALSE(info->x_pkey->dec_pkey); | 
|  | } else { | 
|  | // EVP_PKEY_cmp returns one if the keys are equal. | 
|  | ASSERT_TRUE(info->x_pkey->dec_pkey); | 
|  | EXPECT_EQ(1, EVP_PKEY_cmp(expected->key, info->x_pkey->dec_pkey)); | 
|  | } | 
|  | } else { | 
|  | EXPECT_EQ(nullptr, info->x_pkey); | 
|  | } | 
|  | }; | 
|  |  | 
|  | bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(pem.data(), pem.size())); | 
|  | ASSERT_TRUE(bio); | 
|  | bssl::UniquePtr<STACK_OF(X509_INFO)> 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<STACK_OF(X509_INFO)> 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(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> 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(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> 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<BIO_METHOD> 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(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<X509> 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<uint8_t> 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<size_t>(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<X509> cert2(d2i_X509_bio(bio.get(), nullptr)); | 
|  | ASSERT_TRUE(cert2); | 
|  | EXPECT_EQ(0, X509_cmp(cert.get(), cert2.get())); | 
|  | } | 
|  |  | 
|  | TEST(X509Test, CommonNameFallback) { | 
|  | bssl::UniquePtr<X509> root = CertFromPEM(kSANTypesRoot); | 
|  | ASSERT_TRUE(root); | 
|  | bssl::UniquePtr<X509> with_sans = CertFromPEM(kCommonNameWithSANs); | 
|  | ASSERT_TRUE(with_sans); | 
|  | bssl::UniquePtr<X509> without_sans = CertFromPEM(kCommonNameWithoutSANs); | 
|  | ASSERT_TRUE(without_sans); | 
|  | bssl::UniquePtr<X509> with_email = CertFromPEM(kCommonNameWithEmailSAN); | 
|  | ASSERT_TRUE(with_email); | 
|  | bssl::UniquePtr<X509> 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); | 
|  | }); | 
|  | }; | 
|  |  | 
|  | // By default, the common name is ignored if the SAN list is present but | 
|  | // otherwise is checked. | 
|  | 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_ERR_HOSTNAME_MISMATCH, | 
|  | verify_cert(with_email.get(), 0 /* no flags */, "foo.host1.test")); | 
|  | EXPECT_EQ(X509_V_ERR_HOSTNAME_MISMATCH, | 
|  | verify_cert(with_ip.get(), 0 /* no flags */, "foo.host1.test")); | 
|  |  | 
|  | // X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT is ignored. | 
|  | EXPECT_EQ(X509_V_ERR_HOSTNAME_MISMATCH, | 
|  | 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_ERR_HOSTNAME_MISMATCH, | 
|  | verify_cert(with_email.get(), X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT, | 
|  | "foo.host1.test")); | 
|  | EXPECT_EQ(X509_V_ERR_HOSTNAME_MISMATCH, | 
|  | 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, LooksLikeDNSName) { | 
|  | static const char *kValid[] = { | 
|  | "example.com",  "eXample123-.com", "*.example.com", | 
|  | "exa_mple.com", "example.com.",    "project-dev:us-central1:main", | 
|  | }; | 
|  | static const char *kInvalid[] = { | 
|  | "-eXample123-.com", | 
|  | "", | 
|  | ".", | 
|  | "*", | 
|  | "*.", | 
|  | "example..com", | 
|  | ".example.com", | 
|  | "example.com..", | 
|  | "*foo.example.com", | 
|  | "foo.*.example.com", | 
|  | "foo,bar", | 
|  | }; | 
|  |  | 
|  | for (const char *str : kValid) { | 
|  | SCOPED_TRACE(str); | 
|  | EXPECT_TRUE(x509v3_looks_like_dns_name( | 
|  | reinterpret_cast<const uint8_t *>(str), strlen(str))); | 
|  | } | 
|  | for (const char *str : kInvalid) { | 
|  | SCOPED_TRACE(str); | 
|  | EXPECT_FALSE(x509v3_looks_like_dns_name( | 
|  | reinterpret_cast<const uint8_t *>(str), strlen(str))); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST(X509Test, CommonNameAndNameConstraints) { | 
|  | bssl::UniquePtr<X509> root = CertFromPEM(kSANTypesRoot); | 
|  | ASSERT_TRUE(root); | 
|  | bssl::UniquePtr<X509> intermediate = CertFromPEM(kConstrainedIntermediate); | 
|  | ASSERT_TRUE(intermediate); | 
|  | bssl::UniquePtr<X509> permitted = CertFromPEM(kCommonNamePermittedLeaf); | 
|  | ASSERT_TRUE(permitted); | 
|  | bssl::UniquePtr<X509> not_permitted = | 
|  | CertFromPEM(kCommonNameNotPermittedLeaf); | 
|  | ASSERT_TRUE(not_permitted); | 
|  | bssl::UniquePtr<X509> not_permitted_with_sans = | 
|  | CertFromPEM(kCommonNameNotPermittedWithSANsLeaf); | 
|  | ASSERT_TRUE(not_permitted_with_sans); | 
|  | bssl::UniquePtr<X509> not_dns = CertFromPEM(kCommonNameNotDNSLeaf); | 
|  | ASSERT_TRUE(not_dns); | 
|  |  | 
|  | auto verify_cert = [&](X509 *leaf, unsigned flags, const char *host) { | 
|  | return Verify( | 
|  | leaf, {root.get()}, {intermediate.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); | 
|  | }); | 
|  | }; | 
|  |  | 
|  | // Certificates which would otherwise trigger the common name fallback are | 
|  | // rejected whenever there are name constraints. We do this whether or not | 
|  | // the common name matches the constraints. | 
|  | EXPECT_EQ( | 
|  | X509_V_ERR_NAME_CONSTRAINTS_WITHOUT_SANS, | 
|  | verify_cert(permitted.get(), 0 /* no flags */, kCommonNamePermitted)); | 
|  | EXPECT_EQ(X509_V_ERR_NAME_CONSTRAINTS_WITHOUT_SANS, | 
|  | verify_cert(not_permitted.get(), 0 /* no flags */, | 
|  | kCommonNameNotPermitted)); | 
|  |  | 
|  | // This occurs even if the built-in name checks aren't used. The caller may | 
|  | // separately call |X509_check_host|. | 
|  | EXPECT_EQ(X509_V_ERR_NAME_CONSTRAINTS_WITHOUT_SANS, | 
|  | Verify(not_permitted.get(), {root.get()}, {intermediate.get()}, {}, | 
|  | 0 /* no flags */, nullptr)); | 
|  |  | 
|  | // If the leaf certificate has SANs, the common name fallback is always | 
|  | // disabled, so the name constraints do not apply. | 
|  | EXPECT_EQ(X509_V_OK, Verify(not_permitted_with_sans.get(), {root.get()}, | 
|  | {intermediate.get()}, {}, 0, nullptr)); | 
|  | EXPECT_EQ(X509_V_ERR_HOSTNAME_MISMATCH, | 
|  | verify_cert(not_permitted_with_sans.get(), 0 /* no flags */, | 
|  | kCommonNameNotPermittedWithSANs)); | 
|  |  | 
|  | // If the common name does not look like a DNS name, we apply neither name | 
|  | // constraints nor common name fallback. | 
|  | EXPECT_EQ(X509_V_OK, Verify(not_dns.get(), {root.get()}, {intermediate.get()}, | 
|  | {}, 0, nullptr)); | 
|  | EXPECT_EQ(X509_V_ERR_HOSTNAME_MISMATCH, | 
|  | verify_cert(not_dns.get(), 0 /* no flags */, kCommonNameNotDNS)); | 
|  | } | 
|  |  | 
|  | TEST(X509Test, ServerGatedCryptoEKUs) { | 
|  | bssl::UniquePtr<X509> root = CertFromPEM(kSANTypesRoot); | 
|  | ASSERT_TRUE(root); | 
|  | bssl::UniquePtr<X509> ms_sgc = CertFromPEM(kMicrosoftSGCCert); | 
|  | ASSERT_TRUE(ms_sgc); | 
|  | bssl::UniquePtr<X509> ns_sgc = CertFromPEM(kNetscapeSGCCert); | 
|  | ASSERT_TRUE(ns_sgc); | 
|  | bssl::UniquePtr<X509> server_eku = CertFromPEM(kServerEKUCert); | 
|  | ASSERT_TRUE(server_eku); | 
|  | bssl::UniquePtr<X509> server_eku_plus_ms_sgc = | 
|  | CertFromPEM(kServerEKUPlusMicrosoftSGCCert); | 
|  | ASSERT_TRUE(server_eku_plus_ms_sgc); | 
|  | bssl::UniquePtr<X509> any_eku = CertFromPEM(kAnyEKU); | 
|  | ASSERT_TRUE(any_eku); | 
|  | bssl::UniquePtr<X509> 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<X509> root = CertFromPEM( | 
|  | GetTestData("crypto/x509/test/invalid_extension_root.pem").c_str()); | 
|  | ASSERT_TRUE(root); | 
|  | bssl::UniquePtr<X509> intermediate = CertFromPEM( | 
|  | GetTestData("crypto/x509/test/invalid_extension_intermediate.pem") | 
|  | .c_str()); | 
|  | ASSERT_TRUE(intermediate); | 
|  | bssl::UniquePtr<X509> 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<X509> invalid_root = CertFromPEM( | 
|  | GetTestData((std::string("crypto/x509/test/invalid_extension_root_") + | 
|  | ext + ".pem") | 
|  | .c_str()) | 
|  | .c_str()); | 
|  | ASSERT_TRUE(invalid_root); | 
|  |  | 
|  | bssl::UniquePtr<X509> invalid_intermediate = CertFromPEM( | 
|  | GetTestData( | 
|  | (std::string("crypto/x509/test/invalid_extension_intermediate_") + | 
|  | ext + ".pem") | 
|  | .c_str()) | 
|  | .c_str()); | 
|  | ASSERT_TRUE(invalid_intermediate); | 
|  |  | 
|  | bssl::UniquePtr<X509> invalid_leaf = CertFromPEM( | 
|  | GetTestData((std::string("crypto/x509/test/invalid_extension_leaf_") + | 
|  | ext + ".pem") | 
|  | .c_str()) | 
|  | .c_str()); | 
|  | ASSERT_TRUE(invalid_leaf); | 
|  |  | 
|  | bssl::UniquePtr<X509> 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(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<X509_CRL> 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<X509_REQ> 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<X509> leaf(CertFromPEM(kLeafPEM)); | 
|  | ASSERT_TRUE(leaf); | 
|  | bssl::UniquePtr<X509_STORE_CTX> 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<X509> leaf(CertFromPEM(kLeafPEM)); | 
|  | ASSERT_TRUE(leaf); | 
|  | bssl::UniquePtr<X509_STORE> store(X509_STORE_new()); | 
|  | ASSERT_TRUE(store); | 
|  | bssl::UniquePtr<X509_STORE_CTX> 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<X509> 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----- | 
|  | )"; | 
|  |  | 
|  | TEST(X509Test, AlgorithmParameters) { | 
|  | // P-256 parameters should be omitted, but we accept NULL ones. | 
|  | bssl::UniquePtr<EVP_PKEY> key = PrivateKeyFromPEM(kP256Key); | 
|  | ASSERT_TRUE(key); | 
|  |  | 
|  | bssl::UniquePtr<X509> 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<uint8_t> 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] {} | 
|  | {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<GENERAL_NAME> 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<uint8_t> 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<GENERAL_NAME> 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<uint8_t> 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<X509_ALGOR> 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<ASN1_TYPE> 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<uint8_t> 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<const ASN1_BMPSTRING *>( | 
|  | 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<ASN1_STRING> 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<X509_ATTRIBUTE> 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<EVP_PKEY> key = PrivateKeyFromPEM(kP256Key); | 
|  | ASSERT_TRUE(key); | 
|  |  | 
|  | bssl::UniquePtr<X509> 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<X509> 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<X509> 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<X509> 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<X509> 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<EVP_PKEY> 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<X509> { | 
|  | bssl::UniquePtr<X509> 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<X509> not_yet_valid, valid, expired; | 
|  | }; | 
|  | 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(X509Test, SignatureVerification) { | 
|  | bssl::UniquePtr<EVP_PKEY> key = PrivateKeyFromPEM(kP256Key); | 
|  | ASSERT_TRUE(key); | 
|  |  | 
|  | struct Certs { | 
|  | bssl::UniquePtr<X509> valid; | 
|  | bssl::UniquePtr<X509> bad_key_type, bad_key; | 
|  | bssl::UniquePtr<X509> bad_sig_type, bad_sig; | 
|  | }; | 
|  | 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<uint8_t> pubkey_data(static_cast<uint8_t *>( | 
|  | 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<uint8_t *>(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<X509_ALGOR> 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_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY, | 
|  | Verify(leaf.valid.get(), {root.bad_key.get()}, | 
|  | {intermediate.valid.get()}, {})); | 
|  | EXPECT_EQ(X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY, | 
|  | Verify(leaf.valid.get(), {root.bad_key_type.get()}, | 
|  | {intermediate.valid.get()}, {})); | 
|  | EXPECT_EQ(X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY, | 
|  | Verify(leaf.valid.get(), {root.valid.get()}, | 
|  | {intermediate.bad_key.get()}, {})); | 
|  | EXPECT_EQ(X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY, | 
|  | Verify(leaf.valid.get(), {root.valid.get()}, | 
|  | {intermediate.bad_key_type.get()}, {})); | 
|  |  | 
|  | // Bad keys in the leaf are ignored. The leaf's key is used by the caller. | 
|  | EXPECT_EQ(X509_V_OK, Verify(leaf.bad_key.get(), {root.valid.get()}, | 
|  | {intermediate.valid.get()}, {})); | 
|  | EXPECT_EQ(X509_V_OK, Verify(leaf.bad_key_type.get(), {root.valid.get()}, | 
|  | {intermediate.valid.get()}, {})); | 
|  |  | 
|  | // 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----- | 
|  | )"; | 
|  |  | 
|  | // kNonMinimalLengthOuter is an X.509 certificate where the outermost SEQUENCE | 
|  | // has a non-minimal length. | 
|  | static const char kNonMinimalLengthOuter[] = R"( | 
|  | -----BEGIN CERTIFICATE----- | 
|  | MIMAASAwgcagAwIBAgICBNIwCgYIKoZIzj0EAwIwDzENMAsGA1UEAxMEVGVzdDAg | 
|  | Fw0wMDAxMDEwMDAwMDBaGA8yMTAwMDEwMTAwMDAwMFowDzENMAsGA1UEAxMEVGVz | 
|  | dDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOYraeK/ZZ+Xvi8eDZSKTNWXa7ep | 
|  | Hg1G+92pqR6d3LpaAefWl6gKGPnDxKMeVuJ8g0jbFhoc9R1+8ZQtS89yIsGjEDAO | 
|  | MAwGA1UdEwQFMAMBAf8wCgYIKoZIzj0EAwIDSQAwRgIhAKnSIhfmzfQpeOKFHiAq | 
|  | cml3ex6oaVVGoJWCsPQoZjVAAiEAqTHS9HzZBTQ20cMPXUpf8u5AXZP7adeh4qnk | 
|  | soBsxWI= | 
|  | -----END CERTIFICATE----- | 
|  | )"; | 
|  |  | 
|  | // kNonMinimalLengthSignature is an X.509 certificate where the signature has a | 
|  | // non-minimal length. | 
|  | static const char kNonMinimalLengthSignature[] = R"( | 
|  | -----BEGIN CERTIFICATE----- | 
|  | MIIBITCBxqADAgECAgIE0jAKBggqhkjOPQQDAjAPMQ0wCwYDVQQDEwRUZXN0MCAX | 
|  | DTAwMDEwMTAwMDAwMFoYDzIxMDAwMTAxMDAwMDAwWjAPMQ0wCwYDVQQDEwRUZXN0 | 
|  | MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE5itp4r9ln5e+Lx4NlIpM1Zdrt6ke | 
|  | DUb73ampHp3culoB59aXqAoY+cPEox5W4nyDSNsWGhz1HX7xlC1Lz3IiwaMQMA4w | 
|  | DAYDVR0TBAUwAwEB/zAKBggqhkjOPQQDAgOBSQAwRgIhAKnSIhfmzfQpeOKFHiAq | 
|  | cml3ex6oaVVGoJWCsPQoZjVAAiEAqTHS9HzZBTQ20cMPXUpf8u5AXZP7adeh4qnk | 
|  | soBsxWI= | 
|  | -----END CERTIFICATE----- | 
|  | )"; | 
|  |  | 
|  | // kNonMinimalLengthSerial is an X.509 certificate where the serial number has a | 
|  | // non-minimal length. | 
|  | static const char kNonMinimalLengthSerial[] = R"( | 
|  | -----BEGIN CERTIFICATE----- | 
|  | MIIBITCBx6ADAgECAoECBNIwCgYIKoZIzj0EAwIwDzENMAsGA1UEAxMEVGVzdDAg | 
|  | Fw0wMDAxMDEwMDAwMDBaGA8yMTAwMDEwMTAwMDAwMFowDzENMAsGA1UEAxMEVGVz | 
|  | dDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOYraeK/ZZ+Xvi8eDZSKTNWXa7ep | 
|  | Hg1G+92pqR6d3LpaAefWl6gKGPnDxKMeVuJ8g0jbFhoc9R1+8ZQtS89yIsGjEDAO | 
|  | MAwGA1UdEwQFMAMBAf8wCgYIKoZIzj0EAwIDSQAwRgIhAKnSIhfmzfQpeOKFHiAq | 
|  | cml3ex6oaVVGoJWCsPQoZjVAAiEAqTHS9HzZBTQ20cMPXUpf8u5AXZP7adeh4qnk | 
|  | soBsxWI= | 
|  | -----END CERTIFICATE----- | 
|  | )"; | 
|  |  | 
|  | TEST(X509Test, BER) { | 
|  | // Constructed strings are forbidden in DER. | 
|  | EXPECT_FALSE(CertFromPEM(kConstructedBitString)); | 
|  | EXPECT_FALSE(CertFromPEM(kConstructedOctetString)); | 
|  | // Indefinite lengths are forbidden in DER. | 
|  | EXPECT_FALSE(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)); | 
|  | // Lengths must be minimal in DER. | 
|  | EXPECT_FALSE(CertFromPEM(kNonMinimalLengthOuter)); | 
|  | EXPECT_FALSE(CertFromPEM(kNonMinimalLengthSerial)); | 
|  | // We, for now, accept a non-minimal length in the signature field. See | 
|  | // b/18228011. | 
|  | EXPECT_TRUE(CertFromPEM(kNonMinimalLengthSignature)); | 
|  | } | 
|  |  | 
|  | TEST(X509Test, Names) { | 
|  | bssl::UniquePtr<EVP_PKEY> key = PrivateKeyFromPEM(kP256Key); | 
|  | ASSERT_TRUE(key); | 
|  | bssl::UniquePtr<X509> 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<std::pair<int, std::string>> cert_subject; | 
|  | std::vector<std::string> cert_dns_names; | 
|  | std::vector<std::string> cert_emails; | 
|  | std::vector<std::string> valid_dns_names; | 
|  | std::vector<std::string> invalid_dns_names; | 
|  | std::vector<std::string> valid_emails; | 
|  | std::vector<std::string> 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 | 
|  | // do not support this. | 
|  | { | 
|  | /*cert_subject=*/{}, | 
|  | /*cert_dns_names=*/{"www.a.example", "*.b.test"}, | 
|  | /*cert_emails=*/{}, | 
|  | /*valid_dns_names=*/{}, | 
|  | /*invalid_dns_names=*/ | 
|  | {".www.a.example", ".www.b.test", ".a.example", ".b.test", ".example", | 
|  | ".test"}, | 
|  | /*valid_emails=*/{}, | 
|  | /*invalid_emails=*/{}, | 
|  | /*flags=*/0, | 
|  | }, | 
|  |  | 
|  | // 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<X509> cert = | 
|  | MakeTestCert("Root", "Leaf", key.get(), /*is_ca=*/false); | 
|  | ASSERT_TRUE(cert); | 
|  | if (!t.cert_subject.empty()) { | 
|  | bssl::UniquePtr<X509_NAME> 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<const unsigned char *>(entry.second.data()), | 
|  | entry.second.size(), /*loc=*/-1, /*set=*/0)); | 
|  | } | 
|  | ASSERT_TRUE(X509_set_subject_name(cert.get(), subject.get())); | 
|  | } | 
|  | bssl::UniquePtr<GENERAL_NAMES> sans(sk_GENERAL_NAME_new_null()); | 
|  | ASSERT_TRUE(sans); | 
|  | for (const auto &dns : t.cert_dns_names) { | 
|  | bssl::UniquePtr<GENERAL_NAME> 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<GENERAL_NAME> 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<X509_STORE> store(X509_STORE_new()); | 
|  | bssl::UniquePtr<X509> a(CertFromPEM(kCrossSigningRootPEM)); | 
|  | bssl::UniquePtr<X509> b(CertFromPEM(kRootCAPEM)); | 
|  |  | 
|  | ASSERT_TRUE(store); | 
|  | ASSERT_TRUE(a); | 
|  | ASSERT_TRUE(b); | 
|  |  | 
|  | EXPECT_TRUE(X509_STORE_add_cert(store.get(), a.get())); | 
|  | EXPECT_TRUE(X509_STORE_add_cert(store.get(), b.get())); | 
|  | EXPECT_TRUE(X509_STORE_add_cert(store.get(), a.get())); | 
|  | EXPECT_TRUE(X509_STORE_add_cert(store.get(), b.get())); | 
|  | EXPECT_TRUE(X509_STORE_add_cert(store.get(), a.get())); | 
|  | EXPECT_TRUE(X509_STORE_add_cert(store.get(), b.get())); | 
|  |  | 
|  | EXPECT_EQ(sk_X509_OBJECT_num(X509_STORE_get0_objects(store.get())), 2u); | 
|  | } | 
|  |  | 
|  | TEST(X509Test, BytesToHex) { | 
|  | struct { | 
|  | std::vector<uint8_t> 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<char> 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<X509_NAME> 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 \""}, | 
|  | // Callers can also customize the output, with both |XN_FLAG_*| and | 
|  | // |ASN1_STRFLGS_*|. |XN_FLAG_SEP_SPLUS_SPC| uses semicolon separators. | 
|  | {/*indent=*/0, | 
|  | /*flags=*/XN_FLAG_SEP_SPLUS_SPC | ASN1_STRFLGS_RFC2253 | | 
|  | ASN1_STRFLGS_ESC_QUOTE, | 
|  | "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 \""}, | 
|  | // Node uses these parameters. | 
|  | {/*indent=*/0, | 
|  | /*flags=*/ASN1_STRFLGS_ESC_2253 | ASN1_STRFLGS_ESC_CTRL | | 
|  | ASN1_STRFLGS_UTF8_CONVERT | XN_FLAG_SEP_MULTILINE | XN_FLAG_FN_SN, | 
|  | "C=US\n" | 
|  | "ST=Some State + " | 
|  | "ST=Some Other State \xE2\x98\x83 + " | 
|  | "ST=Another State \xE2\x98\x83 + " | 
|  | "1.2.840.113554.4.1.72585.2=\xE2\x98\x83\n" | 
|  | "1.2.840.113554.4.1.72585.3=0\\06\\02\\01\\01\\02\\01\\02\n" | 
|  | "O=Org Name\n" | 
|  | "CN=Common " | 
|  | "Name/CN=A/CN=B\\,CN=A\\,CN=B\\+CN=A\\+CN=B\\;CN=A\\;CN=B\\0ACN=A\\0A\n" | 
|  | "CN=\\ 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(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<size_t>(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<size_t>(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<char> 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); | 
|  | } | 
|  | } | 
|  |  | 
|  | // 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<X509> cert(CertFromPEM(kLargeSerialPEM)); | 
|  | ASSERT_TRUE(cert); | 
|  |  | 
|  | bssl::UniquePtr<BIO> 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)); | 
|  | auto print = bssl::BytesAsStringView(bssl::Span(data, data_len)); | 
|  | EXPECT_EQ(print, 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: | 
|  | TLS Web Server Authentication, TLS Web Client Authentication | 
|  | X509v3 Basic Constraints: critical | 
|  | CA:FALSE | 
|  | X509v3 Subject Key Identifier: | 
|  | A3:79:A6:F6:EE:AF:B9:A5:5E:37:8C:11:80:34:E2:75 | 
|  | X509v3 Authority Key Identifier: | 
|  | 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 | 
|  | )"); | 
|  | } | 
|  |  | 
|  | TEST(X509Test, AddExt) { | 
|  | bssl::UniquePtr<X509> x509(X509_new()); | 
|  | ASSERT_TRUE(x509); | 
|  |  | 
|  | struct Extension { | 
|  | int nid; | 
|  | bool critical; | 
|  | std::vector<uint8_t> data; | 
|  | }; | 
|  | auto expect_extensions = [&](const std::vector<Extension> &exts) { | 
|  | ASSERT_EQ(static_cast<size_t>(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<int>(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<uint8_t> basic1_der = {0x30, 0x00}; | 
|  | const uint8_t *inp = basic1_der.data(); | 
|  | bssl::UniquePtr<BASIC_CONSTRAINTS> basic1_obj( | 
|  | d2i_BASIC_CONSTRAINTS(nullptr, &inp, basic1_der.size())); | 
|  | EXPECT_EQ(inp, basic1_der.data() + basic1_der.size()); | 
|  |  | 
|  | // SEQUENCE { BOOLEAN { TRUE } } | 
|  | std::vector<uint8_t> basic2_der = {0x30, 0x03, 0x01, 0x01, 0xff}; | 
|  | inp = basic2_der.data(); | 
|  | bssl::UniquePtr<BASIC_CONSTRAINTS> basic2_obj( | 
|  | d2i_BASIC_CONSTRAINTS(nullptr, &inp, basic2_der.size())); | 
|  | EXPECT_EQ(inp, basic2_der.data() + basic2_der.size()); | 
|  |  | 
|  | // OCTET_STRING {} | 
|  | std::vector<uint8_t> skid1_der = {0x04, 0x00}; | 
|  | inp = skid1_der.data(); | 
|  | bssl::UniquePtr<ASN1_OCTET_STRING> 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<uint8_t> skid2_der = {0x04, 0x01, 0x61}; | 
|  | inp = skid2_der.data(); | 
|  | bssl::UniquePtr<ASN1_OCTET_STRING> 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<X509_NAME> 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(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<const unsigned char *>("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<const unsigned char *>("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<const unsigned char *>("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<const unsigned char *>("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<const unsigned char *>("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<const unsigned char *>("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<const unsigned char *>("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<const unsigned char *>("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<X509> root = CertFromPEM(kRootCAPEM); | 
|  | ASSERT_TRUE(root); | 
|  |  | 
|  | // Passing an IA5String to X509_set_serialNumber() should fail. | 
|  | bssl::UniquePtr<ASN1_IA5STRING> 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<ASN1_INTEGER> 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, Policy) { | 
|  | bssl::UniquePtr<ASN1_OBJECT> oid1( | 
|  | OBJ_txt2obj("1.2.840.113554.4.1.72585.2.1", /*dont_search_names=*/1)); | 
|  | ASSERT_TRUE(oid1); | 
|  | bssl::UniquePtr<ASN1_OBJECT> oid2( | 
|  | OBJ_txt2obj("1.2.840.113554.4.1.72585.2.2", /*dont_search_names=*/1)); | 
|  | ASSERT_TRUE(oid2); | 
|  | bssl::UniquePtr<ASN1_OBJECT> oid3( | 
|  | OBJ_txt2obj("1.2.840.113554.4.1.72585.2.3", /*dont_search_names=*/1)); | 
|  | ASSERT_TRUE(oid3); | 
|  | bssl::UniquePtr<ASN1_OBJECT> oid4( | 
|  | OBJ_txt2obj("1.2.840.113554.4.1.72585.2.4", /*dont_search_names=*/1)); | 
|  | ASSERT_TRUE(oid4); | 
|  | bssl::UniquePtr<ASN1_OBJECT> oid5( | 
|  | OBJ_txt2obj("1.2.840.113554.4.1.72585.2.5", /*dont_search_names=*/1)); | 
|  | ASSERT_TRUE(oid5); | 
|  |  | 
|  | bssl::UniquePtr<X509> root( | 
|  | CertFromPEM(GetTestData("crypto/x509/test/policy_root.pem").c_str())); | 
|  | ASSERT_TRUE(root); | 
|  | bssl::UniquePtr<X509> root_cross_inhibit_mapping(CertFromPEM( | 
|  | GetTestData("crypto/x509/test/policy_root_cross_inhibit_mapping.pem") | 
|  | .c_str())); | 
|  | ASSERT_TRUE(root_cross_inhibit_mapping); | 
|  | bssl::UniquePtr<X509> root2( | 
|  | CertFromPEM(GetTestData("crypto/x509/test/policy_root2.pem").c_str())); | 
|  | ASSERT_TRUE(root2); | 
|  | bssl::UniquePtr<X509> intermediate(CertFromPEM( | 
|  | GetTestData("crypto/x509/test/policy_intermediate.pem").c_str())); | 
|  | ASSERT_TRUE(intermediate); | 
|  | bssl::UniquePtr<X509> intermediate_any(CertFromPEM( | 
|  | GetTestData("crypto/x509/test/policy_intermediate_any.pem").c_str())); | 
|  | ASSERT_TRUE(intermediate_any); | 
|  | bssl::UniquePtr<X509> intermediate_duplicate(CertFromPEM( | 
|  | GetTestData("crypto/x509/test/policy_intermediate_duplicate.pem") | 
|  | .c_str())); | 
|  | ASSERT_TRUE(intermediate_duplicate); | 
|  | bssl::UniquePtr<X509> intermediate_invalid(CertFromPEM( | 
|  | GetTestData("crypto/x509/test/policy_intermediate_invalid.pem").c_str())); | 
|  | ASSERT_TRUE(intermediate_invalid); | 
|  | bssl::UniquePtr<X509> intermediate_mapped(CertFromPEM( | 
|  | GetTestData("crypto/x509/test/policy_intermediate_mapped.pem").c_str())); | 
|  | ASSERT_TRUE(intermediate_mapped); | 
|  | bssl::UniquePtr<X509> intermediate_mapped_any(CertFromPEM( | 
|  | GetTestData("crypto/x509/test/policy_intermediate_mapped_any.pem") | 
|  | .c_str())); | 
|  | ASSERT_TRUE(intermediate_mapped_any); | 
|  | bssl::UniquePtr<X509> intermediate_mapped_oid3(CertFromPEM( | 
|  | GetTestData("crypto/x509/test/policy_intermediate_mapped_oid3.pem") | 
|  | .c_str())); | 
|  | ASSERT_TRUE(intermediate_mapped_oid3); | 
|  | bssl::UniquePtr<X509> intermediate_require(CertFromPEM( | 
|  | GetTestData("crypto/x509/test/policy_intermediate_require.pem").c_str())); | 
|  | ASSERT_TRUE(intermediate_require); | 
|  | bssl::UniquePtr<X509> intermediate_require1(CertFromPEM( | 
|  | GetTestData("crypto/x509/test/policy_intermediate_require1.pem") | 
|  | .c_str())); | 
|  | ASSERT_TRUE(intermediate_require1); | 
|  | bssl::UniquePtr<X509> intermediate_require2(CertFromPEM( | 
|  | GetTestData("crypto/x509/test/policy_intermediate_require2.pem") | 
|  | .c_str())); | 
|  | ASSERT_TRUE(intermediate_require2); | 
|  | bssl::UniquePtr<X509> intermediate_require_duplicate(CertFromPEM( | 
|  | GetTestData("crypto/x509/test/policy_intermediate_require_duplicate.pem") | 
|  | .c_str())); | 
|  | ASSERT_TRUE(intermediate_require_duplicate); | 
|  | bssl::UniquePtr<X509> intermediate_require_no_policies(CertFromPEM( | 
|  | GetTestData( | 
|  | "crypto/x509/test/policy_intermediate_require_no_policies.pem") | 
|  | .c_str())); | 
|  | ASSERT_TRUE(intermediate_require_no_policies); | 
|  | bssl::UniquePtr<X509> leaf( | 
|  | CertFromPEM(GetTestData("crypto/x509/test/policy_leaf.pem").c_str())); | 
|  | ASSERT_TRUE(leaf); | 
|  | bssl::UniquePtr<X509> leaf_any( | 
|  | CertFromPEM(GetTestData("crypto/x509/test/policy_leaf_any.pem").c_str())); | 
|  | ASSERT_TRUE(leaf_any); | 
|  | bssl::UniquePtr<X509> leaf_duplicate(CertFromPEM( | 
|  | GetTestData("crypto/x509/test/policy_leaf_duplicate.pem").c_str())); | 
|  | ASSERT_TRUE(leaf_duplicate); | 
|  | bssl::UniquePtr<X509> leaf_invalid(CertFromPEM( | 
|  | GetTestData("crypto/x509/test/policy_leaf_invalid.pem").c_str())); | 
|  | ASSERT_TRUE(leaf_invalid); | 
|  | bssl::UniquePtr<X509> leaf_none(CertFromPEM( | 
|  | GetTestData("crypto/x509/test/policy_leaf_none.pem").c_str())); | 
|  | ASSERT_TRUE(leaf_none); | 
|  | bssl::UniquePtr<X509> leaf_oid1(CertFromPEM( | 
|  | GetTestData("crypto/x509/test/policy_leaf_oid1.pem").c_str())); | 
|  | ASSERT_TRUE(leaf_oid1); | 
|  | bssl::UniquePtr<X509> leaf_oid2(CertFromPEM( | 
|  | GetTestData("crypto/x509/test/policy_leaf_oid2.pem").c_str())); | 
|  | ASSERT_TRUE(leaf_oid2); | 
|  | bssl::UniquePtr<X509> leaf_oid3(CertFromPEM( | 
|  | GetTestData("crypto/x509/test/policy_leaf_oid3.pem").c_str())); | 
|  | ASSERT_TRUE(leaf_oid3); | 
|  | bssl::UniquePtr<X509> leaf_oid4(CertFromPEM( | 
|  | GetTestData("crypto/x509/test/policy_leaf_oid4.pem").c_str())); | 
|  | ASSERT_TRUE(leaf_oid4); | 
|  | bssl::UniquePtr<X509> leaf_oid5(CertFromPEM( | 
|  | GetTestData("crypto/x509/test/policy_leaf_oid5.pem").c_str())); | 
|  | ASSERT_TRUE(leaf_oid5); | 
|  | bssl::UniquePtr<X509> leaf_require(CertFromPEM( | 
|  | GetTestData("crypto/x509/test/policy_leaf_require.pem").c_str())); | 
|  | ASSERT_TRUE(leaf_require); | 
|  | bssl::UniquePtr<X509> leaf_require1(CertFromPEM( | 
|  | GetTestData("crypto/x509/test/policy_leaf_require1.pem").c_str())); | 
|  | ASSERT_TRUE(leaf_require1); | 
|  |  | 
|  | auto set_policies = [](X509_STORE_CTX *ctx, | 
|  | std::vector<const ASN1_OBJECT *> oids) { | 
|  | X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(ctx); | 
|  | for (const ASN1_OBJECT *oid : oids) { | 
|  | bssl::UniquePtr<ASN1_OBJECT> 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)); | 
|  | 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()}); })); | 
|  | EXPECT_EQ(X509_V_ERR_INVALID_POLICY_EXTENSION, | 
|  | Verify(leaf_invalid.get(), {root.get()}, {intermediate.get()}, | 
|  | /*crls=*/{})); | 
|  |  | 
|  | // 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()}); })); | 
|  |  | 
|  | // Without |X509_V_FLAG_EXPLICIT_POLICY|, the policy tree is built and | 
|  | // intersected with user-specified policies, but it is not required to result | 
|  | // in any valid policies. | 
|  | EXPECT_EQ(X509_V_OK, | 
|  | Verify(leaf.get(), {root.get()}, {intermediate.get()}, /*crls=*/{}, | 
|  | /*flags=*/0, [&](X509_STORE_CTX *ctx) { | 
|  | set_policies(ctx, {oid1.get()}); | 
|  | })); | 
|  | EXPECT_EQ(X509_V_OK, | 
|  | Verify(leaf.get(), {root.get()}, {intermediate.get()}, /*crls=*/{}, | 
|  | /*flags=*/0, [&](X509_STORE_CTX *ctx) { | 
|  | set_policies(ctx, {oid3.get()}); | 
|  | })); | 
|  |  | 
|  | // However, a CA with policy constraints can require an explicit policy. | 
|  | EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, | 
|  | {intermediate_require.get()}, /*crls=*/{}, | 
|  | /*flags=*/0, [&](X509_STORE_CTX *ctx) { | 
|  | set_policies(ctx, {oid1.get()}); | 
|  | })); | 
|  | EXPECT_EQ(X509_V_ERR_NO_EXPLICIT_POLICY, | 
|  | Verify(leaf.get(), {root.get()}, {intermediate_require.get()}, | 
|  | /*crls=*/{}, | 
|  | /*flags=*/0, [&](X509_STORE_CTX *ctx) { | 
|  | set_policies(ctx, {oid3.get()}); | 
|  | })); | 
|  |  | 
|  | // requireExplicitPolicy applies even if the application does not configure a | 
|  | // user-initial-policy-set. If the validation results in no policies, the | 
|  | // chain is invalid. | 
|  | EXPECT_EQ(X509_V_ERR_NO_EXPLICIT_POLICY, | 
|  | Verify(leaf_none.get(), {root.get()}, {intermediate_require.get()}, | 
|  | /*crls=*/{})); | 
|  |  | 
|  | // A leaf can also set requireExplicitPolicy. | 
|  | EXPECT_EQ(X509_V_OK, | 
|  | Verify(leaf_require.get(), {root.get()}, {intermediate.get()}, | 
|  | /*crls=*/{}, /*flags=*/0)); | 
|  | EXPECT_EQ(X509_V_OK, Verify(leaf_require.get(), {root.get()}, | 
|  | {intermediate.get()}, /*crls=*/{}, | 
|  | /*flags=*/0, [&](X509_STORE_CTX *ctx) { | 
|  | set_policies(ctx, {oid1.get()}); | 
|  | })); | 
|  | EXPECT_EQ(X509_V_ERR_NO_EXPLICIT_POLICY, | 
|  | Verify(leaf_require.get(), {root.get()}, {intermediate.get()}, | 
|  | /*crls=*/{}, | 
|  | /*flags=*/0, [&](X509_STORE_CTX *ctx) { | 
|  | set_policies(ctx, {oid3.get()}); | 
|  | })); | 
|  |  | 
|  | // requireExplicitPolicy is a count of certificates to skip. If the value is | 
|  | // not zero by the end of the chain, it doesn't count. | 
|  | EXPECT_EQ(X509_V_ERR_NO_EXPLICIT_POLICY, | 
|  | Verify(leaf.get(), {root.get()}, {intermediate_require1.get()}, | 
|  | /*crls=*/{}, | 
|  | /*flags=*/0, [&](X509_STORE_CTX *ctx) { | 
|  | set_policies(ctx, {oid3.get()}); | 
|  | })); | 
|  | EXPECT_EQ(X509_V_OK, | 
|  | Verify(leaf.get(), {root.get()}, {intermediate_require2.get()}, | 
|  | /*crls=*/{}, | 
|  | /*flags=*/0, [&](X509_STORE_CTX *ctx) { | 
|  | set_policies(ctx, {oid3.get()}); | 
|  | })); | 
|  | EXPECT_EQ(X509_V_OK, | 
|  | Verify(leaf_require1.get(), {root.get()}, {intermediate.get()}, | 
|  | /*crls=*/{}, | 
|  | /*flags=*/0, [&](X509_STORE_CTX *ctx) { | 
|  | set_policies(ctx, {oid3.get()}); | 
|  | })); | 
|  |  | 
|  | // If multiple certificates specify the constraint, the more constrained value | 
|  | // wins. | 
|  | EXPECT_EQ( | 
|  | X509_V_ERR_NO_EXPLICIT_POLICY, | 
|  | Verify(leaf_require1.get(), {root.get()}, {intermediate_require1.get()}, | 
|  | /*crls=*/{}, | 
|  | /*flags=*/0, | 
|  | [&](X509_STORE_CTX *ctx) { set_policies(ctx, {oid3.get()}); })); | 
|  | EXPECT_EQ( | 
|  | X509_V_ERR_NO_EXPLICIT_POLICY, | 
|  | Verify(leaf_require.get(), {root.get()}, {intermediate_require2.get()}, | 
|  | /*crls=*/{}, | 
|  | /*flags=*/0, | 
|  | [&](X509_STORE_CTX *ctx) { set_policies(ctx, {oid3.get()}); })); | 
|  |  | 
|  | // An intermediate that requires an explicit policy, but then specifies no | 
|  | // policies should fail verification as a result. | 
|  | EXPECT_EQ(X509_V_ERR_NO_EXPLICIT_POLICY, | 
|  | Verify(leaf.get(), {root.get()}, | 
|  | {intermediate_require_no_policies.get()}, /*crls=*/{}, | 
|  | /*flags=*/0, [&](X509_STORE_CTX *ctx) { | 
|  | set_policies(ctx, {oid1.get()}); | 
|  | })); | 
|  |  | 
|  | // A constrained intermediate's policy extension has a duplicate policy, which | 
|  | // is invalid. Historically this, and the above case, leaked memory. | 
|  | EXPECT_EQ(X509_V_ERR_INVALID_POLICY_EXTENSION, | 
|  | Verify(leaf.get(), {root.get()}, | 
|  | {intermediate_require_duplicate.get()}, /*crls=*/{}, | 
|  | /*flags=*/0, [&](X509_STORE_CTX *ctx) { | 
|  | set_policies(ctx, {oid1.get()}); | 
|  | })); | 
|  |  | 
|  | // The leaf asserts anyPolicy, but the intermediate does not. The resulting | 
|  | // valid policies are the intersection. | 
|  | EXPECT_EQ( | 
|  | X509_V_OK, | 
|  | Verify(leaf_any.get(), {root.get()}, {intermediate.get()}, /*crls=*/{}, | 
|  | X509_V_FLAG_EXPLICIT_POLICY, | 
|  | [&](X509_STORE_CTX *ctx) { set_policies(ctx, {oid1.get()}); })); | 
|  | EXPECT_EQ( | 
|  | X509_V_ERR_NO_EXPLICIT_POLICY, | 
|  | Verify(leaf_any.get(), {root.get()}, {intermediate.get()}, /*crls=*/{}, | 
|  | X509_V_FLAG_EXPLICIT_POLICY, | 
|  | [&](X509_STORE_CTX *ctx) { set_policies(ctx, {oid3.get()}); })); | 
|  |  | 
|  | // The intermediate asserts anyPolicy, but the leaf does not. The resulting | 
|  | // valid policies are the intersection. | 
|  | EXPECT_EQ( | 
|  | X509_V_OK, | 
|  | Verify(leaf.get(), {root.get()}, {intermediate_any.get()}, /*crls=*/{}, | 
|  | X509_V_FLAG_EXPLICIT_POLICY, | 
|  | [&](X509_STORE_CTX *ctx) { set_policies(ctx, {oid1.get()}); })); | 
|  | EXPECT_EQ( | 
|  | X509_V_ERR_NO_EXPLICIT_POLICY, | 
|  | Verify(leaf.get(), {root.get()}, {intermediate_any.get()}, /*crls=*/{}, | 
|  | X509_V_FLAG_EXPLICIT_POLICY, | 
|  | [&](X509_STORE_CTX *ctx) { set_policies(ctx, {oid3.get()}); })); | 
|  |  | 
|  | // Both assert anyPolicy. All policies are valid. | 
|  | EXPECT_EQ( | 
|  | X509_V_OK, | 
|  | Verify(leaf_any.get(), {root.get()}, {intermediate_any.get()}, | 
|  | /*crls=*/{}, X509_V_FLAG_EXPLICIT_POLICY, | 
|  | [&](X509_STORE_CTX *ctx) { set_policies(ctx, {oid1.get()}); })); | 
|  | EXPECT_EQ( | 
|  | X509_V_OK, | 
|  | Verify(leaf_any.get(), {root.get()}, {intermediate_any.get()}, | 
|  | /*crls=*/{}, X509_V_FLAG_EXPLICIT_POLICY, | 
|  | [&](X509_STORE_CTX *ctx) { set_policies(ctx, {oid3.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()}); | 
|  | })); | 
|  |  | 
|  | for (bool use_any : {false, true}) { | 
|  | SCOPED_TRACE(use_any); | 
|  | X509 *cert = | 
|  | use_any ? intermediate_mapped_any.get() : intermediate_mapped.get(); | 
|  | // OID3 is mapped to {OID1, OID2}, which means OID1 and OID2 (or both) are | 
|  | // acceptable for OID3. | 
|  | EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {cert}, | 
|  | /*crls=*/{}, X509_V_FLAG_EXPLICIT_POLICY, | 
|  | [&](X509_STORE_CTX *ctx) { | 
|  | set_policies(ctx, {oid3.get()}); | 
|  | })); | 
|  | EXPECT_EQ(X509_V_OK, Verify(leaf_oid1.get(), {root.get()}, {cert}, | 
|  | /*crls=*/{}, X509_V_FLAG_EXPLICIT_POLICY, | 
|  | [&](X509_STORE_CTX *ctx) { | 
|  | set_policies(ctx, {oid3.get()}); | 
|  | })); | 
|  | EXPECT_EQ(X509_V_OK, Verify(leaf_oid2.get(), {root.get()}, {cert}, | 
|  | /*crls=*/{}, X509_V_FLAG_EXPLICIT_POLICY, | 
|  | [&](X509_STORE_CTX *ctx) { | 
|  | set_policies(ctx, {oid3.get()}); | 
|  | })); | 
|  |  | 
|  | // If the intermediate's policies were anyPolicy, OID3 at the leaf, despite | 
|  | // being mapped, is still acceptable as OID3 at the root. Despite the OID3 | 
|  | // having expected_policy_set = {OID1, OID2}, it can match the anyPolicy | 
|  | // node instead. | 
|  | // | 
|  | // If the intermediate's policies listed OIDs explicitly, OID3 at the leaf | 
|  | // is not acceptable as OID3 at the root. OID3 has expected_polciy_set = | 
|  | // {OID1, OID2} and no other node allows OID3. | 
|  | EXPECT_EQ( | 
|  | use_any ? X509_V_OK : X509_V_ERR_NO_EXPLICIT_POLICY, | 
|  | Verify(leaf_oid3.get(), {root.get()}, {cert}, | 
|  | /*crls=*/{}, X509_V_FLAG_EXPLICIT_POLICY, | 
|  | [&](X509_STORE_CTX *ctx) { set_policies(ctx, {oid3.get()}); })); | 
|  |  | 
|  | // If the intermediate's policies were anyPolicy, OID1 at the leaf is no | 
|  | // longer acceptable as OID1 at the root because policies only match | 
|  | // anyPolicy when they match no other policy. | 
|  | // | 
|  | // If the intermediate's policies listed OIDs explicitly, OID1 at the leaf | 
|  | // is acceptable as OID1 at the root because it will match both OID1 and | 
|  | // OID3 (mapped) policies. | 
|  | EXPECT_EQ( | 
|  | use_any ? X509_V_ERR_NO_EXPLICIT_POLICY : X509_V_OK, | 
|  | Verify(leaf_oid1.get(), {root.get()}, {cert}, | 
|  | /*crls=*/{}, X509_V_FLAG_EXPLICIT_POLICY, | 
|  | [&](X509_STORE_CTX *ctx) { set_policies(ctx, {oid1.get()}); })); | 
|  |  | 
|  | // All pairs of OID4 and OID5 are mapped together, so either can stand for | 
|  | // the other. | 
|  | EXPECT_EQ(X509_V_OK, Verify(leaf_oid4.get(), {root.get()}, {cert}, | 
|  | /*crls=*/{}, X509_V_FLAG_EXPLICIT_POLICY, | 
|  | [&](X509_STORE_CTX *ctx) { | 
|  | set_policies(ctx, {oid4.get()}); | 
|  | })); | 
|  | EXPECT_EQ(X509_V_OK, Verify(leaf_oid4.get(), {root.get()}, {cert}, | 
|  | /*crls=*/{}, X509_V_FLAG_EXPLICIT_POLICY, | 
|  | [&](X509_STORE_CTX *ctx) { | 
|  | set_policies(ctx, {oid5.get()}); | 
|  | })); | 
|  | EXPECT_EQ(X509_V_OK, Verify(leaf_oid5.get(), {root.get()}, {cert}, | 
|  | /*crls=*/{}, X509_V_FLAG_EXPLICIT_POLICY, | 
|  | [&](X509_STORE_CTX *ctx) { | 
|  | set_policies(ctx, {oid4.get()}); | 
|  | })); | 
|  | EXPECT_EQ(X509_V_OK, Verify(leaf_oid5.get(), {root.get()}, {cert}, | 
|  | /*crls=*/{}, X509_V_FLAG_EXPLICIT_POLICY, | 
|  | [&](X509_STORE_CTX *ctx) { | 
|  | set_policies(ctx, {oid5.get()}); | 
|  | })); | 
|  |  | 
|  | EXPECT_EQ(X509_V_OK, Verify(leaf_oid4.get(), {root.get()}, {cert}, | 
|  | /*crls=*/{}, X509_V_FLAG_EXPLICIT_POLICY, | 
|  | [&](X509_STORE_CTX *ctx) { | 
|  | set_policies(ctx, {oid4.get(), oid5.get()}); | 
|  | })); | 
|  | } | 
|  |  | 
|  | // Although |intermediate_mapped_oid3| contains many mappings, it only accepts | 
|  | // OID3. Nodes should not be created for the other mappings. | 
|  | EXPECT_EQ( | 
|  | X509_V_OK, | 
|  | Verify(leaf_oid1.get(), {root.get()}, {intermediate_mapped_oid3.get()}, | 
|  | /*crls=*/{}, X509_V_FLAG_EXPLICIT_POLICY, | 
|  | [&](X509_STORE_CTX *ctx) { set_policies(ctx, {oid3.get()}); })); | 
|  | EXPECT_EQ( | 
|  | X509_V_ERR_NO_EXPLICIT_POLICY, | 
|  | Verify(leaf_oid4.get(), {root.get()}, {intermediate_mapped_oid3.get()}, | 
|  | /*crls=*/{}, X509_V_FLAG_EXPLICIT_POLICY, | 
|  | [&](X509_STORE_CTX *ctx) { set_policies(ctx, {oid4.get()}); })); | 
|  |  | 
|  | // Policy mapping can be inhibited, either by the caller or a certificate in | 
|  | // the chain, in which case mapped policies are unassertable (apart from some | 
|  | // anyPolicy edge cases). | 
|  | EXPECT_EQ( | 
|  | X509_V_ERR_NO_EXPLICIT_POLICY, | 
|  | Verify(leaf_oid1.get(), {root.get()}, {intermediate_mapped_oid3.get()}, | 
|  | /*crls=*/{}, X509_V_FLAG_EXPLICIT_POLICY | X509_V_FLAG_INHIBIT_MAP, | 
|  | [&](X509_STORE_CTX *ctx) { set_policies(ctx, {oid3.get()}); })); | 
|  | EXPECT_EQ( | 
|  | X509_V_ERR_NO_EXPLICIT_POLICY, | 
|  | Verify(leaf_oid1.get(), {root2.get()}, | 
|  | {intermediate_mapped_oid3.get(), root_cross_inhibit_mapping.get()}, | 
|  | /*crls=*/{}, X509_V_FLAG_EXPLICIT_POLICY, | 
|  | [&](X509_STORE_CTX *ctx) { set_policies(ctx, {oid3.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<ASN1_OBJECT> oid1( | 
|  | OBJ_txt2obj("1.2.840.113554.4.1.72585.2.1", /*dont_search_names=*/1)); | 
|  | ASSERT_TRUE(oid1); | 
|  | bssl::UniquePtr<ASN1_OBJECT> oid2( | 
|  | OBJ_txt2obj("1.2.840.113554.4.1.72585.2.2", /*dont_search_names=*/1)); | 
|  | ASSERT_TRUE(oid2); | 
|  | bssl::UniquePtr<ASN1_OBJECT> 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<const ASN1_OBJECT *> oids) { | 
|  | X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(ctx); | 
|  | for (const ASN1_OBJECT *oid : oids) { | 
|  | bssl::UniquePtr<ASN1_OBJECT> 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<X509> root( | 
|  | CertFromPEM(GetTestData("crypto/x509/test/policy_root.pem").c_str())); | 
|  | ASSERT_TRUE(root); | 
|  | bssl::UniquePtr<X509> intermediate(CertFromPEM( | 
|  | GetTestData("crypto/x509/test/policy_intermediate.pem").c_str())); | 
|  | ASSERT_TRUE(intermediate); | 
|  | bssl::UniquePtr<X509> leaf( | 
|  | CertFromPEM(GetTestData("crypto/x509/test/policy_leaf.pem").c_str())); | 
|  | ASSERT_TRUE(leaf); | 
|  |  | 
|  | std::vector<std::thread> 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<X509> root( | 
|  | CertFromPEM(GetTestData("crypto/x509/test/policy_root.pem").c_str())); | 
|  | ASSERT_TRUE(root); | 
|  | bssl::UniquePtr<X509> intermediate(CertFromPEM( | 
|  | GetTestData("crypto/x509/test/policy_intermediate.pem").c_str())); | 
|  | ASSERT_TRUE(intermediate); | 
|  | bssl::UniquePtr<X509> leaf_invalid(CertFromPEM( | 
|  | GetTestData("crypto/x509/test/policy_leaf_invalid.pem").c_str())); | 
|  | ASSERT_TRUE(leaf_invalid); | 
|  |  | 
|  |  | 
|  | std::vector<std::thread> 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<uint8_t> 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> conf; | 
|  | if (t.conf != nullptr) { | 
|  | conf.reset(NCONF_new(nullptr)); | 
|  | ASSERT_TRUE(conf); | 
|  | bssl::UniquePtr<BIO> 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<X509_EXTENSION> 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<uint8_t> 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<uint8_t> free_der(der); | 
|  | EXPECT_EQ(Bytes(t.expected), Bytes(der, len)); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST(X509Test, AddUnserializableExtension) { | 
|  | bssl::UniquePtr<EVP_PKEY> key = PrivateKeyFromPEM(kP256Key); | 
|  | ASSERT_TRUE(key); | 
|  | bssl::UniquePtr<X509> x509 = | 
|  | MakeTestCert("Issuer", "Subject", key.get(), /*is_ca=*/true); | 
|  | ASSERT_TRUE(x509); | 
|  | bssl::UniquePtr<X509_EXTENSION> 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<X509_NAME> 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<const uint8_t *>(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<const uint8_t *>(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<uint8_t> 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, 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<uint8_t> { | 
|  | 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<const uint8_t *>(contents.data()), | 
|  | contents.size()) || | 
|  | !CBB_flush(cbb.get())) { | 
|  | ADD_FAILURE() << "Could not encode name"; | 
|  | return {}; | 
|  | }; | 
|  | return std::vector<uint8_t>(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<X509_NAME> 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<uint8_t> 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<const uint8_t *>(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_UTF8STRING | CBS_ASN1_CONSTRUCTED, ""}, | 
|  | {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<X509_NAME> 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<X509_NAME> name(X509_NAME_new()); | 
|  | ASSERT_TRUE(name); | 
|  | ASSERT_TRUE(X509_NAME_add_entry_by_NID( | 
|  | name.get(), NID_commonName, test.content_type, | 
|  | reinterpret_cast<const uint8_t *>(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, ParamInheritance) { | 
|  | // |X509_VERIFY_PARAM_inherit| with both unset. | 
|  | { | 
|  | bssl::UniquePtr<X509_VERIFY_PARAM> dest(X509_VERIFY_PARAM_new()); | 
|  | ASSERT_TRUE(dest); | 
|  | bssl::UniquePtr<X509_VERIFY_PARAM> 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<X509_VERIFY_PARAM> dest(X509_VERIFY_PARAM_new()); | 
|  | ASSERT_TRUE(dest); | 
|  | bssl::UniquePtr<X509_VERIFY_PARAM> 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<X509_VERIFY_PARAM> dest(X509_VERIFY_PARAM_new()); | 
|  | ASSERT_TRUE(dest); | 
|  | bssl::UniquePtr<X509_VERIFY_PARAM> 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<X509_VERIFY_PARAM> dest(X509_VERIFY_PARAM_new()); | 
|  | ASSERT_TRUE(dest); | 
|  | bssl::UniquePtr<X509_VERIFY_PARAM> 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<X509_VERIFY_PARAM> dest(X509_VERIFY_PARAM_new()); | 
|  | ASSERT_TRUE(dest); | 
|  | bssl::UniquePtr<X509_VERIFY_PARAM> 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<X509_VERIFY_PARAM> dest(X509_VERIFY_PARAM_new()); | 
|  | ASSERT_TRUE(dest); | 
|  | bssl::UniquePtr<X509_VERIFY_PARAM> 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<X509_VERIFY_PARAM> dest(X509_VERIFY_PARAM_new()); | 
|  | ASSERT_TRUE(dest); | 
|  | bssl::UniquePtr<X509_VERIFY_PARAM> 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<X509_VERIFY_PARAM> dest(X509_VERIFY_PARAM_new()); | 
|  | ASSERT_TRUE(dest); | 
|  | bssl::UniquePtr<X509_VERIFY_PARAM> 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<EVP_PKEY> key = PrivateKeyFromPEM(kP256Key); | 
|  | ASSERT_TRUE(key); | 
|  |  | 
|  | X509_PUBKEY *pub = nullptr; | 
|  | ASSERT_TRUE(X509_PUBKEY_set(&pub, key.get())); | 
|  | bssl::UniquePtr<X509_PUBKEY> free_pub(pub); | 
|  |  | 
|  | bssl::UniquePtr<EVP_PKEY> 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); | 
|  | } | 
|  |  | 
|  | // Tests some unusual behavior in |X509_STORE_CTX_set_purpose| and | 
|  | // |X509_STORE_CTX_set_trust|. | 
|  | TEST(X509Test, ContextTrustAndPurpose) { | 
|  | bssl::UniquePtr<X509_STORE> store(X509_STORE_new()); | 
|  | ASSERT_TRUE(store); | 
|  | bssl::UniquePtr<X509> leaf(CertFromPEM(kLeafPEM)); | 
|  | ASSERT_TRUE(leaf); | 
|  |  | 
|  | bssl::UniquePtr<X509_STORE_CTX> 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<EVP_PKEY> key = PrivateKeyFromPEM(kP256Key); | 
|  | ASSERT_TRUE(key); | 
|  |  | 
|  | struct { | 
|  | int purpose; | 
|  | int eku_nid; | 
|  | std::vector<KeyUsage> 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<X509> root = | 
|  | MakeTestCert("Root", "Root", key.get(), /*is_ca=*/true); | 
|  | ASSERT_TRUE(root); | 
|  | ASSERT_TRUE(X509_sign(root.get(), key.get(), EVP_sha256())); | 
|  |  | 
|  | bssl::UniquePtr<X509> intermediate = | 
|  | MakeTestCert("Root", "Intermediate", key.get(), /*is_ca=*/true); | 
|  | ASSERT_TRUE(intermediate); | 
|  | ASSERT_TRUE(X509_sign(intermediate.get(), key.get(), EVP_sha256())); | 
|  |  | 
|  | bssl::UniquePtr<X509> 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<int>(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<KeyUsage> usages; | 
|  | for (int i = 0; i < 10; i++) { | 
|  | auto k = static_cast<KeyUsage>(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<KeyUsage>(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<X509> normal; | 
|  | bssl::UniquePtr<X509> trusted_server, distrusted_server; | 
|  | bssl::UniquePtr<X509> trusted_any, distrusted_any; | 
|  | }; | 
|  | 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 some APIs that rust-openssl uses to look up purposes by name. | 
|  | TEST(X509Test, PurposeByShortName) { | 
|  | int idx = X509_PURPOSE_get_by_sname("sslserver"); | 
|  | ASSERT_NE(idx, -1); | 
|  | const X509_PURPOSE *purpose = X509_PURPOSE_get0(idx); | 
|  | ASSERT_TRUE(purpose); | 
|  | EXPECT_EQ(X509_PURPOSE_get_id(purpose), X509_PURPOSE_SSL_SERVER); | 
|  | } | 
|  |  | 
|  | TEST(X509Test, CriticalExtension) { | 
|  | bssl::UniquePtr<EVP_PKEY> key = PrivateKeyFromPEM(kP256Key); | 
|  | ASSERT_TRUE(key); | 
|  |  | 
|  | bssl::UniquePtr<X509> root = | 
|  | MakeTestCert("Root", "Root", key.get(), /*is_ca=*/true); | 
|  | ASSERT_TRUE(root); | 
|  | ASSERT_TRUE(X509_sign(root.get(), key.get(), EVP_sha256())); | 
|  |  | 
|  | // Issue a certificate with a critical Netscape certificate type extension. We | 
|  | // do not recognize this extension, so this certificate should be rejected. | 
|  | bssl::UniquePtr<X509> leaf = | 
|  | MakeTestCert("Root", "Leaf", key.get(), /*is_ca=*/false); | 
|  | ASSERT_TRUE(leaf); | 
|  | bssl::UniquePtr<ASN1_BIT_STRING> cert_type(ASN1_BIT_STRING_new()); | 
|  | ASSERT_TRUE(cert_type); | 
|  | ASSERT_TRUE(ASN1_BIT_STRING_set_bit(cert_type.get(), /*n=*/0, /*value=*/1)); | 
|  | ASSERT_TRUE(X509_add1_ext_i2d(leaf.get(), NID_netscape_cert_type, | 
|  | cert_type.get(), | 
|  | /*crit=*/1, /*flags=*/0)); | 
|  | ASSERT_TRUE(X509_sign(leaf.get(), key.get(), EVP_sha256())); | 
|  |  | 
|  | EXPECT_EQ(X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION, | 
|  | Verify(leaf.get(), {root.get()}, {}, {})); | 
|  | EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {}, {}, | 
|  | X509_V_FLAG_IGNORE_CRITICAL)); | 
|  | } | 
|  |  | 
|  | enum NameHash { kOldHash, kNewHash }; | 
|  |  | 
|  | // TemporaryHashDir constructs a temporary directory in the format of | 
|  | // |X509_LOOKUP_hash_dir|. | 
|  | class TemporaryHashDir { | 
|  | public: | 
|  | explicit TemporaryHashDir(int type) : type_(type) {} | 
|  |  | 
|  | bool Init() { return dir_.Init(); } | 
|  | const std::string &path() const { return dir_.path(); } | 
|  |  | 
|  | size_t num_cert_hashes() const { return next_cert_.size(); } | 
|  | size_t num_crl_hashes() const { return next_crl_.size(); } | 
|  |  | 
|  | bool AddCert(X509 *x509, NameHash name_hash) { | 
|  | return AddCertWithHash(HashName(name_hash, X509_get_subject_name(x509)), | 
|  | x509); | 
|  | } | 
|  |  | 
|  | bool AddCRL(X509_CRL *crl, NameHash name_hash) { | 
|  | return AddCRLWithHash(HashName(name_hash, X509_CRL_get_issuer(crl)), crl); | 
|  | } | 
|  |  | 
|  | bool AddCertWithHash(uint32_t hash, X509 *cert) { | 
|  | std::vector<uint8_t> data = EncodeCert(cert); | 
|  | if (data.empty()) { | 
|  | return false; | 
|  | } | 
|  | auto &num = next_cert_[hash]; | 
|  | char path[32]; | 
|  | snprintf(path, sizeof(path), "%08x.%d", hash, num); | 
|  | if (!dir_.AddFile(path, data)) { | 
|  | return false; | 
|  | } | 
|  | num++; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool AddCRLWithHash(uint32_t hash, X509_CRL *crl) { | 
|  | std::vector<uint8_t> data = EncodeCRL(crl); | 
|  | if (data.empty()) { | 
|  | return false; | 
|  | } | 
|  | auto &num = next_crl_[hash]; | 
|  | char path[32]; | 
|  | snprintf(path, sizeof(path), "%08x.r%d", hash, num); | 
|  | if (!dir_.AddFile(path, data)) { | 
|  | return false; | 
|  | } | 
|  | num++; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool ReplaceLastCRL(X509_CRL *crl, NameHash name_hash) { | 
|  | uint32_t hash = HashName(name_hash, X509_CRL_get_issuer(crl)); | 
|  | auto iter = next_crl_.find(hash); | 
|  | if (iter == next_crl_.end()) { | 
|  | return false; | 
|  | } | 
|  | std::vector<uint8_t> data = EncodeCRL(crl); | 
|  | if (data.empty()) { | 
|  | return false; | 
|  | } | 
|  | char path[32]; | 
|  | snprintf(path, sizeof(path), "%08x.r%d", hash, iter->second - 1); | 
|  | return dir_.AddFile(path, data); | 
|  | } | 
|  |  | 
|  | private: | 
|  | static uint32_t HashName(NameHash name_hash, X509_NAME *name) { | 
|  | return name_hash == kOldHash ? X509_NAME_hash_old(name) | 
|  | : X509_NAME_hash(name); | 
|  | } | 
|  |  | 
|  | std::vector<uint8_t> EncodeCert(X509 *cert) { | 
|  | if (type_ == X509_FILETYPE_ASN1) { | 
|  | uint8_t *der = nullptr; | 
|  | int der_len = i2d_X509(cert, &der); | 
|  | if (der_len < 0) { | 
|  | return {}; | 
|  | } | 
|  | bssl::UniquePtr<uint8_t> free_der(der); | 
|  | return std::vector<uint8_t>(der, der + der_len); | 
|  | } | 
|  |  | 
|  | bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_mem())); | 
|  | const uint8_t *pem; | 
|  | size_t pem_len; | 
|  | if (bio == nullptr ||  // | 
|  | !PEM_write_bio_X509(bio.get(), cert) || | 
|  | !BIO_mem_contents(bio.get(), &pem, &pem_len)) { | 
|  | return {}; | 
|  | } | 
|  | return std::vector<uint8_t>(pem, pem + pem_len); | 
|  | } | 
|  |  | 
|  | std::vector<uint8_t> EncodeCRL(X509_CRL *crl) { | 
|  | if (type_ == X509_FILETYPE_ASN1) { | 
|  | uint8_t *der = nullptr; | 
|  | int der_len = i2d_X509_CRL(crl, &der); | 
|  | if (der_len < 0) { | 
|  | return {}; | 
|  | } | 
|  | bssl::UniquePtr<uint8_t> free_der(der); | 
|  | return std::vector<uint8_t>(der, der + der_len); | 
|  | } | 
|  |  | 
|  | bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_mem())); | 
|  | const uint8_t *pem; | 
|  | size_t pem_len; | 
|  | if (bio == nullptr ||  // | 
|  | !PEM_write_bio_X509_CRL(bio.get(), crl) || | 
|  | !BIO_mem_contents(bio.get(), &pem, &pem_len)) { | 
|  | return {}; | 
|  | } | 
|  | return std::vector<uint8_t>(pem, pem + pem_len); | 
|  | } | 
|  |  | 
|  | int type_; | 
|  | bssl::TemporaryDirectory dir_; | 
|  | std::map<uint32_t, int> next_cert_; | 
|  | std::map<uint32_t, int> next_crl_; | 
|  | }; | 
|  |  | 
|  | // TODO(davidben): Also test CRL handling. There are some interesting behaviors | 
|  | // in here. | 
|  | TEST(X509Test, DirHash) { | 
|  | if (bssl::SkipTempFileTests()) { | 
|  | GTEST_SKIP(); | 
|  | } | 
|  |  | 
|  | bssl::UniquePtr<EVP_PKEY> key = PrivateKeyFromPEM(kP256Key); | 
|  | ASSERT_TRUE(key); | 
|  |  | 
|  | // Test both formats. | 
|  | for (int type : {X509_FILETYPE_PEM, X509_FILETYPE_ASN1}) { | 
|  | SCOPED_TRACE(type); | 
|  |  | 
|  | // Generate some roots and fill a directory with OpenSSL's directory hash | 
|  | // format. The hash depends only on the name, so we do not need to | 
|  | // pre-generate the certificates. Test both DER and PEM. | 
|  | TemporaryHashDir dir(type); | 
|  | ASSERT_TRUE(dir.Init()); | 
|  |  | 
|  | auto add_root = [&](const std::string &name, NameHash name_hash) -> bool { | 
|  | bssl::UniquePtr<X509> ca = | 
|  | MakeTestCert(name.c_str(), name.c_str(), key.get(), /*is_ca=*/true); | 
|  | if (ca == nullptr || !X509_sign(ca.get(), key.get(), EVP_sha256())) { | 
|  | return false; | 
|  | } | 
|  | return dir.AddCert(ca.get(), name_hash); | 
|  | }; | 
|  |  | 
|  | auto issue_crl = | 
|  | [&](const std::string &name, int this_update_offset_day, | 
|  | const std::vector<uint64_t> &serials) -> bssl::UniquePtr<X509_CRL> { | 
|  | bssl::UniquePtr<X509_CRL> crl = MakeTestCRL( | 
|  | name.c_str(), this_update_offset_day, /*next_update_offset_day=*/1); | 
|  | if (crl == nullptr) { | 
|  | return nullptr; | 
|  | } | 
|  | for (uint64_t serial : serials) { | 
|  | // The revocation time does not matter for this test. Pretend the | 
|  | // certificate was just revoked. | 
|  | if (!AddRevokedSerialU64(crl.get(), serial, | 
|  | /*offset_day=*/this_update_offset_day)) { | 
|  | return nullptr; | 
|  | } | 
|  | } | 
|  | if (!X509_CRL_sign(crl.get(), key.get(), EVP_sha256())) { | 
|  | return nullptr; | 
|  | } | 
|  | return crl; | 
|  | }; | 
|  |  | 
|  | auto add_crl = [&](const std::string &name, NameHash name_hash, | 
|  | int this_update_offset_day, | 
|  | const std::vector<uint64_t> &serials) -> bool { | 
|  | bssl::UniquePtr<X509_CRL> crl = | 
|  | issue_crl(name, this_update_offset_day, serials); | 
|  | return crl != nullptr && dir.AddCRL(crl.get(), name_hash); | 
|  | }; | 
|  |  | 
|  | std::string ca1 = "Test CA 1"; | 
|  | ASSERT_TRUE(add_root(ca1, kNewHash)); | 
|  |  | 
|  | std::string ca2 = "Test CA 2"; | 
|  | ASSERT_TRUE(add_root(ca2, kOldHash)); | 
|  |  | 
|  | // Install CA 3 at its new hash. CA 3's name is not canonical, but the new | 
|  | // hash runs after canonicalization, so OpenSSL should be able to find it. | 
|  | std::string ca3 = "Test CA 3"; | 
|  | std::string ca3_noncanonical = "   test   ca   3   "; | 
|  | ASSERT_TRUE(add_root(ca3_noncanonical, kNewHash)); | 
|  |  | 
|  | // These two CAs collide under |X509_NAME_hash|. | 
|  | std::string collide_name1 = "Test CA 1191514847"; | 
|  | std::string collide_name2 = "Test CA 1570301806"; | 
|  | size_t num_cert_hashes = dir.num_cert_hashes(); | 
|  | ASSERT_TRUE(add_root(collide_name1, kNewHash)); | 
|  | EXPECT_EQ(dir.num_cert_hashes(), num_cert_hashes + 1); | 
|  | ASSERT_TRUE(add_root(collide_name2, kNewHash)); | 
|  | EXPECT_EQ(dir.num_cert_hashes(), num_cert_hashes + 1); | 
|  |  | 
|  | // These two CAs collide under |X509_NAME_hash_old|. | 
|  | std::string old_collide_name1 = "Test CA 1069881739"; | 
|  | std::string old_collide_name2 = "Test CA 940754110"; | 
|  | num_cert_hashes = dir.num_cert_hashes(); | 
|  | ASSERT_TRUE(add_root(old_collide_name1, kOldHash)); | 
|  | EXPECT_EQ(dir.num_cert_hashes(), num_cert_hashes + 1); | 
|  | ASSERT_TRUE(add_root(old_collide_name2, kOldHash)); | 
|  | EXPECT_EQ(dir.num_cert_hashes(), num_cert_hashes + 1); | 
|  |  | 
|  | // Make an |X509_STORE| that gets CAs from |dir|. | 
|  | bssl::UniquePtr<X509_STORE> store(X509_STORE_new()); | 
|  | ASSERT_TRUE(store); | 
|  | X509_LOOKUP *lookup = | 
|  | X509_STORE_add_lookup(store.get(), X509_LOOKUP_hash_dir()); | 
|  | ASSERT_TRUE(lookup); | 
|  | ASSERT_TRUE(X509_LOOKUP_add_dir(lookup, dir.path().c_str(), type)); | 
|  |  | 
|  | auto test_issuer_flags = [&](const std::string &issuer, uint64_t serial, | 
|  | unsigned long flags) -> int { | 
|  | bssl::UniquePtr<X509> cert = | 
|  | MakeTestCert(issuer.c_str(), "Leaf", key.get(), /*is_ca=*/false); | 
|  | bssl::UniquePtr<ASN1_INTEGER> serial_asn1(ASN1_INTEGER_new()); | 
|  | if (cert == nullptr || serial_asn1 == nullptr || | 
|  | !ASN1_INTEGER_set_uint64(serial_asn1.get(), serial) || | 
|  | !X509_set_serialNumber(cert.get(), serial_asn1.get()) || | 
|  | !X509_sign(cert.get(), key.get(), EVP_sha256())) { | 
|  | return X509_V_ERR_UNSPECIFIED; | 
|  | } | 
|  |  | 
|  | bssl::UniquePtr<X509_STORE_CTX> ctx(X509_STORE_CTX_new()); | 
|  | if (ctx == nullptr || | 
|  | !X509_STORE_CTX_init(ctx.get(), store.get(), cert.get(), | 
|  | /*chain=*/nullptr)) { | 
|  | return X509_V_ERR_UNSPECIFIED; | 
|  | } | 
|  | X509_STORE_CTX_set_flags(ctx.get(), flags); | 
|  | X509_STORE_CTX_set_time_posix(ctx.get(), /*flags=*/0, kReferenceTime); | 
|  |  | 
|  | return X509_verify_cert(ctx.get()) ? X509_V_OK | 
|  | : X509_STORE_CTX_get_error(ctx.get()); | 
|  | }; | 
|  |  | 
|  | auto test_issuer = [&](const std::string &issuer) -> int { | 
|  | return test_issuer_flags(issuer, /*serial=*/0, /*flags=*/0); | 
|  | }; | 
|  | auto test_issuer_crl = [&](const std::string &issuer, | 
|  | uint64_t serial) -> int { | 
|  | return test_issuer_flags(issuer, serial, X509_V_FLAG_CRL_CHECK); | 
|  | }; | 
|  |  | 
|  | // All these roots are in the store and should be found. Although Test CA | 
|  | // 3 was installed under a non-canonical name, the new hash accounts for | 
|  | // this. | 
|  | EXPECT_EQ(X509_V_OK, test_issuer(ca1)); | 
|  | EXPECT_EQ(X509_V_OK, test_issuer(ca2)); | 
|  | EXPECT_EQ(X509_V_OK, test_issuer(ca3)); | 
|  | EXPECT_EQ(X509_V_OK, test_issuer(collide_name1)); | 
|  | EXPECT_EQ(X509_V_OK, test_issuer(collide_name2)); | 
|  | EXPECT_EQ(X509_V_OK, test_issuer(old_collide_name1)); | 
|  | EXPECT_EQ(X509_V_OK, test_issuer(old_collide_name2)); | 
|  |  | 
|  | // Repeat the tests. This time it will pick up the certificate from the | 
|  | // cache. | 
|  | EXPECT_EQ(X509_V_OK, test_issuer(ca1)); | 
|  | EXPECT_EQ(X509_V_OK, test_issuer(ca2)); | 
|  | EXPECT_EQ(X509_V_OK, test_issuer(ca3)); | 
|  | EXPECT_EQ(X509_V_OK, test_issuer(collide_name1)); | 
|  | EXPECT_EQ(X509_V_OK, test_issuer(collide_name2)); | 
|  | EXPECT_EQ(X509_V_OK, test_issuer(old_collide_name1)); | 
|  | EXPECT_EQ(X509_V_OK, test_issuer(old_collide_name2)); | 
|  |  | 
|  | // Test a certificate not in the store. | 
|  | ERR_clear_error(); | 
|  | EXPECT_EQ(X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, | 
|  | test_issuer("Not In Store")); | 
|  |  | 
|  | // Although, internally, this hits the filesystem and finds that a file does | 
|  | // not exist, there should not be anything on the error queue about a | 
|  | // missing file. |X509_verify_cert| generally does not use the error queue, | 
|  | // so it will be empty. See https://crbug.com/boringssl/708. | 
|  | EXPECT_EQ(ERR_get_error(), 0u); | 
|  |  | 
|  | // Test CRL handling. First, if we cannot find a CRL, verification will | 
|  | // fail. | 
|  | // | 
|  | // TODO(crbug.com/boringssl/690): We should test both the old and new hash, | 
|  | // but the CRL reloading process does not work for the old hash due to a | 
|  | // bug. It notices the cached old CRL, mistakes it for something loaded via | 
|  | // the new hash, and never bothers checking the old hash. | 
|  | EXPECT_EQ(X509_V_ERR_UNABLE_TO_GET_CRL, | 
|  | test_issuer_crl(collide_name1, /*serial=*/1)); | 
|  |  | 
|  | // Install an empty CRL. Verification should now succeed. | 
|  | ASSERT_TRUE(add_crl(collide_name1, kNewHash, | 
|  | /*this_update_offset_day=*/-10, /*serials=*/{})); | 
|  | EXPECT_EQ(X509_V_OK, test_issuer_crl(collide_name1, /*serial=*/1)); | 
|  |  | 
|  | // Verify again. Unlike roots, which are cached, this will query the | 
|  | // directory again. | 
|  | EXPECT_EQ(X509_V_OK, test_issuer_crl(collide_name1, /*serial=*/1)); | 
|  |  | 
|  | // The extra query is so that a newer CRL is picked up, at an incrementing | 
|  | // number. This feature is less useful than it sounds because all CRLs are | 
|  | // persistently cached. | 
|  | ASSERT_TRUE(add_crl(collide_name1, kNewHash, | 
|  | /*this_update_offset_day=*/-9, /*serials=*/{1})); | 
|  | EXPECT_EQ(X509_V_ERR_CERT_REVOKED, | 
|  | test_issuer_crl(collide_name1, /*serial=*/1)); | 
|  |  | 
|  | // Serial number 2 is not revoked. | 
|  | EXPECT_EQ(X509_V_OK, test_issuer_crl(collide_name1, /*serial=*/2)); | 
|  |  | 
|  | // A new CRL at an already loaded name is ignored because OpenSSL skips | 
|  | // loading the older ones and relies on them being persistently cached in | 
|  | // memory. | 
|  | // | 
|  | // TODO(crbug.com/boringssl/690): This behavior is almost certainly not what | 
|  | // anyone wants. Rework this. | 
|  | bssl::UniquePtr<X509_CRL> crl = issue_crl( | 
|  | collide_name1, /*this_update_offset_day=*/-8, /*serials=*/{1, 2}); | 
|  | ASSERT_TRUE(crl); | 
|  | ASSERT_TRUE(dir.ReplaceLastCRL(crl.get(), kNewHash)); | 
|  | EXPECT_EQ(X509_V_OK, test_issuer_crl(collide_name1, /*serial=*/2)); | 
|  |  | 
|  | // If there are many new CRLs, they are all loaded and the newest is wins. | 
|  | ASSERT_TRUE(add_crl(collide_name1, kNewHash, | 
|  | /*this_update_offset_day=*/-7, /*serials=*/{1, 2})); | 
|  | ASSERT_TRUE(add_crl(collide_name1, kNewHash, | 
|  | /*this_update_offset_day=*/-5, /*serials=*/{1, 2, 3})); | 
|  | ASSERT_TRUE(add_crl(collide_name1, kNewHash, | 
|  | /*this_update_offset_day=*/-6, /*serials=*/{1, 2})); | 
|  |  | 
|  | // r3 should have won, which revokes all three serials: | 
|  | EXPECT_EQ(X509_V_ERR_CERT_REVOKED, | 
|  | test_issuer_crl(collide_name1, /*serial=*/1)); | 
|  | EXPECT_EQ(X509_V_ERR_CERT_REVOKED, | 
|  | test_issuer_crl(collide_name1, /*serial=*/2)); | 
|  | EXPECT_EQ(X509_V_ERR_CERT_REVOKED, | 
|  | test_issuer_crl(collide_name1, /*serial=*/3)); | 
|  |  | 
|  | // If the new CRL is older than a previously loaded one, it is ignored. | 
|  | ASSERT_TRUE(add_crl(collide_name1, kNewHash, | 
|  | /*this_update_offset_day=*/-100, /*serials=*/{})); | 
|  |  | 
|  | // Finally, test hash collisions. The internal book-keeping for where to | 
|  | // start loading should be compatible with a second CA whose hash collides. | 
|  | EXPECT_EQ(X509_V_ERR_UNABLE_TO_GET_CRL, | 
|  | test_issuer_crl(collide_name2, /*serial=*/1)); | 
|  | EXPECT_EQ(X509_V_ERR_UNABLE_TO_GET_CRL, | 
|  | test_issuer_crl(collide_name2, /*serial=*/2)); | 
|  | ASSERT_TRUE(add_crl(collide_name2, kNewHash, | 
|  | /*this_update_offset_day=*/-10, /*serials=*/{1})); | 
|  | EXPECT_EQ(X509_V_ERR_CERT_REVOKED, | 
|  | test_issuer_crl(collide_name2, /*serial=*/1)); | 
|  | EXPECT_EQ(X509_V_OK, test_issuer_crl(collide_name2, /*serial=*/2)); | 
|  |  | 
|  | // Confirm all CRLs we added had the same hash. | 
|  | EXPECT_EQ(dir.num_crl_hashes(), 1u); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Test that two directory hash paths are treated as a sequence of paths, | 
|  | // separated by a separator. | 
|  | TEST(X509Test, DirHashSeparator) { | 
|  | #if defined(OPENSSL_WINDOWS) | 
|  | const char kSeparator = ';'; | 
|  | #else | 
|  | const char kSeparator = ':'; | 
|  | #endif | 
|  |  | 
|  | if (bssl::SkipTempFileTests()) { | 
|  | GTEST_SKIP(); | 
|  | } | 
|  |  | 
|  | bssl::UniquePtr<EVP_PKEY> key = PrivateKeyFromPEM(kP256Key); | 
|  | ASSERT_TRUE(key); | 
|  |  | 
|  | // Make two directories and place one CA in each. | 
|  | TemporaryHashDir dir1(X509_FILETYPE_PEM), dir2(X509_FILETYPE_PEM); | 
|  | ASSERT_TRUE(dir1.Init()); | 
|  | ASSERT_TRUE(dir2.Init()); | 
|  |  | 
|  | bssl::UniquePtr<X509> ca1 = | 
|  | MakeTestCert("Test CA 1", "Test CA 1", key.get(), /*is_ca=*/true); | 
|  | ASSERT_TRUE(ca1); | 
|  | ASSERT_TRUE(X509_sign(ca1.get(), key.get(), EVP_sha256())); | 
|  | ASSERT_TRUE(dir1.AddCert(ca1.get(), kNewHash)); | 
|  |  | 
|  | bssl::UniquePtr<X509> ca2 = | 
|  | MakeTestCert("Test CA 2", "Test CA 2", key.get(), /*is_ca=*/true); | 
|  | ASSERT_TRUE(ca2); | 
|  | ASSERT_TRUE(X509_sign(ca2.get(), key.get(), EVP_sha256())); | 
|  | ASSERT_TRUE(dir1.AddCert(ca2.get(), kNewHash)); | 
|  |  | 
|  | // Make an |X509_STORE| that gets CAs from |dir1| and |dir2|. | 
|  | bssl::UniquePtr<X509_STORE> store(X509_STORE_new()); | 
|  | ASSERT_TRUE(store); | 
|  | std::string paths = dir1.path() + kSeparator + dir2.path(); | 
|  | ASSERT_TRUE( | 
|  | X509_STORE_load_locations(store.get(), /*file=*/nullptr, paths.c_str())); | 
|  |  | 
|  | // Both CAs should work. | 
|  | { | 
|  | bssl::UniquePtr<X509> cert = | 
|  | MakeTestCert("Test CA 1", "Leaf", key.get(), /*is_ca=*/false); | 
|  | ASSERT_TRUE(cert); | 
|  | ASSERT_TRUE(X509_sign(cert.get(), key.get(), EVP_sha256())); | 
|  | bssl::UniquePtr<X509_STORE_CTX> ctx(X509_STORE_CTX_new()); | 
|  | ASSERT_TRUE(ctx); | 
|  | ASSERT_TRUE(X509_STORE_CTX_init(ctx.get(), store.get(), cert.get(), | 
|  | /*chain=*/nullptr)); | 
|  | X509_STORE_CTX_set_time_posix(ctx.get(), /*flags=*/0, kReferenceTime); | 
|  | EXPECT_TRUE(X509_verify_cert(ctx.get())) | 
|  | << X509_verify_cert_error_string(X509_STORE_CTX_get_error(ctx.get())); | 
|  | } | 
|  |  | 
|  | { | 
|  | bssl::UniquePtr<X509> cert = | 
|  | MakeTestCert("Test CA 2", "Leaf", key.get(), /*is_ca=*/false); | 
|  | ASSERT_TRUE(cert); | 
|  | ASSERT_TRUE(X509_sign(cert.get(), key.get(), EVP_sha256())); | 
|  | bssl::UniquePtr<X509_STORE_CTX> ctx(X509_STORE_CTX_new()); | 
|  | ASSERT_TRUE(ctx); | 
|  | ASSERT_TRUE(X509_STORE_CTX_init(ctx.get(), store.get(), cert.get(), | 
|  | /*chain=*/nullptr)); | 
|  | X509_STORE_CTX_set_time_posix(ctx.get(), /*flags=*/0, kReferenceTime); | 
|  | EXPECT_TRUE(X509_verify_cert(ctx.get())) | 
|  | << X509_verify_cert_error_string(X509_STORE_CTX_get_error(ctx.get())); | 
|  | } | 
|  | } | 
|  |  | 
|  | #if defined(OPENSSL_THREADS) | 
|  | // Test that directory hash lookup is thread-safe. | 
|  | TEST(X509Test, DirHashThreads) { | 
|  | if (bssl::SkipTempFileTests()) { | 
|  | GTEST_SKIP(); | 
|  | } | 
|  |  | 
|  | bssl::UniquePtr<EVP_PKEY> key = PrivateKeyFromPEM(kP256Key); | 
|  | ASSERT_TRUE(key); | 
|  |  | 
|  | // Generate some roots and fill a directory with OpenSSL's directory hash | 
|  | // format. The hash depends only on the name, so we do not need to | 
|  | // pre-generate the certificates. Test both DER and PEM. | 
|  | TemporaryHashDir dir(X509_FILETYPE_PEM); | 
|  | ASSERT_TRUE(dir.Init()); | 
|  |  | 
|  | auto add_root = [&](const std::string &name, NameHash name_hash) -> bool { | 
|  | bssl::UniquePtr<X509> ca = | 
|  | MakeTestCert(name.c_str(), name.c_str(), key.get(), /*is_ca=*/true); | 
|  | return ca != nullptr &&  // | 
|  | X509_sign(ca.get(), key.get(), EVP_sha256()) && | 
|  | dir.AddCert(ca.get(), name_hash); | 
|  | }; | 
|  |  | 
|  | auto issue_cert = [&](const std::string &issuer) -> bssl::UniquePtr<X509> { | 
|  | bssl::UniquePtr<X509> cert = | 
|  | MakeTestCert(issuer.c_str(), "Leaf", key.get(), /*is_ca=*/false); | 
|  | if (cert == nullptr || !X509_sign(cert.get(), key.get(), EVP_sha256())) { | 
|  | return nullptr; | 
|  | } | 
|  | return cert; | 
|  | }; | 
|  |  | 
|  | auto add_crl = [&](const std::string &name, int this_update_offset_day, | 
|  | NameHash name_hash) -> bool { | 
|  | bssl::UniquePtr<X509_CRL> crl = MakeTestCRL( | 
|  | name.c_str(), this_update_offset_day, /*next_update_offset_day=*/1); | 
|  | return crl != nullptr && | 
|  | X509_CRL_sign(crl.get(), key.get(), EVP_sha256()) && | 
|  | dir.AddCRL(crl.get(), name_hash); | 
|  | }; | 
|  |  | 
|  | // These two CAs collide under |X509_NAME_hash|. | 
|  | std::string ca1 = "Test CA 1191514847"; | 
|  | std::string ca2 = "Test CA 1570301806"; | 
|  | ASSERT_TRUE(add_root(ca1, kNewHash)); | 
|  | ASSERT_TRUE(add_root(ca2, kNewHash)); | 
|  | ASSERT_TRUE(add_crl(ca1, -2, kNewHash)); | 
|  | ASSERT_TRUE(add_crl(ca2, -1, kNewHash)); | 
|  | ASSERT_TRUE(add_crl(ca2, -2, kNewHash)); | 
|  | ASSERT_TRUE(add_crl(ca1, -1, kNewHash)); | 
|  | // Verify the hashes collided. | 
|  | ASSERT_EQ(dir.num_cert_hashes(), 1u); | 
|  | ASSERT_EQ(dir.num_crl_hashes(), 1u); | 
|  | bssl::UniquePtr<X509> leaf1 = issue_cert(ca1); | 
|  | ASSERT_TRUE(leaf1); | 
|  | bssl::UniquePtr<X509> leaf2 = issue_cert(ca2); | 
|  | ASSERT_TRUE(leaf2); | 
|  |  | 
|  | // These two CAs collide under |X509_NAME_hash_old|. | 
|  | std::string old_ca1 = "Test CA 1069881739"; | 
|  | std::string old_ca2 = "Test CA 940754110"; | 
|  | ASSERT_TRUE(add_root(old_ca1, kOldHash)); | 
|  | ASSERT_TRUE(add_root(old_ca2, kOldHash)); | 
|  | ASSERT_TRUE(add_crl(old_ca1, -2, kOldHash)); | 
|  | ASSERT_TRUE(add_crl(old_ca2, -1, kOldHash)); | 
|  | ASSERT_TRUE(add_crl(old_ca2, -2, kOldHash)); | 
|  | ASSERT_TRUE(add_crl(old_ca1, -1, kOldHash)); | 
|  | // Verify the hashes collided. | 
|  | ASSERT_EQ(dir.num_cert_hashes(), 2u); | 
|  | ASSERT_EQ(dir.num_crl_hashes(), 2u); | 
|  | bssl::UniquePtr<X509> old_leaf1 = issue_cert(old_ca1); | 
|  | ASSERT_TRUE(old_leaf1); | 
|  | bssl::UniquePtr<X509> old_leaf2 = issue_cert(old_ca2); | 
|  | ASSERT_TRUE(old_leaf2); | 
|  |  | 
|  | // Make an |X509_STORE| that gets CAs from |dir|. | 
|  | bssl::UniquePtr<X509_STORE> store(X509_STORE_new()); | 
|  | ASSERT_TRUE(store); | 
|  | ASSERT_TRUE(X509_STORE_load_locations(store.get(), /*file=*/nullptr, | 
|  | dir.path().c_str())); | 
|  |  | 
|  | auto verify = [&](X509 *cert, bool crl_check) { | 
|  | bssl::UniquePtr<X509_STORE_CTX> ctx(X509_STORE_CTX_new()); | 
|  | ASSERT_TRUE(ctx); | 
|  | ASSERT_TRUE(X509_STORE_CTX_init(ctx.get(), store.get(), cert, | 
|  | /*chain=*/nullptr)); | 
|  | X509_STORE_CTX_set_flags(ctx.get(), crl_check ? X509_V_FLAG_CRL_CHECK : 0); | 
|  | X509_STORE_CTX_set_time_posix(ctx.get(), /*flags=*/0, kReferenceTime); | 
|  | EXPECT_TRUE(X509_verify_cert(ctx.get())) | 
|  | << X509_verify_cert_error_string(X509_STORE_CTX_get_error(ctx.get())); | 
|  | }; | 
|  |  | 
|  | const size_t kNumThreads = 10; | 
|  | std::vector<std::thread> threads; | 
|  | for (size_t i = 0; i < kNumThreads; i++) { | 
|  | threads.emplace_back([&] { verify(leaf1.get(), false); }); | 
|  | threads.emplace_back([&] { verify(leaf1.get(), true); }); | 
|  | threads.emplace_back([&] { verify(leaf2.get(), false); }); | 
|  | threads.emplace_back([&] { verify(leaf2.get(), true); }); | 
|  |  | 
|  | threads.emplace_back([&] { verify(old_leaf1.get(), false); }); | 
|  | threads.emplace_back([&] { verify(old_leaf1.get(), true); }); | 
|  | threads.emplace_back([&] { verify(old_leaf2.get(), false); }); | 
|  | threads.emplace_back([&] { verify(old_leaf2.get(), true); }); | 
|  | } | 
|  | for (auto &thread : threads) { | 
|  | thread.join(); | 
|  | } | 
|  | } | 
|  | #endif  // OPENSSL_THREADS | 
|  |  | 
|  | // Test that, when there are two CAs with the same name, but different key | 
|  | // identifiers, certificate and CRL lookup can disambiguate correctly. | 
|  | TEST(X509Test, DuplicateName) { | 
|  | // Make two certificate chains and empty CRLs, with the same names but | 
|  | // different keys. | 
|  | bssl::UniquePtr<EVP_PKEY> key1 = PrivateKeyFromPEM(kP256Key); | 
|  | ASSERT_TRUE(key1); | 
|  | uint8_t key_id1[] = {'K', 'e', 'y', '1'}; | 
|  | bssl::UniquePtr<X509> ca1 = | 
|  | MakeTestCert("CA", "CA", key1.get(), /*is_ca=*/true); | 
|  | ASSERT_TRUE(ca1); | 
|  | ASSERT_TRUE(AddSubjectKeyIdentifier(ca1.get(), key_id1)); | 
|  | ASSERT_TRUE(X509_sign(ca1.get(), key1.get(), EVP_sha256())); | 
|  | bssl::UniquePtr<X509> leaf1 = | 
|  | MakeTestCert("CA", "Leaf", key1.get(), /*is_ca=*/false); | 
|  | ASSERT_TRUE(leaf1); | 
|  | ASSERT_TRUE(AddAuthorityKeyIdentifier(leaf1.get(), key_id1)); | 
|  | ASSERT_TRUE(X509_sign(leaf1.get(), key1.get(), EVP_sha256())); | 
|  | bssl::UniquePtr<X509_CRL> crl1 = MakeTestCRL("CA", -1, 1); | 
|  | ASSERT_TRUE(crl1); | 
|  | ASSERT_TRUE(AddAuthorityKeyIdentifier(crl1.get(), key_id1)); | 
|  | ASSERT_TRUE(X509_CRL_sign(crl1.get(), key1.get(), EVP_sha256())); | 
|  | // TODO(davidben): Some state in CRLs does not get correctly set up unless it | 
|  | // is parsed from data. |X509_CRL_sign| should reset it internally. | 
|  | crl1 = ReencodeCRL(crl1.get()); | 
|  | ASSERT_TRUE(crl1); | 
|  |  | 
|  | bssl::UniquePtr<EVP_PKEY> key2 = PrivateKeyFromPEM(kRSAKey); | 
|  | ASSERT_TRUE(key2); | 
|  | uint8_t key_id2[] = {'K', 'e', 'y', '2'}; | 
|  | bssl::UniquePtr<X509> ca2 = | 
|  | MakeTestCert("CA", "CA", key2.get(), /*is_ca=*/true); | 
|  | ASSERT_TRUE(ca2); | 
|  | ASSERT_TRUE(AddSubjectKeyIdentifier(ca2.get(), key_id2)); | 
|  | ASSERT_TRUE(X509_sign(ca2.get(), key2.get(), EVP_sha256())); | 
|  | bssl::UniquePtr<X509> leaf2 = | 
|  | MakeTestCert("CA", "Leaf", key2.get(), /*is_ca=*/false); | 
|  | ASSERT_TRUE(leaf2); | 
|  | ASSERT_TRUE(AddAuthorityKeyIdentifier(leaf2.get(), key_id2)); | 
|  | ASSERT_TRUE(X509_sign(leaf2.get(), key2.get(), EVP_sha256())); | 
|  | bssl::UniquePtr<X509_CRL> crl2 = MakeTestCRL("CA", -2, 2); | 
|  | ASSERT_TRUE(crl2); | 
|  | ASSERT_TRUE(AddAuthorityKeyIdentifier(crl2.get(), key_id2)); | 
|  | ASSERT_TRUE(X509_CRL_sign(crl2.get(), key2.get(), EVP_sha256())); | 
|  | // TODO(davidben): Some state in CRLs does not get correctly set up unless it | 
|  | // is parsed from data. |X509_CRL_sign| should reset it internally. | 
|  | crl2 = ReencodeCRL(crl2.get()); | 
|  | ASSERT_TRUE(crl2); | 
|  |  | 
|  | for (bool key1_first : {false, true}) { | 
|  | SCOPED_TRACE(key1_first); | 
|  | X509 *first_leaf = leaf1.get(); | 
|  | X509 *second_leaf = leaf2.get(); | 
|  | if (!key1_first) { | 
|  | std::swap(first_leaf, second_leaf); | 
|  | } | 
|  |  | 
|  | for (bool use_dir : {false, true}) { | 
|  | SCOPED_TRACE(use_dir); | 
|  | bssl::UniquePtr<X509_STORE> store(X509_STORE_new()); | 
|  | ASSERT_TRUE(store); | 
|  | TemporaryHashDir dir(X509_FILETYPE_PEM); | 
|  | if (use_dir) { | 
|  | ASSERT_TRUE(dir.Init()); | 
|  | ASSERT_TRUE(dir.AddCert(ca1.get(), kNewHash)); | 
|  | ASSERT_TRUE(dir.AddCert(ca2.get(), kNewHash)); | 
|  | ASSERT_TRUE(dir.AddCRL(crl1.get(), kNewHash)); | 
|  | ASSERT_TRUE(dir.AddCRL(crl2.get(), kNewHash)); | 
|  | ASSERT_EQ(dir.num_cert_hashes(), 1u); | 
|  | ASSERT_EQ(dir.num_crl_hashes(), 1u); | 
|  | ASSERT_TRUE(X509_STORE_load_locations(store.get(), /*file=*/nullptr, | 
|  | dir.path().c_str())); | 
|  | } else { | 
|  | ASSERT_TRUE(X509_STORE_add_cert(store.get(), ca1.get())); | 
|  | ASSERT_TRUE(X509_STORE_add_cert(store.get(), ca2.get())); | 
|  | ASSERT_TRUE(X509_STORE_add_crl(store.get(), crl1.get())); | 
|  | ASSERT_TRUE(X509_STORE_add_crl(store.get(), crl2.get())); | 
|  | } | 
|  |  | 
|  | // Verify the two certificates. Whichever comes first, we should | 
|  | // successfully find their CA and CRL. | 
|  | { | 
|  | bssl::UniquePtr<X509_STORE_CTX> ctx(X509_STORE_CTX_new()); | 
|  | ASSERT_TRUE(ctx); | 
|  | ASSERT_TRUE( | 
|  | X509_STORE_CTX_init(ctx.get(), store.get(), first_leaf, nullptr)); | 
|  | X509_STORE_CTX_set_flags(ctx.get(), X509_V_FLAG_CRL_CHECK); | 
|  | X509_STORE_CTX_set_time_posix(ctx.get(), /*flags=*/0, kReferenceTime); | 
|  | EXPECT_TRUE(X509_verify_cert(ctx.get())) | 
|  | << X509_verify_cert_error_string( | 
|  | X509_STORE_CTX_get_error(ctx.get())); | 
|  | } | 
|  | { | 
|  | bssl::UniquePtr<X509_STORE_CTX> ctx(X509_STORE_CTX_new()); | 
|  | ASSERT_TRUE(ctx); | 
|  | ASSERT_TRUE( | 
|  | X509_STORE_CTX_init(ctx.get(), store.get(), second_leaf, nullptr)); | 
|  | X509_STORE_CTX_set_flags(ctx.get(), X509_V_FLAG_CRL_CHECK); | 
|  | X509_STORE_CTX_set_time_posix(ctx.get(), /*flags=*/0, kReferenceTime); | 
|  | EXPECT_TRUE(X509_verify_cert(ctx.get())) | 
|  | << X509_verify_cert_error_string( | 
|  | X509_STORE_CTX_get_error(ctx.get())); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | 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<uint8_t> 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<ASN1_OCTET_STRING> 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()))); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace |