// 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 #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 CertFromPEM(const char *pem) { bssl::UniquePtr bio(BIO_new_mem_buf(pem, strlen(pem))); return bssl::UniquePtr( 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 CRLFromPEM(const char *pem) { bssl::UniquePtr bio(BIO_new_mem_buf(pem, strlen(pem))); return bssl::UniquePtr( PEM_read_bio_X509_CRL(bio.get(), nullptr, nullptr, nullptr)); } // CSRFromPEM parses the given, NUL-terminated PEM block and returns an // |X509_REQ*|. static bssl::UniquePtr CSRFromPEM(const char *pem) { bssl::UniquePtr bio(BIO_new_mem_buf(pem, strlen(pem))); return bssl::UniquePtr( PEM_read_bio_X509_REQ(bio.get(), nullptr, nullptr, nullptr)); } // PrivateKeyFromPEM parses the given, NUL-terminated PEM block and returns an // |EVP_PKEY*|. static bssl::UniquePtr PrivateKeyFromPEM(const char *pem) { bssl::UniquePtr bio( BIO_new_mem_buf(const_cast(pem), strlen(pem))); return bssl::UniquePtr( PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr)); } // CertsToStack converts a vector of |X509*| to an OpenSSL STACK_OF(X509), // bumping the reference counts for each certificate in question. static bssl::UniquePtr CertsToStack( const std::vector &certs) { bssl::UniquePtr 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 CRLsToStack( const std::vector &crls) { bssl::UniquePtr 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 &roots, const std::vector &intermediates, const std::vector &crls, unsigned long flags = 0, std::function configure_callback = nullptr) { bssl::UniquePtr roots_stack(CertsToStack(roots)); bssl::UniquePtr intermediates_stack( CertsToStack(intermediates)); bssl::UniquePtr crls_stack(CRLsToStack(crls)); if (!roots_stack || // !intermediates_stack || // !crls_stack) { return X509_V_ERR_UNSPECIFIED; } bssl::UniquePtr ctx(X509_STORE_CTX_new()); bssl::UniquePtr 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 cross_signing_root(CertFromPEM(kCrossSigningRootPEM)); bssl::UniquePtr root(CertFromPEM(kRootCAPEM)); bssl::UniquePtr root_cross_signed(CertFromPEM(kRootCrossSignedPEM)); bssl::UniquePtr intermediate(CertFromPEM(kIntermediatePEM)); bssl::UniquePtr intermediate_self_signed( CertFromPEM(kIntermediateSelfSignedPEM)); bssl::UniquePtr leaf(CertFromPEM(kLeafPEM)); bssl::UniquePtr leaf_no_key_usage(CertFromPEM(kLeafNoKeyUsagePEM)); bssl::UniquePtr forgery(CertFromPEM(kForgeryPEM)); ASSERT_TRUE(cross_signing_root); ASSERT_TRUE(root); ASSERT_TRUE(root_cross_signed); ASSERT_TRUE(intermediate); ASSERT_TRUE(intermediate_self_signed); ASSERT_TRUE(leaf); ASSERT_TRUE(forgery); ASSERT_TRUE(leaf_no_key_usage); // Most of these tests work with or without |X509_V_FLAG_TRUSTED_FIRST|, // though in different ways. for (bool trusted_first : {true, false}) { SCOPED_TRACE(trusted_first); bool override_depth = false; int depth = -1; auto configure_callback = [&](X509_STORE_CTX *ctx) { X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(ctx); // Note we need the callback to clear the flag. Setting |flags| to zero // only skips setting new flags. if (!trusted_first) { X509_VERIFY_PARAM_clear_flags(param, X509_V_FLAG_TRUSTED_FIRST); } if (override_depth) { X509_VERIFY_PARAM_set_depth(param, depth); } }; // No trust anchors configured. EXPECT_EQ(X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, Verify(leaf.get(), /*roots=*/{}, /*intermediates=*/{}, /*crls=*/{}, /*flags=*/0, configure_callback)); EXPECT_EQ( X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, Verify(leaf.get(), /*roots=*/{}, {intermediate.get()}, /*crls=*/{}, /*flags=*/0, configure_callback)); // Each chain works individually. EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {intermediate.get()}, /*crls=*/{}, /*flags=*/0, configure_callback)); EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {cross_signing_root.get()}, {intermediate.get(), root_cross_signed.get()}, /*crls=*/{}, /*flags=*/0, configure_callback)); // When both roots are available, we pick one or the other. EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {cross_signing_root.get(), root.get()}, {intermediate.get(), root_cross_signed.get()}, /*crls=*/{}, /*flags=*/0, configure_callback)); // This is the “altchains” test – we remove the cross-signing CA but include // the cross-sign in the intermediates. With |trusted_first|, we // preferentially stop path-building at |intermediate|. Without // |trusted_first|, the "altchains" logic repairs it. EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {intermediate.get(), root_cross_signed.get()}, /*crls=*/{}, /*flags=*/0, configure_callback)); // If |X509_V_FLAG_NO_ALT_CHAINS| is set and |trusted_first| is disabled, we // get stuck on |root_cross_signed|. If either feature is enabled, we can // build the path. // // This test exists to confirm our current behavior, but these modes are // just workarounds for not having an actual path-building verifier. If we // fix it, this test can be removed. EXPECT_EQ(trusted_first ? X509_V_OK : X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, Verify(leaf.get(), {root.get()}, {intermediate.get(), root_cross_signed.get()}, /*crls=*/{}, /*flags=*/X509_V_FLAG_NO_ALT_CHAINS, configure_callback)); // |forgery| is signed by |leaf_no_key_usage|, but is rejected because the // leaf is not a CA. EXPECT_EQ(X509_V_ERR_INVALID_CA, Verify(forgery.get(), {intermediate_self_signed.get()}, {leaf_no_key_usage.get()}, /*crls=*/{}, /*flags=*/0, configure_callback)); // Test that one cannot skip Basic Constraints checking with a contorted set // of roots and intermediates. This is a regression test for CVE-2015-1793. EXPECT_EQ(X509_V_ERR_INVALID_CA, Verify(forgery.get(), {intermediate_self_signed.get(), root_cross_signed.get()}, {leaf_no_key_usage.get(), intermediate.get()}, /*crls=*/{}, /*flags=*/0, configure_callback)); // Test depth limits. |configure_callback| looks at |override_depth| and // |depth|. Negative numbers have historically worked, so test those too. for (int d : {-4, -3, -2, -1, 0, 1, 2, 3, 4, INT_MAX - 3, INT_MAX - 2, INT_MAX - 1, INT_MAX}) { SCOPED_TRACE(d); override_depth = true; depth = d; // A chain with a leaf, two intermediates, and a root is depth two. EXPECT_EQ( depth >= 2 ? X509_V_OK : X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, Verify(leaf.get(), {cross_signing_root.get()}, {intermediate.get(), root_cross_signed.get()}, /*crls=*/{}, /*flags=*/0, configure_callback)); // A chain with a leaf, a root, and no intermediates is depth zero. EXPECT_EQ( depth >= 0 ? X509_V_OK : X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, Verify(root_cross_signed.get(), {cross_signing_root.get()}, {}, /*crls=*/{}, /*flags=*/0, configure_callback)); // An explicitly trusted self-signed certificate is unaffected by depth // checks. EXPECT_EQ(X509_V_OK, Verify(cross_signing_root.get(), {cross_signing_root.get()}, {}, /*crls=*/{}, /*flags=*/0, configure_callback)); } } } #if defined(OPENSSL_THREADS) // Verifying the same |X509| objects on two threads should be safe. TEST(X509Test, VerifyThreads) { bssl::UniquePtr root(CertFromPEM(kRootCAPEM)); bssl::UniquePtr intermediate(CertFromPEM(kIntermediatePEM)); bssl::UniquePtr leaf(CertFromPEM(kLeafPEM)); ASSERT_TRUE(root); ASSERT_TRUE(intermediate); ASSERT_TRUE(leaf); const size_t kNumThreads = 10; std::vector threads; for (size_t i = 0; i < kNumThreads; i++) { threads.emplace_back([&] { EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {intermediate.get()}, /*crls=*/{})); }); } for (auto &thread : threads) { thread.join(); } } // Using the same CRL on two threads should be safe. TEST(X509Test, CRLThreads) { bssl::UniquePtr root(CertFromPEM(kCRLTestRoot)); bssl::UniquePtr leaf(CertFromPEM(kCRLTestLeaf)); bssl::UniquePtr basic_crl(CRLFromPEM(kBasicCRL)); bssl::UniquePtr revoked_crl(CRLFromPEM(kRevokedCRL)); ASSERT_TRUE(root); ASSERT_TRUE(leaf); ASSERT_TRUE(basic_crl); ASSERT_TRUE(revoked_crl); const size_t kNumThreads = 10; std::vector threads; for (size_t i = 0; i < kNumThreads; i++) { threads.emplace_back([&] { EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {root.get()}, {basic_crl.get()}, X509_V_FLAG_CRL_CHECK)); }); threads.emplace_back([&] { EXPECT_EQ(X509_V_ERR_CERT_REVOKED, Verify(leaf.get(), {root.get()}, {root.get()}, {revoked_crl.get()}, X509_V_FLAG_CRL_CHECK)); }); } for (auto &thread : threads) { thread.join(); } // TODO(crbug.com/boringssl/600): Add a thread that iterates // |X509_CRL_get_REVOKED| and a thread that calls |X509_CRL_print|. Those // currently do not work correctly. } TEST(X509Test, StoreThreads) { bssl::UniquePtr root(CertFromPEM(kRootCAPEM)); bssl::UniquePtr intermediate(CertFromPEM(kIntermediatePEM)); bssl::UniquePtr leaf(CertFromPEM(kLeafPEM)); ASSERT_TRUE(root); ASSERT_TRUE(intermediate); ASSERT_TRUE(leaf); bssl::UniquePtr intermediates = CertsToStack({intermediate.get()}); ASSERT_TRUE(intermediates); // Some unrelated certificates. bssl::UniquePtr other1(CertFromPEM(kCRLTestRoot)); bssl::UniquePtr other2(CertFromPEM(kCRLTestLeaf)); ASSERT_TRUE(other1); ASSERT_TRUE(other2); bssl::UniquePtr store(X509_STORE_new()); ASSERT_TRUE(store); ASSERT_TRUE(X509_STORE_add_cert(store.get(), root.get())); const size_t kNumThreads = 10; std::vector threads; for (size_t i = 0; i < kNumThreads; i++) { threads.emplace_back([&] { bssl::UniquePtr ctx(X509_STORE_CTX_new()); ASSERT_TRUE(ctx); ASSERT_TRUE(X509_STORE_CTX_init(ctx.get(), store.get(), leaf.get(), intermediates.get())); X509_STORE_CTX_set_time_posix(ctx.get(), /*flags=*/0, kReferenceTime); ASSERT_TRUE(X509_verify_cert(ctx.get())); ASSERT_EQ(X509_STORE_CTX_get_error(ctx.get()), X509_V_OK); }); threads.emplace_back( [&] { ASSERT_TRUE(X509_STORE_add_cert(store.get(), other1.get())); }); threads.emplace_back( [&] { ASSERT_TRUE(X509_STORE_add_cert(store.get(), other2.get())); }); threads.emplace_back([&] { bssl::UniquePtr 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 leaf(CertFromPEM(kSANTypesLeaf)); bssl::UniquePtr root(CertFromPEM(kSANTypesRoot)); ASSERT_TRUE(leaf); ASSERT_TRUE(root); std::vector empty_crls; struct X509Test { const char *correct_value; size_t correct_value_len; const char *incorrect_value; size_t incorrect_value_len; int (*func)(X509_VERIFY_PARAM *, const char *, size_t); int mismatch_error; }; const std::vector kTests = { {kHostname, strlen(kHostname), kWrongHostname, strlen(kWrongHostname), X509_VERIFY_PARAM_set1_host, X509_V_ERR_HOSTNAME_MISMATCH}, {kEmail, strlen(kEmail), kWrongEmail, strlen(kWrongEmail), X509_VERIFY_PARAM_set1_email, X509_V_ERR_EMAIL_MISMATCH}, }; for (size_t i = 0; i < kTests.size(); i++) { SCOPED_TRACE(i); const X509Test &test = kTests[i]; // The correct value should work. ASSERT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {}, empty_crls, 0, [&test](X509_STORE_CTX *ctx) { X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(ctx); ASSERT_TRUE(test.func(param, test.correct_value, test.correct_value_len)); })); // The wrong value should trigger a verification error. ASSERT_EQ(test.mismatch_error, Verify(leaf.get(), {root.get()}, {}, empty_crls, 0, [&test](X509_STORE_CTX *ctx) { X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(ctx); ASSERT_TRUE(test.func(param, test.incorrect_value, test.incorrect_value_len)); })); // 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 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 root(CertFromPEM(kCRLTestRoot)); bssl::UniquePtr leaf(CertFromPEM(kCRLTestLeaf)); bssl::UniquePtr basic_crl(CRLFromPEM(kBasicCRL)); bssl::UniquePtr revoked_crl(CRLFromPEM(kRevokedCRL)); bssl::UniquePtr bad_issuer_crl(CRLFromPEM(kBadIssuerCRL)); bssl::UniquePtr known_critical_crl(CRLFromPEM(kKnownCriticalCRL)); bssl::UniquePtr unknown_critical_crl( CRLFromPEM(kUnknownCriticalCRL)); bssl::UniquePtr unknown_critical_crl2( CRLFromPEM(kUnknownCriticalCRL2)); bssl::UniquePtr algorithm_mismatch_crl( CRLFromPEM(kAlgorithmMismatchCRL)); bssl::UniquePtr algorithm_mismatch_crl2( CRLFromPEM(kAlgorithmMismatchCRL2)); ASSERT_TRUE(root); ASSERT_TRUE(leaf); ASSERT_TRUE(basic_crl); ASSERT_TRUE(revoked_crl); ASSERT_TRUE(bad_issuer_crl); ASSERT_TRUE(known_critical_crl); ASSERT_TRUE(unknown_critical_crl); ASSERT_TRUE(unknown_critical_crl2); ASSERT_TRUE(algorithm_mismatch_crl); ASSERT_TRUE(algorithm_mismatch_crl2); EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {root.get()}, {basic_crl.get()}, X509_V_FLAG_CRL_CHECK)); EXPECT_EQ( X509_V_ERR_CERT_REVOKED, Verify(leaf.get(), {root.get()}, {root.get()}, {basic_crl.get(), revoked_crl.get()}, X509_V_FLAG_CRL_CHECK)); std::vector empty_crls; EXPECT_EQ(X509_V_ERR_UNABLE_TO_GET_CRL, Verify(leaf.get(), {root.get()}, {root.get()}, empty_crls, X509_V_FLAG_CRL_CHECK)); EXPECT_EQ(X509_V_ERR_UNABLE_TO_GET_CRL, Verify(leaf.get(), {root.get()}, {root.get()}, {bad_issuer_crl.get()}, X509_V_FLAG_CRL_CHECK)); EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {root.get()}, {known_critical_crl.get()}, X509_V_FLAG_CRL_CHECK)); EXPECT_EQ(X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION, Verify(leaf.get(), {root.get()}, {root.get()}, {unknown_critical_crl.get()}, X509_V_FLAG_CRL_CHECK)); EXPECT_EQ(X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION, Verify(leaf.get(), {root.get()}, {root.get()}, {unknown_critical_crl2.get()}, X509_V_FLAG_CRL_CHECK)); EXPECT_EQ(X509_V_ERR_CRL_SIGNATURE_FAILURE, Verify(leaf.get(), {root.get()}, {root.get()}, {algorithm_mismatch_crl.get()}, X509_V_FLAG_CRL_CHECK)); EXPECT_EQ(X509_V_ERR_CRL_SIGNATURE_FAILURE, Verify(leaf.get(), {root.get()}, {root.get()}, {algorithm_mismatch_crl2.get()}, X509_V_FLAG_CRL_CHECK)); // The CRL is valid for a month. EXPECT_EQ(X509_V_ERR_CRL_HAS_EXPIRED, Verify(leaf.get(), {root.get()}, {root.get()}, {basic_crl.get()}, X509_V_FLAG_CRL_CHECK, [](X509_STORE_CTX *ctx) { X509_STORE_CTX_set_time_posix( ctx, /*flags=*/0, kReferenceTime + 2 * 30 * 24 * 3600); })); // X509_V_FLAG_NO_CHECK_TIME suppresses the validity check. EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {root.get()}, {basic_crl.get()}, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_NO_CHECK_TIME, [](X509_STORE_CTX *ctx) { X509_STORE_CTX_set_time_posix( ctx, /*flags=*/0, kReferenceTime + 2 * 30 * 24 * 3600); })); // We no longer support indirect or delta CRLs. EXPECT_EQ(X509_V_ERR_INVALID_CALL, Verify(leaf.get(), {root.get()}, {root.get()}, {basic_crl.get()}, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_EXTENDED_CRL_SUPPORT)); EXPECT_EQ(X509_V_ERR_INVALID_CALL, Verify(leaf.get(), {root.get()}, {root.get()}, {basic_crl.get()}, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_USE_DELTAS)); // Parsing kBadExtensionCRL should fail. EXPECT_FALSE(CRLFromPEM(kBadExtensionCRL)); } TEST(X509Test, ManyNamesAndConstraints) { bssl::UniquePtr many_constraints(CertFromPEM( GetTestData("crypto/x509/test/many_constraints.pem").c_str())); ASSERT_TRUE(many_constraints); bssl::UniquePtr many_names1( CertFromPEM(GetTestData("crypto/x509/test/many_names1.pem").c_str())); ASSERT_TRUE(many_names1); bssl::UniquePtr many_names2( CertFromPEM(GetTestData("crypto/x509/test/many_names2.pem").c_str())); ASSERT_TRUE(many_names2); bssl::UniquePtr many_names3( CertFromPEM(GetTestData("crypto/x509/test/many_names3.pem").c_str())); ASSERT_TRUE(many_names3); bssl::UniquePtr some_names1( CertFromPEM(GetTestData("crypto/x509/test/some_names1.pem").c_str())); ASSERT_TRUE(some_names1); bssl::UniquePtr some_names2( CertFromPEM(GetTestData("crypto/x509/test/some_names2.pem").c_str())); ASSERT_TRUE(some_names2); bssl::UniquePtr some_names3( CertFromPEM(GetTestData("crypto/x509/test/some_names3.pem").c_str())); ASSERT_TRUE(some_names3); EXPECT_EQ(X509_V_ERR_UNSPECIFIED, Verify(many_names1.get(), {many_constraints.get()}, {many_constraints.get()}, {})); EXPECT_EQ(X509_V_ERR_UNSPECIFIED, Verify(many_names2.get(), {many_constraints.get()}, {many_constraints.get()}, {})); EXPECT_EQ(X509_V_ERR_UNSPECIFIED, Verify(many_names3.get(), {many_constraints.get()}, {many_constraints.get()}, {})); EXPECT_EQ(X509_V_OK, Verify(some_names1.get(), {many_constraints.get()}, {many_constraints.get()}, {})); EXPECT_EQ(X509_V_OK, Verify(some_names2.get(), {many_constraints.get()}, {many_constraints.get()}, {})); EXPECT_EQ(X509_V_OK, Verify(some_names3.get(), {many_constraints.get()}, {many_constraints.get()}, {})); } static bssl::UniquePtr MakeGeneralName(int type, const std::string &value) { if (type != GEN_EMAIL && type != GEN_DNS && type != GEN_URI) { // This function only supports the IA5String types. return nullptr; } bssl::UniquePtr str(ASN1_IA5STRING_new()); bssl::UniquePtr name(GENERAL_NAME_new()); if (!str || !name || !ASN1_STRING_set(str.get(), value.data(), value.size())) { return nullptr; } name->type = type; name->d.ia5 = str.release(); return name; } static bssl::UniquePtr MakeTestName(const char *common_name) { bssl::UniquePtr name(X509_NAME_new()); if (name == nullptr || !X509_NAME_add_entry_by_txt( name.get(), "CN", MBSTRING_UTF8, reinterpret_cast(common_name), -1, -1, 0)) { return nullptr; } return name; } static bssl::UniquePtr MakeTestCert(const char *issuer, const char *subject, EVP_PKEY *key, bool is_ca) { bssl::UniquePtr issuer_name = MakeTestName(issuer); bssl::UniquePtr subject_name = MakeTestName(subject); bssl::UniquePtr 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 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 &eku_nids) { bssl::UniquePtr objs(sk_ASN1_OBJECT_new_null()); if (objs == nullptr) { return false; } for (int nid : eku_nids) { if (!sk_ASN1_OBJECT_push(objs.get(), OBJ_nid2obj(nid))) { return false; } } return X509_add1_ext_i2d(x509, NID_ext_key_usage, objs.get(), /*crit=*/1, /*flags=*/0); } enum class KeyUsage : int { kDigitalSignature = 0, kNonRepudiation = 1, kKeyEncipherment = 2, kDataEncipherment = 3, kKeyAgreement = 4, kKeyCertSign = 5, kCRLSign = 6, kEncipherOnly = 7, kDecipherOnly = 8, }; static bool AddKeyUsage(X509 *x509, const std::vector usages) { bssl::UniquePtr str(ASN1_BIT_STRING_new()); if (str == nullptr) { return false; } for (KeyUsage usage : usages) { if (!ASN1_BIT_STRING_set_bit(str.get(), static_cast(usage), 1)) { return false; } } return X509_add1_ext_i2d(x509, NID_key_usage, str.get(), /*crit=*/1, /*flags=*/0); } static bool AddSubjectKeyIdentifier(X509 *x509, bssl::Span key_id) { bssl::UniquePtr 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 key_id) { bssl::UniquePtr 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 MakeTestCRL(const char *issuer, int this_update_offset_day, int next_update_offset_day) { bssl::UniquePtr issuer_name = MakeTestName(issuer); bssl::UniquePtr crl(X509_CRL_new()); bssl::UniquePtr this_update(ASN1_TIME_adj( nullptr, kReferenceTime, this_update_offset_day, /*offset_sec=*/0)); bssl::UniquePtr 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 rev(X509_REVOKED_new()); bssl::UniquePtr serial_asn1(ASN1_INTEGER_new()); bssl::UniquePtr 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 key_id) { bssl::UniquePtr 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 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 name = MakeGeneralName(t.type, t.name); ASSERT_TRUE(name); bssl::UniquePtr names(GENERAL_NAMES_new()); ASSERT_TRUE(names); ASSERT_TRUE(bssl::PushToStack(names.get(), std::move(name))); bssl::UniquePtr nc(NAME_CONSTRAINTS_new()); ASSERT_TRUE(nc); nc->permittedSubtrees = sk_GENERAL_SUBTREE_new_null(); ASSERT_TRUE(nc->permittedSubtrees); bssl::UniquePtr 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 root = MakeTestCert("Root", "Root", key.get(), /*is_ca=*/true); ASSERT_TRUE(root); ASSERT_TRUE(X509_add1_ext_i2d(root.get(), NID_name_constraints, nc.get(), /*crit=*/1, /*flags=*/0)); ASSERT_TRUE(X509_sign(root.get(), key.get(), EVP_sha256())); bssl::UniquePtr leaf = MakeTestCert("Root", "Leaf", key.get(), /*is_ca=*/false); ASSERT_TRUE(leaf); ASSERT_TRUE(X509_add1_ext_i2d(leaf.get(), NID_subject_alt_name, names.get(), /*crit=*/0, /*flags=*/0)); ASSERT_TRUE(X509_sign(leaf.get(), key.get(), EVP_sha256())); int 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 gen = MakeGeneralName(GEN_DNS, "example.com"); ASSERT_TRUE(gen); bssl::UniquePtr values( i2v_GENERAL_NAME(nullptr, gen.get(), nullptr)); ASSERT_TRUE(values); ASSERT_EQ(1u, sk_CONF_VALUE_num(values.get())); const CONF_VALUE *value = sk_CONF_VALUE_value(values.get(), 0); EXPECT_STREQ(value->name, "DNS"); EXPECT_STREQ(value->value, "example.com"); } TEST(X509Test, TestPSS) { 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 cert = CertFromPEM(GetTestData(path).c_str()); ASSERT_TRUE(cert); bssl::UniquePtr 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 cert = CertFromPEM(GetTestData(path).c_str()); ASSERT_TRUE(cert); bssl::UniquePtr pkey(X509_get_pubkey(cert.get())); ASSERT_TRUE(pkey); EXPECT_FALSE(X509_verify(cert.get(), pkey.get())); } } TEST(X509Test, TestPSSBadParameters) { bssl::UniquePtr cert(CertFromPEM(kBadPSSCertPEM)); ASSERT_TRUE(cert); bssl::UniquePtr pkey(X509_get_pubkey(cert.get())); ASSERT_TRUE(pkey); ASSERT_FALSE(X509_verify(cert.get(), pkey.get())); ERR_clear_error(); } TEST(X509Test, TestEd25519) { bssl::UniquePtr cert(CertFromPEM(kEd25519Cert)); ASSERT_TRUE(cert); bssl::UniquePtr pkey(X509_get_pubkey(cert.get())); ASSERT_TRUE(pkey); ASSERT_TRUE(X509_verify(cert.get(), pkey.get())); } TEST(X509Test, TestEd25519BadParameters) { bssl::UniquePtr cert(CertFromPEM(kEd25519CertNull)); ASSERT_TRUE(cert); bssl::UniquePtr pkey(X509_get_pubkey(cert.get())); ASSERT_TRUE(pkey); ASSERT_FALSE(X509_verify(cert.get(), pkey.get())); EXPECT_TRUE( ErrorEquals(ERR_get_error(), ERR_LIB_X509, X509_R_INVALID_PARAMETER)); ERR_clear_error(); } TEST(X509Test, TestX25519) { bssl::UniquePtr cert(CertFromPEM(kX25519Cert)); ASSERT_TRUE(cert); bssl::UniquePtr pkey(X509_get_pubkey(cert.get())); ASSERT_TRUE(pkey); EXPECT_EQ(EVP_PKEY_id(pkey.get()), EVP_PKEY_X25519); constexpr uint8_t kExpectedPublicValue[] = { 0x85, 0x20, 0xf0, 0x09, 0x89, 0x30, 0xa7, 0x54, 0x74, 0x8b, 0x7d, 0xdc, 0xb4, 0x3e, 0xf7, 0x5a, 0x0d, 0xbf, 0x3a, 0x0d, 0x26, 0x38, 0x1a, 0xf4, 0xeb, 0xa4, 0xa9, 0x8e, 0xaa, 0x9b, 0x4e, 0x6a, }; uint8_t public_value[sizeof(kExpectedPublicValue)]; size_t public_value_size = sizeof(public_value); ASSERT_TRUE(EVP_PKEY_get_raw_public_key(pkey.get(), public_value, &public_value_size)); EXPECT_EQ(Bytes(kExpectedPublicValue), Bytes(public_value, public_value_size)); } static bssl::UniquePtr ReencodeCertificate(X509 *cert) { uint8_t *der = nullptr; int len = i2d_X509(cert, &der); bssl::UniquePtr free_der(der); if (len <= 0) { return nullptr; } const uint8_t *inp = der; return bssl::UniquePtr(d2i_X509(nullptr, &inp, len)); } static bssl::UniquePtr ReencodeCRL(X509_CRL *crl) { uint8_t *der = nullptr; int len = i2d_X509_CRL(crl, &der); bssl::UniquePtr free_der(der); if (len <= 0) { return nullptr; } const uint8_t *inp = der; return bssl::UniquePtr(d2i_X509_CRL(nullptr, &inp, len)); } static bssl::UniquePtr ReencodeCSR(X509_REQ *req) { uint8_t *der = nullptr; int len = i2d_X509_REQ(req, &der); bssl::UniquePtr free_der(der); if (len <= 0) { return nullptr; } const uint8_t *inp = der; return bssl::UniquePtr(d2i_X509_REQ(nullptr, &inp, len)); } static bool SignatureRoundTrips(EVP_MD_CTX *md_ctx, EVP_PKEY *pkey) { // Make a certificate like signed with |md_ctx|'s settings.' bssl::UniquePtr cert(CertFromPEM(kLeafPEM)); if (!cert || !X509_sign_ctx(cert.get(), md_ctx)) { return false; } // Ensure that |pkey| may still be used to verify the resulting signature. All // settings in |md_ctx| must have been serialized appropriately. if (!X509_verify(cert.get(), pkey)) { return false; } // Re-encode the certificate. X509 objects contain a cached TBSCertificate // encoding and |X509_sign_ctx| should have dropped that cache. bssl::UniquePtr copy = ReencodeCertificate(cert.get()); return copy && X509_verify(copy.get(), pkey); } TEST(X509Test, RSASign) { bssl::UniquePtr pkey(PrivateKeyFromPEM(kRSAKey)); ASSERT_TRUE(pkey); // Test PKCS#1 v1.5. bssl::ScopedEVP_MD_CTX md_ctx; ASSERT_TRUE( EVP_DigestSignInit(md_ctx.get(), NULL, EVP_sha256(), NULL, pkey.get())); ASSERT_TRUE(SignatureRoundTrips(md_ctx.get(), pkey.get())); // 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 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 pkey(PrivateKeyFromPEM(kRSAKey)); ASSERT_TRUE(pkey); bssl::UniquePtr algor(X509_ALGOR_new()); ASSERT_TRUE(algor); ASSERT_TRUE(X509_ALGOR_set0(algor.get(), OBJ_nid2obj(kSignatureNID), V_ASN1_NULL, nullptr)); // Test both signing with |X509_sign| and constructing a signature manually. for (bool sign_manual : {true, false}) { SCOPED_TRACE(sign_manual); // Test certificates made both from other certificates and |X509_new|, in // case there are bugs in filling in fields from different states. (Parsed // certificates contain a TBSCertificate cache, and |X509_new| initializes // fields based on complex ASN.1 template logic.) for (bool new_cert : {true, false}) { SCOPED_TRACE(new_cert); bssl::UniquePtr cert; if (new_cert) { cert.reset(X509_new()); ASSERT_TRUE(cert); // Fill in some fields for the certificate arbitrarily. EXPECT_TRUE(X509_set_version(cert.get(), X509_VERSION_3)); EXPECT_TRUE( ASN1_INTEGER_set_int64(X509_get_serialNumber(cert.get()), 1)); EXPECT_TRUE(X509_gmtime_adj(X509_getm_notBefore(cert.get()), 0)); EXPECT_TRUE( X509_gmtime_adj(X509_getm_notAfter(cert.get()), 60 * 60 * 24)); X509_NAME *subject = X509_get_subject_name(cert.get()); X509_NAME_add_entry_by_txt(subject, "CN", MBSTRING_ASC, reinterpret_cast("Test"), -1, -1, 0); EXPECT_TRUE(X509_set_issuer_name(cert.get(), subject)); EXPECT_TRUE(X509_set_pubkey(cert.get(), pkey.get())); } else { // Extract fields from a parsed certificate. cert = CertFromPEM(kLeafPEM); ASSERT_TRUE(cert); // We should test with a different algorithm from what is already in the // certificate. EXPECT_NE(kSignatureNID, X509_get_signature_nid(cert.get())); } if (sign_manual) { // Fill in the signature algorithm. ASSERT_TRUE(X509_set1_signature_algo(cert.get(), algor.get())); // Extract the TBSCertificiate. uint8_t *tbs_cert = nullptr; int tbs_cert_len = i2d_re_X509_tbs(cert.get(), &tbs_cert); bssl::UniquePtr free_tbs_cert(tbs_cert); ASSERT_GT(tbs_cert_len, 0); // Generate a signature externally and fill it in. bssl::ScopedEVP_MD_CTX md_ctx; ASSERT_TRUE(EVP_DigestSignInit(md_ctx.get(), nullptr, kSignatureHash, nullptr, pkey.get())); size_t sig_len; ASSERT_TRUE(EVP_DigestSign(md_ctx.get(), nullptr, &sig_len, tbs_cert, tbs_cert_len)); std::vector sig(sig_len); ASSERT_TRUE(EVP_DigestSign(md_ctx.get(), sig.data(), &sig_len, tbs_cert, tbs_cert_len)); sig.resize(sig_len); ASSERT_TRUE( X509_set1_signature_value(cert.get(), sig.data(), sig.size())); } else { int ret = X509_sign(cert.get(), pkey.get(), EVP_sha384()); ASSERT_GT(ret, 0); // |X509_sign| returns the length of the signature on success. const ASN1_BIT_STRING *sig; X509_get0_signature(&sig, /*out_alg=*/nullptr, cert.get()); EXPECT_EQ(ret, ASN1_STRING_length(sig)); } // Check the signature. EXPECT_TRUE(X509_verify(cert.get(), pkey.get())); // Re-encode the certificate. X509 objects contain a cached TBSCertificate // encoding and re-signing should have dropped that cache. bssl::UniquePtr copy = ReencodeCertificate(cert.get()); ASSERT_TRUE(copy); EXPECT_TRUE(X509_verify(copy.get(), pkey.get())); } } } // Test the APIs for signing a CRL, particularly whether they correctly handle // the TBSCertList cache. TEST(X509Test, SignCRL) { const int kSignatureNID = NID_sha384WithRSAEncryption; const EVP_MD *kSignatureHash = EVP_sha384(); bssl::UniquePtr pkey(PrivateKeyFromPEM(kRSAKey)); ASSERT_TRUE(pkey); bssl::UniquePtr algor(X509_ALGOR_new()); ASSERT_TRUE(algor); ASSERT_TRUE(X509_ALGOR_set0(algor.get(), OBJ_nid2obj(kSignatureNID), V_ASN1_NULL, nullptr)); // Test both signing with |X509_CRL_sign| and constructing a signature // manually. for (bool sign_manual : {true, false}) { SCOPED_TRACE(sign_manual); // Test CRLs made both from other CRLs and |X509_CRL_new|, in case there are // bugs in filling in fields from different states. (Parsed CRLs contain a // TBSCertList cache, and |X509_CRL_new| initializes fields based on complex // ASN.1 template logic.) for (bool new_crl : {true, false}) { SCOPED_TRACE(new_crl); bssl::UniquePtr crl; if (new_crl) { crl.reset(X509_CRL_new()); ASSERT_TRUE(crl); // Fill in some fields for the certificate arbitrarily. ASSERT_TRUE(X509_CRL_set_version(crl.get(), X509_CRL_VERSION_2)); bssl::UniquePtr last_update(ASN1_TIME_new()); ASSERT_TRUE(last_update); ASSERT_TRUE(ASN1_TIME_set_posix(last_update.get(), kReferenceTime)); ASSERT_TRUE(X509_CRL_set1_lastUpdate(crl.get(), last_update.get())); bssl::UniquePtr issuer(X509_NAME_new()); ASSERT_TRUE(issuer); ASSERT_TRUE(X509_NAME_add_entry_by_txt( issuer.get(), "CN", MBSTRING_ASC, reinterpret_cast("Test"), -1, -1, 0)); EXPECT_TRUE(X509_CRL_set_issuer_name(crl.get(), issuer.get())); } else { // Extract fields from a parsed CRL. crl = CRLFromPEM(kBasicCRL); ASSERT_TRUE(crl); // We should test with a different algorithm from what is already in the // CRL. EXPECT_NE(kSignatureNID, X509_CRL_get_signature_nid(crl.get())); } if (sign_manual) { // Fill in the signature algorithm. ASSERT_TRUE(X509_CRL_set1_signature_algo(crl.get(), algor.get())); // Extract the TBSCertList. uint8_t *tbs = nullptr; int tbs_len = i2d_re_X509_CRL_tbs(crl.get(), &tbs); bssl::UniquePtr free_tbs(tbs); ASSERT_GT(tbs_len, 0); // Generate a signature externally and fill it in. bssl::ScopedEVP_MD_CTX md_ctx; ASSERT_TRUE(EVP_DigestSignInit(md_ctx.get(), nullptr, kSignatureHash, nullptr, pkey.get())); size_t sig_len; ASSERT_TRUE( EVP_DigestSign(md_ctx.get(), nullptr, &sig_len, tbs, tbs_len)); std::vector sig(sig_len); ASSERT_TRUE( EVP_DigestSign(md_ctx.get(), sig.data(), &sig_len, tbs, tbs_len)); sig.resize(sig_len); ASSERT_TRUE( X509_CRL_set1_signature_value(crl.get(), sig.data(), sig.size())); } else { ASSERT_TRUE(X509_CRL_sign(crl.get(), pkey.get(), EVP_sha384())); } // Check the signature. EXPECT_TRUE(X509_CRL_verify(crl.get(), pkey.get())); // Re-encode the CRL. X509_CRL objects contain a cached TBSCertList // encoding and re-signing should have dropped that cache. bssl::UniquePtr copy = ReencodeCRL(crl.get()); ASSERT_TRUE(copy); EXPECT_TRUE(X509_CRL_verify(copy.get(), pkey.get())); } } } static const char kTestCSR[] = R"( -----BEGIN CERTIFICATE REQUEST----- MIICVDCCATwCAQAwDzENMAsGA1UEAwwEVGVzdDCCASIwDQYJKoZIhvcNAQEBBQAD ggEPADCCAQoCggEBAK+UkwcNJfRhg5MzIQzxDdrqF9a76jNoK/BwCflKYFX7QEqf rsLkI0J+m60fUD0v50LnKwbGoMFKZ1R/3cBNXLcdXb7ZP/ZJ7A7QwUrL+W9n3sov U8/HSU3rHbg+V5L6egSZYuhDHoXKi33HDOL4DVUzMoU1ykmP4QwF1wUXHLqvqjbU teQBoJWO53/XOGQu8bX04muCFnHZWT2Ubqol70JwPU2PqDU1EBlgUFO79NEmflev b++H8tu42UCDUZXD9k5weftjneO4cud3IsUX6mDsyf7k1e2mxsS4TSZsJcG0iLBX HSr1udXazQsjlAKjJkoI3cWshF6LGRWssAtbGiUCAwEAAaAAMA0GCSqGSIb3DQEB CwUAA4IBAQAniYZL+amXu+wED+AwBZz+zPuxY16bveF27/gxcs/jq6hVpEQvMxfO jfAGeDRtAU7DMxdJPjvWwwNe2JlTMSRoVDMYaiKqB5yxIYa2cjQvp7swSxuFJwbG T8h7/d7yqem6NYYzgYsNOE5QJyNu/PsIEdvzrysfDAnREiT2ituOcVpiqUZq3DTj NaTd1GNG3j4E87ZUmayUJD5nH91UNzKvJbpfo+bLyfy73x4QeU0SRitsZmbSBTAi s9+zmCErxzMlAdJHGzxPkXmtvBnUzGRIsAD5h/DjYNUmQJkB60yplt84ZgThhx54 rZGEJG3+X9OuhczVKGJyg+3gU7oDbecc -----END CERTIFICATE REQUEST----- )"; // Test the APIs for signing a CSR, particularly whether they correctly handle // the CertificationRequestInfo cache. TEST(X509Test, SignCSR) { const int kSignatureNID = NID_sha384WithRSAEncryption; const EVP_MD *kSignatureHash = EVP_sha384(); bssl::UniquePtr pkey(PrivateKeyFromPEM(kRSAKey)); ASSERT_TRUE(pkey); bssl::UniquePtr algor(X509_ALGOR_new()); ASSERT_TRUE(algor); ASSERT_TRUE(X509_ALGOR_set0(algor.get(), OBJ_nid2obj(kSignatureNID), V_ASN1_NULL, nullptr)); // Test both signing with |X509_REQ_sign| and constructing a signature // manually. for (bool sign_manual : {true, false}) { SCOPED_TRACE(sign_manual); // Test CSRs made both from other CSRs and |X509_REQ_new|, in case there are // bugs in filling in fields from different states. (Parsed CSRs contain a // CertificationRequestInfo cache, and |X509_REQ_new| initializes fields // based on complex ASN.1 template logic.) for (bool new_csr : {true, false}) { SCOPED_TRACE(new_csr); bssl::UniquePtr csr; if (new_csr) { csr.reset(X509_REQ_new()); ASSERT_TRUE(csr); bssl::UniquePtr subject(X509_NAME_new()); ASSERT_TRUE(subject); ASSERT_TRUE(X509_NAME_add_entry_by_txt( subject.get(), "CN", MBSTRING_ASC, reinterpret_cast("New CSR"), -1, -1, 0)); EXPECT_TRUE(X509_REQ_set_subject_name(csr.get(), subject.get())); } else { // Extract fields from a parsed CSR. csr = CSRFromPEM(kTestCSR); ASSERT_TRUE(csr); } // Override the public key from the CSR unconditionally. Unlike // certificates and CRLs, CSRs do not contain a signed copy of the // signature algorithm, so we use a different field to confirm // |i2d_re_X509_REQ_tbs| clears the cache as expected. EXPECT_TRUE(X509_REQ_set_pubkey(csr.get(), pkey.get())); if (sign_manual) { // Fill in the signature algorithm. ASSERT_TRUE(X509_REQ_set1_signature_algo(csr.get(), algor.get())); // Extract the CertificationRequestInfo. uint8_t *tbs = nullptr; int tbs_len = i2d_re_X509_REQ_tbs(csr.get(), &tbs); bssl::UniquePtr free_tbs(tbs); ASSERT_GT(tbs_len, 0); // Generate a signature externally and fill it in. bssl::ScopedEVP_MD_CTX md_ctx; ASSERT_TRUE(EVP_DigestSignInit(md_ctx.get(), nullptr, kSignatureHash, nullptr, pkey.get())); size_t sig_len; ASSERT_TRUE( EVP_DigestSign(md_ctx.get(), nullptr, &sig_len, tbs, tbs_len)); std::vector sig(sig_len); ASSERT_TRUE( EVP_DigestSign(md_ctx.get(), sig.data(), &sig_len, tbs, tbs_len)); sig.resize(sig_len); ASSERT_TRUE( X509_REQ_set1_signature_value(csr.get(), sig.data(), sig.size())); } else { ASSERT_TRUE(X509_REQ_sign(csr.get(), pkey.get(), EVP_sha384())); } // Check the signature. EXPECT_TRUE(X509_REQ_verify(csr.get(), pkey.get())); // Re-encode the CSR. X509_REQ objects contain a cached // CertificationRequestInfo encoding and re-signing should have dropped // that cache. bssl::UniquePtr copy = ReencodeCSR(csr.get()); ASSERT_TRUE(copy); EXPECT_TRUE(X509_REQ_verify(copy.get(), pkey.get())); // Check the signature was over the new public key. bssl::UniquePtr copy_pubkey(X509_REQ_get_pubkey(copy.get())); ASSERT_TRUE(copy_pubkey); EXPECT_EQ(1, EVP_PKEY_cmp(pkey.get(), copy_pubkey.get())); } } } TEST(X509Test, Ed25519Sign) { uint8_t pub_bytes[32], priv_bytes[64]; ED25519_keypair(pub_bytes, priv_bytes); bssl::UniquePtr pub( EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, nullptr, pub_bytes, 32)); ASSERT_TRUE(pub); bssl::UniquePtr priv( EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, nullptr, priv_bytes, 32)); ASSERT_TRUE(priv); bssl::ScopedEVP_MD_CTX md_ctx; ASSERT_TRUE( EVP_DigestSignInit(md_ctx.get(), nullptr, nullptr, nullptr, priv.get())); ASSERT_TRUE(SignatureRoundTrips(md_ctx.get(), pub.get())); } static bool PEMToDER(bssl::UniquePtr *out, size_t *out_len, const char *pem) { bssl::UniquePtr bio(BIO_new_mem_buf(pem, strlen(pem))); if (!bio) { return false; } char *name, *header; uint8_t *data; long data_len; if (!PEM_read_bio(bio.get(), &name, &header, &data, &data_len)) { fprintf(stderr, "failed to read PEM data.\n"); return false; } OPENSSL_free(name); OPENSSL_free(header); out->reset(data); *out_len = data_len; return true; } TEST(X509Test, TestFromBuffer) { size_t data_len; bssl::UniquePtr data; ASSERT_TRUE(PEMToDER(&data, &data_len, kRootCAPEM)); bssl::UniquePtr buf( CRYPTO_BUFFER_new(data.get(), data_len, nullptr)); ASSERT_TRUE(buf); bssl::UniquePtr root(X509_parse_from_buffer(buf.get())); ASSERT_TRUE(root); const uint8_t *enc_pointer = root->cert_info->enc.enc; const uint8_t *buf_pointer = CRYPTO_BUFFER_data(buf.get()); ASSERT_GE(enc_pointer, buf_pointer); ASSERT_LT(enc_pointer, buf_pointer + CRYPTO_BUFFER_len(buf.get())); buf.reset(); /* This ensures the X509 took a reference to |buf|, otherwise this will be a * reference to free memory and ASAN should notice. */ ASSERT_EQ(0x30, enc_pointer[0]); } TEST(X509Test, TestFromBufferWithTrailingData) { size_t data_len; bssl::UniquePtr data; ASSERT_TRUE(PEMToDER(&data, &data_len, kRootCAPEM)); auto trailing_data = std::make_unique(data_len + 1); OPENSSL_memcpy(trailing_data.get(), data.get(), data_len); bssl::UniquePtr buf_trailing_data( CRYPTO_BUFFER_new(trailing_data.get(), data_len + 1, nullptr)); ASSERT_TRUE(buf_trailing_data); bssl::UniquePtr root_trailing_data( X509_parse_from_buffer(buf_trailing_data.get())); ASSERT_FALSE(root_trailing_data); } TEST(X509Test, TestFromBufferModified) { size_t data_len; bssl::UniquePtr data; ASSERT_TRUE(PEMToDER(&data, &data_len, kRootCAPEM)); bssl::UniquePtr buf( CRYPTO_BUFFER_new(data.get(), data_len, nullptr)); ASSERT_TRUE(buf); bssl::UniquePtr root(X509_parse_from_buffer(buf.get())); ASSERT_TRUE(root); bssl::UniquePtr fourty_two(ASN1_INTEGER_new()); ASN1_INTEGER_set_int64(fourty_two.get(), 42); X509_set_serialNumber(root.get(), fourty_two.get()); ASSERT_EQ(static_cast(data_len), i2d_X509(root.get(), nullptr)); // Re-encode the TBSCertificate. i2d_re_X509_tbs(root.get(), nullptr); ASSERT_NE(static_cast(data_len), i2d_X509(root.get(), nullptr)); } TEST(X509Test, TestFromBufferReused) { size_t data_len; bssl::UniquePtr data; ASSERT_TRUE(PEMToDER(&data, &data_len, kRootCAPEM)); bssl::UniquePtr buf( CRYPTO_BUFFER_new(data.get(), data_len, nullptr)); ASSERT_TRUE(buf); bssl::UniquePtr root(X509_parse_from_buffer(buf.get())); ASSERT_TRUE(root); size_t data2_len; bssl::UniquePtr data2; ASSERT_TRUE(PEMToDER(&data2, &data2_len, kLeafPEM)); 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 i2d_storage(i2d); ASSERT_TRUE(PEMToDER(&data2, &data2_len, kLeafPEM)); ASSERT_EQ(static_cast(data2_len), i2d_len); ASSERT_EQ(0, OPENSSL_memcmp(data2.get(), i2d, i2d_len)); ASSERT_EQ(nullptr, root->cert_info->enc.buf); } TEST(X509Test, TestFailedParseFromBuffer) { static const uint8_t kNonsense[] = {1, 2, 3, 4, 5}; bssl::UniquePtr buf( CRYPTO_BUFFER_new(kNonsense, sizeof(kNonsense), nullptr)); ASSERT_TRUE(buf); bssl::UniquePtr cert(X509_parse_from_buffer(buf.get())); ASSERT_FALSE(cert); ERR_clear_error(); // Test a buffer with trailing data. size_t data_len; bssl::UniquePtr data; ASSERT_TRUE(PEMToDER(&data, &data_len, kRootCAPEM)); auto data_with_trailing_byte = std::make_unique(data_len + 1); OPENSSL_memcpy(data_with_trailing_byte.get(), data.get(), data_len); data_with_trailing_byte[data_len] = 0; bssl::UniquePtr buf_with_trailing_byte( CRYPTO_BUFFER_new(data_with_trailing_byte.get(), data_len + 1, nullptr)); ASSERT_TRUE(buf_with_trailing_byte); bssl::UniquePtr root( X509_parse_from_buffer(buf_with_trailing_byte.get())); ASSERT_FALSE(root); ERR_clear_error(); } TEST(X509Test, TestPrintUTCTIME) { static const struct { const char *val, *want; } asn1_utctime_tests[] = { {"", "Bad time value"}, // Correct RFC 5280 form. Test years < 2000 and > 2000. {"090303125425Z", "Mar 3 12:54:25 2009 GMT"}, {"900303125425Z", "Mar 3 12:54:25 1990 GMT"}, {"000303125425Z", "Mar 3 12:54:25 2000 GMT"}, // Correct form, bad values. {"000000000000Z", "Bad time value"}, {"999999999999Z", "Bad time value"}, // Missing components. {"090303125425", "Bad time value"}, {"9003031254", "Bad time value"}, {"9003031254Z", "Bad time value"}, // GENERALIZEDTIME confused for UTCTIME. {"20090303125425Z", "Bad time value"}, // Legal ASN.1, but not legal RFC 5280. {"9003031254+0800", "Bad time value"}, {"9003031254-0800", "Bad time value"}, // Trailing garbage. {"9003031254Z ", "Bad time value"}, }; for (auto t : asn1_utctime_tests) { SCOPED_TRACE(t.val); bssl::UniquePtr tm(ASN1_UTCTIME_new()); ASSERT_TRUE(tm); bssl::UniquePtr bio(BIO_new(BIO_s_mem())); ASSERT_TRUE(bio); // Use this instead of ASN1_UTCTIME_set() because some callers get // type-confused and pass ASN1_GENERALIZEDTIME to ASN1_UTCTIME_print(). // ASN1_UTCTIME_set_string() is stricter, and would reject the inputs in // question. ASSERT_TRUE(ASN1_STRING_set(tm.get(), t.val, strlen(t.val))); const int ok = ASN1_UTCTIME_print(bio.get(), tm.get()); const uint8_t *contents; size_t len; ASSERT_TRUE(BIO_mem_contents(bio.get(), &contents, &len)); EXPECT_EQ(ok, (strcmp(t.want, "Bad time value") != 0) ? 1 : 0); EXPECT_EQ(t.want, 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 free_bn(bn); { bssl::UniquePtr asn1(BN_to_ASN1_INTEGER(bn, nullptr)); ASSERT_TRUE(asn1); bssl::UniquePtr out(i2s_ASN1_INTEGER(nullptr, asn1.get())); ASSERT_TRUE(out.get()); EXPECT_STREQ(in, out.get()); } { bssl::UniquePtr asn1(BN_to_ASN1_ENUMERATED(bn, nullptr)); ASSERT_TRUE(asn1); bssl::UniquePtr out(i2s_ASN1_ENUMERATED(nullptr, asn1.get())); ASSERT_TRUE(out.get()); EXPECT_STREQ(in, out.get()); } } } TEST(X509Test, X509AlgorSetMd) { bssl::UniquePtr alg(X509_ALGOR_new()); ASSERT_TRUE(alg); EXPECT_TRUE(X509_ALGOR_set_md(alg.get(), EVP_sha256())); const ASN1_OBJECT *obj; const void *pval; int ptype = 0; X509_ALGOR_get0(&obj, &ptype, &pval, alg.get()); EXPECT_TRUE(obj); EXPECT_EQ(OBJ_obj2nid(obj), NID_sha256); EXPECT_EQ(ptype, V_ASN1_NULL); // OpenSSL has V_ASN1_UNDEF EXPECT_EQ(pval, nullptr); EXPECT_TRUE(X509_ALGOR_set_md(alg.get(), EVP_md5())); X509_ALGOR_get0(&obj, &ptype, &pval, alg.get()); EXPECT_EQ(OBJ_obj2nid(obj), NID_md5); EXPECT_EQ(ptype, V_ASN1_NULL); EXPECT_EQ(pval, nullptr); } TEST(X509Test, X509NameSet) { bssl::UniquePtr name(X509_NAME_new()); ASSERT_TRUE(name); EXPECT_TRUE(X509_NAME_add_entry_by_txt( name.get(), "C", MBSTRING_ASC, reinterpret_cast("US"), -1, -1, 0)); EXPECT_EQ(X509_NAME_entry_count(name.get()), 1); EXPECT_TRUE(X509_NAME_add_entry_by_txt( name.get(), "C", MBSTRING_ASC, reinterpret_cast("CA"), -1, -1, 0)); EXPECT_EQ(X509_NAME_entry_count(name.get()), 2); EXPECT_TRUE(X509_NAME_add_entry_by_txt( name.get(), "C", MBSTRING_ASC, reinterpret_cast("UK"), -1, -1, 0)); EXPECT_EQ(X509_NAME_entry_count(name.get()), 3); EXPECT_TRUE(X509_NAME_add_entry_by_txt( name.get(), "C", MBSTRING_ASC, reinterpret_cast("JP"), -1, 1, 0)); EXPECT_EQ(X509_NAME_entry_count(name.get()), 4); // Check that the correct entries get incremented when inserting new entry. EXPECT_EQ(X509_NAME_ENTRY_set(X509_NAME_get_entry(name.get(), 1)), 1); EXPECT_EQ(X509_NAME_ENTRY_set(X509_NAME_get_entry(name.get(), 2)), 2); } // Tests that |X509_NAME_hash| and |X509_NAME_hash_old|'s values never change. // These functions figure into |X509_LOOKUP_hash_dir|'s on-disk format, so they // must remain stable. In particular, if we ever remove name canonicalization, // we'll need to preserve it for |X509_NAME_hash|. TEST(X509Test, NameHash) { struct { std::vector name_der; uint32_t hash; uint32_t hash_old; } kTests[] = { // SEQUENCE { // SET { // SEQUENCE { // # commonName // OBJECT_IDENTIFIER { 2.5.4.3 } // UTF8String { "Test Name" } // } // } // } {{0x30, 0x14, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x09, 0x54, 0x65, 0x73, 0x74, 0x20, 0x4e, 0x61, 0x6d, 0x65}, 0xc90fba01, 0x8c0d4fea}, // This name canonicalizes to the same value, with OpenSSL's algorithm, as // the above input, so |hash| matches. |hash_old| doesn't use // canonicalization and does not match. // // SEQUENCE { // SET { // SEQUENCE { // # commonName // OBJECT_IDENTIFIER { 2.5.4.3 } // BMPString { // u"\x09\n\x0b\x0c\x0d tEST\x09\n\x0b\x0c\x0d " // u"\x09\n\x0b\x0c\x0d nAME\x09\n\x0b\x0c\x0d " // } // } // } // } {{0x30, 0x4b, 0x31, 0x49, 0x30, 0x47, 0x06, 0x03, 0x55, 0x04, 0x03, 0x1e, 0x40, 0x00, 0x09, 0x00, 0x0a, 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x0d, 0x00, 0x20, 0x00, 0x74, 0x00, 0x45, 0x00, 0x53, 0x00, 0x54, 0x00, 0x09, 0x00, 0x0a, 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x0d, 0x00, 0x20, 0x00, 0x09, 0x00, 0x0a, 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x0d, 0x00, 0x20, 0x00, 0x6e, 0x00, 0x41, 0x00, 0x4d, 0x00, 0x45, 0x00, 0x09, 0x00, 0x0a, 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x0d, 0x00, 0x20}, 0xc90fba01, 0xbe2dd8c8}, }; for (const auto &t : kTests) { SCOPED_TRACE(Bytes(t.name_der)); const uint8_t *der = t.name_der.data(); bssl::UniquePtr name( d2i_X509_NAME(nullptr, &der, t.name_der.size())); ASSERT_TRUE(name); EXPECT_EQ(t.hash, X509_NAME_hash(name.get())); EXPECT_EQ(t.hash_old, X509_NAME_hash_old(name.get())); } } TEST(X509Test, NoBasicConstraintsCertSign) { bssl::UniquePtr root(CertFromPEM(kSANTypesRoot)); bssl::UniquePtr intermediate( CertFromPEM(kNoBasicConstraintsCertSignIntermediate)); bssl::UniquePtr leaf(CertFromPEM(kNoBasicConstraintsCertSignLeaf)); ASSERT_TRUE(root); ASSERT_TRUE(intermediate); ASSERT_TRUE(leaf); // The intermediate has keyUsage certSign, but is not marked as a CA in the // basicConstraints. EXPECT_EQ(X509_V_ERR_INVALID_CA, Verify(leaf.get(), {root.get()}, {intermediate.get()}, {}, 0)); // |X509_check_purpose| with |X509_PURPOSE_ANY| and purpose -1 do not check // basicConstraints, but other purpose types do. (This is redundant with the // actual basicConstraints check, but |X509_check_purpose| is public API.) EXPECT_TRUE(X509_check_purpose(intermediate.get(), -1, /*ca=*/1)); EXPECT_TRUE( X509_check_purpose(intermediate.get(), X509_PURPOSE_ANY, /*ca=*/1)); EXPECT_FALSE(X509_check_purpose(intermediate.get(), X509_PURPOSE_SSL_SERVER, /*ca=*/1)); } TEST(X509Test, NoBasicConstraintsNetscapeCA) { bssl::UniquePtr root(CertFromPEM(kSANTypesRoot)); bssl::UniquePtr intermediate( CertFromPEM(kNoBasicConstraintsNetscapeCAIntermediate)); bssl::UniquePtr leaf(CertFromPEM(kNoBasicConstraintsNetscapeCALeaf)); ASSERT_TRUE(root); ASSERT_TRUE(intermediate); ASSERT_TRUE(leaf); // The intermediate has a Netscape certificate type of "SSL CA", but is not // marked as a CA in the basicConstraints. EXPECT_EQ(X509_V_ERR_INVALID_CA, Verify(leaf.get(), {root.get()}, {intermediate.get()}, {}, 0)); } TEST(X509Test, MismatchAlgorithms) { bssl::UniquePtr cert(CertFromPEM(kSelfSignedMismatchAlgorithms)); ASSERT_TRUE(cert); bssl::UniquePtr pkey(X509_get_pubkey(cert.get())); ASSERT_TRUE(pkey); EXPECT_FALSE(X509_verify(cert.get(), pkey.get())); EXPECT_TRUE(ErrorEquals(ERR_get_error(), ERR_LIB_X509, X509_R_SIGNATURE_ALGORITHM_MISMATCH)); } // 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 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_new_mem_buf(pem.data(), pem.size())); ASSERT_TRUE(bio); bssl::UniquePtr infos( PEM_X509_INFO_read_bio(bio.get(), nullptr, nullptr, nullptr)); ASSERT_TRUE(infos); ASSERT_EQ(OPENSSL_ARRAY_SIZE(kExpected), sk_X509_INFO_num(infos.get())); for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(kExpected); i++) { SCOPED_TRACE(i); check_info(&kExpected[i], sk_X509_INFO_value(infos.get(), i)); } // Passing an existing stack appends to it. bio.reset(BIO_new_mem_buf(pem.data(), pem.size())); ASSERT_TRUE(bio); ASSERT_EQ(infos.get(), PEM_X509_INFO_read_bio(bio.get(), infos.get(), nullptr, nullptr)); ASSERT_EQ(2 * OPENSSL_ARRAY_SIZE(kExpected), sk_X509_INFO_num(infos.get())); for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(kExpected); i++) { SCOPED_TRACE(i); check_info(&kExpected[i], sk_X509_INFO_value(infos.get(), i)); check_info( &kExpected[i], sk_X509_INFO_value(infos.get(), i + OPENSSL_ARRAY_SIZE(kExpected))); } // Gracefully handle errors in both the append and fresh cases. std::string bad_pem = cert + cert + invalid; bio.reset(BIO_new_mem_buf(bad_pem.data(), bad_pem.size())); ASSERT_TRUE(bio); bssl::UniquePtr infos2( PEM_X509_INFO_read_bio(bio.get(), nullptr, nullptr, nullptr)); EXPECT_FALSE(infos2); bio.reset(BIO_new_mem_buf(bad_pem.data(), bad_pem.size())); ASSERT_TRUE(bio); EXPECT_FALSE( PEM_X509_INFO_read_bio(bio.get(), infos.get(), nullptr, nullptr)); EXPECT_EQ(2 * OPENSSL_ARRAY_SIZE(kExpected), sk_X509_INFO_num(infos.get())); } TEST(X509Test, ReadBIOEmpty) { bssl::UniquePtr bio(BIO_new_mem_buf(nullptr, 0)); ASSERT_TRUE(bio); // CPython expects |ASN1_R_HEADER_TOO_LONG| on EOF, to terminate a series of // certificates. bssl::UniquePtr x509(d2i_X509_bio(bio.get(), nullptr)); EXPECT_FALSE(x509); EXPECT_TRUE( ErrorEquals(ERR_get_error(), ERR_LIB_ASN1, ASN1_R_HEADER_TOO_LONG)); } TEST(X509Test, ReadBIOOneByte) { bssl::UniquePtr bio(BIO_new_mem_buf("\x30", 1)); ASSERT_TRUE(bio); // CPython expects |ASN1_R_HEADER_TOO_LONG| on EOF, to terminate a series of // certificates. This EOF appeared after some data, however, so we do not wish // to signal EOF. bssl::UniquePtr x509(d2i_X509_bio(bio.get(), nullptr)); EXPECT_FALSE(x509); EXPECT_TRUE( ErrorEquals(ERR_get_error(), ERR_LIB_ASN1, ASN1_R_NOT_ENOUGH_DATA)); } TEST(X509Test, PartialBIOReturn) { // Create a filter BIO that only reads and writes one byte at a time. bssl::UniquePtr method(BIO_meth_new(0, nullptr)); ASSERT_TRUE(method); ASSERT_TRUE(BIO_meth_set_create(method.get(), [](BIO *b) -> int { BIO_set_init(b, 1); return 1; })); ASSERT_TRUE( BIO_meth_set_read(method.get(), [](BIO *b, char *out, int len) -> int { return BIO_read(BIO_next(b), out, std::min(len, 1)); })); ASSERT_TRUE(BIO_meth_set_write( method.get(), [](BIO *b, const char *in, int len) -> int { return BIO_write(BIO_next(b), in, std::min(len, 1)); })); bssl::UniquePtr bio(BIO_new(method.get())); ASSERT_TRUE(bio); BIO *mem_bio = BIO_new(BIO_s_mem()); ASSERT_TRUE(mem_bio); BIO_push(bio.get(), mem_bio); // BIO_push takes ownership. bssl::UniquePtr cert(CertFromPEM(kLeafPEM)); ASSERT_TRUE(cert); uint8_t *der = nullptr; int der_len = i2d_X509(cert.get(), &der); ASSERT_GT(der_len, 0); bssl::UniquePtr free_der(der); // Write the certificate into the BIO. Though we only write one byte at a // time, the write should succeed. ASSERT_EQ(1, i2d_X509_bio(bio.get(), cert.get())); const uint8_t *der2; size_t der2_len; ASSERT_TRUE(BIO_mem_contents(mem_bio, &der2, &der2_len)); EXPECT_EQ(Bytes(der, static_cast(der_len)), Bytes(der2, der2_len)); // Read the certificate back out of the BIO. Though we only read one byte at a // time, the read should succeed. bssl::UniquePtr cert2(d2i_X509_bio(bio.get(), nullptr)); ASSERT_TRUE(cert2); EXPECT_EQ(0, X509_cmp(cert.get(), cert2.get())); } TEST(X509Test, CommonNameFallback) { bssl::UniquePtr root = CertFromPEM(kSANTypesRoot); ASSERT_TRUE(root); bssl::UniquePtr with_sans = CertFromPEM(kCommonNameWithSANs); ASSERT_TRUE(with_sans); bssl::UniquePtr without_sans = CertFromPEM(kCommonNameWithoutSANs); ASSERT_TRUE(without_sans); bssl::UniquePtr with_email = CertFromPEM(kCommonNameWithEmailSAN); ASSERT_TRUE(with_email); bssl::UniquePtr with_ip = CertFromPEM(kCommonNameWithIPSAN); ASSERT_TRUE(with_ip); auto verify_cert = [&](X509 *leaf, unsigned flags, const char *host) { return Verify(leaf, {root.get()}, {}, {}, 0, [&](X509_STORE_CTX *ctx) { X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(ctx); ASSERT_TRUE(X509_VERIFY_PARAM_set1_host(param, host, strlen(host))); X509_VERIFY_PARAM_set_hostflags(param, flags); }); }; // 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(str), strlen(str))); } for (const char *str : kInvalid) { SCOPED_TRACE(str); EXPECT_FALSE(x509v3_looks_like_dns_name( reinterpret_cast(str), strlen(str))); } } TEST(X509Test, CommonNameAndNameConstraints) { bssl::UniquePtr root = CertFromPEM(kSANTypesRoot); ASSERT_TRUE(root); bssl::UniquePtr intermediate = CertFromPEM(kConstrainedIntermediate); ASSERT_TRUE(intermediate); bssl::UniquePtr permitted = CertFromPEM(kCommonNamePermittedLeaf); ASSERT_TRUE(permitted); bssl::UniquePtr not_permitted = CertFromPEM(kCommonNameNotPermittedLeaf); ASSERT_TRUE(not_permitted); bssl::UniquePtr not_permitted_with_sans = CertFromPEM(kCommonNameNotPermittedWithSANsLeaf); ASSERT_TRUE(not_permitted_with_sans); bssl::UniquePtr 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 root = CertFromPEM(kSANTypesRoot); ASSERT_TRUE(root); bssl::UniquePtr ms_sgc = CertFromPEM(kMicrosoftSGCCert); ASSERT_TRUE(ms_sgc); bssl::UniquePtr ns_sgc = CertFromPEM(kNetscapeSGCCert); ASSERT_TRUE(ns_sgc); bssl::UniquePtr server_eku = CertFromPEM(kServerEKUCert); ASSERT_TRUE(server_eku); bssl::UniquePtr server_eku_plus_ms_sgc = CertFromPEM(kServerEKUPlusMicrosoftSGCCert); ASSERT_TRUE(server_eku_plus_ms_sgc); bssl::UniquePtr any_eku = CertFromPEM(kAnyEKU); ASSERT_TRUE(any_eku); bssl::UniquePtr no_eku = CertFromPEM(kNoEKU); ASSERT_TRUE(no_eku); auto verify_cert = [&root](X509 *leaf) { return Verify(leaf, {root.get()}, /*intermediates=*/{}, /*crls=*/{}, /*flags=*/0, [&](X509_STORE_CTX *ctx) { X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(ctx); ASSERT_TRUE(X509_VERIFY_PARAM_set_purpose( param, X509_PURPOSE_SSL_SERVER)); }); }; // Neither the Microsoft nor Netscape SGC EKU should be sufficient for // |X509_PURPOSE_SSL_SERVER|. The "any" EKU probably, technically, should be. // However, we've never accepted it and it's not acceptable in leaf // certificates by the Baseline, so perhaps we don't need this complexity. for (X509 *leaf : {ms_sgc.get(), ns_sgc.get(), any_eku.get()}) { EXPECT_EQ(X509_V_ERR_INVALID_PURPOSE, verify_cert(leaf)); } // The server-auth EKU is sufficient, and it doesn't matter if an SGC EKU is // also included. Lastly, not specifying an EKU is also valid. for (X509 *leaf : {server_eku.get(), server_eku_plus_ms_sgc.get(), no_eku.get()}) { EXPECT_EQ(X509_V_OK, verify_cert(leaf)); } } // Test that invalid extensions are rejected by, if not the parser, at least the // verifier. TEST(X509Test, InvalidExtensions) { bssl::UniquePtr root = CertFromPEM( GetTestData("crypto/x509/test/invalid_extension_root.pem").c_str()); ASSERT_TRUE(root); bssl::UniquePtr intermediate = CertFromPEM( GetTestData("crypto/x509/test/invalid_extension_intermediate.pem") .c_str()); ASSERT_TRUE(intermediate); bssl::UniquePtr leaf = CertFromPEM( GetTestData("crypto/x509/test/invalid_extension_leaf.pem").c_str()); ASSERT_TRUE(leaf); // Sanity-check that the baseline chain is accepted. EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {intermediate.get()}, {})); static const char *kExtensions[] = { "authority_key_identifier", "basic_constraints", "ext_key_usage", "key_usage", "name_constraints", "subject_alt_name", "subject_key_identifier", }; for (const char *ext : kExtensions) { SCOPED_TRACE(ext); bssl::UniquePtr invalid_root = CertFromPEM( GetTestData((std::string("crypto/x509/test/invalid_extension_root_") + ext + ".pem") .c_str()) .c_str()); ASSERT_TRUE(invalid_root); bssl::UniquePtr invalid_intermediate = CertFromPEM( GetTestData( (std::string("crypto/x509/test/invalid_extension_intermediate_") + ext + ".pem") .c_str()) .c_str()); ASSERT_TRUE(invalid_intermediate); bssl::UniquePtr invalid_leaf = CertFromPEM( GetTestData((std::string("crypto/x509/test/invalid_extension_leaf_") + ext + ".pem") .c_str()) .c_str()); ASSERT_TRUE(invalid_leaf); bssl::UniquePtr trailing_leaf = CertFromPEM( GetTestData( (std::string("crypto/x509/test/trailing_data_leaf_") + ext + ".pem") .c_str()) .c_str()); ASSERT_TRUE(trailing_leaf); EXPECT_EQ( X509_V_ERR_INVALID_EXTENSION, Verify(invalid_leaf.get(), {root.get()}, {intermediate.get()}, {})); EXPECT_EQ( X509_V_ERR_INVALID_EXTENSION, Verify(trailing_leaf.get(), {root.get()}, {intermediate.get()}, {})); // If the invalid extension is on an intermediate or root, // |X509_verify_cert| notices by way of being unable to build a path to // a valid issuer. EXPECT_EQ( X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, Verify(leaf.get(), {root.get()}, {invalid_intermediate.get()}, {})); EXPECT_EQ( X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, Verify(leaf.get(), {invalid_root.get()}, {intermediate.get()}, {})); } } // kExplicitDefaultVersionPEM is an X.509v1 certificate with the version number // encoded explicitly, rather than omitted as required by DER. static const char kExplicitDefaultVersionPEM[] = R"( -----BEGIN CERTIFICATE----- MIIBfTCCASSgAwIBAAIJANlMBNpJfb/rMAkGByqGSM49BAEwRTELMAkGA1UEBhMC QVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdp dHMgUHR5IEx0ZDAeFw0xNDA0MjMyMzIxNTdaFw0xNDA1MjMyMzIxNTdaMEUxCzAJ BgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5l dCBXaWRnaXRzIFB0eSBMdGQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATmK2ni v2Wfl74vHg2UikzVl2u3qR4NRvvdqakendy6WgHn1peoChj5w8SjHlbifINI2xYa HPUdfvGULUvPciLBMAkGByqGSM49BAEDSAAwRQIhAPKgNV5ROjbDgnmb7idQhY5w BnSVV9IpdAD0vhWHXcQHAiB8HnkUaiGD8Hp0aHlfFJmaaLTxy54VXuYfMlJhXnXJ FA== -----END CERTIFICATE----- )"; // kNegativeVersionPEM is an X.509 certificate with a negative version number. static const char kNegativeVersionPEM[] = R"( -----BEGIN CERTIFICATE----- MIIBfTCCASSgAwIB/wIJANlMBNpJfb/rMAkGByqGSM49BAEwRTELMAkGA1UEBhMC QVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdp dHMgUHR5IEx0ZDAeFw0xNDA0MjMyMzIxNTdaFw0xNDA1MjMyMzIxNTdaMEUxCzAJ BgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5l dCBXaWRnaXRzIFB0eSBMdGQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATmK2ni v2Wfl74vHg2UikzVl2u3qR4NRvvdqakendy6WgHn1peoChj5w8SjHlbifINI2xYa HPUdfvGULUvPciLBMAkGByqGSM49BAEDSAAwRQIhAPKgNV5ROjbDgnmb7idQhY5w BnSVV9IpdAD0vhWHXcQHAiB8HnkUaiGD8Hp0aHlfFJmaaLTxy54VXuYfMlJhXnXJ FA== -----END CERTIFICATE----- )"; // kFutureVersionPEM is an X.509 certificate with a version number value of // three, which is not defined. (v3 has value two). static const char kFutureVersionPEM[] = R"( -----BEGIN CERTIFICATE----- MIIBfTCCASSgAwIBAwIJANlMBNpJfb/rMAkGByqGSM49BAEwRTELMAkGA1UEBhMC QVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdp dHMgUHR5IEx0ZDAeFw0xNDA0MjMyMzIxNTdaFw0xNDA1MjMyMzIxNTdaMEUxCzAJ BgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5l dCBXaWRnaXRzIFB0eSBMdGQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATmK2ni v2Wfl74vHg2UikzVl2u3qR4NRvvdqakendy6WgHn1peoChj5w8SjHlbifINI2xYa HPUdfvGULUvPciLBMAkGByqGSM49BAEDSAAwRQIhAPKgNV5ROjbDgnmb7idQhY5w BnSVV9IpdAD0vhWHXcQHAiB8HnkUaiGD8Hp0aHlfFJmaaLTxy54VXuYfMlJhXnXJ FA== -----END CERTIFICATE----- )"; // kOverflowVersionPEM is an X.509 certificate with a version field which // overflows |uint64_t|. static const char kOverflowVersionPEM[] = R"( -----BEGIN CERTIFICATE----- MIIBoDCCAUegJgIkAP////////////////////////////////////////////// AgkA2UwE2kl9v+swCQYHKoZIzj0EATBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwK U29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMB4X DTE0MDQyMzIzMjE1N1oXDTE0MDUyMzIzMjE1N1owRTELMAkGA1UEBhMCQVUxEzAR BgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5 IEx0ZDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOYraeK/ZZ+Xvi8eDZSKTNWX a7epHg1G+92pqR6d3LpaAefWl6gKGPnDxKMeVuJ8g0jbFhoc9R1+8ZQtS89yIsEw CQYHKoZIzj0EAQNIADBFAiEA8qA1XlE6NsOCeZvuJ1CFjnAGdJVX0il0APS+FYdd xAcCIHweeRRqIYPwenRoeV8UmZpotPHLnhVe5h8yUmFedckU -----END CERTIFICATE----- )"; // kV1WithExtensionsPEM is an X.509v1 certificate with extensions. static const char kV1WithExtensionsPEM[] = R"( -----BEGIN CERTIFICATE----- MIIByjCCAXECCQDZTATaSX2/6zAJBgcqhkjOPQQBMEUxCzAJBgNVBAYTAkFVMRMw EQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0 eSBMdGQwHhcNMTQwNDIzMjMyMTU3WhcNMTQwNTIzMjMyMTU3WjBFMQswCQYDVQQG EwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lk Z2l0cyBQdHkgTHRkMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE5itp4r9ln5e+ Lx4NlIpM1Zdrt6keDUb73ampHp3culoB59aXqAoY+cPEox5W4nyDSNsWGhz1HX7x lC1Lz3IiwaNQME4wHQYDVR0OBBYEFKuE0qyrlfCCThZ4B1VXX+QmjYLRMB8GA1Ud IwQYMBaAFKuE0qyrlfCCThZ4B1VXX+QmjYLRMAwGA1UdEwQFMAMBAf8wCQYHKoZI zj0EAQNIADBFAiEA8qA1XlE6NsOCeZvuJ1CFjnAGdJVX0il0APS+FYddxAcCIHwe eRRqIYPwenRoeV8UmZpotPHLnhVe5h8yUmFedckU -----END CERTIFICATE----- )"; // kV2WithExtensionsPEM is an X.509v2 certificate with extensions. static const char kV2WithExtensionsPEM[] = R"( -----BEGIN CERTIFICATE----- MIIBzzCCAXagAwIBAQIJANlMBNpJfb/rMAkGByqGSM49BAEwRTELMAkGA1UEBhMC QVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdp dHMgUHR5IEx0ZDAeFw0xNDA0MjMyMzIxNTdaFw0xNDA1MjMyMzIxNTdaMEUxCzAJ BgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5l dCBXaWRnaXRzIFB0eSBMdGQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATmK2ni v2Wfl74vHg2UikzVl2u3qR4NRvvdqakendy6WgHn1peoChj5w8SjHlbifINI2xYa HPUdfvGULUvPciLBo1AwTjAdBgNVHQ4EFgQUq4TSrKuV8IJOFngHVVdf5CaNgtEw HwYDVR0jBBgwFoAUq4TSrKuV8IJOFngHVVdf5CaNgtEwDAYDVR0TBAUwAwEB/zAJ BgcqhkjOPQQBA0gAMEUCIQDyoDVeUTo2w4J5m+4nUIWOcAZ0lVfSKXQA9L4Vh13E BwIgfB55FGohg/B6dGh5XxSZmmi08cueFV7mHzJSYV51yRQ= -----END CERTIFICATE----- )"; // kV1WithIssuerUniqueIDPEM is an X.509v1 certificate with an issuerUniqueID. static const char kV1WithIssuerUniqueIDPEM[] = R"( -----BEGIN CERTIFICATE----- MIIBgzCCASoCCQDZTATaSX2/6zAJBgcqhkjOPQQBMEUxCzAJBgNVBAYTAkFVMRMw EQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0 eSBMdGQwHhcNMTQwNDIzMjMyMTU3WhcNMTQwNTIzMjMyMTU3WjBFMQswCQYDVQQG EwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lk Z2l0cyBQdHkgTHRkMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE5itp4r9ln5e+ Lx4NlIpM1Zdrt6keDUb73ampHp3culoB59aXqAoY+cPEox5W4nyDSNsWGhz1HX7x lC1Lz3IiwYEJAAEjRWeJq83vMAkGByqGSM49BAEDSAAwRQIhAPKgNV5ROjbDgnmb 7idQhY5wBnSVV9IpdAD0vhWHXcQHAiB8HnkUaiGD8Hp0aHlfFJmaaLTxy54VXuYf MlJhXnXJFA== -----END CERTIFICATE----- )"; // kV1WithSubjectUniqueIDPEM is an X.509v1 certificate with an issuerUniqueID. static const char kV1WithSubjectUniqueIDPEM[] = R"( -----BEGIN CERTIFICATE----- MIIBgzCCASoCCQDZTATaSX2/6zAJBgcqhkjOPQQBMEUxCzAJBgNVBAYTAkFVMRMw EQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0 eSBMdGQwHhcNMTQwNDIzMjMyMTU3WhcNMTQwNTIzMjMyMTU3WjBFMQswCQYDVQQG EwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lk Z2l0cyBQdHkgTHRkMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE5itp4r9ln5e+ Lx4NlIpM1Zdrt6keDUb73ampHp3culoB59aXqAoY+cPEox5W4nyDSNsWGhz1HX7x lC1Lz3IiwYIJAAEjRWeJq83vMAkGByqGSM49BAEDSAAwRQIhAPKgNV5ROjbDgnmb 7idQhY5wBnSVV9IpdAD0vhWHXcQHAiB8HnkUaiGD8Hp0aHlfFJmaaLTxy54VXuYf MlJhXnXJFA== -----END CERTIFICATE----- )"; // kV1CRLWithExtensionsPEM is a v1 CRL with extensions. static const char kV1CRLWithExtensionsPEM[] = R"( -----BEGIN X509 CRL----- MIIBpDCBjTANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJVUzETMBEGA1UECAwK Q2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzESMBAGA1UECgwJQm9y aW5nU1NMFw0xNjA5MjYxNTEwNTVaFw0xNjEwMjYxNTEwNTVaoA4wDDAKBgNVHRQE AwIBATANBgkqhkiG9w0BAQsFAAOCAQEAnrBKKgvd9x9zwK9rtUvVeFeJ7+LNZEAc +a5oxpPNEsJx6hXoApYEbzXMxuWBQoCs5iEBycSGudct21L+MVf27M38KrWoeOkq 0a2siqViQZO2Fb/SUFR0k9zb8xl86Zf65lgPplALun0bV/HT7MJcl04Tc4osdsAR eBs5nqTGNEd5AlC1iKHvQZkM//MD51DspKnDpsDiUVi54h9C1SpfZmX8H2Vvdiyu 0fZ/bPAM3VAGawatf/SyWfBMyKpoPXEG39oAzmjjOj8en82psn7m474IGaho/vBb hl1ms5qQiLYPjm4YELtnXQoFyC72tBjbdFd/ZE9k4CNKDbxFUXFbkw== -----END X509 CRL----- )"; // kExplicitDefaultVersionCRLPEM is a v1 CRL with an explicitly-encoded version // field. static const char kExplicitDefaultVersionCRLPEM[] = R"( -----BEGIN X509 CRL----- MIIBlzCBgAIBADANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJVUzETMBEGA1UE CAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzESMBAGA1UECgwJ Qm9yaW5nU1NMFw0xNjA5MjYxNTEwNTVaFw0xNjEwMjYxNTEwNTVaMA0GCSqGSIb3 DQEBCwUAA4IBAQCesEoqC933H3PAr2u1S9V4V4nv4s1kQBz5rmjGk80SwnHqFegC lgRvNczG5YFCgKzmIQHJxIa51y3bUv4xV/bszfwqtah46SrRrayKpWJBk7YVv9JQ VHST3NvzGXzpl/rmWA+mUAu6fRtX8dPswlyXThNziix2wBF4GzmepMY0R3kCULWI oe9BmQz/8wPnUOykqcOmwOJRWLniH0LVKl9mZfwfZW92LK7R9n9s8AzdUAZrBq1/ 9LJZ8EzIqmg9cQbf2gDOaOM6Px6fzamyfubjvggZqGj+8FuGXWazmpCItg+ObhgQ u2ddCgXILva0GNt0V39kT2TgI0oNvEVRcVuT -----END X509 CRL----- )"; // kV3CRLPEM is a v3 CRL. CRL versions only go up to v2. static const char kV3CRLPEM[] = R"( -----BEGIN X509 CRL----- MIIBpzCBkAIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJVUzETMBEGA1UE CAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzESMBAGA1UECgwJ Qm9yaW5nU1NMFw0xNjA5MjYxNTEwNTVaFw0xNjEwMjYxNTEwNTVaoA4wDDAKBgNV HRQEAwIBATANBgkqhkiG9w0BAQsFAAOCAQEAnrBKKgvd9x9zwK9rtUvVeFeJ7+LN ZEAc+a5oxpPNEsJx6hXoApYEbzXMxuWBQoCs5iEBycSGudct21L+MVf27M38KrWo eOkq0a2siqViQZO2Fb/SUFR0k9zb8xl86Zf65lgPplALun0bV/HT7MJcl04Tc4os dsAReBs5nqTGNEd5AlC1iKHvQZkM//MD51DspKnDpsDiUVi54h9C1SpfZmX8H2Vv diyu0fZ/bPAM3VAGawatf/SyWfBMyKpoPXEG39oAzmjjOj8en82psn7m474IGaho /vBbhl1ms5qQiLYPjm4YELtnXQoFyC72tBjbdFd/ZE9k4CNKDbxFUXFbkw== -----END X509 CRL----- )"; // kV2CSRPEM is a v2 CSR. CSR versions only go up to v1. static const char kV2CSRPEM[] = R"( -----BEGIN CERTIFICATE REQUEST----- MIHJMHECAQEwDzENMAsGA1UEAwwEVGVzdDBZMBMGByqGSM49AgEGCCqGSM49AwEH A0IABJjsayyAQod1J7UJYNT8AH4WWxLdKV0ozhrIz6hCzBAze7AqXWOSH8G+1EWC pSfL3oMQNtBdJS0kpXXaUqEAgTSgADAKBggqhkjOPQQDAgNIADBFAiAUXVaEYATg 4Cc917T73KBImxh6xyhsA5pKuYpq1S4m9wIhAK+G93HR4ur7Ghel6+zUTvIAsj9e rsn4lSYsqI4OI4ei -----END CERTIFICATE REQUEST----- )"; // kV3CSRPEM is a v3 CSR. CSR versions only go up to v1. static const char kV3CSRPEM[] = R"( -----BEGIN CERTIFICATE REQUEST----- MIHJMHECAQIwDzENMAsGA1UEAwwEVGVzdDBZMBMGByqGSM49AgEGCCqGSM49AwEH A0IABJjsayyAQod1J7UJYNT8AH4WWxLdKV0ozhrIz6hCzBAze7AqXWOSH8G+1EWC pSfL3oMQNtBdJS0kpXXaUqEAgTSgADAKBggqhkjOPQQDAgNIADBFAiAUXVaEYATg 4Cc917T73KBImxh6xyhsA5pKuYpq1S4m9wIhAK+G93HR4ur7Ghel6+zUTvIAsj9e rsn4lSYsqI4OI4ei -----END CERTIFICATE REQUEST----- )"; // Test that the library enforces versions are valid and match the fields // present. TEST(X509Test, InvalidVersion) { // kExplicitDefaultVersionPEM is invalid but, for now, we accept it. See // https://crbug.com/boringssl/364. EXPECT_TRUE(CertFromPEM(kExplicitDefaultVersionPEM)); EXPECT_TRUE(CRLFromPEM(kExplicitDefaultVersionCRLPEM)); EXPECT_FALSE(CertFromPEM(kNegativeVersionPEM)); EXPECT_FALSE(CertFromPEM(kFutureVersionPEM)); EXPECT_FALSE(CertFromPEM(kOverflowVersionPEM)); EXPECT_FALSE(CertFromPEM(kV1WithExtensionsPEM)); EXPECT_FALSE(CertFromPEM(kV2WithExtensionsPEM)); EXPECT_FALSE(CertFromPEM(kV1WithIssuerUniqueIDPEM)); EXPECT_FALSE(CertFromPEM(kV1WithSubjectUniqueIDPEM)); EXPECT_FALSE(CRLFromPEM(kV1CRLWithExtensionsPEM)); EXPECT_FALSE(CRLFromPEM(kV3CRLPEM)); EXPECT_FALSE(CSRFromPEM(kV2CSRPEM)); // kV3CSRPEM is invalid but, for now, we accept it. See // https://github.com/certbot/certbot/pull/9334 EXPECT_TRUE(CSRFromPEM(kV3CSRPEM)); bssl::UniquePtr x509(X509_new()); ASSERT_TRUE(x509); EXPECT_FALSE(X509_set_version(x509.get(), -1)); EXPECT_FALSE(X509_set_version(x509.get(), X509_VERSION_3 + 1)); EXPECT_FALSE(X509_set_version(x509.get(), 9999)); bssl::UniquePtr crl(X509_CRL_new()); ASSERT_TRUE(crl); EXPECT_FALSE(X509_CRL_set_version(crl.get(), -1)); EXPECT_FALSE(X509_CRL_set_version(crl.get(), X509_CRL_VERSION_2 + 1)); EXPECT_FALSE(X509_CRL_set_version(crl.get(), 9999)); bssl::UniquePtr req(X509_REQ_new()); ASSERT_TRUE(req); EXPECT_FALSE(X509_REQ_set_version(req.get(), -1)); EXPECT_FALSE(X509_REQ_set_version(req.get(), X509_REQ_VERSION_1 + 1)); EXPECT_FALSE(X509_REQ_set_version(req.get(), 9999)); } // Unlike upstream OpenSSL, we require a non-null store in // |X509_STORE_CTX_init|. TEST(X509Test, NullStore) { bssl::UniquePtr leaf(CertFromPEM(kLeafPEM)); ASSERT_TRUE(leaf); bssl::UniquePtr ctx(X509_STORE_CTX_new()); ASSERT_TRUE(ctx); EXPECT_FALSE(X509_STORE_CTX_init(ctx.get(), nullptr, leaf.get(), nullptr)); } TEST(X509Test, StoreCtxReuse) { bssl::UniquePtr leaf(CertFromPEM(kLeafPEM)); ASSERT_TRUE(leaf); bssl::UniquePtr store(X509_STORE_new()); ASSERT_TRUE(store); bssl::UniquePtr ctx(X509_STORE_CTX_new()); ASSERT_TRUE(ctx); ASSERT_TRUE(X509_STORE_CTX_init(ctx.get(), store.get(), leaf.get(), nullptr)); // Re-initializing |ctx| should not leak memory. ASSERT_TRUE(X509_STORE_CTX_init(ctx.get(), store.get(), leaf.get(), nullptr)); } TEST(X509Test, BasicConstraints) { const uint32_t kFlagMask = EXFLAG_CA | EXFLAG_BCONS | EXFLAG_INVALID; static const struct { const char *file; uint32_t flags; int path_len; } kTests[] = { {"basic_constraints_none.pem", 0, -1}, {"basic_constraints_ca.pem", EXFLAG_CA | EXFLAG_BCONS, -1}, {"basic_constraints_ca_pathlen_0.pem", EXFLAG_CA | EXFLAG_BCONS, 0}, {"basic_constraints_ca_pathlen_1.pem", EXFLAG_CA | EXFLAG_BCONS, 1}, {"basic_constraints_ca_pathlen_10.pem", EXFLAG_CA | EXFLAG_BCONS, 10}, {"basic_constraints_leaf.pem", EXFLAG_BCONS, -1}, {"invalid_extension_leaf_basic_constraints.pem", EXFLAG_INVALID, -1}, }; for (const auto &test : kTests) { SCOPED_TRACE(test.file); std::string path = "crypto/x509/test/"; path += test.file; bssl::UniquePtr cert = CertFromPEM(GetTestData(path.c_str()).c_str()); ASSERT_TRUE(cert); EXPECT_EQ(test.flags, X509_get_extension_flags(cert.get()) & kFlagMask); EXPECT_EQ(test.path_len, X509_get_pathlen(cert.get())); } } // The following strings are test certificates signed by kP256Key and kRSAKey, // with missing, NULL, or invalid algorithm parameters. static const char kP256NoParam[] = R"( -----BEGIN CERTIFICATE----- MIIBIDCBxqADAgECAgIE0jAKBggqhkjOPQQDAjAPMQ0wCwYDVQQDEwRUZXN0MCAX DTAwMDEwMTAwMDAwMFoYDzIxMDAwMTAxMDAwMDAwWjAPMQ0wCwYDVQQDEwRUZXN0 MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE5itp4r9ln5e+Lx4NlIpM1Zdrt6ke DUb73ampHp3culoB59aXqAoY+cPEox5W4nyDSNsWGhz1HX7xlC1Lz3IiwaMQMA4w DAYDVR0TBAUwAwEB/zAKBggqhkjOPQQDAgNJADBGAiEAqdIiF+bN9Cl44oUeICpy aXd7HqhpVUaglYKw9ChmNUACIQCpMdL0fNkFNDbRww9dSl/y7kBdk/tp16HiqeSy gGzFYg== -----END CERTIFICATE----- )"; static const char kP256NullParam[] = R"( -----BEGIN CERTIFICATE----- MIIBJDCByKADAgECAgIE0jAMBggqhkjOPQQDAgUAMA8xDTALBgNVBAMTBFRlc3Qw IBcNMDAwMTAxMDAwMDAwWhgPMjEwMDAxMDEwMDAwMDBaMA8xDTALBgNVBAMTBFRl c3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATmK2niv2Wfl74vHg2UikzVl2u3 qR4NRvvdqakendy6WgHn1peoChj5w8SjHlbifINI2xYaHPUdfvGULUvPciLBoxAw DjAMBgNVHRMEBTADAQH/MAwGCCqGSM49BAMCBQADSQAwRgIhAKILHmyo+F3Cn/VX UUeSXOQQKX5aLzsQitwwmNF3ZgH3AiEAsYHcrVj/ftmoQIORARkQ/+PrqntXev8r t6uPxHrmpUY= -----END CERTIFICATE----- )"; static const char kP256InvalidParam[] = R"( -----BEGIN CERTIFICATE----- MIIBMTCBz6ADAgECAgIE0jATBggqhkjOPQQDAgQHZ2FyYmFnZTAPMQ0wCwYDVQQD EwRUZXN0MCAXDTAwMDEwMTAwMDAwMFoYDzIxMDAwMTAxMDAwMDAwWjAPMQ0wCwYD VQQDEwRUZXN0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE5itp4r9ln5e+Lx4N lIpM1Zdrt6keDUb73ampHp3culoB59aXqAoY+cPEox5W4nyDSNsWGhz1HX7xlC1L z3IiwaMQMA4wDAYDVR0TBAUwAwEB/zATBggqhkjOPQQDAgQHZ2FyYmFnZQNIADBF AiAglpDf/YhN89LeJ2WAs/F0SJIrsuhS4uoInIz6WXUiuQIhAIu5Pwhp5E3Pbo8y fLULTZnynuQUULQkRcF7S7T2WpIL -----END CERTIFICATE----- )"; static const char kRSANoParam[] = R"( -----BEGIN CERTIFICATE----- MIIBWzCBx6ADAgECAgIE0jALBgkqhkiG9w0BAQswDzENMAsGA1UEAxMEVGVzdDAg Fw0wMDAxMDEwMDAwMDBaGA8yMTAwMDEwMTAwMDAwMFowDzENMAsGA1UEAxMEVGVz dDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOYraeK/ZZ+Xvi8eDZSKTNWXa7ep Hg1G+92pqR6d3LpaAefWl6gKGPnDxKMeVuJ8g0jbFhoc9R1+8ZQtS89yIsGjEDAO MAwGA1UdEwQFMAMBAf8wCwYJKoZIhvcNAQELA4GBAC1f8W3W0Ao7CPfIBQYDSbPh brZpbxdBU5x27JOS7iSa+Lc9pEH5VCX9vIypHVHXLPEfZ38yIt11eiyrmZB6w62N l9kIeZ6FVPmC30d3sXx70Jjs+ZX9yt7kD1gLyNAQQfeYfa4rORAZT1n2YitD74NY TWUH2ieFP3l+ecj1SeQR -----END CERTIFICATE----- )"; static const char kRSANullParam[] = R"( -----BEGIN CERTIFICATE----- MIIBXzCByaADAgECAgIE0jANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDEwRUZXN0 MCAXDTAwMDEwMTAwMDAwMFoYDzIxMDAwMTAxMDAwMDAwWjAPMQ0wCwYDVQQDEwRU ZXN0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE5itp4r9ln5e+Lx4NlIpM1Zdr t6keDUb73ampHp3culoB59aXqAoY+cPEox5W4nyDSNsWGhz1HX7xlC1Lz3IiwaMQ MA4wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOBgQAzVcfIv+Rq1KrMXqIL fPq/cWZjgqFZA1RGaGElNaqp+rkJfamq5tDGzckWpebrK+jjRN7yIlcWDtPpy3Gy seZfvtBDR0TwJm0S/pQl8prKB4wgALcwe3bmi56Rq85nzY5ZLNcP16LQxL+jAAua SwmQUz4bRpckRBj+sIyp1We+pg== -----END CERTIFICATE----- )"; static const char kRSAInvalidParam[] = R"( -----BEGIN CERTIFICATE----- MIIBbTCB0KADAgECAgIE0jAUBgkqhkiG9w0BAQsEB2dhcmJhZ2UwDzENMAsGA1UE AxMEVGVzdDAgFw0wMDAxMDEwMDAwMDBaGA8yMTAwMDEwMTAwMDAwMFowDzENMAsG A1UEAxMEVGVzdDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOYraeK/ZZ+Xvi8e DZSKTNWXa7epHg1G+92pqR6d3LpaAefWl6gKGPnDxKMeVuJ8g0jbFhoc9R1+8ZQt S89yIsGjEDAOMAwGA1UdEwQFMAMBAf8wFAYJKoZIhvcNAQELBAdnYXJiYWdlA4GB AHTJ6cWWjCNrZhqiWWVI3jdK+h5xpRG8jGMXxR4JnjtoYRRusJLOXhmapwCB6fA0 4vc+66O27v36yDmQX+tIc/hDrTpKNJptU8q3n2VagREvoHhkOTYkcCeS8vmnMtn8 5OMNZ/ajVwOssw61GcAlScRqEHkZFBoGp7e+QpgB2tf9 -----END CERTIFICATE----- )"; TEST(X509Test, AlgorithmParameters) { // P-256 parameters should be omitted, but we accept NULL ones. bssl::UniquePtr key = PrivateKeyFromPEM(kP256Key); ASSERT_TRUE(key); bssl::UniquePtr cert = CertFromPEM(kP256NoParam); ASSERT_TRUE(cert); EXPECT_TRUE(X509_verify(cert.get(), key.get())); cert = CertFromPEM(kP256NullParam); ASSERT_TRUE(cert); EXPECT_TRUE(X509_verify(cert.get(), key.get())); cert = CertFromPEM(kP256InvalidParam); ASSERT_TRUE(cert); EXPECT_FALSE(X509_verify(cert.get(), key.get())); EXPECT_TRUE( ErrorEquals(ERR_get_error(), ERR_LIB_X509, X509_R_INVALID_PARAMETER)); // RSA parameters should be NULL, but we accept omitted ones. key = PrivateKeyFromPEM(kRSAKey); ASSERT_TRUE(key); cert = CertFromPEM(kRSANoParam); ASSERT_TRUE(cert); EXPECT_TRUE(X509_verify(cert.get(), key.get())); cert = CertFromPEM(kRSANullParam); ASSERT_TRUE(cert); EXPECT_TRUE(X509_verify(cert.get(), key.get())); cert = CertFromPEM(kRSAInvalidParam); ASSERT_TRUE(cert); EXPECT_FALSE(X509_verify(cert.get(), key.get())); EXPECT_TRUE( ErrorEquals(ERR_get_error(), ERR_LIB_X509, X509_R_INVALID_PARAMETER)); } TEST(X509Test, GeneralName) { const std::vector kNames[] = { // [0] { // OBJECT_IDENTIFIER { 1.2.840.113554.4.1.72585.2.1 } // [0] { // SEQUENCE {} // } // } {0xa0, 0x13, 0x06, 0x0d, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x01, 0xa0, 0x02, 0x30, 0x00}, // [0] { // OBJECT_IDENTIFIER { 1.2.840.113554.4.1.72585.2.1 } // [0] { // [APPLICATION 0] {} // } // } {0xa0, 0x13, 0x06, 0x0d, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x01, 0xa0, 0x02, 0x60, 0x00}, // [0] { // OBJECT_IDENTIFIER { 1.2.840.113554.4.1.72585.2.1 } // [0] { // UTF8String { "a" } // } // } {0xa0, 0x14, 0x06, 0x0d, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x01, 0xa0, 0x03, 0x0c, 0x01, 0x61}, // [0] { // OBJECT_IDENTIFIER { 1.2.840.113554.4.1.72585.2.2 } // [0] { // UTF8String { "a" } // } // } {0xa0, 0x14, 0x06, 0x0d, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x02, 0xa0, 0x03, 0x0c, 0x01, 0x61}, // [0] { // OBJECT_IDENTIFIER { 1.2.840.113554.4.1.72585.2.1 } // [0] { // UTF8String { "b" } // } // } {0xa0, 0x14, 0x06, 0x0d, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x01, 0xa0, 0x03, 0x0c, 0x01, 0x62}, // [0] { // OBJECT_IDENTIFIER { 1.2.840.113554.4.1.72585.2.1 } // [0] { // BOOLEAN { TRUE } // } // } {0xa0, 0x14, 0x06, 0x0d, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x01, 0xa0, 0x03, 0x01, 0x01, 0xff}, // [0] { // OBJECT_IDENTIFIER { 1.2.840.113554.4.1.72585.2.1 } // [0] { // BOOLEAN { FALSE } // } // } {0xa0, 0x14, 0x06, 0x0d, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x01, 0xa0, 0x03, 0x01, 0x01, 0x00}, // [1 PRIMITIVE] { "a" } {0x81, 0x01, 0x61}, // [1 PRIMITIVE] { "b" } {0x81, 0x01, 0x62}, // [2 PRIMITIVE] { "a" } {0x82, 0x01, 0x61}, // [2 PRIMITIVE] { "b" } {0x82, 0x01, 0x62}, // [3] {} {0xa3, 0x00}, // [4] { // SEQUENCE { // SET { // SEQUENCE { // # commonName // OBJECT_IDENTIFIER { 2.5.4.3 } // UTF8String { "a" } // } // } // } // } {0xa4, 0x0e, 0x30, 0x0c, 0x31, 0x0a, 0x30, 0x08, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x01, 0x61}, // [4] { // SEQUENCE { // SET { // SEQUENCE { // # commonName // OBJECT_IDENTIFIER { 2.5.4.3 } // UTF8String { "b" } // } // } // } // } {0xa4, 0x0e, 0x30, 0x0c, 0x31, 0x0a, 0x30, 0x08, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x01, 0x62}, // [5] { // [1] { // UTF8String { "a" } // } // } {0xa5, 0x05, 0xa1, 0x03, 0x0c, 0x01, 0x61}, // [5] { // [1] { // UTF8String { "b" } // } // } {0xa5, 0x05, 0xa1, 0x03, 0x0c, 0x01, 0x62}, // [5] { // [0] { // UTF8String {} // } // [1] { // UTF8String { "a" } // } // } {0xa5, 0x09, 0xa0, 0x02, 0x0c, 0x00, 0xa1, 0x03, 0x0c, 0x01, 0x61}, // [5] { // [0] { // UTF8String { "a" } // } // [1] { // UTF8String { "a" } // } // } {0xa5, 0x0a, 0xa0, 0x03, 0x0c, 0x01, 0x61, 0xa1, 0x03, 0x0c, 0x01, 0x61}, // [5] { // [0] { // UTF8String { "b" } // } // [1] { // UTF8String { "a" } // } // } {0xa5, 0x0a, 0xa0, 0x03, 0x0c, 0x01, 0x62, 0xa1, 0x03, 0x0c, 0x01, 0x61}, // [6 PRIMITIVE] { "a" } {0x86, 0x01, 0x61}, // [6 PRIMITIVE] { "b" } {0x86, 0x01, 0x62}, // [7 PRIMITIVE] { `11111111` } {0x87, 0x04, 0x11, 0x11, 0x11, 0x11}, // [7 PRIMITIVE] { `22222222`} {0x87, 0x04, 0x22, 0x22, 0x22, 0x22}, // [7 PRIMITIVE] { `11111111111111111111111111111111` } {0x87, 0x10, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, // [7 PRIMITIVE] { `22222222222222222222222222222222` } {0x87, 0x10, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22}, // [8 PRIMITIVE] { 1.2.840.113554.4.1.72585.2.1 } {0x88, 0x0d, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x01}, // [8 PRIMITIVE] { 1.2.840.113554.4.1.72585.2.2 } {0x88, 0x0d, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x02}, }; // Every name should be equal to itself and not equal to any others. for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(kNames); i++) { SCOPED_TRACE(Bytes(kNames[i])); const uint8_t *ptr = kNames[i].data(); bssl::UniquePtr a( d2i_GENERAL_NAME(nullptr, &ptr, kNames[i].size())); ASSERT_TRUE(a); ASSERT_EQ(ptr, kNames[i].data() + kNames[i].size()); uint8_t *enc = nullptr; int enc_len = i2d_GENERAL_NAME(a.get(), &enc); ASSERT_GE(enc_len, 0); bssl::UniquePtr free_enc(enc); EXPECT_EQ(Bytes(enc, enc_len), Bytes(kNames[i])); for (size_t j = 0; j < OPENSSL_ARRAY_SIZE(kNames); j++) { SCOPED_TRACE(Bytes(kNames[j])); ptr = kNames[j].data(); bssl::UniquePtr b( d2i_GENERAL_NAME(nullptr, &ptr, kNames[j].size())); ASSERT_TRUE(b); ASSERT_EQ(ptr, kNames[j].data() + kNames[j].size()); if (i == j) { EXPECT_EQ(GENERAL_NAME_cmp(a.get(), b.get()), 0); } else { EXPECT_NE(GENERAL_NAME_cmp(a.get(), b.get()), 0); } } } } // Test that extracting fields of an |X509_ALGOR| works correctly. TEST(X509Test, X509AlgorExtract) { static const char kTestOID[] = "1.2.840.113554.4.1.72585.2"; const struct { int param_type; std::vector param_der; } kTests[] = { // No parameter. {V_ASN1_UNDEF, {}}, // BOOLEAN { TRUE } {V_ASN1_BOOLEAN, {0x01, 0x01, 0xff}}, // BOOLEAN { FALSE } {V_ASN1_BOOLEAN, {0x01, 0x01, 0x00}}, // OCTET_STRING { "a" } {V_ASN1_OCTET_STRING, {0x04, 0x01, 0x61}}, // BIT_STRING { `01` `00` } {V_ASN1_BIT_STRING, {0x03, 0x02, 0x01, 0x00}}, // INTEGER { -1 } {V_ASN1_INTEGER, {0x02, 0x01, 0xff}}, // OBJECT_IDENTIFIER { 1.2.840.113554.4.1.72585.2 } {V_ASN1_OBJECT, {0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02}}, // NULL {} {V_ASN1_NULL, {0x05, 0x00}}, // SEQUENCE {} {V_ASN1_SEQUENCE, {0x30, 0x00}}, // SET {} {V_ASN1_SET, {0x31, 0x00}}, // [0] { UTF8String { "a" } } {V_ASN1_OTHER, {0xa0, 0x03, 0x0c, 0x01, 0x61}}, }; for (const auto &t : kTests) { SCOPED_TRACE(Bytes(t.param_der)); // Assemble an AlgorithmIdentifier with the parameter. bssl::ScopedCBB cbb; CBB seq, oid; ASSERT_TRUE(CBB_init(cbb.get(), 64)); ASSERT_TRUE(CBB_add_asn1(cbb.get(), &seq, CBS_ASN1_SEQUENCE)); ASSERT_TRUE(CBB_add_asn1(&seq, &oid, CBS_ASN1_OBJECT)); ASSERT_TRUE(CBB_add_asn1_oid_from_text(&oid, kTestOID, strlen(kTestOID))); ASSERT_TRUE(CBB_add_bytes(&seq, t.param_der.data(), t.param_der.size())); ASSERT_TRUE(CBB_flush(cbb.get())); const uint8_t *ptr = CBB_data(cbb.get()); bssl::UniquePtr alg( d2i_X509_ALGOR(nullptr, &ptr, CBB_len(cbb.get()))); ASSERT_TRUE(alg); const ASN1_OBJECT *obj; int param_type; const void *param_value; X509_ALGOR_get0(&obj, ¶m_type, ¶m_value, alg.get()); EXPECT_EQ(param_type, t.param_type); char oid_buf[sizeof(kTestOID)]; ASSERT_EQ(int(sizeof(oid_buf) - 1), OBJ_obj2txt(oid_buf, sizeof(oid_buf), obj, /*always_return_oid=*/1)); EXPECT_STREQ(oid_buf, kTestOID); // |param_type| and |param_value| must be consistent with |ASN1_TYPE|. if (param_type == V_ASN1_UNDEF) { EXPECT_EQ(nullptr, param_value); } else { bssl::UniquePtr param(ASN1_TYPE_new()); ASSERT_TRUE(param); ASSERT_TRUE(ASN1_TYPE_set1(param.get(), param_type, param_value)); uint8_t *param_der = nullptr; int param_len = i2d_ASN1_TYPE(param.get(), ¶m_der); ASSERT_GE(param_len, 0); bssl::UniquePtr free_param_der(param_der); EXPECT_EQ(Bytes(param_der, param_len), Bytes(t.param_der)); } } } // Test the various |X509_ATTRIBUTE| creation functions. TEST(X509Test, Attribute) { // The expected attribute values are: // 1. BMPString U+2603 // 2. BMPString "test" // 3. INTEGER -1 (not valid for friendlyName) static const uint8_t kTest1[] = {0x26, 0x03}; // U+2603 SNOWMAN static const uint8_t kTest1UTF8[] = {0xe2, 0x98, 0x83}; static const uint8_t kTest2[] = {0, 't', 0, 'e', 0, 's', 0, 't'}; constexpr uint32_t kTest1Mask = 1 << 0; constexpr uint32_t kTest2Mask = 1 << 1; constexpr uint32_t kTest3Mask = 1 << 2; auto check_attribute = [&](X509_ATTRIBUTE *attr, uint32_t mask) { EXPECT_EQ(NID_friendlyName, OBJ_obj2nid(X509_ATTRIBUTE_get0_object(attr))); int idx = 0; if (mask & kTest1Mask) { // The first attribute should contain |kTest1|. const ASN1_TYPE *value = X509_ATTRIBUTE_get0_type(attr, idx); ASSERT_TRUE(value); EXPECT_EQ(V_ASN1_BMPSTRING, value->type); EXPECT_EQ(Bytes(kTest1), Bytes(ASN1_STRING_get0_data(value->value.bmpstring), ASN1_STRING_length(value->value.bmpstring))); // |X509_ATTRIBUTE_get0_data| requires the type match. EXPECT_FALSE( X509_ATTRIBUTE_get0_data(attr, idx, V_ASN1_OCTET_STRING, nullptr)); const ASN1_BMPSTRING *bmpstring = static_cast( X509_ATTRIBUTE_get0_data(attr, idx, V_ASN1_BMPSTRING, nullptr)); ASSERT_TRUE(bmpstring); EXPECT_EQ(Bytes(kTest1), Bytes(ASN1_STRING_get0_data(bmpstring), ASN1_STRING_length(bmpstring))); idx++; } if (mask & kTest2Mask) { const ASN1_TYPE *value = X509_ATTRIBUTE_get0_type(attr, idx); ASSERT_TRUE(value); EXPECT_EQ(V_ASN1_BMPSTRING, value->type); EXPECT_EQ(Bytes(kTest2), Bytes(ASN1_STRING_get0_data(value->value.bmpstring), ASN1_STRING_length(value->value.bmpstring))); idx++; } if (mask & kTest3Mask) { const ASN1_TYPE *value = X509_ATTRIBUTE_get0_type(attr, idx); ASSERT_TRUE(value); EXPECT_EQ(V_ASN1_INTEGER, value->type); int64_t v; ASSERT_TRUE(ASN1_INTEGER_get_int64(&v, value->value.integer)); EXPECT_EQ(v, -1); idx++; } EXPECT_FALSE(X509_ATTRIBUTE_get0_type(attr, idx)); }; bssl::UniquePtr str(ASN1_STRING_type_new(V_ASN1_BMPSTRING)); ASSERT_TRUE(str); ASSERT_TRUE(ASN1_STRING_set(str.get(), kTest1, sizeof(kTest1))); // Test |X509_ATTRIBUTE_create|. bssl::UniquePtr attr( X509_ATTRIBUTE_create(NID_friendlyName, V_ASN1_BMPSTRING, str.get())); ASSERT_TRUE(attr); str.release(); // |X509_ATTRIBUTE_create| takes ownership on success. check_attribute(attr.get(), kTest1Mask); // Test the |MBSTRING_*| form of |X509_ATTRIBUTE_set1_data|. attr.reset(X509_ATTRIBUTE_new()); ASSERT_TRUE(attr); ASSERT_TRUE( X509_ATTRIBUTE_set1_object(attr.get(), OBJ_nid2obj(NID_friendlyName))); ASSERT_TRUE(X509_ATTRIBUTE_set1_data(attr.get(), MBSTRING_UTF8, kTest1UTF8, sizeof(kTest1UTF8))); check_attribute(attr.get(), kTest1Mask); // Test the |ASN1_STRING| form of |X509_ATTRIBUTE_set1_data|. ASSERT_TRUE(X509_ATTRIBUTE_set1_data(attr.get(), V_ASN1_BMPSTRING, kTest2, sizeof(kTest2))); check_attribute(attr.get(), kTest1Mask | kTest2Mask); // The |ASN1_STRING| form of |X509_ATTRIBUTE_set1_data| should correctly // handle negative integers. const uint8_t kOne = 1; ASSERT_TRUE( X509_ATTRIBUTE_set1_data(attr.get(), V_ASN1_NEG_INTEGER, &kOne, 1)); check_attribute(attr.get(), kTest1Mask | kTest2Mask | kTest3Mask); // Test the |ASN1_TYPE| form of |X509_ATTRIBUTE_set1_data|. attr.reset(X509_ATTRIBUTE_new()); ASSERT_TRUE(attr); ASSERT_TRUE( X509_ATTRIBUTE_set1_object(attr.get(), OBJ_nid2obj(NID_friendlyName))); str.reset(ASN1_STRING_type_new(V_ASN1_BMPSTRING)); ASSERT_TRUE(str); ASSERT_TRUE(ASN1_STRING_set(str.get(), kTest1, sizeof(kTest1))); ASSERT_TRUE( X509_ATTRIBUTE_set1_data(attr.get(), V_ASN1_BMPSTRING, str.get(), -1)); check_attribute(attr.get(), kTest1Mask); // An |attrtype| of zero leaves the attribute empty. attr.reset(X509_ATTRIBUTE_create_by_NID( nullptr, NID_friendlyName, /*attrtype=*/0, /*data=*/nullptr, /*len=*/0)); ASSERT_TRUE(attr); check_attribute(attr.get(), 0); } // Test that, by default, |X509_V_FLAG_TRUSTED_FIRST| is set, which means we'll // skip over server-sent expired intermediates when there is a local trust // anchor that works better. TEST(X509Test, TrustedFirst) { // Generate the following certificates: // // Root 2 (in store, expired) // | // Root 1 (in store) Root 1 (cross-sign) // \ / // Intermediate // | // Leaf bssl::UniquePtr key = PrivateKeyFromPEM(kP256Key); ASSERT_TRUE(key); bssl::UniquePtr root2 = MakeTestCert("Root 2", "Root 2", key.get(), /*is_ca=*/true); ASSERT_TRUE(root2); ASSERT_TRUE(ASN1_TIME_adj(X509_getm_notAfter(root2.get()), kReferenceTime, /*offset_day=*/0, /*offset_sec=*/-1)); ASSERT_TRUE(X509_sign(root2.get(), key.get(), EVP_sha256())); bssl::UniquePtr root1 = MakeTestCert("Root 1", "Root 1", key.get(), /*is_ca=*/true); ASSERT_TRUE(root1); ASSERT_TRUE(X509_sign(root1.get(), key.get(), EVP_sha256())); bssl::UniquePtr root1_cross = MakeTestCert("Root 2", "Root 1", key.get(), /*is_ca=*/true); ASSERT_TRUE(root1_cross); ASSERT_TRUE(X509_sign(root1_cross.get(), key.get(), EVP_sha256())); bssl::UniquePtr intermediate = MakeTestCert("Root 1", "Intermediate", key.get(), /*is_ca=*/true); ASSERT_TRUE(intermediate); ASSERT_TRUE(X509_sign(intermediate.get(), key.get(), EVP_sha256())); bssl::UniquePtr leaf = MakeTestCert("Intermediate", "Leaf", key.get(), /*is_ca=*/false); ASSERT_TRUE(leaf); ASSERT_TRUE(X509_sign(leaf.get(), key.get(), EVP_sha256())); // As a control, confirm that |leaf| -> |intermediate| -> |root1| is valid, // but the path through |root1_cross| is expired. EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root1.get()}, {intermediate.get()}, {})); EXPECT_EQ(X509_V_ERR_CERT_HAS_EXPIRED, Verify(leaf.get(), {root2.get()}, {intermediate.get(), root1_cross.get()}, {})); // By default, we should find the |leaf| -> |intermediate| -> |root2| chain, // skipping |root1_cross|. EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root1.get(), root2.get()}, {intermediate.get(), root1_cross.get()}, {})); // When |X509_V_FLAG_TRUSTED_FIRST| is disabled, we get stuck on the expired // intermediate. Note we need the callback to clear the flag. Setting |flags| // to zero only skips setting new flags. // // This test exists to confirm our current behavior, but these modes are just // workarounds for not having an actual path-building verifier. If we fix it, // this test can be removed. EXPECT_EQ(X509_V_ERR_CERT_HAS_EXPIRED, Verify(leaf.get(), {root1.get(), root2.get()}, {intermediate.get(), root1_cross.get()}, {}, /*flags=*/0, [&](X509_STORE_CTX *ctx) { X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(ctx); X509_VERIFY_PARAM_clear_flags(param, X509_V_FLAG_TRUSTED_FIRST); })); // Even when |X509_V_FLAG_TRUSTED_FIRST| is disabled, if |root2| is not // trusted, the alt chains logic recovers the path. EXPECT_EQ( X509_V_OK, Verify(leaf.get(), {root1.get()}, {intermediate.get(), root1_cross.get()}, {}, /*flags=*/0, [&](X509_STORE_CTX *ctx) { X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(ctx); X509_VERIFY_PARAM_clear_flags(param, X509_V_FLAG_TRUSTED_FIRST); })); } // Test that notBefore and notAfter checks work correctly. TEST(X509Test, Expiry) { bssl::UniquePtr key = PrivateKeyFromPEM(kP256Key); ASSERT_TRUE(key); auto make_cert = [&](const char *issuer, const char *subject, bool is_ca, int not_before_offset, int not_after_offset) -> bssl::UniquePtr { bssl::UniquePtr cert = MakeTestCert(issuer, subject, key.get(), is_ca); if (cert == nullptr || !ASN1_TIME_adj(X509_getm_notBefore(cert.get()), kReferenceTime, /*offset_day=*/not_before_offset, /*offset_sec=*/0) || !ASN1_TIME_adj(X509_getm_notAfter(cert.get()), kReferenceTime, /*offset_day=*/not_after_offset, /*offset_sec=*/0) || !X509_sign(cert.get(), key.get(), EVP_sha256())) { return nullptr; } return cert; }; struct Certs { bssl::UniquePtr not_yet_valid, 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 key = PrivateKeyFromPEM(kP256Key); ASSERT_TRUE(key); struct Certs { bssl::UniquePtr valid; bssl::UniquePtr bad_key_type, bad_key; bssl::UniquePtr 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 pubkey_data(static_cast( OPENSSL_memdup(ASN1_STRING_get0_data(pubkey), pubkey_len))); certs.bad_key_type = MakeTestCert(issuer, subject, key.get(), is_ca); if (pubkey_data == nullptr || certs.bad_key_type == nullptr || !X509_PUBKEY_set0_param(X509_get_X509_PUBKEY(certs.bad_key_type.get()), OBJ_nid2obj(NID_subject_alt_name), V_ASN1_UNDEF, /*param_value=*/nullptr, pubkey_data.release(), pubkey_len) || !X509_sign(certs.bad_key_type.get(), key.get(), EVP_sha256())) { return Certs{}; } // Sign a copy of the certificate where the key data is unparsable. pubkey_data.reset( static_cast(OPENSSL_memdup(kInvalid, sizeof(kInvalid)))); certs.bad_key = MakeTestCert(issuer, subject, key.get(), is_ca); if (pubkey_data == nullptr || certs.bad_key == nullptr || !X509_PUBKEY_set0_param(X509_get_X509_PUBKEY(certs.bad_key.get()), OBJ_nid2obj(NID_X9_62_id_ecPublicKey), V_ASN1_OBJECT, OBJ_nid2obj(NID_X9_62_prime256v1), pubkey_data.release(), sizeof(kInvalid)) || !X509_sign(certs.bad_key.get(), key.get(), EVP_sha256())) { return Certs{}; } bssl::UniquePtr wrong_algo(X509_ALGOR_new()); if (wrong_algo == nullptr || !X509_ALGOR_set0(wrong_algo.get(), OBJ_nid2obj(NID_subject_alt_name), V_ASN1_NULL, nullptr)) { return Certs{}; } certs.bad_sig_type.reset(X509_dup(certs.valid.get())); if (certs.bad_sig_type == nullptr || !X509_set1_signature_algo(certs.bad_sig_type.get(), wrong_algo.get())) { return Certs{}; } certs.bad_sig.reset(X509_dup(certs.valid.get())); if (certs.bad_sig == nullptr || !X509_set1_signature_value(certs.bad_sig.get(), kInvalid, sizeof(kInvalid))) { return Certs{}; } return certs; }; Certs root(make_certs("Root", "Root", /*is_ca=*/true)); ASSERT_TRUE(root.valid); Certs intermediate(make_certs("Root", "Intermediate", /*is_ca=*/true)); ASSERT_TRUE(intermediate.valid); Certs leaf(make_certs("Intermediate", "Leaf", /*is_ca=*/false)); ASSERT_TRUE(leaf.valid); // Check the base chain. EXPECT_EQ(X509_V_OK, Verify(leaf.valid.get(), {root.valid.get()}, {intermediate.valid.get()}, {})); // An invalid or unsupported signature in the leaf or intermediate is noticed. EXPECT_EQ(X509_V_ERR_CERT_SIGNATURE_FAILURE, Verify(leaf.bad_sig.get(), {root.valid.get()}, {intermediate.valid.get()}, {})); EXPECT_EQ(X509_V_ERR_CERT_SIGNATURE_FAILURE, Verify(leaf.bad_sig_type.get(), {root.valid.get()}, {intermediate.valid.get()}, {})); EXPECT_EQ(X509_V_ERR_CERT_SIGNATURE_FAILURE, Verify(leaf.valid.get(), {root.valid.get()}, {intermediate.bad_sig.get()}, {})); EXPECT_EQ(X509_V_ERR_CERT_SIGNATURE_FAILURE, Verify(leaf.valid.get(), {root.valid.get()}, {intermediate.bad_sig_type.get()}, {})); // By default, the redundant root signature is not checked. EXPECT_EQ(X509_V_OK, Verify(leaf.valid.get(), {root.bad_sig.get()}, {intermediate.valid.get()}, {})); EXPECT_EQ(X509_V_OK, Verify(leaf.valid.get(), {root.bad_sig_type.get()}, {intermediate.valid.get()}, {})); // The caller can request checking it, although it's pointless. EXPECT_EQ( X509_V_ERR_CERT_SIGNATURE_FAILURE, Verify(leaf.valid.get(), {root.bad_sig.get()}, {intermediate.valid.get()}, {}, X509_V_FLAG_CHECK_SS_SIGNATURE)); EXPECT_EQ( X509_V_ERR_CERT_SIGNATURE_FAILURE, Verify(leaf.valid.get(), {root.bad_sig_type.get()}, {intermediate.valid.get()}, {}, X509_V_FLAG_CHECK_SS_SIGNATURE)); // The above also applies when accepting a trusted, self-signed root as the // target certificate. EXPECT_EQ(X509_V_OK, Verify(root.bad_sig.get(), {root.bad_sig.get()}, {}, {})); EXPECT_EQ(X509_V_OK, Verify(root.bad_sig_type.get(), {root.bad_sig_type.get()}, {}, {})); EXPECT_EQ(X509_V_ERR_CERT_SIGNATURE_FAILURE, Verify(root.bad_sig.get(), {root.bad_sig.get()}, {}, {}, X509_V_FLAG_CHECK_SS_SIGNATURE)); EXPECT_EQ(X509_V_ERR_CERT_SIGNATURE_FAILURE, Verify(root.bad_sig_type.get(), {root.bad_sig_type.get()}, {}, {}, X509_V_FLAG_CHECK_SS_SIGNATURE)); // If an intermediate is a trust anchor, the redundant signature is always // ignored, even with |X509_V_FLAG_CHECK_SS_SIGNATURE|. (We cannot check the // signature without the key.) EXPECT_EQ(X509_V_OK, Verify(leaf.valid.get(), {intermediate.bad_sig.get()}, {}, {}, X509_V_FLAG_CHECK_SS_SIGNATURE | X509_V_FLAG_PARTIAL_CHAIN)); EXPECT_EQ(X509_V_OK, Verify(leaf.valid.get(), {intermediate.bad_sig_type.get()}, {}, {}, X509_V_FLAG_CHECK_SS_SIGNATURE | X509_V_FLAG_PARTIAL_CHAIN)); EXPECT_EQ(X509_V_OK, Verify(leaf.valid.get(), {intermediate.bad_sig.get()}, {}, {}, X509_V_FLAG_PARTIAL_CHAIN)); EXPECT_EQ(X509_V_OK, Verify(leaf.valid.get(), {intermediate.bad_sig_type.get()}, {}, {}, X509_V_FLAG_PARTIAL_CHAIN)); // Bad keys in the root and intermediate are rejected. EXPECT_EQ(X509_V_ERR_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 key = PrivateKeyFromPEM(kP256Key); ASSERT_TRUE(key); bssl::UniquePtr root = MakeTestCert("Root", "Root", key.get(), /*is_ca=*/true); ASSERT_TRUE(root); ASSERT_TRUE(X509_sign(root.get(), key.get(), EVP_sha256())); struct { std::vector> cert_subject; std::vector cert_dns_names; std::vector cert_emails; std::vector valid_dns_names; std::vector invalid_dns_names; std::vector valid_emails; std::vector invalid_emails; unsigned flags; } kTests[] = { // DNS names only match DNS names and do so case-insensitively. { /*cert_subject=*/{}, /*cert_dns_names=*/{"example.com", "WWW.EXAMPLE.COM"}, /*cert_emails=*/{}, /*valid_dns_names=*/ {"example.com", "EXAMPLE.COM", "www.example.com", "WWW.EXAMPLE.COM"}, /*invalid_dns_names=*/{"test.example.com", "example.org"}, /*valid_emails=*/{}, /*invalid_emails=*/{"test@example.com", "example.com"}, /*flags=*/0, }, // DNS wildcards match exactly one component. { /*cert_subject=*/{}, /*cert_dns_names=*/{"*.example.com", "*.EXAMPLE.ORG"}, /*cert_emails=*/{}, /*valid_dns_names=*/ {"www.example.com", "WWW.EXAMPLE.COM", "www.example.org", "WWW.EXAMPLE.ORG"}, /*invalid_dns_names=*/{"example.com", "test.www.example.com"}, /*valid_emails=*/{}, /*invalid_emails=*/{"test@example.com", "www.example.com"}, /*flags=*/0, }, // DNS wildcards can be disabled. // TODO(davidben): Can we remove this feature? Does anyone use it? { /*cert_subject=*/{}, /*cert_dns_names=*/{"example.com", "*.example.com"}, /*cert_emails=*/{}, /*valid_dns_names=*/{"example.com"}, /*invalid_dns_names=*/{"www.example.com"}, /*valid_emails=*/{}, /*invalid_emails=*/{}, /*flags=*/X509_CHECK_FLAG_NO_WILDCARDS, }, // Invalid DNS wildcards do not match. { /*cert_subject=*/{}, /*cert_dns_names=*/ {"a.*", "**.b.example", "*c.example", "d*.example", "e*e.example", "*", ".", "..", "*."}, /*cert_emails=*/{}, /*valid_dns_names=*/{}, /*invalid_dns_names=*/ {"a.example", "test.b.example", "cc.example", "dd.example", "eee.example", "f", "g."}, /*valid_emails=*/{}, /*invalid_emails=*/{}, /*flags=*/0, }, // IDNs match like any other DNS labels. { /*cert_subject=*/{}, /*cert_dns_names=*/ {"xn--rger-koa.a.example", "*.xn--rger-koa.b.example", "www.xn--rger-koa.c.example"}, /*cert_emails=*/{}, /*valid_dns_names=*/ {"xn--rger-koa.a.example", "www.xn--rger-koa.b.example", "www.xn--rger-koa.c.example"}, /*invalid_dns_names=*/ {"www.xn--rger-koa.a.example", "xn--rger-koa.b.example", "www.xn--rger-koa.d.example"}, /*valid_emails=*/{}, /*invalid_emails=*/{}, /*flags=*/0, }, // For now, DNS names are also extracted out of the common name, but only // there is no SAN list. // TODO(https://crbug.com/boringssl/464): Remove this. { /*cert_subject=*/{{NID_commonName, "a.example"}, {NID_commonName, "*.b.example"}}, /*cert_dns_names=*/{}, /*cert_emails=*/{}, /*valid_dns_names=*/ {"a.example", "A.EXAMPLE", "test.b.example", "TEST.B.EXAMPLE"}, /*invalid_dns_names=*/{}, /*valid_emails=*/{}, /*invalid_emails=*/{}, /*flags=*/0, }, { /*cert_subject=*/{{NID_commonName, "a.example"}, {NID_commonName, "*.b.example"}}, /*cert_dns_names=*/{"example.com"}, /*cert_emails=*/{}, /*valid_dns_names=*/{}, /*invalid_dns_names=*/ {"a.example", "A.EXAMPLE", "test.b.example", "TEST.B.EXAMPLE"}, /*valid_emails=*/{}, /*invalid_emails=*/{}, /*flags=*/0, }, // Other subject RDNs do not provide DNS names. { /*cert_subject=*/{{NID_organizationName, "example.com"}}, /*cert_dns_names=*/{}, /*cert_emails=*/{}, /*valid_dns_names=*/{}, /*invalid_dns_names=*/{"example.com"}, /*valid_emails=*/{}, /*invalid_emails=*/{}, /*flags=*/0, }, // Input DNS names cannot have wildcards. { /*cert_subject=*/{}, /*cert_dns_names=*/{"www.example.com"}, /*cert_emails=*/{}, /*valid_dns_names=*/{}, /*invalid_dns_names=*/{"*.example.com"}, /*valid_emails=*/{}, /*invalid_emails=*/{}, /*flags=*/0, }, // OpenSSL has some non-standard wildcard syntax for input DNS names. We // 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 cert = MakeTestCert("Root", "Leaf", key.get(), /*is_ca=*/false); ASSERT_TRUE(cert); if (!t.cert_subject.empty()) { bssl::UniquePtr subject(X509_NAME_new()); ASSERT_TRUE(subject); for (const auto &entry : t.cert_subject) { ASSERT_TRUE(X509_NAME_add_entry_by_NID( subject.get(), entry.first, MBSTRING_ASC, reinterpret_cast(entry.second.data()), entry.second.size(), /*loc=*/-1, /*set=*/0)); } ASSERT_TRUE(X509_set_subject_name(cert.get(), subject.get())); } bssl::UniquePtr sans(sk_GENERAL_NAME_new_null()); ASSERT_TRUE(sans); for (const auto &dns : t.cert_dns_names) { bssl::UniquePtr name(GENERAL_NAME_new()); ASSERT_TRUE(name); name->type = GEN_DNS; name->d.dNSName = ASN1_IA5STRING_new(); ASSERT_TRUE(name->d.dNSName); ASSERT_TRUE(ASN1_STRING_set(name->d.dNSName, dns.data(), dns.size())); ASSERT_TRUE(bssl::PushToStack(sans.get(), std::move(name))); } for (const auto &email : t.cert_emails) { bssl::UniquePtr name(GENERAL_NAME_new()); ASSERT_TRUE(name); name->type = GEN_EMAIL; name->d.rfc822Name = ASN1_IA5STRING_new(); ASSERT_TRUE(name->d.rfc822Name); ASSERT_TRUE( ASN1_STRING_set(name->d.rfc822Name, email.data(), email.size())); ASSERT_TRUE(bssl::PushToStack(sans.get(), std::move(name))); } if (sk_GENERAL_NAME_num(sans.get()) != 0) { ASSERT_TRUE(X509_add1_ext_i2d(cert.get(), NID_subject_alt_name, sans.get(), /*crit=*/0, /*flags=*/0)); } ASSERT_TRUE(X509_sign(cert.get(), key.get(), EVP_sha256())); for (const auto &dns : t.valid_dns_names) { SCOPED_TRACE(dns); EXPECT_EQ(1, X509_check_host(cert.get(), dns.data(), dns.size(), t.flags, /*peername=*/nullptr)); EXPECT_EQ(X509_V_OK, Verify(cert.get(), {root.get()}, /*intermediates=*/{}, /*crls=*/{}, /*flags=*/0, [&](X509_STORE_CTX *ctx) { X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(ctx); ASSERT_TRUE(X509_VERIFY_PARAM_set1_host( param, dns.data(), dns.size())); X509_VERIFY_PARAM_set_hostflags(param, t.flags); })); } for (const auto &dns : t.invalid_dns_names) { SCOPED_TRACE(dns); EXPECT_EQ(0, X509_check_host(cert.get(), dns.data(), dns.size(), t.flags, /*peername=*/nullptr)); EXPECT_EQ(X509_V_ERR_HOSTNAME_MISMATCH, Verify(cert.get(), {root.get()}, /*intermediates=*/{}, /*crls=*/{}, /*flags=*/0, [&](X509_STORE_CTX *ctx) { X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(ctx); ASSERT_TRUE(X509_VERIFY_PARAM_set1_host( param, dns.data(), dns.size())); X509_VERIFY_PARAM_set_hostflags(param, t.flags); })); } for (const auto &email : t.valid_emails) { SCOPED_TRACE(email); EXPECT_EQ( 1, X509_check_email(cert.get(), email.data(), email.size(), t.flags)); EXPECT_EQ(X509_V_OK, Verify(cert.get(), {root.get()}, /*intermediates=*/{}, /*crls=*/{}, /*flags=*/0, [&](X509_STORE_CTX *ctx) { X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(ctx); ASSERT_TRUE(X509_VERIFY_PARAM_set1_email( param, email.data(), email.size())); X509_VERIFY_PARAM_set_hostflags(param, t.flags); })); } for (const auto &email : t.invalid_emails) { SCOPED_TRACE(email); EXPECT_EQ( 0, X509_check_email(cert.get(), email.data(), email.size(), t.flags)); EXPECT_EQ(X509_V_ERR_EMAIL_MISMATCH, Verify(cert.get(), {root.get()}, /*intermediates=*/{}, /*crls=*/{}, /*flags=*/0, [&](X509_STORE_CTX *ctx) { X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(ctx); ASSERT_TRUE(X509_VERIFY_PARAM_set1_email( param, email.data(), email.size())); X509_VERIFY_PARAM_set_hostflags(param, t.flags); })); } } } TEST(X509Test, AddDuplicates) { bssl::UniquePtr store(X509_STORE_new()); bssl::UniquePtr a(CertFromPEM(kCrossSigningRootPEM)); bssl::UniquePtr b(CertFromPEM(kRootCAPEM)); ASSERT_TRUE(store); ASSERT_TRUE(a); ASSERT_TRUE(b); 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 bytes; const char *hex; } kTests[] = { {{}, ""}, {{0x00}, "00"}, {{0x00, 0x11, 0x22}, "00:11:22"}, {{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, "01:23:45:67:89:AB:CD:EF"}, }; for (const auto &t : kTests) { SCOPED_TRACE(Bytes(t.bytes)); bssl::UniquePtr hex( x509v3_bytes_to_hex(t.bytes.data(), t.bytes.size())); ASSERT_TRUE(hex); EXPECT_STREQ(hex.get(), t.hex); } } TEST(X509Test, NamePrint) { // kTestName is a DER-encoded X.509 that covers many cases. // // SEQUENCE { // SET { // SEQUENCE { // # countryName // OBJECT_IDENTIFIER { 2.5.4.6 } // PrintableString { "US" } // } // } // # Sets may be multi-valued, with different attributes. Try to keep this // # in DER set order, in case we ever enforce this in the parser. // SET { // SEQUENCE { // # stateOrProvinceName // OBJECT_IDENTIFIER { 2.5.4.8 } // PrintableString { "Some State" } // } // SEQUENCE { // # stateOrProvinceName // OBJECT_IDENTIFIER { 2.5.4.8 } // UTF8String { "Some Other State \xe2\x98\x83" } // } // SEQUENCE { // # stateOrProvinceName // OBJECT_IDENTIFIER { 2.5.4.8 } // BMPString { u"Another State \u2603" } // } // SEQUENCE { // # A custom OID // OBJECT_IDENTIFIER { 1.2.840.113554.4.1.72585.2 } // UniversalString { U"\u2603" } // } // } // # Custom OIDs may have non-string values. // SET { // SEQUENCE { // OBJECT_IDENTIFIER { 1.2.840.113554.4.1.72585.3 } // SEQUENCE { INTEGER { 1 } INTEGER { 2 } } // } // } // SET { // SEQUENCE { // # organizationName // OBJECT_IDENTIFIER { 2.5.4.10 } // PrintableString { "Org Name" } // } // } // SET { // SEQUENCE { // # commonName // OBJECT_IDENTIFIER { 2.5.4.3 } // # Embed common delimiter forms to test how well they get escaped. // UTF8String { "Common // Name/CN=A/CN=B,CN=A,CN=B+CN=A+CN=B;CN=A;CN=B\nCN=A\n" } // } // } // SET { // SEQUENCE { // # commonName // OBJECT_IDENTIFIER { 2.5.4.3 } // # Test escaping of leading and trailing spaces. // UTF8String { " spaces " } // } // } static const uint8_t kTestName[] = { 0x30, 0x82, 0x01, 0x00, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x6d, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x20, 0x53, 0x74, 0x61, 0x74, 0x65, 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x14, 0x53, 0x6f, 0x6d, 0x65, 0x20, 0x4f, 0x74, 0x68, 0x65, 0x72, 0x20, 0x53, 0x74, 0x61, 0x74, 0x65, 0x20, 0xe2, 0x98, 0x83, 0x30, 0x25, 0x06, 0x03, 0x55, 0x04, 0x08, 0x1e, 0x1e, 0x00, 0x41, 0x00, 0x6e, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x68, 0x00, 0x65, 0x00, 0x72, 0x00, 0x20, 0x00, 0x53, 0x00, 0x74, 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00, 0x20, 0x26, 0x03, 0x30, 0x14, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x1c, 0x04, 0x00, 0x00, 0x26, 0x03, 0x31, 0x18, 0x30, 0x16, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x03, 0x30, 0x06, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x08, 0x4f, 0x72, 0x67, 0x20, 0x4e, 0x61, 0x6d, 0x65, 0x31, 0x42, 0x30, 0x40, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x39, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x20, 0x4e, 0x61, 0x6d, 0x65, 0x2f, 0x43, 0x4e, 0x3d, 0x41, 0x2f, 0x43, 0x4e, 0x3d, 0x42, 0x2c, 0x43, 0x4e, 0x3d, 0x41, 0x2c, 0x43, 0x4e, 0x3d, 0x42, 0x2b, 0x43, 0x4e, 0x3d, 0x41, 0x2b, 0x43, 0x4e, 0x3d, 0x42, 0x3b, 0x43, 0x4e, 0x3d, 0x41, 0x3b, 0x43, 0x4e, 0x3d, 0x42, 0x0a, 0x43, 0x4e, 0x3d, 0x41, 0x0a, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x08, 0x20, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x20}; const uint8_t *ptr = kTestName; bssl::UniquePtr name( d2i_X509_NAME(nullptr, &ptr, sizeof(kTestName))); ASSERT_TRUE(name); EXPECT_EQ(ptr, kTestName + sizeof(kTestName)); struct { int indent; unsigned long flags; std::string printed; } kTests[] = { // RFC 2253 uses , and + separators and encodes the RDNs in reverse. // OpenSSL's implementation additionally happens to reverse the values // within each RDN. RFC 2253 says any order is permissible. {/*indent=*/0, /*flags=*/XN_FLAG_RFC2253, "CN=\\ spaces\\ ," "CN=Common " "Name/CN=A/CN=B\\,CN=A\\,CN=B\\+CN=A\\+CN=B\\;CN=A\\;CN=B\\0ACN=A\\0A," "O=Org Name," "1.2.840.113554.4.1.72585.3=#3006020101020102," "1.2.840.113554.4.1.72585.2=#1C0400002603+" "ST=Another State \\E2\\98\\83+" "ST=Some Other State \\E2\\98\\83+" "ST=Some State," "C=US"}, {/*indent=*/2, /*flags=*/XN_FLAG_RFC2253, " " "CN=\\ spaces\\ ," "CN=Common " "Name/CN=A/CN=B\\,CN=A\\,CN=B\\+CN=A\\+CN=B\\;CN=A\\;CN=B\\0ACN=A\\0A," "O=Org Name," "1.2.840.113554.4.1.72585.3=#3006020101020102," "1.2.840.113554.4.1.72585.2=#1C0400002603+" "ST=Another State \\E2\\98\\83+" "ST=Some Other State \\E2\\98\\83+" "ST=Some State," "C=US"}, // |XN_FLAG_ONELINE| is an OpenSSL-specific single-line format. It also // omits |XN_FLAG_DUMP_UNKNOWN_FIELDS|, so unknown OIDs that use known // string types will still be decoded. (This may drop important // information if the unknown OID distinguishes between string types.) It // also passes |ASN1_STRFLGS_ESC_QUOTE|. {/*indent=*/0, /*flags=*/XN_FLAG_ONELINE, "C = US, " "ST = Some State + " "ST = Some Other State \\E2\\98\\83 + " "ST = Another State \\E2\\98\\83 + " "1.2.840.113554.4.1.72585.2 = \\E2\\98\\83, " "1.2.840.113554.4.1.72585.3 = #3006020101020102, " "O = Org Name, " "CN = \"Common " "Name/CN=A/CN=B,CN=A,CN=B+CN=A+CN=B;CN=A;CN=B\\0ACN=A\\0A\", " "CN = \" spaces \""}, // 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_new(BIO_s_mem())); ASSERT_TRUE(bio); int len = X509_NAME_print_ex(bio.get(), name.get(), t.indent, t.flags); ASSERT_GT(len, 0); const uint8_t *printed; size_t printed_len; ASSERT_TRUE(BIO_mem_contents(bio.get(), &printed, &printed_len)); EXPECT_EQ(std::string(printed, printed + printed_len), t.printed); if (t.flags != XN_FLAG_COMPAT) { // TODO(davidben): |XN_FLAG_COMPAT| does not return the length. EXPECT_EQ(static_cast(len), printed_len); // Passing a null |BIO| measures the output instead. len = X509_NAME_print_ex(nullptr, name.get(), t.indent, t.flags); EXPECT_GT(len, 0); EXPECT_EQ(static_cast(len), printed_len); } } // TODO(davidben): This escapes the underlying bytes in the string, but that // is ambiguous without capturing the type. Should this escape like // |ASN1_STRFLGS_UTF8_CONVERT| instead? static const char *kOnelineComponents[] = { "/C=US", "/ST=Some State", "/ST=Some Other State \\xE2\\x98\\x83", ("/ST=\\x00A\\x00n\\x00o\\x00t\\x00h\\x00e\\x00r\\x00 " "\\x00S\\x00t\\x00a\\x00t\\x00e\\x00 &\\x03"), "/1.2.840.113554.4.1.72585.2=\\x00\\x00&\\x03", "/1.2.840.113554.4.1.72585.3=0\\x06\\x02\\x01\\x01\\x02\\x01\\x02", "/O=Org Name", "/CN=Common Name/CN=A/CN=B,CN=A,CN=B+CN=A+CN=B;CN=A;CN=B\\x0ACN=A\\x0A", "/CN= spaces ", }; std::string oneline_expected; for (const auto &component : kOnelineComponents) { oneline_expected += component; } // Given null buffer, |X509_NAME_oneline| allocates a new output. bssl::UniquePtr oneline(X509_NAME_oneline(name.get(), nullptr, 0)); ASSERT_TRUE(oneline); EXPECT_EQ(oneline.get(), oneline_expected); // Otherwise it writes to the specified buffer. Note one extra byte is needed // for the trailing NUL. char buf[1024]; ASSERT_GE(sizeof(buf), oneline_expected.size() + 2); ASSERT_EQ(buf, X509_NAME_oneline(name.get(), buf, oneline_expected.size() + 1)); EXPECT_EQ(buf, oneline_expected); memset(buf, 'a', sizeof(buf)); ASSERT_EQ(buf, X509_NAME_oneline(name.get(), buf, oneline_expected.size() + 2)); EXPECT_EQ(buf, oneline_expected); // If the length is too small, |X509_NAME_oneline| truncates at name // entry boundaries. EXPECT_EQ(nullptr, X509_NAME_oneline(name.get(), buf, 0)); for (size_t len = 1; len < oneline_expected.size(); len++) { SCOPED_TRACE(len); memset(buf, 'a', sizeof(buf)); EXPECT_EQ(buf, X509_NAME_oneline(name.get(), buf, len)); std::string truncated; for (const auto &component : kOnelineComponents) { if (truncated.size() + strlen(component) + 1 > len) { break; } truncated += component; } EXPECT_EQ(buf, truncated); } } // kLargeSerialPEM is a certificate with a large serial number. static const char kLargeSerialPEM[] = R"( -----BEGIN CERTIFICATE----- MIICZjCCAc+gAwIBAgIQASNFZ4mrze8BI0VniavN7zANBgkqhkiG9w0BAQsFADA2 MRowGAYDVQQKExFCb3JpbmdTU0wgVEVTVElORzEYMBYGA1UEAxMPSW50ZXJtZWRp YXRlIENBMCAXDTE1MDEwMTAwMDAwMFoYDzIxMDAwMTAxMDAwMDAwWjAyMRowGAYD VQQKExFCb3JpbmdTU0wgVEVTVElORzEUMBIGA1UEAxMLZXhhbXBsZS5jb20wgZ8w DQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMPRTRliCpKEnug6OzI0rJVcQep5p+aT 9sCg+pj+HVyg/DYTwqZ6qJRKhM+MbkhdJuU7FyqlsBeCeM/OjwMjcY0yEB/xJg1i ygfuBztTLuPnHxtSuKwae5MeqSofp3j97sRMnuLcKlHxu8rXoOCAS9BO50uKnPwU Ee1iEVqR92FPAgMBAAGjdzB1MA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggr BgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAZBgNVHQ4EEgQQo3mm9u6v uaVeN4wRgDTidTAbBgNVHSMEFDASgBCMGmiotXbbXVd7H40UsgajMA0GCSqGSIb3 DQEBCwUAA4GBAGP+n4kKGn/8uddYLWTXbUsz+KLuEXNDMyu3vRufLjTpIbP2MCNo 85fhLeC3fzKuGOk+6QGVLOBBcWDrrLqrmqnWdBMPULDo2QoF71a4GVjeJh+ax/tZ PyeGVPUK21TE0LDIxf2a11d1CJw582MgZQIPk4tXk+AcU9EqIceKgECG -----END CERTIFICATE----- )"; TEST(X509Test, Print) { bssl::UniquePtr cert(CertFromPEM(kLargeSerialPEM)); ASSERT_TRUE(cert); bssl::UniquePtr bio(BIO_new(BIO_s_mem())); ASSERT_TRUE(bio); EXPECT_TRUE(X509_print_ex(bio.get(), cert.get(), 0, 0)); // Nothing should be left in the error queue. EXPECT_EQ(0u, ERR_peek_error()); // This output is not guaranteed to be stable, but we assert on it to make // sure something is printed. const uint8_t *data; size_t data_len; ASSERT_TRUE(BIO_mem_contents(bio.get(), &data, &data_len)); 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_new()); ASSERT_TRUE(x509); struct Extension { int nid; bool critical; std::vector data; }; auto expect_extensions = [&](const std::vector &exts) { ASSERT_EQ(static_cast(X509_get_ext_count(x509.get())), exts.size()); for (size_t i = 0; i < exts.size(); i++) { SCOPED_TRACE(i); const X509_EXTENSION *ext = X509_get_ext(x509.get(), static_cast(i)); EXPECT_EQ(OBJ_obj2nid(X509_EXTENSION_get_object(ext)), exts[i].nid); EXPECT_EQ(X509_EXTENSION_get_critical(ext), exts[i].critical ? 1 : 0); const ASN1_OCTET_STRING *data = X509_EXTENSION_get_data(ext); EXPECT_EQ(Bytes(ASN1_STRING_get0_data(data), ASN1_STRING_length(data)), Bytes(exts[i].data)); } }; // Make a few sample extensions. // SEQUENCE {} std::vector basic1_der = {0x30, 0x00}; const uint8_t *inp = basic1_der.data(); bssl::UniquePtr basic1_obj( d2i_BASIC_CONSTRAINTS(nullptr, &inp, basic1_der.size())); EXPECT_EQ(inp, basic1_der.data() + basic1_der.size()); // SEQUENCE { BOOLEAN { TRUE } } std::vector basic2_der = {0x30, 0x03, 0x01, 0x01, 0xff}; inp = basic2_der.data(); bssl::UniquePtr basic2_obj( d2i_BASIC_CONSTRAINTS(nullptr, &inp, basic2_der.size())); EXPECT_EQ(inp, basic2_der.data() + basic2_der.size()); // OCTET_STRING {} std::vector skid1_der = {0x04, 0x00}; inp = skid1_der.data(); bssl::UniquePtr skid1_obj( d2i_ASN1_OCTET_STRING(nullptr, &inp, skid1_der.size())); EXPECT_EQ(inp, skid1_der.data() + skid1_der.size()); // OCTET_STRING { "a" } std::vector skid2_der = {0x04, 0x01, 0x61}; inp = skid2_der.data(); bssl::UniquePtr skid2_obj( d2i_ASN1_OCTET_STRING(nullptr, &inp, skid2_der.size())); EXPECT_EQ(inp, skid2_der.data() + skid2_der.size()); // Initially, the extension list is empty. expect_extensions({}); // Adding extensions works with the default settings. EXPECT_EQ( 1, X509_add1_ext_i2d(x509.get(), NID_basic_constraints, basic1_obj.get(), /*crit=*/1, X509V3_ADD_DEFAULT)); expect_extensions({{NID_basic_constraints, true, basic1_der}}); EXPECT_EQ(1, X509_add1_ext_i2d(x509.get(), NID_subject_key_identifier, skid1_obj.get(), /*crit=*/0, X509V3_ADD_DEFAULT)); expect_extensions({{NID_basic_constraints, true, basic1_der}, {NID_subject_key_identifier, false, skid1_der}}); // By default, we cannot add duplicates. EXPECT_EQ( 0, X509_add1_ext_i2d(x509.get(), NID_basic_constraints, basic2_obj.get(), /*crit=*/0, X509V3_ADD_DEFAULT)); expect_extensions({{NID_basic_constraints, true, basic1_der}, {NID_subject_key_identifier, false, skid1_der}}); // |X509V3_ADD_KEEP_EXISTING| silently keeps the existing extension if already // present. EXPECT_EQ( 1, X509_add1_ext_i2d(x509.get(), NID_basic_constraints, basic2_obj.get(), /*crit=*/0, X509V3_ADD_KEEP_EXISTING)); expect_extensions({{NID_basic_constraints, true, basic1_der}, {NID_subject_key_identifier, false, skid1_der}}); // |X509V3_ADD_REPLACE| replaces it. EXPECT_EQ( 1, X509_add1_ext_i2d(x509.get(), NID_basic_constraints, basic2_obj.get(), /*crit=*/0, X509V3_ADD_REPLACE)); expect_extensions({{NID_basic_constraints, false, basic2_der}, {NID_subject_key_identifier, false, skid1_der}}); // |X509V3_ADD_REPLACE_EXISTING| also replaces matches. EXPECT_EQ(1, X509_add1_ext_i2d(x509.get(), NID_subject_key_identifier, skid2_obj.get(), /*crit=*/1, X509V3_ADD_REPLACE_EXISTING)); expect_extensions({{NID_basic_constraints, false, basic2_der}, {NID_subject_key_identifier, true, skid2_der}}); // |X509V3_ADD_DELETE| ignores the value and deletes the extension. EXPECT_EQ(1, X509_add1_ext_i2d(x509.get(), NID_basic_constraints, nullptr, 0, X509V3_ADD_DELETE)); expect_extensions({{NID_subject_key_identifier, true, skid2_der}}); // Not finding an extension to delete is an error. EXPECT_EQ(0, X509_add1_ext_i2d(x509.get(), NID_basic_constraints, nullptr, 0, X509V3_ADD_DELETE)); expect_extensions({{NID_subject_key_identifier, true, skid2_der}}); // |X509V3_ADD_REPLACE_EXISTING| fails if it cannot find a match. EXPECT_EQ( 0, X509_add1_ext_i2d(x509.get(), NID_basic_constraints, basic1_obj.get(), /*crit=*/1, X509V3_ADD_REPLACE_EXISTING)); expect_extensions({{NID_subject_key_identifier, true, skid2_der}}); // |X509V3_ADD_REPLACE| adds a new extension if not preseent. EXPECT_EQ( 1, X509_add1_ext_i2d(x509.get(), NID_basic_constraints, basic1_obj.get(), /*crit=*/1, X509V3_ADD_REPLACE)); expect_extensions({{NID_subject_key_identifier, true, skid2_der}, {NID_basic_constraints, true, basic1_der}}); // Delete the extension again. EXPECT_EQ(1, X509_add1_ext_i2d(x509.get(), NID_basic_constraints, nullptr, 0, X509V3_ADD_DELETE)); expect_extensions({{NID_subject_key_identifier, true, skid2_der}}); // |X509V3_ADD_KEEP_EXISTING| adds a new extension if not preseent. EXPECT_EQ( 1, X509_add1_ext_i2d(x509.get(), NID_basic_constraints, basic1_obj.get(), /*crit=*/1, X509V3_ADD_KEEP_EXISTING)); expect_extensions({{NID_subject_key_identifier, true, skid2_der}, {NID_basic_constraints, true, basic1_der}}); // Delete the extension again. EXPECT_EQ(1, X509_add1_ext_i2d(x509.get(), NID_basic_constraints, nullptr, 0, X509V3_ADD_DELETE)); expect_extensions({{NID_subject_key_identifier, true, skid2_der}}); // |X509V3_ADD_APPEND| adds a new extension if not present. EXPECT_EQ( 1, X509_add1_ext_i2d(x509.get(), NID_basic_constraints, basic1_obj.get(), /*crit=*/1, X509V3_ADD_APPEND)); expect_extensions({{NID_subject_key_identifier, true, skid2_der}, {NID_basic_constraints, true, basic1_der}}); // |X509V3_ADD_APPEND| keeps adding duplicates (invalid) even if present. EXPECT_EQ( 1, X509_add1_ext_i2d(x509.get(), NID_basic_constraints, basic2_obj.get(), /*crit=*/0, X509V3_ADD_APPEND)); expect_extensions({{NID_subject_key_identifier, true, skid2_der}, {NID_basic_constraints, true, basic1_der}, {NID_basic_constraints, false, basic2_der}}); // |X509V3_ADD_DELETE| only deletes one extension at a time. EXPECT_EQ(1, X509_add1_ext_i2d(x509.get(), NID_basic_constraints, nullptr, 0, X509V3_ADD_DELETE)); expect_extensions({{NID_subject_key_identifier, true, skid2_der}, {NID_basic_constraints, false, basic2_der}}); EXPECT_EQ(1, X509_add1_ext_i2d(x509.get(), NID_basic_constraints, nullptr, 0, X509V3_ADD_DELETE)); expect_extensions({{NID_subject_key_identifier, true, skid2_der}}); } TEST(X509Test, NameEntry) { bssl::UniquePtr name(X509_NAME_new()); ASSERT_TRUE(name); auto check_name = [&](const char *expected_rfc2253) { // Check RDN indices are self-consistent. int num = X509_NAME_entry_count(name.get()); if (num > 0) { // RDN indices must start at zero. EXPECT_EQ(0, X509_NAME_ENTRY_set(X509_NAME_get_entry(name.get(), 0))); } for (int i = 1; i < num; i++) { int prev = X509_NAME_ENTRY_set(X509_NAME_get_entry(name.get(), i - 1)); int current = X509_NAME_ENTRY_set(X509_NAME_get_entry(name.get(), i)); // RDN indices must increase consecutively. EXPECT_TRUE(prev == current || prev + 1 == current) << "Entry " << i << " has RDN index " << current << " which is inconsistent with previous index " << prev; } // Check the name based on the RFC 2253 serialization. Note the RFC 2253 // serialization is in reverse. bssl::UniquePtr bio(BIO_new(BIO_s_mem())); ASSERT_TRUE(bio); EXPECT_GE(X509_NAME_print_ex(bio.get(), name.get(), 0, XN_FLAG_RFC2253), 0); const uint8_t *data; size_t len; ASSERT_TRUE(BIO_mem_contents(bio.get(), &data, &len)); EXPECT_EQ(expected_rfc2253, std::string(data, data + len)); }; check_name(""); // |loc| = -1, |set| = 0 appends as new RDNs. ASSERT_TRUE(X509_NAME_add_entry_by_NID( name.get(), NID_organizationName, MBSTRING_UTF8, reinterpret_cast("Org"), /*len=*/-1, /*loc=*/-1, /*set=*/0)); check_name("O=Org"); // |loc| = -1, |set| = 0 appends as new RDNs. ASSERT_TRUE(X509_NAME_add_entry_by_NID( name.get(), NID_commonName, MBSTRING_UTF8, reinterpret_cast("Name"), /*len=*/-1, /*loc=*/-1, /*set=*/0)); check_name("CN=Name,O=Org"); // Inserting in the middle of the set, but with |set| = 0 inserts a new RDN // and fixes the "set" values as needed. ASSERT_TRUE(X509_NAME_add_entry_by_NID( name.get(), NID_organizationalUnitName, MBSTRING_UTF8, reinterpret_cast("Unit"), /*len=*/-1, /*loc=*/1, /*set=*/0)); check_name("CN=Name,OU=Unit,O=Org"); // |set = -1| adds to the previous entry's RDN. (Although putting O and OU at // the same level makes little sense, the test is written this way to check // the function isn't using attribute types to order things.) ASSERT_TRUE(X509_NAME_add_entry_by_NID( name.get(), NID_organizationName, MBSTRING_UTF8, reinterpret_cast("Org2"), /*len=*/-1, /*loc=*/2, /*set=*/-1)); check_name("CN=Name,O=Org2+OU=Unit,O=Org"); // |set| = 1 adds to the next entry's RDN. ASSERT_TRUE(X509_NAME_add_entry_by_NID( name.get(), NID_commonName, MBSTRING_UTF8, reinterpret_cast("Name2"), /*len=*/-1, /*loc=*/2, /*set=*/-1)); check_name("CN=Name,O=Org2+CN=Name2+OU=Unit,O=Org"); // If there is no previous RDN, |set| = -1 makes a new RDN. ASSERT_TRUE(X509_NAME_add_entry_by_NID( name.get(), NID_countryName, MBSTRING_UTF8, reinterpret_cast("US"), /*len=*/-1, /*loc=*/0, /*set=*/-1)); check_name("CN=Name,O=Org2+CN=Name2+OU=Unit,O=Org,C=US"); // Likewise if there is no next RDN. ASSERT_TRUE(X509_NAME_add_entry_by_NID( name.get(), NID_commonName, MBSTRING_UTF8, reinterpret_cast("Name3"), /*len=*/-1, /*loc=*/-1, /*set=*/1)); check_name("CN=Name3,CN=Name,O=Org2+CN=Name2+OU=Unit,O=Org,C=US"); // If |set| = 0 and we insert in the middle of an existing RDN, it adds an // RDN boundary after the entry but not before. This is a quirk of how the // function is implemented and hopefully not something any caller depends on. ASSERT_TRUE(X509_NAME_add_entry_by_NID( name.get(), NID_commonName, MBSTRING_UTF8, reinterpret_cast("Name4"), /*len=*/-1, /*loc=*/3, /*set=*/0)); check_name("CN=Name3,CN=Name,O=Org2+CN=Name2,CN=Name4+OU=Unit,O=Org,C=US"); // Entries may be deleted. X509_NAME_ENTRY_free(X509_NAME_delete_entry(name.get(), 7)); check_name("CN=Name,O=Org2+CN=Name2,CN=Name4+OU=Unit,O=Org,C=US"); // When deleting the only attribute in an RDN, index invariants should still // hold. X509_NAME_ENTRY_free(X509_NAME_delete_entry(name.get(), 0)); check_name("CN=Name,O=Org2+CN=Name2,CN=Name4+OU=Unit,O=Org"); // Index invariants also hold when deleting attributes from non-singular RDNs. X509_NAME_ENTRY_free(X509_NAME_delete_entry(name.get(), 1)); check_name("CN=Name,O=Org2+CN=Name2,CN=Name4,O=Org"); X509_NAME_ENTRY_free(X509_NAME_delete_entry(name.get(), 1)); check_name("CN=Name,O=Org2+CN=Name2,O=Org"); // Same as above, but delete the second attribute first. X509_NAME_ENTRY_free(X509_NAME_delete_entry(name.get(), 2)); check_name("CN=Name,CN=Name2,O=Org"); X509_NAME_ENTRY_free(X509_NAME_delete_entry(name.get(), 1)); check_name("CN=Name,O=Org"); } // Tests that non-integer types are rejected when passed as an argument to // X509_set_serialNumber(). TEST(X509Test, SetSerialNumberChecksASN1StringType) { bssl::UniquePtr root = CertFromPEM(kRootCAPEM); ASSERT_TRUE(root); // Passing an IA5String to X509_set_serialNumber() should fail. bssl::UniquePtr str(ASN1_IA5STRING_new()); ASSERT_TRUE(str); EXPECT_FALSE(X509_set_serialNumber(root.get(), str.get())); // Passing a negative serial number is allowed. While invalid, we do accept // them and some callers rely in this for tests. bssl::UniquePtr serial(ASN1_INTEGER_new()); ASSERT_TRUE(serial); ASSERT_TRUE(ASN1_INTEGER_set_int64(serial.get(), -1)); ASSERT_TRUE(X509_set_serialNumber(root.get(), serial.get())); int64_t val; ASSERT_TRUE(ASN1_INTEGER_get_int64(&val, X509_get0_serialNumber(root.get()))); EXPECT_EQ(-1, val); } TEST(X509Test, Policy) { bssl::UniquePtr oid1( OBJ_txt2obj("1.2.840.113554.4.1.72585.2.1", /*dont_search_names=*/1)); ASSERT_TRUE(oid1); bssl::UniquePtr oid2( OBJ_txt2obj("1.2.840.113554.4.1.72585.2.2", /*dont_search_names=*/1)); ASSERT_TRUE(oid2); bssl::UniquePtr oid3( OBJ_txt2obj("1.2.840.113554.4.1.72585.2.3", /*dont_search_names=*/1)); ASSERT_TRUE(oid3); bssl::UniquePtr oid4( OBJ_txt2obj("1.2.840.113554.4.1.72585.2.4", /*dont_search_names=*/1)); ASSERT_TRUE(oid4); bssl::UniquePtr oid5( OBJ_txt2obj("1.2.840.113554.4.1.72585.2.5", /*dont_search_names=*/1)); ASSERT_TRUE(oid5); bssl::UniquePtr root( CertFromPEM(GetTestData("crypto/x509/test/policy_root.pem").c_str())); ASSERT_TRUE(root); bssl::UniquePtr 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 root2( CertFromPEM(GetTestData("crypto/x509/test/policy_root2.pem").c_str())); ASSERT_TRUE(root2); bssl::UniquePtr intermediate(CertFromPEM( GetTestData("crypto/x509/test/policy_intermediate.pem").c_str())); ASSERT_TRUE(intermediate); bssl::UniquePtr intermediate_any(CertFromPEM( GetTestData("crypto/x509/test/policy_intermediate_any.pem").c_str())); ASSERT_TRUE(intermediate_any); bssl::UniquePtr intermediate_duplicate(CertFromPEM( GetTestData("crypto/x509/test/policy_intermediate_duplicate.pem") .c_str())); ASSERT_TRUE(intermediate_duplicate); bssl::UniquePtr intermediate_invalid(CertFromPEM( GetTestData("crypto/x509/test/policy_intermediate_invalid.pem").c_str())); ASSERT_TRUE(intermediate_invalid); bssl::UniquePtr intermediate_mapped(CertFromPEM( GetTestData("crypto/x509/test/policy_intermediate_mapped.pem").c_str())); ASSERT_TRUE(intermediate_mapped); bssl::UniquePtr intermediate_mapped_any(CertFromPEM( GetTestData("crypto/x509/test/policy_intermediate_mapped_any.pem") .c_str())); ASSERT_TRUE(intermediate_mapped_any); bssl::UniquePtr intermediate_mapped_oid3(CertFromPEM( GetTestData("crypto/x509/test/policy_intermediate_mapped_oid3.pem") .c_str())); ASSERT_TRUE(intermediate_mapped_oid3); bssl::UniquePtr intermediate_require(CertFromPEM( GetTestData("crypto/x509/test/policy_intermediate_require.pem").c_str())); ASSERT_TRUE(intermediate_require); bssl::UniquePtr intermediate_require1(CertFromPEM( GetTestData("crypto/x509/test/policy_intermediate_require1.pem") .c_str())); ASSERT_TRUE(intermediate_require1); bssl::UniquePtr intermediate_require2(CertFromPEM( GetTestData("crypto/x509/test/policy_intermediate_require2.pem") .c_str())); ASSERT_TRUE(intermediate_require2); bssl::UniquePtr intermediate_require_duplicate(CertFromPEM( GetTestData("crypto/x509/test/policy_intermediate_require_duplicate.pem") .c_str())); ASSERT_TRUE(intermediate_require_duplicate); bssl::UniquePtr 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 leaf( CertFromPEM(GetTestData("crypto/x509/test/policy_leaf.pem").c_str())); ASSERT_TRUE(leaf); bssl::UniquePtr leaf_any( CertFromPEM(GetTestData("crypto/x509/test/policy_leaf_any.pem").c_str())); ASSERT_TRUE(leaf_any); bssl::UniquePtr leaf_duplicate(CertFromPEM( GetTestData("crypto/x509/test/policy_leaf_duplicate.pem").c_str())); ASSERT_TRUE(leaf_duplicate); bssl::UniquePtr leaf_invalid(CertFromPEM( GetTestData("crypto/x509/test/policy_leaf_invalid.pem").c_str())); ASSERT_TRUE(leaf_invalid); bssl::UniquePtr leaf_none(CertFromPEM( GetTestData("crypto/x509/test/policy_leaf_none.pem").c_str())); ASSERT_TRUE(leaf_none); bssl::UniquePtr leaf_oid1(CertFromPEM( GetTestData("crypto/x509/test/policy_leaf_oid1.pem").c_str())); ASSERT_TRUE(leaf_oid1); bssl::UniquePtr leaf_oid2(CertFromPEM( GetTestData("crypto/x509/test/policy_leaf_oid2.pem").c_str())); ASSERT_TRUE(leaf_oid2); bssl::UniquePtr leaf_oid3(CertFromPEM( GetTestData("crypto/x509/test/policy_leaf_oid3.pem").c_str())); ASSERT_TRUE(leaf_oid3); bssl::UniquePtr leaf_oid4(CertFromPEM( GetTestData("crypto/x509/test/policy_leaf_oid4.pem").c_str())); ASSERT_TRUE(leaf_oid4); bssl::UniquePtr leaf_oid5(CertFromPEM( GetTestData("crypto/x509/test/policy_leaf_oid5.pem").c_str())); ASSERT_TRUE(leaf_oid5); bssl::UniquePtr leaf_require(CertFromPEM( GetTestData("crypto/x509/test/policy_leaf_require.pem").c_str())); ASSERT_TRUE(leaf_require); bssl::UniquePtr 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 oids) { X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(ctx); for (const ASN1_OBJECT *oid : oids) { bssl::UniquePtr copy(OBJ_dup(oid)); ASSERT_TRUE(copy); ASSERT_TRUE(X509_VERIFY_PARAM_add0_policy(param, copy.get())); copy.release(); // |X509_VERIFY_PARAM_add0_policy| takes ownership on // success. } }; // The chain is good for |oid1| and |oid2|, but not |oid3|. EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {intermediate.get()}, /*crls=*/{}, X509_V_FLAG_EXPLICIT_POLICY)); 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 oid1( OBJ_txt2obj("1.2.840.113554.4.1.72585.2.1", /*dont_search_names=*/1)); ASSERT_TRUE(oid1); bssl::UniquePtr oid2( OBJ_txt2obj("1.2.840.113554.4.1.72585.2.2", /*dont_search_names=*/1)); ASSERT_TRUE(oid2); bssl::UniquePtr oid3( OBJ_txt2obj("1.2.840.113554.4.1.72585.2.3", /*dont_search_names=*/1)); ASSERT_TRUE(oid3); auto set_policies = [](X509_STORE_CTX *ctx, std::vector oids) { X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(ctx); for (const ASN1_OBJECT *oid : oids) { bssl::UniquePtr copy(OBJ_dup(oid)); ASSERT_TRUE(copy); ASSERT_TRUE(X509_VERIFY_PARAM_add0_policy(param, copy.get())); copy.release(); // |X509_VERIFY_PARAM_add0_policy| takes ownership on // success. } }; { bssl::UniquePtr root( CertFromPEM(GetTestData("crypto/x509/test/policy_root.pem").c_str())); ASSERT_TRUE(root); bssl::UniquePtr intermediate(CertFromPEM( GetTestData("crypto/x509/test/policy_intermediate.pem").c_str())); ASSERT_TRUE(intermediate); bssl::UniquePtr leaf( CertFromPEM(GetTestData("crypto/x509/test/policy_leaf.pem").c_str())); ASSERT_TRUE(leaf); std::vector threads; for (size_t i = 0; i < kNumThreads; i++) { threads.emplace_back([&] { EXPECT_EQ( X509_V_OK, Verify(leaf.get(), {root.get()}, {intermediate.get()}, /*crls=*/{}, X509_V_FLAG_EXPLICIT_POLICY, [&](X509_STORE_CTX *ctx) { set_policies(ctx, {oid1.get()}); })); }); } for (auto &thread : threads) { thread.join(); } } { bssl::UniquePtr root( CertFromPEM(GetTestData("crypto/x509/test/policy_root.pem").c_str())); ASSERT_TRUE(root); bssl::UniquePtr intermediate(CertFromPEM( GetTestData("crypto/x509/test/policy_intermediate.pem").c_str())); ASSERT_TRUE(intermediate); bssl::UniquePtr leaf_invalid(CertFromPEM( GetTestData("crypto/x509/test/policy_leaf_invalid.pem").c_str())); ASSERT_TRUE(leaf_invalid); std::vector threads; for (size_t i = 0; i < kNumThreads; i++) { threads.emplace_back([&] { EXPECT_EQ(X509_V_ERR_INVALID_POLICY_EXTENSION, Verify(leaf_invalid.get(), {root.get()}, {intermediate.get()}, /*crls=*/{}, X509_V_FLAG_EXPLICIT_POLICY, [&](X509_STORE_CTX *ctx) { set_policies(ctx, {oid1.get()}); })); }); } for (auto &thread : threads) { thread.join(); } } } #endif // OPENSSL_THREADS TEST(X509Test, ExtensionFromConf) { static const char kTestOID[] = "1.2.840.113554.4.1.72585.2"; const struct { const char *name; std::string value; // conf is the serialized confdb, or nullptr if none is to be provided. const char *conf; // expected is the resulting extension, encoded in DER, or the empty string // if an error is expected. std::vector expected; } kTests[] = { // Many extensions have built-in syntax. {"basicConstraints", "critical,CA:true", nullptr, {0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff}}, {"basicConstraints", "critical,CA:true,pathlen:1", nullptr, {0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x01}}, // key:value tuples can be repeated and just override the previous value. {"basicConstraints", "critical,CA:true,pathlen:100,pathlen:1", nullptr, {0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x01}}, // Extension contents may be referenced from a config section. {"basicConstraints", "critical,@section", "[section]\nCA = true\n", {0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff}}, // If no config is provided, this should fail. {"basicConstraints", "critical,@section", nullptr, {}}, // issuingDistributionPoint takes a list of name:value pairs. Omitting the // value is not allowed. {"issuingDistributionPoint", "fullname", nullptr, {}}, {"issuingDistributionPoint", "relativename:name", "[name]\nCN=Hello\n", {0x30, 0x1b, 0x06, 0x03, 0x55, 0x1d, 0x1c, 0x04, 0x14, 0x30, 0x12, 0xa0, 0x10, 0xa1, 0x0e, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f}}, // relativename referencing a section which doesn't exist. {"issuingDistributionPoint", "relativename:wrong_section_name", "[name]\nCN=Hello\n", {}}, // relativename must be a single RDN. By default, the section-based name // syntax puts each attribute into its own RDN. {"issuingDistributionPoint", "relativename:name", "[name]\nCN=Hello\nC=US\n", {}}, // A single RDN with multiple attributes is allowed. {"issuingDistributionPoint", "relativename:name", "[name]\nCN=Hello\n+C=US\n", {0x30, 0x26, 0x06, 0x03, 0x55, 0x1d, 0x1c, 0x04, 0x1f, 0x30, 0x1d, 0xa0, 0x1b, 0xa1, 0x19, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f}}, // Duplicate reason keys are an error. Reaching this case is interesting. // The value can a string like "key:value,key:value", or it can be // "@section" and reference a config section. If using a string, duplicate // keys are possible, but then it is impossible to put commas in the // value, as onlysomereasons expects. If using a section reference, it is // impossible to have a duplicate key because the config file parser // overrides the old value. {"issuingDistributionPoint", "onlysomereasons:keyCompromise", nullptr, {0x30, 0x0d, 0x06, 0x03, 0x55, 0x1d, 0x1c, 0x04, 0x06, 0x30, 0x04, 0x83, 0x02, 0x06, 0x40}}, {"issuingDistributionPoint", "onlysomereasons:keyCompromise,onlysomereasons:CACompromise\n", nullptr, {}}, // subjectAltName has a series of string-based inputs for each name type. {"subjectAltName", "email:foo@example.com, URI:https://example.com, DNS:example.com, " "RID:1.2.3.4, IP:127.0.0.1, IP:::1, dirName:section, " "otherName:1.2.3.4;BOOLEAN:TRUE", "[section]\nCN=Test\n", {0x30, 0x78, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x71, 0x30, 0x6f, 0x81, 0x0f, 0x66, 0x6f, 0x6f, 0x40, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x86, 0x13, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x82, 0x0b, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x88, 0x03, 0x2a, 0x03, 0x04, 0x87, 0x04, 0x7f, 0x00, 0x00, 0x01, 0x87, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xa4, 0x11, 0x30, 0x0f, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x04, 0x54, 0x65, 0x73, 0x74, 0xa0, 0x0a, 0x06, 0x03, 0x2a, 0x03, 0x04, 0xa0, 0x03, 0x01, 0x01, 0xff}}, // Syntax errors in each case, where they exist. (The string types just // copy the string in as-is.) {"subjectAltName", "RID:not_an_oid", nullptr, {}}, {"subjectAltName", "IP:not_an_ip", nullptr, {}}, {"subjectAltName", "dirName:no_conf_db", nullptr, {}}, {"subjectAltName", "dirName:missing_section", "[section]\nCN=Test\n", {}}, {"subjectAltName", "otherName:missing_semicolon", nullptr, {}}, {"subjectAltName", "otherName:1.2.3.4", nullptr, {}}, {"subjectAltName", "otherName:invalid_oid;BOOLEAN:TRUE", nullptr, {}}, {"subjectAltName", "otherName:1.2.3.4;invalid_value", nullptr, {}}, {"policyMappings", "1.1.1.1:2.2.2.2", nullptr, {0x30, 0x15, 0x06, 0x03, 0x55, 0x1d, 0x21, 0x04, 0x0e, 0x30, 0x0c, 0x30, 0x0a, 0x06, 0x03, 0x29, 0x01, 0x01, 0x06, 0x03, 0x52, 0x02, 0x02}}, {"policyMappings", "invalid_oid:2.2.2.2", nullptr, {}}, {"policyMappings", "1.1.1.1:invalid_oid", nullptr, {}}, // The "DER:" prefix just specifies an arbitrary byte string. Colons // separators are ignored. {kTestOID, "DER:0001020304", nullptr, {0x30, 0x15, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x05, 0x00, 0x01, 0x02, 0x03, 0x04}}, {kTestOID, "DER:00:01:02:03:04", nullptr, {0x30, 0x15, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x05, 0x00, 0x01, 0x02, 0x03, 0x04}}, {kTestOID, "DER:invalid hex", nullptr, {}}, // The "ASN1:" prefix implements a complex language for describing ASN.1 // structures. See // https://www.openssl.org/docs/man1.1.1/man3/ASN1_generate_nconf.html {kTestOID, "ASN1:invalid", nullptr, {}}, {kTestOID, "ASN1:BOOLEAN:TRUE", nullptr, {0x30, 0x13, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x03, 0x01, 0x01, 0xff}}, {kTestOID, "ASN1:BOOL:yes", nullptr, {0x30, 0x13, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x03, 0x01, 0x01, 0xff}}, {kTestOID, "ASN1:BOOLEAN:NO", nullptr, {0x30, 0x13, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x03, 0x01, 0x01, 0x00}}, {kTestOID, "ASN1:BOOLEAN", // Missing value nullptr, {}}, {kTestOID, "ASN1:BOOLEAN:invalid", nullptr, {}}, {kTestOID, "ASN1:BOOLEAN:TRUE,invalid", nullptr, {}}, {kTestOID, "ASN1:NULL", nullptr, {0x30, 0x12, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x02, 0x05, 0x00}}, {kTestOID, "ASN1:NULL,invalid", nullptr, {}}, {kTestOID, "ASN1:NULL:invalid", nullptr, {}}, // Missing value. {kTestOID, "ASN1:INTEGER", nullptr, {}}, {kTestOID, "ASN1:INTEGER:", nullptr, {}}, {kTestOID, "ASN1:INTEGER,invalid", nullptr, {}}, // INTEGER may be decimal or hexadecimal. {kTestOID, "ASN1:INT:-0x10", nullptr, {0x30, 0x13, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x03, 0x02, 0x01, 0xf0}}, {kTestOID, "ASN1:INT:-10", nullptr, {0x30, 0x13, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x03, 0x02, 0x01, 0xf6}}, {kTestOID, "ASN1:INT:0", nullptr, {0x30, 0x13, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x03, 0x02, 0x01, 0x00}}, {kTestOID, "ASN1:INTEGER:10", nullptr, {0x30, 0x13, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x03, 0x02, 0x01, 0x0a}}, {kTestOID, "ASN1:INTEGER:0x10", nullptr, {0x30, 0x13, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x03, 0x02, 0x01, 0x10}}, {kTestOID, "ASN1:ENUM:0", nullptr, {0x30, 0x13, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x03, 0x0a, 0x01, 0x00}}, {kTestOID, "ASN1:ENUMERATED:0", nullptr, {0x30, 0x13, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x03, 0x0a, 0x01, 0x00}}, // OIDs may be spelled out or specified by name. {kTestOID, "ASN1:OBJECT:invalid", nullptr, {}}, {kTestOID, "ASN1:OBJECT:basicConstraints", nullptr, {0x30, 0x15, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x05, 0x06, 0x03, 0x55, 0x1d, 0x13}}, {kTestOID, "ASN1:OBJECT:2.5.29.19", nullptr, {0x30, 0x15, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x05, 0x06, 0x03, 0x55, 0x1d, 0x13}}, {kTestOID, "ASN1:OID:2.5.29.19", nullptr, {0x30, 0x15, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x05, 0x06, 0x03, 0x55, 0x1d, 0x13}}, {kTestOID, "ASN1:UTC:invalid", nullptr, {}}, {kTestOID, "ASN1:UTC:20001231235959Z", nullptr, {}}, {kTestOID, "ASN1:UTCTIME:invalid", nullptr, {}}, {kTestOID, "ASN1:UTC:001231235959Z", nullptr, {0x30, 0x1f, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x0f, 0x17, 0x0d, 0x30, 0x30, 0x31, 0x32, 0x33, 0x31, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a}}, {kTestOID, "ASN1:UTCTIME:001231235959Z", nullptr, {0x30, 0x1f, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x0f, 0x17, 0x0d, 0x30, 0x30, 0x31, 0x32, 0x33, 0x31, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a}}, {kTestOID, "ASN1:GENTIME:invalid", nullptr, {}}, {kTestOID, "ASN1:GENTIME:001231235959Z", nullptr, {}}, {kTestOID, "ASN1:GENERALIZEDTIME:invalid", nullptr, {}}, {kTestOID, "ASN1:GENTIME:20001231235959Z", nullptr, {0x30, 0x21, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x11, 0x18, 0x0f, 0x32, 0x30, 0x30, 0x30, 0x31, 0x32, 0x33, 0x31, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a}}, {kTestOID, "ASN1:GENERALIZEDTIME:20001231235959Z", nullptr, {0x30, 0x21, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x11, 0x18, 0x0f, 0x32, 0x30, 0x30, 0x30, 0x31, 0x32, 0x33, 0x31, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a}}, // The default input format for string types is ASCII, which is then // converted into the target string type. {kTestOID, "ASN1:UTF8:hello", nullptr, {0x30, 0x17, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x07, 0x0c, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f}}, {kTestOID, "ASN1:UTF8String:hello", nullptr, {0x30, 0x17, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x07, 0x0c, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f}}, {kTestOID, "ASN1:UNIV:hello", nullptr, {0x30, 0x26, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x16, 0x1c, 0x14, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x6f}}, {kTestOID, "ASN1:UNIVERSALSTRING:hello", nullptr, {0x30, 0x26, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x16, 0x1c, 0x14, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x6f}}, {kTestOID, "ASN1:BMP:hello", nullptr, {0x30, 0x1c, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x0c, 0x1e, 0x0a, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x6f}}, {kTestOID, "ASN1:BMPSTRING:hello", nullptr, {0x30, 0x1c, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x0c, 0x1e, 0x0a, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x6f}}, {kTestOID, "ASN1:IA5:hello", nullptr, {0x30, 0x17, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x07, 0x16, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f}}, {kTestOID, "ASN1:IA5STRING:hello", nullptr, {0x30, 0x17, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x07, 0x16, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f}}, {kTestOID, "ASN1:PRINTABLE:hello", nullptr, {0x30, 0x17, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x07, 0x13, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f}}, {kTestOID, "ASN1:PRINTABLESTRING:hello", nullptr, {0x30, 0x17, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x07, 0x13, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f}}, {kTestOID, "ASN1:T61:hello", nullptr, {0x30, 0x17, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x07, 0x14, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f}}, {kTestOID, "ASN1:T61STRING:hello", nullptr, {0x30, 0x17, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x07, 0x14, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f}}, {kTestOID, "ASN1:TELETEXSTRING:hello", nullptr, {0x30, 0x17, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x07, 0x14, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f}}, // FORMAT:UTF8 switches the input format to UTF-8. This should be // converted to the destination string, or rejected if invalid. {kTestOID, "ASN1:FORMAT:UTF8,UTF8:\xe2\x98\x83", nullptr, {0x30, 0x15, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x05, 0x0c, 0x03, 0xe2, 0x98, 0x83}}, {kTestOID, "ASN1:FORMAT:UTF8,UNIV:\xe2\x98\x83", nullptr, {0x30, 0x16, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x06, 0x1c, 0x04, 0x00, 0x00, 0x26, 0x03}}, {kTestOID, "ASN1:FORMAT:UTF8,BMP:\xe2\x98\x83", nullptr, {0x30, 0x14, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x04, 0x1e, 0x02, 0x26, 0x03}}, {kTestOID, "ASN1:FORMAT:UTF8,IA5:\xe2\x98\x83", nullptr, {}}, {kTestOID, "ASN1:FORMAT:UTF8,IA5:hello", nullptr, {0x30, 0x17, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x07, 0x16, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f}}, {kTestOID, "ASN1:FORMAT:UTF8,PRINTABLE:\xe2\x98\x83", nullptr, {}}, {kTestOID, "ASN1:FORMAT:UTF8,PRINTABLE:hello", nullptr, {0x30, 0x17, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x07, 0x13, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f}}, {kTestOID, "ASN1:FORMAT:UTF8,T61:\xe2\x98\x83", nullptr, {}}, {kTestOID, "ASN1:FORMAT:UTF8,T61:\xc3\xb7", nullptr, {0x30, 0x13, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x03, 0x14, 0x01, 0xf7}}, // Invalid UTF-8. {kTestOID, "ASN1:FORMAT:UTF8,UTF8:\xff", nullptr, {}}, // We don't support these string types. {kTestOID, "ASN1:NUMERIC:0", nullptr, {}}, {kTestOID, "ASN1:NUMERICSTRING:0", nullptr, {}}, {kTestOID, "ASN1:VISIBLE:hello", nullptr, {}}, {kTestOID, "ASN1:VISIBLESTRING:hello", nullptr, {}}, {kTestOID, "ASN1:GeneralString:hello", nullptr, {}}, // OCTET STRING and BIT STRING also default to ASCII, but also accept HEX. // BIT STRING interprets OCTET STRING formats by having zero unused bits. {kTestOID, "ASN1:OCT:hello", nullptr, {0x30, 0x17, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x07, 0x04, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f}}, {kTestOID, "ASN1:OCTETSTRING:hello", nullptr, {0x30, 0x17, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x07, 0x04, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f}}, {kTestOID, "ASN1:FORMAT:HEX,OCT:0123abcd", nullptr, {0x30, 0x16, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x06, 0x04, 0x04, 0x01, 0x23, 0xab, 0xcd}}, {kTestOID, "ASN1:BITSTR:hello", nullptr, {0x30, 0x18, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x08, 0x03, 0x06, 0x00, 0x68, 0x65, 0x6c, 0x6c, 0x6f}}, {kTestOID, "ASN1:BITSTRING:hello", nullptr, {0x30, 0x18, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x08, 0x03, 0x06, 0x00, 0x68, 0x65, 0x6c, 0x6c, 0x6f}}, {kTestOID, "ASN1:FORMAT:HEX,BITSTR:0123abcd", nullptr, {0x30, 0x17, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x07, 0x03, 0x05, 0x00, 0x01, 0x23, 0xab, 0xcd}}, {kTestOID, "ASN1:FORMAT:HEX,OCT:invalid hex", nullptr, {}}, // BIT STRING additionally supports a BITLIST type, which specifies a // list of bits to set. {kTestOID, "ASN1:FORMAT:BITLIST,BITSTR:1,5", nullptr, {0x30, 0x14, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x04, 0x03, 0x02, 0x02, 0x44}}, {kTestOID, "ASN1:FORMAT:BITLIST,BITSTR:1,invalid,5", nullptr, {}}, // Negative bit inidices are not allowed. {kTestOID, "ASN1:FORMAT:BITLIST,BITSTR:-1", nullptr, {}}, // We cap bit indices at 256. {kTestOID, "ASN1:FORMAT:BITLIST,BITSTR:257", nullptr, {}}, {kTestOID, "ASN1:FORMAT:BITLIST,BITSTR:256", nullptr, {0x30, 0x34, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x24, 0x03, 0x22, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80}}, // Unsupported formats for string types. {kTestOID, "ASN1:FORMAT:BITLIST,IA5:abcd", nullptr, {}}, {kTestOID, "ASN1:FORMAT:BITLIST,UTF8:abcd", nullptr, {}}, {kTestOID, "ASN1:FORMAT:BITLIST,OCT:abcd", nullptr, {}}, {kTestOID, "ASN1:FORMAT:BITLIST,UTC:abcd", nullptr, {}}, {kTestOID, "ASN1:FORMAT:HEX,IA5:abcd", nullptr, {}}, {kTestOID, "ASN1:FORMAT:HEX,UTF8:abcd", nullptr, {}}, {kTestOID, "ASN1:FORMAT:HEX,UTC:abcd", nullptr, {}}, {kTestOID, "ASN1:FORMAT:UTF8,OCT:abcd", nullptr, {}}, {kTestOID, "ASN1:FORMAT:UTF8,UTC:abcd", nullptr, {}}, // Invalid format type. {kTestOID, "ASN1:FORMAT:invalid,IA5:abcd", nullptr, {}}, // SEQUENCE and SET encode empty values when there is no value. {kTestOID, "ASN1:SEQ", nullptr, {0x30, 0x12, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x02, 0x30, 0x00}}, {kTestOID, "ASN1:SET", nullptr, {0x30, 0x12, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x02, 0x31, 0x00}}, {kTestOID, "ASN1:SEQUENCE", nullptr, {0x30, 0x12, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x02, 0x30, 0x00}}, // Otherwise, they require a corresponding section in the config database // to encode values. This can be nested recursively. {kTestOID, "ASN1:SEQ:missing_confdb", nullptr, {}}, {kTestOID, "ASN1:SET:missing_confdb", nullptr, {}}, {kTestOID, "ASN1:SEQ:seq", R"( [seq] val1 = NULL val2 = IA5:a val3 = SET:set [set] # Config names do not matter, only the order. val4 = INT:1 val3 = INT:2 val2 = SEQ:empty val1 = INT:3 [empty] )", {0x30, 0x24, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x14, 0x30, 0x12, 0x05, 0x00, 0x16, 0x01, 0x61, 0x31, 0x0b, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x01, 0x03, 0x30, 0x00}}, // There is a recursion limit to stop infinite recursion. {kTestOID, "ASN1:SEQ:seq1", R"( [seq1] val = SEQ:seq2 [seq2] val = SEQ:seq1 )", {}}, // Various modifiers wrap with explicit tagging or universal types. {kTestOID, "ASN1:EXP:0,EXP:16U,EXP:100A,EXP:1000C,OCTWRAP,SEQWRAP,SETWRAP,BITWRAP," "NULL", nullptr, {0x30, 0x26, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x16, 0xa0, 0x14, 0x30, 0x12, 0x7f, 0x64, 0x0f, 0xbf, 0x87, 0x68, 0x0b, 0x04, 0x09, 0x30, 0x07, 0x31, 0x05, 0x03, 0x03, 0x00, 0x05, 0x00}}, // Invalid tag numbers. {kTestOID, "ASN1:EXP:-1,NULL", nullptr, {}}, {kTestOID, "ASN1:EXP:1?,NULL", nullptr, {}}, // Fits in |uint32_t| but exceeds |CBS_ASN1_TAG_NUMBER_MASK|, the largest // tag number we support. {kTestOID, "ASN1:EXP:536870912,NULL", nullptr, {}}, // Implicit tagging may also be applied to the underlying type, or the // wrapping modifiers. {kTestOID, "ASN1:IMP:1A,OCTWRAP,IMP:10,SEQWRAP,IMP:100,SETWRAP,IMP:1000,BITWRAP," "IMP:10000,NULL", nullptr, {0x30, 0x20, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x10, 0x41, 0x0e, 0xaa, 0x0c, 0xbf, 0x64, 0x09, 0x9f, 0x87, 0x68, 0x05, 0x00, 0x9f, 0xce, 0x10, 0x00}}, // Implicit tagging may not be applied to explicit tagging or itself. // There's no rule against this in ASN.1, but OpenSSL does not allow it // here. {kTestOID, "ASN1:IMP:1,EXP:1,NULL", nullptr, {}}, {kTestOID, "ASN1:IMP:1,IMP:1,NULL", nullptr, {}}, // [UNIVERSAL 0] is reserved. {kTestOID, "ASN1:0U,NULL", nullptr, {}}, // Leading and trailing spaces on name:value pairs are removed. However, // while these pairs are delimited by commas, a type will consumes // everything after it, including commas, and spaces. So this is the // string " a, b ". {kTestOID, "ASN1: EXP:0 , IA5: a, b ", nullptr, {0x30, 0x1a, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x0a, 0xa0, 0x08, 0x16, 0x06, 0x20, 0x61, 0x2c, 0x20, 0x62, 0x20}}, // Modifiers without a final type. {kTestOID, "ASN1:EXP:1", nullptr, {}}, // Put it all together to describe a test Ed25519 key (wrapped inside an // X.509 extension). {kTestOID, "ASN1:SEQUENCE:pkcs8", R"( [pkcs8] vers = INT:0 alg = SEQWRAP,OID:1.3.101.112 key = FORMAT:HEX,OCTWRAP,OCT:9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60 )", {0x30, 0x40, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x30, 0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x04, 0x22, 0x04, 0x20, 0x9d, 0x61, 0xb1, 0x9d, 0xef, 0xfd, 0x5a, 0x60, 0xba, 0x84, 0x4a, 0xf4, 0x92, 0xec, 0x2c, 0xc4, 0x44, 0x49, 0xc5, 0x69, 0x7b, 0x32, 0x69, 0x19, 0x70, 0x3b, 0xac, 0x03, 0x1c, 0xae, 0x7f, 0x60}}, // Sections can be referenced multiple times. {kTestOID, "ASN1:SEQUENCE:seq1", R"( [seq1] val1 = SEQUENCE:seq2 val2 = SEQUENCE:seq2 [seq2] val1 = INT:1 val2 = INT:2 )", {0x30, 0x22, 0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02, 0x04, 0x12, 0x30, 0x10, 0x30, 0x06, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02, 0x30, 0x06, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02}}, // But we cap this before it blows up exponentially. {kTestOID, "ASN1:SEQ:seq1", R"( [seq1] val1 = SEQ:seq2 val2 = SEQ:seq2 [seq2] val1 = SEQ:seq3 val2 = SEQ:seq3 [seq3] val1 = SEQ:seq4 val2 = SEQ:seq4 [seq4] val1 = SEQ:seq5 val2 = SEQ:seq5 [seq5] val1 = SEQ:seq6 val2 = SEQ:seq6 [seq6] val1 = SEQ:seq7 val2 = SEQ:seq7 [seq7] val1 = SEQ:seq8 val2 = SEQ:seq8 [seq8] val1 = SEQ:seq9 val2 = SEQ:seq9 [seq9] val1 = SEQ:seq10 val2 = SEQ:seq10 [seq10] val1 = SEQ:seq11 val2 = SEQ:seq11 [seq11] val1 = SEQ:seq12 val2 = SEQ:seq12 [seq12] val1 = IA5:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA val2 = IA5:BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB )", {}}, // Integer sizes are capped to mitigate quadratic behavior. {kTestOID, "ASN1:INT:" + std::string(16384, '9'), nullptr, {}}, }; for (const auto &t : kTests) { SCOPED_TRACE(t.name); SCOPED_TRACE(t.value); SCOPED_TRACE(t.conf); bssl::UniquePtr conf; if (t.conf != nullptr) { conf.reset(NCONF_new(nullptr)); ASSERT_TRUE(conf); bssl::UniquePtr bio(BIO_new_mem_buf(t.conf, strlen(t.conf))); ASSERT_TRUE(bio); long error_line; ASSERT_TRUE(NCONF_load_bio(conf.get(), bio.get(), &error_line)) << "Failed to load config at line " << error_line; } bssl::UniquePtr ext( X509V3_EXT_nconf(conf.get(), nullptr, t.name, t.value.c_str())); if (t.expected.empty()) { EXPECT_FALSE(ext); } else { ASSERT_TRUE(ext); uint8_t *der = nullptr; int len = i2d_X509_EXTENSION(ext.get(), &der); ASSERT_GE(len, 0); bssl::UniquePtr free_der(der); EXPECT_EQ(Bytes(t.expected), Bytes(der, len)); } // Repeat the test with an explicit |X509V3_CTX|. X509V3_CTX ctx; X509V3_set_ctx(&ctx, nullptr, nullptr, nullptr, nullptr, 0); X509V3_set_nconf(&ctx, conf.get()); ext.reset(X509V3_EXT_nconf(conf.get(), &ctx, t.name, t.value.c_str())); if (t.expected.empty()) { EXPECT_FALSE(ext); } else { ASSERT_TRUE(ext); uint8_t *der = nullptr; int len = i2d_X509_EXTENSION(ext.get(), &der); ASSERT_GE(len, 0); bssl::UniquePtr free_der(der); EXPECT_EQ(Bytes(t.expected), Bytes(der, len)); } } } TEST(X509Test, AddUnserializableExtension) { bssl::UniquePtr key = PrivateKeyFromPEM(kP256Key); ASSERT_TRUE(key); bssl::UniquePtr x509 = MakeTestCert("Issuer", "Subject", key.get(), /*is_ca=*/true); ASSERT_TRUE(x509); bssl::UniquePtr ext(X509_EXTENSION_new()); ASSERT_TRUE(X509_EXTENSION_set_object(ext.get(), OBJ_get_undef())); EXPECT_FALSE(X509_add_ext(x509.get(), ext.get(), /*loc=*/-1)); } // Test that, when constructing an |X509_NAME|, names are sorted by DER order. TEST(X509Test, SortRDN) { bssl::UniquePtr name(X509_NAME_new()); ASSERT_TRUE(name); auto append_entry_new_rdn = [&](const char *str) { return X509_NAME_add_entry_by_NID(name.get(), NID_commonName, MBSTRING_ASC, reinterpret_cast(str), strlen(str), /*loc=*/-1, /*set=*/0); }; auto append_entry_prev_rdn = [&](const char *str) { return X509_NAME_add_entry_by_NID(name.get(), NID_commonName, MBSTRING_ASC, reinterpret_cast(str), strlen(str), /*loc=*/-1, /*set=*/-1); }; // This is the sort order to expect. ASSERT_TRUE(append_entry_new_rdn("A")); ASSERT_TRUE(append_entry_prev_rdn("B")); ASSERT_TRUE(append_entry_prev_rdn("AA")); ASSERT_TRUE(append_entry_prev_rdn("AB")); // The same RDN, with entries added in a different order. ASSERT_TRUE(append_entry_new_rdn("AB")); ASSERT_TRUE(append_entry_prev_rdn("AA")); ASSERT_TRUE(append_entry_prev_rdn("B")); ASSERT_TRUE(append_entry_prev_rdn("A")); // The same RDN, with entries added in a different order. ASSERT_TRUE(append_entry_new_rdn("A")); ASSERT_TRUE(append_entry_prev_rdn("AA")); ASSERT_TRUE(append_entry_prev_rdn("B")); ASSERT_TRUE(append_entry_prev_rdn("AB")); uint8_t *der = nullptr; int der_len = i2d_X509_NAME(name.get(), &der); ASSERT_GT(der_len, 0); bssl::UniquePtr free_der(der); // SEQUENCE { // SET { // SEQUENCE { // # commonName // OBJECT_IDENTIFIER { 2.5.4.3 } // UTF8String { "A" } // } // SEQUENCE { // # commonName // OBJECT_IDENTIFIER { 2.5.4.3 } // UTF8String { "B" } // } // SEQUENCE { // # commonName // OBJECT_IDENTIFIER { 2.5.4.3 } // UTF8String { "AA" } // } // SEQUENCE { // # commonName // OBJECT_IDENTIFIER { 2.5.4.3 } // UTF8String { "AB" } // } // } // ...two more copies of the above SET... // } static uint8_t kExpected[] = { 0x30, 0x81, 0x84, 0x31, 0x2a, 0x30, 0x08, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x01, 0x41, 0x30, 0x08, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x01, 0x42, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x02, 0x41, 0x41, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x02, 0x41, 0x42, 0x31, 0x2a, 0x30, 0x08, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x01, 0x41, 0x30, 0x08, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x01, 0x42, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x02, 0x41, 0x41, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x02, 0x41, 0x42, 0x31, 0x2a, 0x30, 0x08, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x01, 0x41, 0x30, 0x08, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x01, 0x42, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x02, 0x41, 0x41, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x02, 0x41, 0x42}; EXPECT_EQ(Bytes(kExpected), Bytes(der, der_len)); } TEST(X509Test, NameAttributeValues) { // 1.2.840.113554.4.1.72585.0. We use an unrecognized OID because using an // arbitrary ASN.1 type as the value for commonName is invalid. Our parser // does not check this, but best to avoid unrelated errors in tests, in case // we decide to later. static const uint8_t kOID[] = {0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09, 0x00}; static const char kOIDText[] = "1.2.840.113554.4.1.72585.0"; auto encode_single_attribute_name = [](CBS_ASN1_TAG tag, const std::string &contents) -> std::vector { bssl::ScopedCBB cbb; CBB seq, rdn, attr, attr_type, attr_value; if (!CBB_init(cbb.get(), 128) || !CBB_add_asn1(cbb.get(), &seq, CBS_ASN1_SEQUENCE) || !CBB_add_asn1(&seq, &rdn, CBS_ASN1_SET) || !CBB_add_asn1(&rdn, &attr, CBS_ASN1_SEQUENCE) || !CBB_add_asn1(&attr, &attr_type, CBS_ASN1_OBJECT) || !CBB_add_bytes(&attr_type, kOID, sizeof(kOID)) || !CBB_add_asn1(&attr, &attr_value, tag) || !CBB_add_bytes(&attr_value, reinterpret_cast(contents.data()), contents.size()) || !CBB_flush(cbb.get())) { ADD_FAILURE() << "Could not encode name"; return {}; }; return std::vector(CBB_data(cbb.get()), CBB_data(cbb.get()) + CBB_len(cbb.get())); }; const struct { CBS_ASN1_TAG der_tag; std::string der_contents; int str_type; std::string str_contents; } kTests[] = { // String types are parsed as string types. {CBS_ASN1_BITSTRING, std::string("\0", 1), V_ASN1_BIT_STRING, ""}, {CBS_ASN1_UTF8STRING, "abc", V_ASN1_UTF8STRING, "abc"}, {CBS_ASN1_NUMERICSTRING, "123", V_ASN1_NUMERICSTRING, "123"}, {CBS_ASN1_PRINTABLESTRING, "abc", V_ASN1_PRINTABLESTRING, "abc"}, {CBS_ASN1_T61STRING, "abc", V_ASN1_T61STRING, "abc"}, {CBS_ASN1_IA5STRING, "abc", V_ASN1_IA5STRING, "abc"}, {CBS_ASN1_UNIVERSALSTRING, std::string("\0\0\0a", 4), V_ASN1_UNIVERSALSTRING, std::string("\0\0\0a", 4)}, {CBS_ASN1_BMPSTRING, std::string("\0a", 2), V_ASN1_BMPSTRING, std::string("\0a", 2)}, // ENUMERATED is supported but, currently, INTEGER is not. {CBS_ASN1_ENUMERATED, "\x01", V_ASN1_ENUMERATED, "\x01"}, // Test negative values. These are interesting because, when encoding, the // ASN.1 type must be determined from the string type, but the string type // has an extra |V_ASN1_NEG| bit. {CBS_ASN1_ENUMERATED, "\xff", V_ASN1_NEG_ENUMERATED, "\x01"}, // SEQUENCE is supported but, currently, SET is not. Note the // |ASN1_STRING| representation will include the tag and length. {CBS_ASN1_SEQUENCE, "", V_ASN1_SEQUENCE, std::string("\x30\x00", 2)}, // These types are not actually supported by the library but, // historically, we would parse them, and not other unsupported types, due // to quirks of |ASN1_tag2bit|. {7, "", V_ASN1_OBJECT_DESCRIPTOR, ""}, {8, "", V_ASN1_EXTERNAL, ""}, {9, "", V_ASN1_REAL, ""}, {11, "", 11 /* EMBEDDED PDV */, ""}, {13, "", 13 /* RELATIVE-OID */, ""}, {14, "", 14 /* TIME */, ""}, {15, "", 15 /* not a type; reserved value */, ""}, {29, "", 29 /* CHARACTER STRING */, ""}, // TODO(crbug.com/boringssl/412): Attribute values are an ANY DEFINED BY // type, so we actually shoudl be accepting all ASN.1 types. We currently // do not and only accept the above types. Extend this test when we fix // this. }; for (const auto &t : kTests) { SCOPED_TRACE(t.der_tag); SCOPED_TRACE(Bytes(t.der_contents)); // Construct an X.509 name containing a single RDN with a single attribute: // kOID with the specified value. auto encoded = encode_single_attribute_name(t.der_tag, t.der_contents); ASSERT_FALSE(encoded.empty()); SCOPED_TRACE(Bytes(encoded)); // The input should parse. const uint8_t *inp = encoded.data(); bssl::UniquePtr name( d2i_X509_NAME(nullptr, &inp, encoded.size())); ASSERT_TRUE(name); EXPECT_EQ(inp, encoded.data() + encoded.size()) << "input was not fully consumed"; // Check there is a single attribute with the expected in-memory // representation. ASSERT_EQ(1, X509_NAME_entry_count(name.get())); const X509_NAME_ENTRY *entry = X509_NAME_get_entry(name.get(), 0); const ASN1_OBJECT *obj = X509_NAME_ENTRY_get_object(entry); EXPECT_EQ(Bytes(OBJ_get0_data(obj), OBJ_length(obj)), Bytes(kOID)); const ASN1_STRING *value = X509_NAME_ENTRY_get_data(entry); EXPECT_EQ(ASN1_STRING_type(value), t.str_type); EXPECT_EQ(Bytes(ASN1_STRING_get0_data(value), ASN1_STRING_length(value)), Bytes(t.str_contents)); // The name should re-encode with the same input. uint8_t *der = nullptr; int der_len = i2d_X509_NAME(name.get(), &der); ASSERT_GE(der_len, 0); bssl::UniquePtr free_der(der); EXPECT_EQ(Bytes(der, der_len), Bytes(encoded)); // X509_NAME internally caches its encoding, which means the check above // does not fully test re-encoding. Repeat the test by constructing an // |X509_NAME| from the string representation. name.reset(X509_NAME_new()); ASSERT_TRUE(name); ASSERT_TRUE(X509_NAME_add_entry_by_txt( name.get(), kOIDText, t.str_type, reinterpret_cast(t.str_contents.data()), t.str_contents.size(), /*loc=*/-1, /*set=*/0)); // The name should re-encode with the same input. der = nullptr; der_len = i2d_X509_NAME(name.get(), &der); ASSERT_GE(der_len, 0); free_der.reset(der); EXPECT_EQ(Bytes(der, der_len), Bytes(encoded)); } const struct { CBS_ASN1_TAG der_tag; std::string der_contents; } kInvalidTests[] = { // Errors in supported universal types should be handled. {CBS_ASN1_NULL, "not null"}, {CBS_ASN1_BOOLEAN, "not bool"}, {CBS_ASN1_OBJECT, ""}, {CBS_ASN1_INTEGER, std::string("\0\0", 2)}, {CBS_ASN1_ENUMERATED, std::string("\0\0", 2)}, {CBS_ASN1_BITSTRING, ""}, {CBS_ASN1_UTF8STRING, "not utf-8 \xff"}, {CBS_ASN1_BMPSTRING, "not utf-16 "}, {CBS_ASN1_UNIVERSALSTRING, "not utf-32"}, {CBS_ASN1_UTCTIME, "not utctime"}, {CBS_ASN1_GENERALIZEDTIME, "not generalizedtime"}, {CBS_ASN1_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 name( d2i_X509_NAME(nullptr, &inp, encoded.size())); EXPECT_FALSE(name); } } TEST(X509Test, GetTextByOBJ) { struct OBJTestCase { const char *content; int content_type; int len; int expected_result; const char *expected_string; } kTests[] = { {"", V_ASN1_UTF8STRING, 0, 0, ""}, {"derp", V_ASN1_UTF8STRING, 4, 4, "derp"}, {"\x30\x00", // Empty sequence can not be converted to UTF-8 V_ASN1_SEQUENCE, 2, -1, ""}, { "der\0p", V_ASN1_TELETEXSTRING, 5, -1, "", }, { "0123456789ABCDEF", V_ASN1_IA5STRING, 16, 16, "0123456789ABCDEF", }, { "\x07\xff", V_ASN1_BMPSTRING, 2, 2, "\xdf\xbf", }, { "\x00\xc3\x00\xaf", V_ASN1_BMPSTRING, 4, 4, "\xc3\x83\xc2\xaf", }, }; for (const auto &test : kTests) { bssl::UniquePtr name(X509_NAME_new()); ASSERT_TRUE(name); ASSERT_TRUE(X509_NAME_add_entry_by_NID( name.get(), NID_commonName, test.content_type, reinterpret_cast(test.content), test.len, /*loc=*/-1, /*set=*/0)); char text[256] = {}; EXPECT_EQ(test.expected_result, X509_NAME_get_text_by_NID(name.get(), NID_commonName, text, sizeof(text))); EXPECT_STREQ(text, test.expected_string); if (test.expected_result > 0) { // Test truncation. The function writes a trailing NUL byte so the // buffer needs to be one bigger than the expected result. char small[2] = "a"; EXPECT_EQ( -1, X509_NAME_get_text_by_NID(name.get(), NID_commonName, small, 1)); // The buffer should be unmodified by truncation failure. EXPECT_STREQ(small, "a"); } } } TEST(X509Test, ParamInheritance) { // |X509_VERIFY_PARAM_inherit| with both unset. { bssl::UniquePtr dest(X509_VERIFY_PARAM_new()); ASSERT_TRUE(dest); bssl::UniquePtr src(X509_VERIFY_PARAM_new()); ASSERT_TRUE(src); ASSERT_TRUE(X509_VERIFY_PARAM_inherit(dest.get(), src.get())); EXPECT_EQ(X509_VERIFY_PARAM_get_depth(dest.get()), -1); } // |X509_VERIFY_PARAM_inherit| with source set. { bssl::UniquePtr dest(X509_VERIFY_PARAM_new()); ASSERT_TRUE(dest); bssl::UniquePtr src(X509_VERIFY_PARAM_new()); ASSERT_TRUE(src); X509_VERIFY_PARAM_set_depth(src.get(), 5); ASSERT_TRUE(X509_VERIFY_PARAM_inherit(dest.get(), src.get())); EXPECT_EQ(X509_VERIFY_PARAM_get_depth(dest.get()), 5); } // |X509_VERIFY_PARAM_inherit| with destination set. { bssl::UniquePtr dest(X509_VERIFY_PARAM_new()); ASSERT_TRUE(dest); bssl::UniquePtr src(X509_VERIFY_PARAM_new()); ASSERT_TRUE(src); X509_VERIFY_PARAM_set_depth(dest.get(), 5); ASSERT_TRUE(X509_VERIFY_PARAM_inherit(dest.get(), src.get())); EXPECT_EQ(X509_VERIFY_PARAM_get_depth(dest.get()), 5); } // |X509_VERIFY_PARAM_inherit| with both set. { bssl::UniquePtr dest(X509_VERIFY_PARAM_new()); ASSERT_TRUE(dest); bssl::UniquePtr src(X509_VERIFY_PARAM_new()); ASSERT_TRUE(src); X509_VERIFY_PARAM_set_depth(dest.get(), 5); X509_VERIFY_PARAM_set_depth(src.get(), 10); ASSERT_TRUE(X509_VERIFY_PARAM_inherit(dest.get(), src.get())); // The existing value is used. EXPECT_EQ(X509_VERIFY_PARAM_get_depth(dest.get()), 5); } // |X509_VERIFY_PARAM_set1| with both unset. { bssl::UniquePtr dest(X509_VERIFY_PARAM_new()); ASSERT_TRUE(dest); bssl::UniquePtr src(X509_VERIFY_PARAM_new()); ASSERT_TRUE(src); ASSERT_TRUE(X509_VERIFY_PARAM_set1(dest.get(), src.get())); EXPECT_EQ(X509_VERIFY_PARAM_get_depth(dest.get()), -1); } // |X509_VERIFY_PARAM_set1| with source set. { bssl::UniquePtr dest(X509_VERIFY_PARAM_new()); ASSERT_TRUE(dest); bssl::UniquePtr src(X509_VERIFY_PARAM_new()); ASSERT_TRUE(src); X509_VERIFY_PARAM_set_depth(src.get(), 5); ASSERT_TRUE(X509_VERIFY_PARAM_set1(dest.get(), src.get())); EXPECT_EQ(X509_VERIFY_PARAM_get_depth(dest.get()), 5); } // |X509_VERIFY_PARAM_set1| with destination set. { bssl::UniquePtr dest(X509_VERIFY_PARAM_new()); ASSERT_TRUE(dest); bssl::UniquePtr src(X509_VERIFY_PARAM_new()); ASSERT_TRUE(src); X509_VERIFY_PARAM_set_depth(dest.get(), 5); ASSERT_TRUE(X509_VERIFY_PARAM_set1(dest.get(), src.get())); EXPECT_EQ(X509_VERIFY_PARAM_get_depth(dest.get()), 5); } // |X509_VERIFY_PARAM_set1| with both set. { bssl::UniquePtr dest(X509_VERIFY_PARAM_new()); ASSERT_TRUE(dest); bssl::UniquePtr src(X509_VERIFY_PARAM_new()); ASSERT_TRUE(src); X509_VERIFY_PARAM_set_depth(dest.get(), 5); X509_VERIFY_PARAM_set_depth(src.get(), 10); ASSERT_TRUE(X509_VERIFY_PARAM_set1(dest.get(), src.get())); // The new value is used. EXPECT_EQ(X509_VERIFY_PARAM_get_depth(dest.get()), 10); } } TEST(X509Test, PublicKeyCache) { bssl::UniquePtr key = PrivateKeyFromPEM(kP256Key); ASSERT_TRUE(key); X509_PUBKEY *pub = nullptr; ASSERT_TRUE(X509_PUBKEY_set(&pub, key.get())); bssl::UniquePtr free_pub(pub); bssl::UniquePtr key2(X509_PUBKEY_get(pub)); ASSERT_TRUE(key2); EXPECT_EQ(1, EVP_PKEY_cmp(key.get(), key2.get())); // Replace |pub| with different (garbage) values. ASSERT_TRUE(X509_PUBKEY_set0_param(pub, OBJ_nid2obj(NID_subject_alt_name), V_ASN1_NULL, nullptr, nullptr, 0)); // The cached key should no longer be returned. key2.reset(X509_PUBKEY_get(pub)); EXPECT_FALSE(key2); } // Tests some unusual behavior in |X509_STORE_CTX_set_purpose| and // |X509_STORE_CTX_set_trust|. TEST(X509Test, ContextTrustAndPurpose) { bssl::UniquePtr store(X509_STORE_new()); ASSERT_TRUE(store); bssl::UniquePtr leaf(CertFromPEM(kLeafPEM)); ASSERT_TRUE(leaf); bssl::UniquePtr ctx(X509_STORE_CTX_new()); ASSERT_TRUE(ctx); ASSERT_TRUE(X509_STORE_CTX_init(ctx.get(), store.get(), leaf.get(), nullptr)); // Initially, neither parameter is set. EXPECT_EQ(ctx->param->purpose, 0); EXPECT_EQ(ctx->param->trust, 0); // Invalid purpose and trust types fail. EXPECT_FALSE(X509_STORE_CTX_set_purpose(ctx.get(), 999)); EXPECT_FALSE(X509_STORE_CTX_set_trust(ctx.get(), 999)); // It is not possible to set |X509_PURPOSE_ANY| with this API, because there // is no corresponding trust. EXPECT_FALSE(X509_STORE_CTX_set_purpose(ctx.get(), X509_PURPOSE_ANY)); // Setting a purpose also sets the corresponding trust. ASSERT_TRUE(X509_STORE_CTX_set_purpose(ctx.get(), X509_PURPOSE_SSL_SERVER)); EXPECT_EQ(ctx->param->purpose, X509_PURPOSE_SSL_SERVER); EXPECT_EQ(ctx->param->trust, X509_TRUST_SSL_SERVER); // Once set, the functions silently do nothing. ASSERT_TRUE(X509_STORE_CTX_set_purpose(ctx.get(), X509_PURPOSE_SSL_CLIENT)); ASSERT_TRUE(X509_STORE_CTX_set_trust(ctx.get(), X509_TRUST_SSL_CLIENT)); EXPECT_EQ(ctx->param->purpose, X509_PURPOSE_SSL_SERVER); EXPECT_EQ(ctx->param->trust, X509_TRUST_SSL_SERVER); // Start over. ctx.reset(X509_STORE_CTX_new()); ASSERT_TRUE(ctx); ASSERT_TRUE(X509_STORE_CTX_init(ctx.get(), store.get(), leaf.get(), nullptr)); EXPECT_EQ(ctx->param->purpose, 0); EXPECT_EQ(ctx->param->trust, 0); // Setting trust leaves purpose unset. ASSERT_TRUE(X509_STORE_CTX_set_trust(ctx.get(), X509_TRUST_SSL_SERVER)); EXPECT_EQ(ctx->param->purpose, 0); EXPECT_EQ(ctx->param->trust, X509_TRUST_SSL_SERVER); // If trust is set, but not purpose, |X509_STORE_CTX_set_purpose| only sets // purpose. ASSERT_TRUE(X509_STORE_CTX_set_purpose(ctx.get(), X509_PURPOSE_SSL_CLIENT)); EXPECT_EQ(ctx->param->purpose, X509_PURPOSE_SSL_CLIENT); EXPECT_EQ(ctx->param->trust, X509_TRUST_SSL_SERVER); // Start over. ctx.reset(X509_STORE_CTX_new()); ASSERT_TRUE(ctx); ASSERT_TRUE(X509_STORE_CTX_init(ctx.get(), store.get(), leaf.get(), nullptr)); EXPECT_EQ(ctx->param->purpose, 0); EXPECT_EQ(ctx->param->trust, 0); // If purpose is set, but not trust, |X509_STORE_CTX_set_purpose| only sets // trust. ASSERT_TRUE(X509_VERIFY_PARAM_set_purpose( X509_STORE_CTX_get0_param(ctx.get()), X509_PURPOSE_SSL_CLIENT)); EXPECT_EQ(ctx->param->purpose, X509_PURPOSE_SSL_CLIENT); EXPECT_EQ(ctx->param->trust, 0); ASSERT_TRUE(X509_STORE_CTX_set_purpose(ctx.get(), X509_PURPOSE_SSL_SERVER)); EXPECT_EQ(ctx->param->purpose, X509_PURPOSE_SSL_CLIENT); EXPECT_EQ(ctx->param->trust, X509_TRUST_SSL_SERVER); } TEST(X509Test, Purpose) { bssl::UniquePtr key = PrivateKeyFromPEM(kP256Key); ASSERT_TRUE(key); struct { int purpose; int eku_nid; std::vector key_usages; } kTests[] = { {X509_PURPOSE_SSL_CLIENT, NID_client_auth, {KeyUsage::kDigitalSignature, KeyUsage::kKeyAgreement}}, {X509_PURPOSE_SSL_SERVER, NID_server_auth, {KeyUsage::kDigitalSignature, KeyUsage::kKeyAgreement, KeyUsage::kKeyEncipherment}}, {X509_PURPOSE_NS_SSL_SERVER, NID_server_auth, {KeyUsage::kKeyEncipherment}}, {X509_PURPOSE_SMIME_SIGN, NID_email_protect, {KeyUsage::kDigitalSignature, KeyUsage::kNonRepudiation}}, {X509_PURPOSE_SMIME_ENCRYPT, NID_email_protect, {KeyUsage::kKeyEncipherment}}, {X509_PURPOSE_CRL_SIGN, NID_undef, {KeyUsage::kCRLSign}}, }; for (const auto &t : kTests) { SCOPED_TRACE(t.purpose); auto configure_callback = [&](X509_STORE_CTX *ctx) { X509_STORE_CTX_set_purpose(ctx, t.purpose); }; // An unconstrained cert chain is valid. bssl::UniquePtr root = MakeTestCert("Root", "Root", key.get(), /*is_ca=*/true); ASSERT_TRUE(root); ASSERT_TRUE(X509_sign(root.get(), key.get(), EVP_sha256())); bssl::UniquePtr intermediate = MakeTestCert("Root", "Intermediate", key.get(), /*is_ca=*/true); ASSERT_TRUE(intermediate); ASSERT_TRUE(X509_sign(intermediate.get(), key.get(), EVP_sha256())); bssl::UniquePtr leaf = MakeTestCert("Intermediate", "Leaf", key.get(), /*is_ca=*/false); ASSERT_TRUE(leaf); ASSERT_TRUE(X509_sign(leaf.get(), key.get(), EVP_sha256())); EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {intermediate.get()}, {}, 0, configure_callback)); // A leaf and intermediate with compatible constraints is valid. intermediate = MakeTestCert("Root", "Intermediate", key.get(), /*is_ca=*/true); ASSERT_TRUE(intermediate); ASSERT_TRUE(AddKeyUsage(intermediate.get(), {KeyUsage::kKeyCertSign})); if (t.eku_nid != NID_undef) { ASSERT_TRUE(AddExtendedKeyUsage(intermediate.get(), {t.eku_nid})); } ASSERT_TRUE(X509_sign(intermediate.get(), key.get(), EVP_sha256())); leaf = MakeTestCert("Intermediate", "Leaf", key.get(), /*is_ca=*/false); ASSERT_TRUE(leaf); if (t.eku_nid != NID_undef) { ASSERT_TRUE(AddExtendedKeyUsage(leaf.get(), {t.eku_nid})); } ASSERT_TRUE(AddKeyUsage(leaf.get(), t.key_usages)); ASSERT_TRUE(X509_sign(leaf.get(), key.get(), EVP_sha256())); EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {intermediate.get()}, {}, 0, configure_callback)); // Each key usage asserted individually is valid. for (KeyUsage usage : t.key_usages) { SCOPED_TRACE(static_cast(usage)); leaf = MakeTestCert("Intermediate", "Leaf", key.get(), /*is_ca=*/false); ASSERT_TRUE(leaf); if (t.eku_nid != NID_undef) { ASSERT_TRUE(AddExtendedKeyUsage(leaf.get(), {t.eku_nid})); } ASSERT_TRUE(AddKeyUsage(leaf.get(), {usage})); ASSERT_TRUE(X509_sign(leaf.get(), key.get(), EVP_sha256())); EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {intermediate.get()}, {}, 0, configure_callback)); } // A leaf with the wrong EKU is invalid. if (t.eku_nid != NID_undef) { leaf = MakeTestCert("Intermediate", "Leaf", key.get(), /*is_ca=*/false); ASSERT_TRUE(leaf); ASSERT_TRUE(AddExtendedKeyUsage(leaf.get(), {NID_rsaEncryption})); ASSERT_TRUE(AddKeyUsage(leaf.get(), t.key_usages)); ASSERT_TRUE(X509_sign(leaf.get(), key.get(), EVP_sha256())); EXPECT_EQ(X509_V_ERR_INVALID_PURPOSE, Verify(leaf.get(), {root.get()}, {intermediate.get()}, {}, 0, configure_callback)); } // A leaf without any of the requested key usages is invalid. std::vector usages; for (int i = 0; i < 10; i++) { auto k = static_cast(i); if (std::find(t.key_usages.begin(), t.key_usages.end(), k) == t.key_usages.end()) { usages.push_back(k); } } leaf = MakeTestCert("Intermediate", "Leaf", key.get(), /*is_ca=*/false); ASSERT_TRUE(leaf); if (t.eku_nid != NID_undef) { ASSERT_TRUE(AddExtendedKeyUsage(leaf.get(), {t.eku_nid})); } ASSERT_TRUE(AddKeyUsage(leaf.get(), usages)); ASSERT_TRUE(X509_sign(leaf.get(), key.get(), EVP_sha256())); EXPECT_EQ(X509_V_ERR_INVALID_PURPOSE, Verify(leaf.get(), {root.get()}, {intermediate.get()}, {}, 0, configure_callback)); // Extra EKUs and key usages are fine. usages.clear(); for (int i = 0; i < 10; i++) { usages.push_back(static_cast(i)); } leaf = MakeTestCert("Intermediate", "Leaf", key.get(), /*is_ca=*/false); ASSERT_TRUE(leaf); if (t.eku_nid != NID_undef) { ASSERT_TRUE( AddExtendedKeyUsage(leaf.get(), {t.eku_nid, NID_rsaEncryption})); } ASSERT_TRUE(AddKeyUsage(leaf.get(), usages)); ASSERT_TRUE(X509_sign(leaf.get(), key.get(), EVP_sha256())); EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {intermediate.get()}, {}, 0, configure_callback)); // anyExtendedKeyUsage is not allowed in place of a concrete EKU. if (t.eku_nid != NID_undef) { leaf = MakeTestCert("Intermediate", "Leaf", key.get(), /*is_ca=*/false); ASSERT_TRUE(leaf); ASSERT_TRUE(AddExtendedKeyUsage(leaf.get(), {NID_anyExtendedKeyUsage})); ASSERT_TRUE(AddKeyUsage(leaf.get(), t.key_usages)); ASSERT_TRUE(X509_sign(leaf.get(), key.get(), EVP_sha256())); EXPECT_EQ(X509_V_ERR_INVALID_PURPOSE, Verify(leaf.get(), {root.get()}, {intermediate.get()}, {}, 0, configure_callback)); } // Restore |leaf| to a valid option. leaf = MakeTestCert("Intermediate", "Leaf", key.get(), /*is_ca=*/false); ASSERT_TRUE(leaf); ASSERT_TRUE(X509_sign(leaf.get(), key.get(), EVP_sha256())); // The intermediate must have the keyCertSign bit. This bit is checked in // multiple places. The first place that fails is in looking for candidate // issuers. intermediate = MakeTestCert("Root", "Intermediate", key.get(), /*is_ca=*/true); ASSERT_TRUE(intermediate); ASSERT_TRUE(AddKeyUsage(intermediate.get(), {KeyUsage::kDigitalSignature})); if (t.eku_nid != NID_undef) { ASSERT_TRUE(AddExtendedKeyUsage(intermediate.get(), {t.eku_nid})); } ASSERT_TRUE(X509_sign(intermediate.get(), key.get(), EVP_sha256())); EXPECT_EQ(X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, Verify(leaf.get(), {root.get()}, {intermediate.get()}, {}, 0, configure_callback)); // The intermediate must have the EKU asserted. if (t.eku_nid != NID_undef) { intermediate = MakeTestCert("Root", "Intermediate", key.get(), /*is_ca=*/true); ASSERT_TRUE(intermediate); ASSERT_TRUE(AddKeyUsage(intermediate.get(), {KeyUsage::kKeyCertSign})); ASSERT_TRUE(AddExtendedKeyUsage(intermediate.get(), {NID_rsaEncryption})); ASSERT_TRUE(X509_sign(intermediate.get(), key.get(), EVP_sha256())); EXPECT_EQ(X509_V_ERR_INVALID_PURPOSE, Verify(leaf.get(), {root.get()}, {intermediate.get()}, {}, 0, configure_callback)); } } } TEST(X509Test, Trust) { struct Certs { bssl::UniquePtr normal; bssl::UniquePtr trusted_server, distrusted_server; bssl::UniquePtr 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 key = PrivateKeyFromPEM(kP256Key); ASSERT_TRUE(key); bssl::UniquePtr root = MakeTestCert("Root", "Root", key.get(), /*is_ca=*/true); ASSERT_TRUE(root); ASSERT_TRUE(X509_sign(root.get(), key.get(), EVP_sha256())); // Issue a certificate with a critical Netscape certificate type extension. We // do not recognize this extension, so this certificate should be rejected. bssl::UniquePtr leaf = MakeTestCert("Root", "Leaf", key.get(), /*is_ca=*/false); ASSERT_TRUE(leaf); bssl::UniquePtr 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 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 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 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 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 free_der(der); return std::vector(der, der + der_len); } bssl::UniquePtr 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(pem, pem + pem_len); } std::vector 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 free_der(der); return std::vector(der, der + der_len); } bssl::UniquePtr 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(pem, pem + pem_len); } int type_; bssl::TemporaryDirectory dir_; std::map next_cert_; std::map 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 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 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 &serials) -> bssl::UniquePtr { bssl::UniquePtr 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 &serials) -> bool { bssl::UniquePtr 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 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 cert = MakeTestCert(issuer.c_str(), "Leaf", key.get(), /*is_ca=*/false); bssl::UniquePtr 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 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 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 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 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 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 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 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 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 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 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 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 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 { bssl::UniquePtr 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 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 leaf1 = issue_cert(ca1); ASSERT_TRUE(leaf1); bssl::UniquePtr 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 old_leaf1 = issue_cert(old_ca1); ASSERT_TRUE(old_leaf1); bssl::UniquePtr old_leaf2 = issue_cert(old_ca2); ASSERT_TRUE(old_leaf2); // Make an |X509_STORE| that gets CAs from |dir|. bssl::UniquePtr 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 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 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 key1 = PrivateKeyFromPEM(kP256Key); ASSERT_TRUE(key1); uint8_t key_id1[] = {'K', 'e', 'y', '1'}; bssl::UniquePtr 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 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 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 key2 = PrivateKeyFromPEM(kRSAKey); ASSERT_TRUE(key2); uint8_t key_id2[] = {'K', 'e', 'y', '2'}; bssl::UniquePtr 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 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 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 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 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 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 out; } kIPTests[] = { // Valid IPv4 addresses. {"127.0.0.1", {127, 0, 0, 1}}, {"1.2.3.4", {1, 2, 3, 4}}, {"1.2.3.255", {1, 2, 3, 255}}, {"255.255.255.255", {255, 255, 255, 255}}, // Valid IPv6 addresses {"::", {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, {"::1", {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}}, {"::01", {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}}, {"::001", {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}}, {"::0001", {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}}, {"ffff::", {0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, {"1::2", {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2}}, {"1:1:1:1:1:1:1:1", {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}, {"2001:db8::ff00:42:8329", {0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x42, 0x83, 0x29}}, {"1234::1.2.3.4", {0x12, 0x34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4}}, {"::1.2.3.4", {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4}}, {"ffff:ffff:ffff:ffff:ffff:ffff:1.2.3.4", {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 1, 2, 3, 4}}, // Too few IPv4 components. {"1", {}}, {"1.", {}}, {"1.2", {}}, {"1.2.", {}}, {"1.2.3", {}}, {"1.2.3.", {}}, // Invalid embedded IPv4 address. {"::1.2.3", {}}, // Too many components. {"1.2.3.4.5", {}}, {"1:2:3:4:5:6:7:8:9", {}}, {"1:2:3:4:5::6:7:8:9", {}}, // IPv4 literals take the place of two IPv6 components. {"1:2:3:4:5:6:7:1.2.3.4", {}}, // '::' should have fewer than 16 components or it is redundant. {"1:2:3:4:5:6:7::8", {}}, // Embedded IPv4 addresses must be at the end. {"::1.2.3.4:1", {}}, // Stray whitespace or other invalid characters. {"1.2.3.4 ", {}}, {"1.2.3 .4", {}}, {"1.2.3. 4", {}}, {" 1.2.3.4", {}}, {"1.2.3.4.", {}}, {"1.2.3.+4", {}}, {"1.2.3.-4", {}}, {"1.2.3.4.example.test", {}}, {"::1 ", {}}, {" ::1", {}}, {":: 1", {}}, {": :1", {}}, {"1.2.3.nope", {}}, {"::nope", {}}, // Components too large. {"1.2.3.256", {}}, // Overflows when adding {"1.2.3.260", {}}, // Overflows when multiplying by 10 {"1.2.3.999999999999999999999999999999999999999999", {}}, {"::fffff", {}}, // Although not an overflow, more than four hex digits is an error. {"::00000", {}}, // Too many colons. {":::", {}}, {"1:::", {}}, {":::2", {}}, {"1:::2", {}}, // Only one group of zeros may be elided. {"1::2::3", {}}, // We only support decimal. {"1.2.3.01", {}}, {"1.2.3.0x1", {}}, // Random garbage. {"example.test", {}}, {"", {}}, }; for (const auto &t : kIPTests) { SCOPED_TRACE(t.inp); bssl::UniquePtr oct(a2i_IPADDRESS(t.inp)); if (t.out.empty()) { EXPECT_FALSE(oct); } else { ASSERT_TRUE(oct); EXPECT_EQ(Bytes(t.out), Bytes(ASN1_STRING_get0_data(oct.get()), ASN1_STRING_length(oct.get()))); } } } } // namespace