From 43866bbb1a40a2df9ddfde79b83f7c777b7b337e Mon Sep 17 00:00:00 2001 From: Sean Parkinson Date: Wed, 2 Sep 2026 15:15:23 +1000 Subject: [PATCH] Adding or make functional OpenSSL compat APIs X509_check_purpose SSL_CTX_set0_tmp_dh_pey SSL_get1_peer_certificate EVP_AES_*_wrap, EVP_AES_*_wrap_pad EVP_PKEY_get_raw_private_key, EVP_PKEY_get_raw_public_key OCSP_crl_reason_str X509_get_default_cert_dir, X509_get_default_cert_dir_env X509_get_default_cert_file_dir SSL_add_dir_cert_subjects_to_stack, SSL_add_file_cert_subjects_to_stack EVP_md_null SSL_CTX_get_ciphers SSL_CTX_set_client_hello_cb, SSL_client_hello_get0_ext i2a_ASN1_STRING SSL_CTX_load_verify_file, SSL_CTX_load_verify_dir --- .github/configs/os-check-linux.json | 18 + certs/any-eku-cert.pem | 91 +++ certs/check_cert_chains.sh | 6 +- certs/code-sign-cert.pem | 28 + certs/include.am | 6 + certs/ns-ca-only-cert.pem | 28 + certs/ns-cert-type-cert.pem | 94 +++ certs/renewcerts.sh | 145 ++++ certs/renewcerts/wolfssl.cnf | 47 ++ certs/statickeys/gen-static.sh | 6 + certs/statickeys/include.am | 7 + certs/statickeys/x448-pub.der | Bin 0 -> 68 bytes certs/statickeys/x448-pub.pem | 4 + certs/statickeys/x448.der | Bin 0 -> 72 bytes certs/statickeys/x448.pem | 4 + certs/v1-root-cert.pem | 22 + certs/v1-self-issued-cert.pem | 18 + doc/dox_comments/header_files/evp.h | 200 ++++++ doc/dox_comments/header_files/pem.h | 89 +++ doc/dox_comments/header_files/ssl.h | 282 ++++++++ src/internal.c | 101 +++ src/ocsp.c | 42 +- src/pk.c | 20 + src/ssl.c | 200 ++++++ src/ssl_api_cert.c | 156 +++++ src/ssl_api_ext.c | 5 + src/ssl_asn1.c | 65 ++ src/ssl_load.c | 97 +++ src/tls13.c | 21 + src/x509.c | 500 +++++++++++++- tests/api.c | 48 +- tests/api/test_aes.c | 12 +- tests/api/test_dtls.c | 1 - tests/api/test_evp_cipher.c | 985 +++++++++++++++++++++++++++- tests/api/test_evp_cipher.h | 14 +- tests/api/test_evp_digest.c | 52 ++ tests/api/test_evp_digest.h | 2 + tests/api/test_evp_pkey.c | 470 +++++++++++++ tests/api/test_evp_pkey.h | 2 + tests/api/test_ocsp.c | 40 ++ tests/api/test_ocsp.h | 1 + tests/api/test_ossl_asn1.c | 65 ++ tests/api/test_ossl_asn1.h | 2 + tests/api/test_ossl_obj.c | 2 + tests/api/test_ossl_rsa.c | 12 +- tests/api/test_ossl_x509.c | 599 ++++++++++++++++- tests/api/test_ossl_x509.h | 10 + tests/api/test_ossl_x509_io.c | 6 + tests/api/test_ssl_cert.c | 125 ++++ tests/api/test_ssl_cert.h | 4 + tests/api/test_ssl_ext.c | 104 +++ tests/api/test_ssl_ext.h | 4 +- tests/api/test_tls.c | 334 +++++++++- tests/api/test_tls.h | 6 +- tests/api/test_tls13.c | 3 +- tests/api/test_tls_bounds.c | 6 +- tests/api/test_tls_msgtype.c | 3 +- tests/api/test_tls_parse.c | 6 +- tests/api/test_wc_encrypt.c | 6 +- tests/suites.c | 12 +- wolfcrypt/src/asn.c | 2 + wolfcrypt/src/evp.c | 725 +++++++++++++++++++- wolfcrypt/src/evp_pk.c | 517 +++++++++++++++ wolfssl/error-ssl.h | 1 + wolfssl/internal.h | 19 + wolfssl/openssl/evp.h | 90 ++- wolfssl/openssl/ssl.h | 27 +- wolfssl/openssl/x509v3.h | 29 +- wolfssl/ssl.h | 35 + 69 files changed, 6608 insertions(+), 75 deletions(-) create mode 100644 certs/any-eku-cert.pem create mode 100644 certs/code-sign-cert.pem create mode 100644 certs/ns-ca-only-cert.pem create mode 100644 certs/ns-cert-type-cert.pem create mode 100644 certs/statickeys/x448-pub.der create mode 100644 certs/statickeys/x448-pub.pem create mode 100644 certs/statickeys/x448.der create mode 100644 certs/statickeys/x448.pem create mode 100644 certs/v1-root-cert.pem create mode 100644 certs/v1-self-issued-cert.pem diff --git a/.github/configs/os-check-linux.json b/.github/configs/os-check-linux.json index d3ec20c7331..846bfce4261 100644 --- a/.github/configs/os-check-linux.json +++ b/.github/configs/os-check-linux.json @@ -6,6 +6,18 @@ "configure": ["--enable-usersettings"], "prepare": [["sed", "-i", "s/if 0/if 1/", "user_settings.h"]]}, {"name": "all-no-client-auth", "minutes": 9.0, "configure": ["--enable-all", "CPPFLAGS=-DWOLFSSL_NO_CLIENT_AUTH"]}, +{"name": "opensslall-no-aes-128", "minutes": 9.0, + "comment": "AES-128 compiled out. Tests that pick a 128 bit key or an AES-128 cipher suite have to say so; --enable-all cannot be used here because it turns on HAVE_CMAC_KDF, which settings.h rejects alongside NO_AES_128.", + "configure": ["--enable-opensslall", "--enable-crl", "--enable-ocsp", "--enable-certgen", "--enable-certreq", "--enable-certext", "--enable-keygen", "--enable-aeskeywrap=padding", "CPPFLAGS=-DNO_AES_128"]}, +{"name": "opensslall-single-ec-curve", "minutes": 9.0, + "comment": "Only P-256 built. Tests naming P-384 or P-521 have to say so; --enable-all cannot be used here because it defines HAVE_ECC384 and HAVE_ECC521 regardless of ECC_USER_CURVES, which would silently un-restrict the build.", + "configure": ["--enable-opensslall", "--enable-crl", "--enable-ocsp", "--enable-certgen", "--enable-certreq", "--enable-certext", "--enable-keygen", "--enable-aeskeywrap=padding", "CPPFLAGS=-DECC_USER_CURVES -DHAVE_ECC256"]}, +{"name": "all-ignore-netscape-cert-type", "minutes": 9.0, + "comment": "Builds with the netscape certificate type compiled out. The type is optional, so the code that reads it has to be guarded everywhere; this configuration is what catches a guard that was missed.", + "configure": ["--enable-all", "CPPFLAGS=-DIGNORE_NETSCAPE_CERT_TYPE"]}, +{"name": "all-aeskeywrap-padding", "minutes": 9.0, + "comment": "--enable-all brings the compatibility layer and RFC 3394 key wrap, but not RFC 5649 padding; this builds and runs the EVP_aes_*_wrap_pad tests.", + "configure": ["--enable-all", "--enable-aeskeywrap=padding"]}, {"name": "all-public-mp", "minutes": 8.0, "comment": "--enable-all plus WOLFSSL_PUBLIC_MP. The shared-secret-at-infinity regression assertion needs direct mp access to plant an out-of-range scalar, because the import path itself now rejects one; without this the assertion is compiled out of every mainstream build.", "configure": ["--enable-all", "CPPFLAGS=-DWOLFSSL_PUBLIC_MP"]}, @@ -298,6 +310,12 @@ "configure": ["CPPFLAGS=-DNO_WOLFSSL_SERVER -DWOLFSSL_NO_CLIENT_AUTH"]}, {"name": "no-wolfssl-client", "minutes": 1.0, "configure": ["CPPFLAGS=-DNO_WOLFSSL_CLIENT"]}, +{"name": "static-no-wolfssl-client", + "comment": "A static build with the client compiled out. --disable-shared is what defines WOLFSSL_TEST_STATIC_BUILD, and no other entry in this list is a static build, so the tests behind that macro are never compiled anywhere else. Those tests reach for client-only methods unless guarded, which is how the TLSX key share tests came to not compile here at all.", + "configure": ["--disable-shared", "--enable-static", "--disable-asm", "--disable-sp-asm", "--enable-tlsv12", "--enable-tls13", "CPPFLAGS=-DNO_WOLFSSL_CLIENT"]}, +{"name": "static-no-wolfssl-server", + "comment": "The mirror of static-no-wolfssl-client: the same static build with the server compiled out instead, so a WOLFSSL_TEST_STATIC_BUILD test that reaches for a server-only method is caught the same way.", + "configure": ["--disable-shared", "--enable-static", "--disable-asm", "--disable-sp-asm", "--enable-tlsv12", "--enable-tls13", "CPPFLAGS=-DNO_WOLFSSL_SERVER"]}, {"name": "testwolfcrypt-ca", "minutes": 1.0, "comment": "user_settings.h builds running only testwolfcrypt: pure crypto, no platform-specific features, so Linux-only coverage is sufficient. Not converted: user_settings_baremetal.h.", "user_settings": "examples/configs/user_settings_ca.h", diff --git a/certs/any-eku-cert.pem b/certs/any-eku-cert.pem new file mode 100644 index 00000000000..068c23e98ab --- /dev/null +++ b/certs/any-eku-cert.pem @@ -0,0 +1,91 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 8 (0x8) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, ST=Montana, L=Bozeman, O=Sawtooth, OU=Consulting, CN=www.wolfssl.com, emailAddress=facts@wolfssl.com + Validity + Not Before: Sep 2 00:07:02 2026 GMT + Not After : May 29 00:07:02 2029 GMT + Subject: C=US, ST=Montana, L=Bozeman, O=wolfSSL, OU=AnyEKU, CN=www.wolfssl.com, emailAddress=facts@wolfssl.com + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:c0:95:08:e1:57:41:f2:71:6d:b7:d2:45:41:27: + 01:65:c6:45:ae:f2:bc:24:30:b8:95:ce:2f:4e:d6: + f6:1c:88:bc:7c:9f:fb:a8:67:7f:fe:5c:9c:51:75: + f7:8a:ca:07:e7:35:2f:8f:e1:bd:7b:c0:2f:7c:ab: + 64:a8:17:fc:ca:5d:7b:ba:e0:21:e5:72:2e:6f:2e: + 86:d8:95:73:da:ac:1b:53:b9:5f:3f:d7:19:0d:25: + 4f:e1:63:63:51:8b:0b:64:3f:ad:43:b8:a5:1c:5c: + 34:b3:ae:00:a0:63:c5:f6:7f:0b:59:68:78:73:a6: + 8c:18:a9:02:6d:af:c3:19:01:2e:b8:10:e3:c6:cc: + 40:b4:69:a3:46:33:69:87:6e:c4:bb:17:a6:f3:e8: + dd:ad:73:bc:7b:2f:21:b5:fd:66:51:0c:bd:54:b3: + e1:6d:5f:1c:bc:23:73:d1:09:03:89:14:d2:10:b9: + 64:c3:2a:d0:a1:96:4a:bc:e1:d4:1a:5b:c7:a0:c0: + c1:63:78:0f:44:37:30:32:96:80:32:23:95:a1:77: + ba:13:d2:97:73:e2:5d:25:c9:6a:0d:c3:39:60:a4: + b4:b0:69:42:42:09:e9:d8:08:bc:33:20:b3:58:22: + a7:aa:eb:c4:e1:e6:61:83:c5:d2:96:df:d9:d0:4f: + ad:d7 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Key Identifier: + B3:11:32:C9:92:98:84:E2:C9:F8:D0:3B:6E:03:42:CA:1F:0E:8E:3C + X509v3 Authority Key Identifier: + keyid:27:8E:67:11:74:C3:26:1D:3F:ED:33:63:B3:A4:D8:1D:30:E5:E8:D5 + DirName:/C=US/ST=Montana/L=Bozeman/O=Sawtooth/OU=Consulting/CN=www.wolfssl.com/emailAddress=facts@wolfssl.com + serial:67:19:D2:A8:7F:E3:2D:FA:75:7A:4F:E7:B2:02:D9:AD:C4:77:5E:F8 + X509v3 Basic Constraints: + CA:FALSE + X509v3 Key Usage: + Digital Signature, Key Encipherment + X509v3 Extended Key Usage: + Any Extended Key Usage + Signature Algorithm: sha256WithRSAEncryption + Signature Value: + b6:67:56:e6:f3:73:c3:b4:bf:85:9a:c2:e8:2e:ed:b7:a8:19: + 09:5f:c5:71:b1:03:be:fc:b9:c8:66:77:6e:f7:52:67:d8:39: + 3a:7c:58:7e:51:59:63:55:a0:89:64:83:bd:e4:f4:74:35:e3: + 99:2c:7b:45:09:2e:88:a0:6c:0a:c5:e3:ba:91:47:22:f8:3c: + e1:55:6a:16:77:2c:37:05:82:b1:71:43:40:85:f3:f9:8d:f7: + 6e:d5:e1:59:f3:2c:cd:18:10:7e:aa:a6:f5:97:ca:ed:a0:cd: + 53:f6:96:52:b9:43:6f:b4:a2:d5:b2:bf:3b:86:68:9e:56:0f: + b4:87:29:4d:a1:14:61:94:72:64:37:ac:2d:f7:5c:ad:14:ba: + a2:ff:b1:e6:fd:ae:48:26:c9:a5:38:e7:2a:f2:2e:41:05:10: + f4:aa:1a:62:b5:6a:34:de:f7:68:74:78:bb:16:22:89:77:d0: + 82:f0:07:bc:ce:4e:4e:8f:9b:df:04:b6:82:ae:4b:0e:b9:c9: + 57:32:d8:31:8e:8f:8a:13:a1:eb:dd:fa:21:6c:24:5a:27:16: + 07:d7:1d:07:74:4b:9c:9f:96:ae:54:8d:eb:93:10:a8:ef:3f: + ea:2b:fe:89:6b:27:5b:d1:23:a7:ec:43:a5:eb:a4:40:e1:97: + 24:51:f0:83 +-----BEGIN CERTIFICATE----- +MIIEyDCCA7CgAwIBAgIBCDANBgkqhkiG9w0BAQsFADCBlTELMAkGA1UEBhMCVVMx +EDAOBgNVBAgMB01vbnRhbmExEDAOBgNVBAcMB0JvemVtYW4xETAPBgNVBAoMCFNh +d3Rvb3RoMRMwEQYDVQQLDApDb25zdWx0aW5nMRgwFgYDVQQDDA93d3cud29sZnNz +bC5jb20xIDAeBgkqhkiG9w0BCQEWEWZhY3RzQHdvbGZzc2wuY29tMB4XDTI2MDkw +MjAwMDcwMloXDTI5MDUyOTAwMDcwMlowgZAxCzAJBgNVBAYTAlVTMRAwDgYDVQQI +DAdNb250YW5hMRAwDgYDVQQHDAdCb3plbWFuMRAwDgYDVQQKDAd3b2xmU1NMMQ8w +DQYDVQQLDAZBbnlFS1UxGDAWBgNVBAMMD3d3dy53b2xmc3NsLmNvbTEgMB4GCSqG +SIb3DQEJARYRZmFjdHNAd29sZnNzbC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDAlQjhV0HycW230kVBJwFlxkWu8rwkMLiVzi9O1vYciLx8n/uo +Z3/+XJxRdfeKygfnNS+P4b17wC98q2SoF/zKXXu64CHlci5vLobYlXParBtTuV8/ +1xkNJU/hY2NRiwtkP61DuKUcXDSzrgCgY8X2fwtZaHhzpowYqQJtr8MZAS64EOPG +zEC0aaNGM2mHbsS7F6bz6N2tc7x7LyG1/WZRDL1Us+FtXxy8I3PRCQOJFNIQuWTD +KtChlkq84dQaW8egwMFjeA9ENzAyloAyI5Whd7oT0pdz4l0lyWoNwzlgpLSwaUJC +CenYCLwzILNYIqeq68Th5mGDxdKW39nQT63XAgMBAAGjggEkMIIBIDAdBgNVHQ4E +FgQUsxEyyZKYhOLJ+NA7bgNCyh8OjjwwgdUGA1UdIwSBzTCByoAUJ45nEXTDJh0/ +7TNjs6TYHTDl6NWhgZukgZgwgZUxCzAJBgNVBAYTAlVTMRAwDgYDVQQIDAdNb250 +YW5hMRAwDgYDVQQHDAdCb3plbWFuMREwDwYDVQQKDAhTYXd0b290aDETMBEGA1UE +CwwKQ29uc3VsdGluZzEYMBYGA1UEAwwPd3d3LndvbGZzc2wuY29tMSAwHgYJKoZI +hvcNAQkBFhFmYWN0c0B3b2xmc3NsLmNvbYIUZxnSqH/jLfp1ek/nsgLZrcR3Xvgw +CQYDVR0TBAIwADALBgNVHQ8EBAMCBaAwDwYDVR0lBAgwBgYEVR0lADANBgkqhkiG +9w0BAQsFAAOCAQEAtmdW5vNzw7S/hZrC6C7tt6gZCV/FcbEDvvy5yGZ3bvdSZ9g5 +OnxYflFZY1WgiWSDveT0dDXjmSx7RQkuiKBsCsXjupFHIvg84VVqFncsNwWCsXFD +QIXz+Y33btXhWfMszRgQfqqm9ZfK7aDNU/aWUrlDb7Si1bK/O4ZonlYPtIcpTaEU +YZRyZDesLfdcrRS6ov+x5v2uSCbJpTjnKvIuQQUQ9KoaYrVqNN73aHR4uxYiiXfQ +gvAHvM5OTo+b3wS2gq5LDrnJVzLYMY6PihOh6936IWwkWicWB9cdB3RLnJ+WrlSN +65MQqO8/6iv+iWsnW9Ejp+xDpeukQOGXJFHwgw== +-----END CERTIFICATE----- diff --git a/certs/check_cert_chains.sh b/certs/check_cert_chains.sh index e8b1cd936ed..49fb02a0009 100755 --- a/certs/check_cert_chains.sh +++ b/certs/check_cert_chains.sh @@ -21,7 +21,11 @@ pairs="rsapss/ecc-leaf-rsapss.pem rsapss/ca-rsapss.pem classic mldsa/ecc-leaf-mldsa44.pem mldsa/mldsa44-cert.pem mldsa server-cert-sha1.pem ca-cert.pem sha1 client-cert-sha1.pem ca-cert.pem sha1 -server-cert-sha1-root.pem ca-cert.pem classic" +server-cert-sha1-root.pem ca-cert.pem classic +any-eku-cert.pem ca-cert.pem classic +ns-cert-type-cert.pem ca-cert.pem classic +ns-ca-only-cert.pem ca-cert.pem classic +code-sign-cert.pem ca-cert.pem classic" failed=0 diff --git a/certs/code-sign-cert.pem b/certs/code-sign-cert.pem new file mode 100644 index 00000000000..01ed5d67eca --- /dev/null +++ b/certs/code-sign-cert.pem @@ -0,0 +1,28 @@ +-----BEGIN CERTIFICATE----- +MIIE0TCCA7mgAwIBAgIBCzANBgkqhkiG9w0BAQsFADCBlTELMAkGA1UEBhMCVVMx +EDAOBgNVBAgMB01vbnRhbmExEDAOBgNVBAcMB0JvemVtYW4xETAPBgNVBAoMCFNh +d3Rvb3RoMRMwEQYDVQQLDApDb25zdWx0aW5nMRgwFgYDVQQDDA93d3cud29sZnNz +bC5jb20xIDAeBgkqhkiG9w0BCQEWEWZhY3RzQHdvbGZzc2wuY29tMB4XDTI2MDkw +MjA4MDExNVoXDTI5MDUyOTA4MDExNVowgZIxCzAJBgNVBAYTAlVTMRAwDgYDVQQI +DAdNb250YW5hMRAwDgYDVQQHDAdCb3plbWFuMRAwDgYDVQQKDAd3b2xmU1NMMREw +DwYDVQQLDAhDb2RlU2lnbjEYMBYGA1UEAwwPd3d3LndvbGZzc2wuY29tMSAwHgYJ +KoZIhvcNAQkBFhFmYWN0c0B3b2xmc3NsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAMCVCOFXQfJxbbfSRUEnAWXGRa7yvCQwuJXOL07W9hyIvHyf ++6hnf/5cnFF194rKB+c1L4/hvXvAL3yrZKgX/Mpde7rgIeVyLm8uhtiVc9qsG1O5 +Xz/XGQ0lT+FjY1GLC2Q/rUO4pRxcNLOuAKBjxfZ/C1loeHOmjBipAm2vwxkBLrgQ +48bMQLRpo0YzaYduxLsXpvPo3a1zvHsvIbX9ZlEMvVSz4W1fHLwjc9EJA4kU0hC5 +ZMMq0KGWSrzh1Bpbx6DAwWN4D0Q3MDKWgDIjlaF3uhPSl3PiXSXJag3DOWCktLBp +QkIJ6dgIvDMgs1gip6rrxOHmYYPF0pbf2dBPrdcCAwEAAaOCASswggEnMB0GA1Ud +DgQWBBSzETLJkpiE4sn40DtuA0LKHw6OPDCB1QYDVR0jBIHNMIHKgBQnjmcRdMMm +HT/tM2OzpNgdMOXo1aGBm6SBmDCBlTELMAkGA1UEBhMCVVMxEDAOBgNVBAgMB01v +bnRhbmExEDAOBgNVBAcMB0JvemVtYW4xETAPBgNVBAoMCFNhd3Rvb3RoMRMwEQYD +VQQLDApDb25zdWx0aW5nMRgwFgYDVQQDDA93d3cud29sZnNzbC5jb20xIDAeBgkq +hkiG9w0BCQEWEWZhY3RzQHdvbGZzc2wuY29tghRnGdKof+Mt+nV6T+eyAtmtxHde ++DAJBgNVHRMEAjAAMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcD +AzANBgkqhkiG9w0BAQsFAAOCAQEAWNcuUFFKcIucIE1+YrPItb7fLvktk4tVSgpz +BK5tI2zp/etq4YMqv04hAGUZ26VXriiJwgAxRo/d/bMAnOJpsWD4r/QjcAiCqvAS +d2tk0eqfHbLOI/hQPowDddUKzM+MeEV0G+GgV4hTMBAEICYdCk9DjlSefHdtnaHD +Qh5I2Fa/ERK9L8cQGDZ0xfH5Uxn8TmAtH+4BX0NbDjkRkb/4HrpNeVgSbA+9OTOk +Oiq3EonyAVqWrIWeZBfM2hX8ynUJbrpK1/C7T+wCV9MGlHRudY9al6ujBqfhgock +VkAEsL8m8vUdtURYSWi0f+72OgqjNjtXZl1GIBoAhdfkJdvy6w== +-----END CERTIFICATE----- diff --git a/certs/include.am b/certs/include.am index 45c467b295c..a784183ed1b 100644 --- a/certs/include.am +++ b/certs/include.am @@ -92,6 +92,12 @@ EXTRA_DIST += \ certs/fpki-cert.der \ certs/fpki-certpol-cert.der \ certs/rid-cert.der \ + certs/any-eku-cert.pem \ + certs/ns-cert-type-cert.pem \ + certs/ns-ca-only-cert.pem \ + certs/code-sign-cert.pem \ + certs/v1-root-cert.pem \ + certs/v1-self-issued-cert.pem \ certs/dh-priv-2048.der \ certs/dh-priv-2048.pem \ certs/dh-pub-2048.der \ diff --git a/certs/ns-ca-only-cert.pem b/certs/ns-ca-only-cert.pem new file mode 100644 index 00000000000..02420a628d5 --- /dev/null +++ b/certs/ns-ca-only-cert.pem @@ -0,0 +1,28 @@ +-----BEGIN CERTIFICATE----- +MIIEtDCCA5ygAwIBAgIBCjANBgkqhkiG9w0BAQsFADCBlTELMAkGA1UEBhMCVVMx +EDAOBgNVBAgMB01vbnRhbmExEDAOBgNVBAcMB0JvemVtYW4xETAPBgNVBAoMCFNh +d3Rvb3RoMRMwEQYDVQQLDApDb25zdWx0aW5nMRgwFgYDVQQDDA93d3cud29sZnNz +bC5jb20xIDAeBgkqhkiG9w0BCQEWEWZhY3RzQHdvbGZzc2wuY29tMB4XDTI2MDkw +MjA3NDE1OFoXDTI5MDUyOTA3NDE1OFowgZIxCzAJBgNVBAYTAlVTMRAwDgYDVQQI +DAdNb250YW5hMRAwDgYDVQQHDAdCb3plbWFuMRAwDgYDVQQKDAd3b2xmU1NMMREw +DwYDVQQLDAhOc0NhT25seTEYMBYGA1UEAwwPd3d3LndvbGZzc2wuY29tMSAwHgYJ +KoZIhvcNAQkBFhFmYWN0c0B3b2xmc3NsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAMCVCOFXQfJxbbfSRUEnAWXGRa7yvCQwuJXOL07W9hyIvHyf ++6hnf/5cnFF194rKB+c1L4/hvXvAL3yrZKgX/Mpde7rgIeVyLm8uhtiVc9qsG1O5 +Xz/XGQ0lT+FjY1GLC2Q/rUO4pRxcNLOuAKBjxfZ/C1loeHOmjBipAm2vwxkBLrgQ +48bMQLRpo0YzaYduxLsXpvPo3a1zvHsvIbX9ZlEMvVSz4W1fHLwjc9EJA4kU0hC5 +ZMMq0KGWSrzh1Bpbx6DAwWN4D0Q3MDKWgDIjlaF3uhPSl3PiXSXJag3DOWCktLBp +QkIJ6dgIvDMgs1gip6rrxOHmYYPF0pbf2dBPrdcCAwEAAaOCAQ4wggEKMB0GA1Ud +DgQWBBSzETLJkpiE4sn40DtuA0LKHw6OPDCB1QYDVR0jBIHNMIHKgBQnjmcRdMMm +HT/tM2OzpNgdMOXo1aGBm6SBmDCBlTELMAkGA1UEBhMCVVMxEDAOBgNVBAgMB01v +bnRhbmExEDAOBgNVBAcMB0JvemVtYW4xETAPBgNVBAoMCFNhd3Rvb3RoMRMwEQYD +VQQLDApDb25zdWx0aW5nMRgwFgYDVQQDDA93d3cud29sZnNzbC5jb20xIDAeBgkq +hkiG9w0BCQEWEWZhY3RzQHdvbGZzc2wuY29tghRnGdKof+Mt+nV6T+eyAtmtxHde ++DARBglghkgBhvhCAQEEBAMCAgQwDQYJKoZIhvcNAQELBQADggEBAF1cEysIplz3 +RNxaFTGxOQrvX9WidcBbWzBECrpIjOhT0+x/kZNUdC4cT4cjiQdu0GSwb6dNv5nS +8nvBypvfw7q5v7VMRBuz7379A0gF4OXaOjAxn0rgi8GADDqzyGS/KTb01ZAZO66T +WJ3OnWT3JoypLbX/3URLY5d9mLX6y/zoLsScm1k1FiYDxjeEUf5ucbdqw2FkNqQA +t0rpeVqrAHIMZ08DXJy6+fAg7h2wvKDEVvIfDGTSdGnLyKEkk46B8agYoLnFwqkw +a/CzizH5vJQGJrwV9TiC7igmGkEJoIoCMYSwJLjie8Jiua/HRWnwPnosGlBGbWnq +FziSMN5OVJE= +-----END CERTIFICATE----- diff --git a/certs/ns-cert-type-cert.pem b/certs/ns-cert-type-cert.pem new file mode 100644 index 00000000000..a239aa44443 --- /dev/null +++ b/certs/ns-cert-type-cert.pem @@ -0,0 +1,94 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 9 (0x9) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, ST=Montana, L=Bozeman, O=Sawtooth, OU=Consulting, CN=www.wolfssl.com, emailAddress=facts@wolfssl.com + Validity + Not Before: Sep 2 00:51:23 2026 GMT + Not After : May 29 00:51:23 2029 GMT + Subject: C=US, ST=Montana, L=Bozeman, O=wolfSSL, OU=NsCertType, CN=www.wolfssl.com, emailAddress=facts@wolfssl.com + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:c0:95:08:e1:57:41:f2:71:6d:b7:d2:45:41:27: + 01:65:c6:45:ae:f2:bc:24:30:b8:95:ce:2f:4e:d6: + f6:1c:88:bc:7c:9f:fb:a8:67:7f:fe:5c:9c:51:75: + f7:8a:ca:07:e7:35:2f:8f:e1:bd:7b:c0:2f:7c:ab: + 64:a8:17:fc:ca:5d:7b:ba:e0:21:e5:72:2e:6f:2e: + 86:d8:95:73:da:ac:1b:53:b9:5f:3f:d7:19:0d:25: + 4f:e1:63:63:51:8b:0b:64:3f:ad:43:b8:a5:1c:5c: + 34:b3:ae:00:a0:63:c5:f6:7f:0b:59:68:78:73:a6: + 8c:18:a9:02:6d:af:c3:19:01:2e:b8:10:e3:c6:cc: + 40:b4:69:a3:46:33:69:87:6e:c4:bb:17:a6:f3:e8: + dd:ad:73:bc:7b:2f:21:b5:fd:66:51:0c:bd:54:b3: + e1:6d:5f:1c:bc:23:73:d1:09:03:89:14:d2:10:b9: + 64:c3:2a:d0:a1:96:4a:bc:e1:d4:1a:5b:c7:a0:c0: + c1:63:78:0f:44:37:30:32:96:80:32:23:95:a1:77: + ba:13:d2:97:73:e2:5d:25:c9:6a:0d:c3:39:60:a4: + b4:b0:69:42:42:09:e9:d8:08:bc:33:20:b3:58:22: + a7:aa:eb:c4:e1:e6:61:83:c5:d2:96:df:d9:d0:4f: + ad:d7 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Key Identifier: + B3:11:32:C9:92:98:84:E2:C9:F8:D0:3B:6E:03:42:CA:1F:0E:8E:3C + X509v3 Authority Key Identifier: + keyid:27:8E:67:11:74:C3:26:1D:3F:ED:33:63:B3:A4:D8:1D:30:E5:E8:D5 + DirName:/C=US/ST=Montana/L=Bozeman/O=Sawtooth/OU=Consulting/CN=www.wolfssl.com/emailAddress=facts@wolfssl.com + serial:67:19:D2:A8:7F:E3:2D:FA:75:7A:4F:E7:B2:02:D9:AD:C4:77:5E:F8 + X509v3 Basic Constraints: + CA:FALSE + X509v3 Key Usage: + Digital Signature, Key Encipherment + X509v3 Extended Key Usage: + TLS Web Server Authentication, TLS Web Client Authentication + Netscape Cert Type: + SSL Client + Signature Algorithm: sha256WithRSAEncryption + Signature Value: + 45:f2:69:de:54:df:5c:69:6c:a1:2b:e2:78:07:a2:1a:11:3f: + 04:e8:c8:0c:4e:fe:0d:e5:72:a7:92:b1:6b:b2:48:f4:59:19: + c3:b6:3b:b6:52:c8:b9:76:83:85:0b:f5:b5:fd:20:ff:77:e6: + f9:b1:77:d7:eb:58:6f:cd:16:f4:11:b5:9c:61:16:7e:1f:92: + 68:98:9c:16:35:cd:98:ae:40:f4:84:96:45:40:8b:3c:31:4d: + 3d:38:1f:57:3b:13:15:40:a7:e6:ad:ca:18:4b:91:18:e5:9d: + 92:95:3d:1d:c4:a0:2d:74:37:ae:9e:72:d5:e5:cd:0f:24:c3: + 80:11:ed:39:61:6b:d4:14:9a:a6:c0:ee:21:32:2a:99:c5:e8: + e8:c6:95:f4:4d:12:59:12:67:aa:59:e2:c4:66:7b:e2:e2:ab: + 4c:b9:d9:63:d7:f8:32:70:d2:15:76:88:cd:c3:9a:00:45:5d: + 32:f1:6d:c1:fb:05:13:c2:d1:70:55:60:b5:14:27:d8:eb:c6: + 20:fb:81:28:08:4d:8b:bd:95:17:52:98:e6:62:79:8e:30:1f: + d9:ef:93:c9:a4:6c:5a:f4:34:4b:2b:cc:29:91:90:96:2e:75: + 1a:44:49:c9:98:a1:6d:3f:ae:7e:e3:57:06:dc:d0:23:78:cd: + f3:1c:65:54 +-----BEGIN CERTIFICATE----- +MIIE7TCCA9WgAwIBAgIBCTANBgkqhkiG9w0BAQsFADCBlTELMAkGA1UEBhMCVVMx +EDAOBgNVBAgMB01vbnRhbmExEDAOBgNVBAcMB0JvemVtYW4xETAPBgNVBAoMCFNh +d3Rvb3RoMRMwEQYDVQQLDApDb25zdWx0aW5nMRgwFgYDVQQDDA93d3cud29sZnNz +bC5jb20xIDAeBgkqhkiG9w0BCQEWEWZhY3RzQHdvbGZzc2wuY29tMB4XDTI2MDkw +MjAwNTEyM1oXDTI5MDUyOTAwNTEyM1owgZQxCzAJBgNVBAYTAlVTMRAwDgYDVQQI +DAdNb250YW5hMRAwDgYDVQQHDAdCb3plbWFuMRAwDgYDVQQKDAd3b2xmU1NMMRMw +EQYDVQQLDApOc0NlcnRUeXBlMRgwFgYDVQQDDA93d3cud29sZnNzbC5jb20xIDAe +BgkqhkiG9w0BCQEWEWZhY3RzQHdvbGZzc2wuY29tMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAwJUI4VdB8nFtt9JFQScBZcZFrvK8JDC4lc4vTtb2HIi8 +fJ/7qGd//lycUXX3isoH5zUvj+G9e8AvfKtkqBf8yl17uuAh5XIuby6G2JVz2qwb +U7lfP9cZDSVP4WNjUYsLZD+tQ7ilHFw0s64AoGPF9n8LWWh4c6aMGKkCba/DGQEu +uBDjxsxAtGmjRjNph27Euxem8+jdrXO8ey8htf1mUQy9VLPhbV8cvCNz0QkDiRTS +ELlkwyrQoZZKvOHUGlvHoMDBY3gPRDcwMpaAMiOVoXe6E9KXc+JdJclqDcM5YKS0 +sGlCQgnp2Ai8MyCzWCKnquvE4eZhg8XSlt/Z0E+t1wIDAQABo4IBRTCCAUEwHQYD +VR0OBBYEFLMRMsmSmITiyfjQO24DQsofDo48MIHVBgNVHSMEgc0wgcqAFCeOZxF0 +wyYdP+0zY7Ok2B0w5ejVoYGbpIGYMIGVMQswCQYDVQQGEwJVUzEQMA4GA1UECAwH +TW9udGFuYTEQMA4GA1UEBwwHQm96ZW1hbjERMA8GA1UECgwIU2F3dG9vdGgxEzAR +BgNVBAsMCkNvbnN1bHRpbmcxGDAWBgNVBAMMD3d3dy53b2xmc3NsLmNvbTEgMB4G +CSqGSIb3DQEJARYRZmFjdHNAd29sZnNzbC5jb22CFGcZ0qh/4y36dXpP57IC2a3E +d174MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMB +BggrBgEFBQcDAjARBglghkgBhvhCAQEEBAMCB4AwDQYJKoZIhvcNAQELBQADggEB +AEXyad5U31xpbKEr4ngHohoRPwToyAxO/g3lcqeSsWuySPRZGcO2O7ZSyLl2g4UL +9bX9IP935vmxd9frWG/NFvQRtZxhFn4fkmiYnBY1zZiuQPSElkVAizwxTT04H1c7 +ExVAp+atyhhLkRjlnZKVPR3EoC10N66ectXlzQ8kw4AR7Tlha9QUmqbA7iEyKpnF +6OjGlfRNElkSZ6pZ4sRme+Liq0y52WPX+DJw0hV2iM3DmgBFXTLxbcH7BRPC0XBV +YLUUJ9jrxiD7gSgITYu9lRdSmOZieY4wH9nvk8mkbFr0NEsrzCmRkJYudRpEScmY +oW0/rn7jVwbc0CN4zfMcZVQ= +-----END CERTIFICATE----- diff --git a/certs/renewcerts.sh b/certs/renewcerts.sh index d0743711175..0f24513efdc 100755 --- a/certs/renewcerts.sh +++ b/certs/renewcerts.sh @@ -38,6 +38,12 @@ # fpki-cert.der # fpki-certpol-cert.der # rid-cert.der +# any-eku-cert.pem +# ns-cert-type-cert.pem +# ns-ca-only-cert.pem +# code-sign-cert.pem +# v1-root-cert.pem +# v1-self-issued-cert.pem # aia/ca-issuers-cert.pem # aia/multi-aia-cert.pem # aia/overflow-aia-cert.pem @@ -616,6 +622,145 @@ run_renewcerts(){ echo "End of section" echo "---------------------------------------------------------------------" ########################################################### + ########## update and sign any-eku-cert.pem ############### + ########################################################### + echo "Updating any-eku-cert.pem" + echo "" + #pipe the following arguments to openssl req... + echo -e "US\\nMontana\\nBozeman\\nwolfSSL\\nAnyEKU\\nwww.wolfssl.com\\nfacts@wolfssl.com\\n.\\n.\\n" | openssl req -new -key server-key.pem -config ./wolfssl.cnf -nodes > any-eku-req.pem + check_result $? "Step 1" + + openssl x509 -req -in any-eku-req.pem -extfile wolfssl.cnf -extensions any_eku -days 1000 -CA ca-cert.pem -CAkey ca-key.pem -set_serial 8 > any-eku-cert.pem + check_result $? "Step 2" + + rm any-eku-req.pem + + openssl x509 -in any-eku-cert.pem -text > any_eku_tmp.pem + check_result $? "Step 3" + mv any_eku_tmp.pem any-eku-cert.pem + echo "End of section" + echo "---------------------------------------------------------------------" + ########################################################### + ########## update and sign ns-cert-type-cert.pem ########## + ########################################################### + echo "Updating ns-cert-type-cert.pem" + echo "" + #pipe the following arguments to openssl req... + echo -e "US\\nMontana\\nBozeman\\nwolfSSL\\nNsCertType\\nwww.wolfssl.com\\nfacts@wolfssl.com\\n.\\n.\\n" | openssl req -new -key server-key.pem -config ./wolfssl.cnf -nodes > ns-cert-type-req.pem + check_result $? "Step 1" + + openssl x509 -req -in ns-cert-type-req.pem -extfile wolfssl.cnf -extensions ns_cert_type -days 1000 -CA ca-cert.pem -CAkey ca-key.pem -set_serial 9 > ns-cert-type-cert.pem + check_result $? "Step 2" + + rm ns-cert-type-req.pem + + openssl x509 -in ns-cert-type-cert.pem -text > ns_cert_type_tmp.pem + check_result $? "Step 3" + mv ns_cert_type_tmp.pem ns-cert-type-cert.pem + echo "End of section" + + echo "---------------------------------------------------------------------" + ########################################################### + ############ update and sign ns-ca-only-cert.pem ########## + ########################################################### + echo "Updating ns-ca-only-cert.pem" + echo "" + #pipe the following arguments to openssl req... + echo -e "US\\nMontana\\nBozeman\\nwolfSSL\\nNsCaOnly\\nwww.wolfssl.com\\nfacts@wolfssl.com\\n.\\n.\\n" | openssl req -new -key server-key.pem -config ./wolfssl.cnf -nodes > ns-ca-only-req.pem + check_result $? "Step 1" + + openssl x509 -req -in ns-ca-only-req.pem -extfile wolfssl.cnf -extensions ns_ca_only -days 1000 -CA ca-cert.pem -CAkey ca-key.pem -set_serial 10 > ns-ca-only-cert.pem + check_result $? "Step 2" + + rm ns-ca-only-req.pem + + echo "---------------------------------------------------------------------" + ########################################################### + ########### update and sign code-sign-cert.pem ############ + ########################################################### + echo "Updating code-sign-cert.pem" + echo "" + #pipe the following arguments to openssl req... + echo -e "US\\nMontana\\nBozeman\\nwolfSSL\\nCodeSign\\nwww.wolfssl.com\\nfacts@wolfssl.com\\n.\\n.\\n" | openssl req -new -key server-key.pem -config ./wolfssl.cnf -nodes > code-sign-req.pem + check_result $? "Step 1" + + openssl x509 -req -in code-sign-req.pem -extfile wolfssl.cnf -extensions code_sign -days 1000 -CA ca-cert.pem -CAkey ca-key.pem -set_serial 11 > code-sign-cert.pem + check_result $? "Step 2" + + rm code-sign-req.pem + + echo "End of section" + echo "---------------------------------------------------------------------" + ########################################################### + ####### update and sign the version 1 certificates ######## + ########################################################### + # A version 1 certificate carries no extensions at all. "openssl req + # -x509" adds a subject key identifier of its own and moves the + # certificate to version 3 unless it is given -x509v1, and that option + # only exists from OpenSSL 3.2, so a binary that takes it is looked for + # first. Without one the pair is skipped cleanly rather than writing + # version 3 certificates under a version 1 name. + OPENSSL_V1="" + for candidate in \ + "/usr/local/opt/openssl@3.5/bin/openssl" \ + "/usr/local/opt/openssl@3/bin/openssl" \ + "/opt/homebrew/opt/openssl@3.5/bin/openssl" \ + "/opt/homebrew/opt/openssl@3/bin/openssl" \ + "openssl"; do + if [ "$candidate" = "openssl" ]; then + command -v openssl >/dev/null 2>&1 || continue + else + [ -x "$candidate" ] || continue + fi + if "$candidate" req -help 2>&1 | grep -q -- "-x509v1"; then + OPENSSL_V1="$candidate" + break + fi + done + + if [ -n "$OPENSSL_V1" ]; then + echo "Updating v1-root-cert.pem using: $OPENSSL_V1" + echo "" + # Self-signed, so its signature algorithm matches its own public key + # algorithm and OpenSSL's check_ca() takes it for a version 1 root. + # The empty v1_no_exts section stops the x509_extensions named in + # [ req ] from being applied. + "$OPENSSL_V1" req -x509 -x509v1 -config ./wolfssl.cnf \ + -extensions v1_no_exts -key server-key.pem -days 1000 \ + -set_serial 12 \ + -subj "/C=US/ST=Montana/L=Bozeman/O=wolfSSL/OU=V1Root/CN=www.wolfssl.com/emailAddress=info@wolfssl.com" -out v1-root-cert.pem + check_result $? "Step 1" + echo "End of section" + echo "---------------------------------------------------------------------" + echo "Updating v1-self-issued-cert.pem using: $OPENSSL_V1" + echo "" + # A version 1 certificate that is self-issued but not self-signed: it + # carries an RSA public key under an ECDSA signature. OpenSSL only + # takes a version 1 certificate for a root when the signature + # algorithm matches the public key algorithm, so this one is not a CA + # to it. The issuer is a throwaway certificate that shares the + # subject name and is signed with an EC key; it is removed once it has + # served its purpose. + "$OPENSSL_V1" req -x509 -x509v1 -config ./wolfssl.cnf \ + -extensions v1_no_exts -key ecc-key.pem -days 1000 \ + -set_serial 99 \ + -subj "/C=US/ST=Montana/L=Bozeman/O=wolfSSL/OU=V1SelfIssued/CN=www.wolfssl.com/emailAddress=info@wolfssl.com" -out v1-self-issued-ca.pem + check_result $? "Step 1" + + "$OPENSSL_V1" req -x509 -x509v1 -config ./wolfssl.cnf \ + -extensions v1_no_exts -CA v1-self-issued-ca.pem \ + -CAkey ecc-key.pem -key server-key.pem -days 1000 \ + -set_serial 13 \ + -subj "/C=US/ST=Montana/L=Bozeman/O=wolfSSL/OU=V1SelfIssued/CN=www.wolfssl.com/emailAddress=info@wolfssl.com" -out v1-self-issued-cert.pem + check_result $? "Step 2" + + rm v1-self-issued-ca.pem + echo "End of section" + else + echo "Skipping version 1 cert generation (no OpenSSL with req -x509v1 found)" + fi + echo "---------------------------------------------------------------------" + ########################################################### ########## update and sign server-cert.pem ################ ########################################################### echo "Updating server-cert.pem" diff --git a/certs/renewcerts/wolfssl.cnf b/certs/renewcerts/wolfssl.cnf index 8ba00360716..5e9ccb0d1da 100644 --- a/certs/renewcerts/wolfssl.cnf +++ b/certs/renewcerts/wolfssl.cnf @@ -129,6 +129,53 @@ basicConstraints=CA:true subjectAltName=DNS:example.com, IP:127.0.0.1 extendedKeyUsage=serverAuth, clientAuth +# Leaf whose extendedKeyUsage is anyExtendedKeyUsage (2.5.29.37.0). Every +# purpose check has to accept it, which is what X509_check_purpose() and the +# handshake's own key usage check are exercised against. +[ any_eku ] +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid:always,issuer:always +basicConstraints=CA:FALSE +keyUsage=digitalSignature, keyEncipherment +extendedKeyUsage=anyExtendedKeyUsage + +[ code_sign ] +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid:always,issuer:always +basicConstraints=CA:FALSE +# The CA/Browser Forum baseline that OpenSSL's check_purpose_code_sign() +# applies: keyUsage has to be critical and allow digital signatures while +# allowing neither certificate nor CRL signing, and extendedKeyUsage has to +# allow code signing and neither any use nor server authentication. +keyUsage=critical, digitalSignature +extendedKeyUsage=codeSigning + +[ ns_ca_only ] +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid:always,issuer:always +# Deliberately no basicConstraints and no keyUsage: the netscape certificate +# type is then the only thing that can say this is a CA, which is the case +# OpenSSL's check_ca() reports differently from all the others. sslCA is +# named but emailCA is not, so the TLS roles accept it and S/MIME does not. +nsCertType=sslCA + +# Leaf carrying a netscape-cert-type extension. Its extendedKeyUsage allows +# both TLS roles, so the netscape type is what decides between them: the +# client role is accepted and the server role rejected. +[ ns_cert_type ] +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid:always,issuer:always +basicConstraints=CA:FALSE +keyUsage=digitalSignature, keyEncipherment +extendedKeyUsage=serverAuth, clientAuth +nsCertType=client + +# Deliberately empty. A version 1 certificate carries no extensions at all, +# and "openssl req -x509 -x509v1" still applies the x509_extensions named in +# [ req ] unless it is pointed at some other section, so the version 1 +# certificates name this one in order to get none. +[ v1_no_exts ] + #wolfssl extensions for intermediate CAs [wolfssl_opts_ICA] subjectKeyIdentifier=hash diff --git a/certs/statickeys/gen-static.sh b/certs/statickeys/gen-static.sh index a3fe1d5f9e5..8de0dcc9f64 100644 --- a/certs/statickeys/gen-static.sh +++ b/certs/statickeys/gen-static.sh @@ -19,3 +19,9 @@ openssl genpkey -algorithm x25519 -outform pem -out certs/statickeys/x25519.pem openssl pkey -inform pem -in certs/statickeys/x25519.pem -outform der -out certs/statickeys/x25519.der openssl pkey -inform pem -in certs/statickeys/x25519.pem -outform der -out certs/statickeys/x25519-pub.der -pubout openssl pkey -inform pem -in certs/statickeys/x25519.pem -outform pem -out certs/statickeys/x25519-pub.pem -pubout + +# X448 (Curve448) +openssl genpkey -algorithm x448 -outform pem -out certs/statickeys/x448.pem +openssl pkey -inform pem -in certs/statickeys/x448.pem -outform der -out certs/statickeys/x448.der +openssl pkey -inform pem -in certs/statickeys/x448.pem -outform der -out certs/statickeys/x448-pub.der -pubout +openssl pkey -inform pem -in certs/statickeys/x448.pem -outform pem -out certs/statickeys/x448-pub.pem -pubout diff --git a/certs/statickeys/include.am b/certs/statickeys/include.am index 84641be1094..379cba80c07 100644 --- a/certs/statickeys/include.am +++ b/certs/statickeys/include.am @@ -24,3 +24,10 @@ EXTRA_DIST += \ certs/statickeys/x25519.pem \ certs/statickeys/x25519-pub.der \ certs/statickeys/x25519-pub.pem + +# Curve448 Keys +EXTRA_DIST += \ + certs/statickeys/x448.der \ + certs/statickeys/x448.pem \ + certs/statickeys/x448-pub.der \ + certs/statickeys/x448-pub.pem diff --git a/certs/statickeys/x448-pub.der b/certs/statickeys/x448-pub.der new file mode 100644 index 0000000000000000000000000000000000000000..7969128e6fab3f29d75e80ac18ba3c65c382bd79 GIT binary patch literal 68 zcmV-K0K5M%LNEme11n{3133U*)?RmYE2I9HgRuzy+i6Znf-peAf~Kpv&Et2SS>`7N a2fl(3MS*9nRfF`)t>FUKbs97vSiU{5tsS)h literal 0 HcmV?d00001 diff --git a/certs/statickeys/x448-pub.pem b/certs/statickeys/x448-pub.pem new file mode 100644 index 00000000000..41aea788214 --- /dev/null +++ b/certs/statickeys/x448-pub.pem @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MEIwBQYDK2VvAzkAXtZed3Uro/6Yg7EI/ttpTkiCMEDBgqaruc3jd51Z5icFB76C +EEWBZ61Vg/TLreEC13UaNCBYvj0= +-----END PUBLIC KEY----- diff --git a/certs/statickeys/x448.der b/certs/statickeys/x448.der new file mode 100644 index 0000000000000000000000000000000000000000..8a45672482710252628d508a86337c6ba5a4e85f GIT binary patch literal 72 zcmV-O0Jr}zMgjoref) != 0) { + WOLFSSL_MSG("Failed to lock CTX mutex to free cipher stack"); + return; + } + + if (ctx->suitesStack != NULL) { + wolfSSL_sk_SSL_CIPHER_free(ctx->suitesStack); + ctx->suitesStack = NULL; + } + + if (wolfSSL_RefWithMutexUnlock(&ctx->ref) != 0) { + WOLFSSL_MSG("Failed to unlock CTX mutex after freeing cipher stack"); + } +} +#endif + void SSL_CtxResourceFree(WOLFSSL_CTX* ctx) { #if defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2) && \ @@ -3069,6 +3102,13 @@ void SSL_CtxResourceFree(WOLFSSL_CTX* ctx) #endif ctx->method = NULL; +#if defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL) || \ + defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY) + if (ctx->suitesStack != NULL) { + wolfSSL_sk_SSL_CIPHER_free(ctx->suitesStack); + ctx->suitesStack = NULL; + } +#endif XFREE(ctx->suites, ctx->heap, DYNAMIC_TYPE_SUITES); ctx->suites = NULL; @@ -30344,6 +30384,9 @@ const char* wolfSSL_ERR_reason_error_string(unsigned long e) case SSL_SHUTDOWN_ALREADY_DONE_E: return "Shutdown has already occurred"; + case CLIENT_HELLO_CB_E: + return "ClientHello callback failed handshake"; + case TLS13_SECRET_CB_E: return "TLS1.3 Secret Callback Error"; @@ -41166,6 +41209,39 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) return ret; } +#if defined(OPENSSL_EXTRA) && defined(HAVE_TLS_EXTENSIONS) && \ + !defined(NO_WOLFSSL_SERVER) + /* Hand the ClientHello to the application before its extensions are + * processed, so it can pick a context. exts may be NULL: extensions are + * optional in a TLS 1.2 ClientHello and the callback still runs for one + * that carries none, as it does in OpenSSL, where + * wolfSSL_client_hello_get0_ext() then simply finds nothing. Returns 0 to + * carry on, or CLIENT_HELLO_CB_E after sending the alert the callback + * asked for. */ + static int DoClientHelloCb(WOLFSSL* ssl, const byte* exts, word16 extsSz) + { + int ret = 0; + int chAlert = handshake_failure; + + ssl->chExts = exts; + ssl->chExtsSz = extsSz; + ret = ssl->ctx->chCb(ssl, &chAlert, ssl->ctx->chCbArg); + ssl->chExts = NULL; + ssl->chExtsSz = 0; + + if (ret != WOLFSSL_CLIENT_HELLO_SUCCESS) { + WOLFSSL_MSG("ClientHello callback failed handshake"); + SendAlert(ssl, alert_fatal, chAlert); + ret = CLIENT_HELLO_CB_E; + } + else { + ret = 0; + } + + return ret; + } +#endif + /* handle processing of client_hello (1) */ int DoClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, word32 helloSz) @@ -41666,6 +41742,31 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) ssl->options.peerNoUncompPF = 0; #endif +#if defined(OPENSSL_EXTRA) && defined(HAVE_TLS_EXTENSIONS) && \ + !defined(NO_WOLFSSL_SERVER) + if (ssl->ctx->chCb != NULL) { + const byte* chExts = NULL; + word16 chExtsSz = 0; + + /* The extension block is optional, so offer it only when one is + * there in full. A truncated block is left for the parsing below + * to reject, rather than shown to the application. */ + if (((i - begin) + OPAQUE16_LEN) <= helloSz) { + ato16(&input[i], &chExtsSz); + if (((i - begin) + OPAQUE16_LEN + chExtsSz) <= helloSz) { + chExts = input + i + OPAQUE16_LEN; + } + else { + chExtsSz = 0; + } + } + + if ((ret = DoClientHelloCb(ssl, chExts, chExtsSz)) != 0) { + goto out; + } + } +#endif + /* tls extensions */ if ((i - begin) < helloSz) { #ifdef HAVE_TLS_EXTENSIONS diff --git a/src/ocsp.c b/src/ocsp.c index 87ab3800973..9e96b70bad0 100644 --- a/src/ocsp.c +++ b/src/ocsp.c @@ -34,6 +34,9 @@ #include #include #include +#if defined(OPENSSL_EXTRA) +#include +#endif #ifdef NO_INLINE #include @@ -2061,14 +2064,43 @@ OcspResponse* wolfSSL_OCSP_response_create(int status, } #endif -#ifndef NO_WOLFSSL_STUB +/* Get the string representation of a CRL revocation reason code. + * + * Reason codes are those of RFC 5280 section 5.3.1 as used by the OCSP + * CRLReason field. Value 7 is not assigned. Strings match those returned by + * OpenSSL's OCSP_crl_reason_str(). + * + * @param [in] s Revocation reason code. + * @return String representation of the reason code. + * @return "(UNKNOWN)" when the code is not recognized. + */ const char* wolfSSL_OCSP_crl_reason_str(long s) { - WOLFSSL_STUB("wolfSSL_OCSP_crl_reason_str"); - (void)s; - return NULL; + switch (s) { + case CRL_REASON_UNSPECIFIED: + return "unspecified"; + case CRL_REASON_KEY_COMPROMISE: + return "keyCompromise"; + case CRL_REASON_CA_COMPROMISE: + return "cACompromise"; + case CRL_REASON_AFFILIATION_CHANGED: + return "affiliationChanged"; + case CRL_REASON_SUPERSEDED: + return "superseded"; + case CRL_REASON_CESSATION_OF_OPERATION: + return "cessationOfOperation"; + case CRL_REASON_CERTIFICATE_HOLD: + return "certificateHold"; + case CRL_REASON_REMOVE_FROM_CRL: + return "removeFromCRL"; + case CRL_REASON_PRIVILEGE_WITHDRAWN: + return "privilegeWithdrawn"; + case CRL_REASON_AA_COMPROMISE: + return "aACompromise"; + default: + return "(UNKNOWN)"; + } } -#endif /* Returns elements of an OCSP_CERTID struct. Currently only supports * returning the serial number, and returns an error if user requests diff --git a/src/pk.c b/src/pk.c index c86f4b4c107..3e827124886 100644 --- a/src/pk.c +++ b/src/pk.c @@ -6594,6 +6594,16 @@ WOLFSSL_EVP_PKEY* wolfSSL_PEM_read_bio_PrivateKey(WOLFSSL_BIO* bio, type = WC_EVP_PKEY_ED448; break; #endif + #ifdef HAVE_CURVE25519 + case X25519k: + type = WC_EVP_PKEY_X25519; + break; + #endif + #ifdef HAVE_CURVE448 + case X448k: + type = WC_EVP_PKEY_X448; + break; + #endif #ifdef WOLFSSL_HAVE_MLDSA case ML_DSA_44k: case ML_DSA_65k: @@ -6763,6 +6773,16 @@ WOLFSSL_EVP_PKEY* wolfSSL_PEM_read_PrivateKey(XFILE fp, WOLFSSL_EVP_PKEY **key, type = WC_EVP_PKEY_ED448; break; #endif + #ifdef HAVE_CURVE25519 + case X25519k: + type = WC_EVP_PKEY_X25519; + break; + #endif + #ifdef HAVE_CURVE448 + case X448k: + type = WC_EVP_PKEY_X448; + break; + #endif #ifdef WOLFSSL_HAVE_MLDSA case ML_DSA_44k: case ML_DSA_65k: diff --git a/src/ssl.c b/src/ssl.c index 329d5e60c99..213b556dde8 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -3231,6 +3231,10 @@ static int wolfSSL_parse_cipher_list(WOLFSSL_CTX* ctx, WOLFSSL* ssl, int wolfSSL_CTX_set_cipher_list(WOLFSSL_CTX* ctx, const char* list) { +#if defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL) || \ + defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY) + CtxFreeSuitesStack(ctx); +#endif WOLFSSL_ENTER("wolfSSL_CTX_set_cipher_list"); if (ctx == NULL) @@ -3251,6 +3255,10 @@ int wolfSSL_CTX_set_cipher_list(WOLFSSL_CTX* ctx, const char* list) int wolfSSL_CTX_set_cipher_list_bytes(WOLFSSL_CTX* ctx, const byte* list, const int listSz) { +#if defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL) || \ + defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY) + CtxFreeSuitesStack(ctx); +#endif WOLFSSL_ENTER("wolfSSL_CTX_set_cipher_list_bytes"); if (ctx == NULL) @@ -7025,6 +7033,13 @@ char* wolfSSL_CIPHER_description(const WOLFSSL_CIPHER* cipher, char* in, } #endif + /* Everything below describes the suite negotiated on a connection. A + * cipher taken from a context's list has none - wolfSSL_CTX_get_ciphers() + * reports configured suites, not a session - so there is nothing to + * describe rather than a null connection to walk into. */ + if (cipher->ssl == NULL) + return NULL; + /* Get the cipher description based on the SSL session cipher */ keaStr = wolfssl_kea_to_string(cipher->ssl->specs.kea); authStr = wolfssl_sigalg_to_string(cipher->ssl->specs.sig_algo); @@ -9397,6 +9412,176 @@ const char* wolfSSL_get_cipher_list_compat(const WOLFSSL* ssl, int priority) return NULL; } + +#if defined(OPENSSL_EXTRA) && defined(HAVE_TLS_EXTENSIONS) && \ + !defined(NO_WOLFSSL_SERVER) +/* Set the callback invoked when a ClientHello has been received. + * + * The callback runs before the extensions are processed, so it may inspect + * them with wolfSSL_client_hello_get0_ext() and switch the context in use. + * + * @param [in, out] ctx SSL/TLS context. + * @param [in] cb Callback, or NULL to remove one. + * @param [in] arg User context passed to the callback. + */ +void wolfSSL_CTX_set_client_hello_cb(WOLFSSL_CTX* ctx, CallbackClientHello cb, + void* arg) +{ + WOLFSSL_ENTER("wolfSSL_CTX_set_client_hello_cb"); + + if (ctx != NULL) { + ctx->chCb = cb; + ctx->chCbArg = arg; + } +} + +/* Find an extension in the ClientHello being processed. + * + * Only valid from within a ClientHello callback - the data points into the + * record being parsed and is not available at any other time. The returned + * data is the extension body, without its type and length. + * + * @param [in] ssl SSL object. + * @param [in] type Extension type to find. + * @param [out] out Extension data. + * @param [out] outLen Length of extension data. + * @return 1 when the extension is present. + * @return 0 when it is not, on bad parameters, or when called outside a + * ClientHello callback. + */ +int wolfSSL_client_hello_get0_ext(WOLFSSL* ssl, unsigned int type, + const unsigned char** out, size_t* outLen) +{ + word16 idx = 0; + + WOLFSSL_ENTER("wolfSSL_client_hello_get0_ext"); + + if ((ssl == NULL) || (out == NULL) || (outLen == NULL) || + (ssl->chExts == NULL)) { + return 0; + } + + /* Walk the extension block: 2 byte type, 2 byte length, then body. */ + while ((word32)idx + OPAQUE16_LEN + OPAQUE16_LEN <= ssl->chExtsSz) { + word16 extType; + word16 extLen; + + ato16(ssl->chExts + idx, &extType); + idx += OPAQUE16_LEN; + ato16(ssl->chExts + idx, &extLen); + idx += OPAQUE16_LEN; + + if ((word32)idx + extLen > ssl->chExtsSz) { + /* Truncated extension - stop rather than read past the end. */ + break; + } + if (extType == (word16)type) { + *out = ssl->chExts + idx; + *outLen = (size_t)extLen; + return 1; + } + idx += extLen; + } + + return 0; +} +#endif + +/* Get the cipher suites configured on a context. + * + * The suites of a context are only populated once a cipher list has been set + * on it, so a context left with the library defaults reports no list. The + * returned stack is owned by the context and must not be freed by the caller. + * + * @param [in] ctx SSL/TLS context. + * @return Stack of cipher suites on success. + * @return NULL when ctx is NULL or no cipher list has been set on it. + */ +WOLF_STACK_OF(WOLFSSL_CIPHER) *wolfSSL_CTX_get_ciphers(const WOLFSSL_CTX *ctx) +{ + WOLF_STACK_OF(WOLFSSL_CIPHER)* ret; + + WOLFSSL_ENTER("wolfSSL_CTX_get_ciphers"); + + if ((ctx == NULL) || (ctx->suites == NULL)) { + return NULL; + } + + /* A context is shared between connections, so the stack is built under + * the same lock that guards the suites it is built from. Two threads + * arriving here together would otherwise each build one, and every stack + * but the last would be leaked. */ + if (wolfSSL_RefWithMutexLock(&((WOLFSSL_CTX*)ctx)->ref) != 0) { + WOLFSSL_MSG("Failed to lock CTX mutex for cipher stack"); + return NULL; + } + + /* Populate the stack on first use, re-checked now the lock is held. */ + if (ctx->suitesStack == NULL) { + int i; + + ((WOLFSSL_CTX*)ctx)->suitesStack = + wolfssl_sk_new_type_ex(STACK_TYPE_CIPHER, ctx->heap); + + /* Highest priority suite ends up on top of the stack. A failed + * allocation stops the loop before it starts rather than returning, + * which would leave the lock held. */ + for (i = ctx->suites->suiteSz - 2; + (ctx->suitesStack != NULL) && (i >= 0); i -= 2) { + struct WOLFSSL_CIPHER cipher; + + /* A couple of suites are placeholders for special options, + * skip those. */ + if (SCSV_Check(ctx->suites->suites[i], + ctx->suites->suites[i+1])) { + continue; + } + + XMEMSET(&cipher, 0, sizeof(cipher)); + cipher.cipherSuite0 = ctx->suites->suites[i]; + cipher.cipherSuite = ctx->suites->suites[i+1]; + /* No connection is associated with a context level suite. */ + cipher.ssl = NULL; +#if defined(OPENSSL_ALL) + cipher.in_stack = 1; + { + const CipherSuiteInfo* names = GetCipherNames(); + int cipherSz = GetCipherNamesSize(); + int j; + + for (j = 0; j < cipherSz; j++) { + if ((names[j].cipherSuite0 == cipher.cipherSuite0) && + (names[j].cipherSuite == cipher.cipherSuite)) { + cipher.offset = (unsigned long)j; + break; + } + } + } +#endif + if (wolfSSL_sk_insert(ctx->suitesStack, &cipher, 0) <= 0) { + WOLFSSL_MSG("Error inserting cipher onto stack"); + wolfSSL_sk_CIPHER_free(ctx->suitesStack); + ((WOLFSSL_CTX*)ctx)->suitesStack = NULL; + break; + } + } + + /* If no ciphers were added, free empty stack and return NULL. */ + if ((ctx->suitesStack != NULL) && + (wolfSSL_sk_num(ctx->suitesStack) == 0)) { + wolfSSL_sk_CIPHER_free(ctx->suitesStack); + ((WOLFSSL_CTX*)ctx)->suitesStack = NULL; + } + } + + ret = ctx->suitesStack; + + if (wolfSSL_RefWithMutexUnlock(&((WOLFSSL_CTX*)ctx)->ref) != 0) { + WOLFSSL_MSG("Failed to unlock CTX mutex after cipher stack build"); + } + + return ret; +} #endif /* OPENSSL_EXTRA || OPENSSL_ALL || WOLFSSL_NGINX || WOLFSSL_HAPROXY */ #ifdef OPENSSL_ALL /* returned pointer is to an internal element in WOLFSSL struct and should not @@ -10412,6 +10597,21 @@ void wolfSSL_BUF_MEM_free(WOLFSSL_BUF_MEM* buf) switch (ctx->cipherType) { #ifndef NO_AES +#if defined(HAVE_AES_KEYWRAP) + case WC_AES_128_WRAP_TYPE : + case WC_AES_192_WRAP_TYPE : + case WC_AES_256_WRAP_TYPE : +#ifdef WOLFSSL_AES_KEYWRAP_PADDING + case WC_AES_128_WRAP_PAD_TYPE : + case WC_AES_192_WRAP_PAD_TYPE : + case WC_AES_256_WRAP_PAD_TYPE : +#endif + /* The value ctx->iv holds for a key wrap is the integrity + * check value, not a chaining register the cipher updates, + * so there is nothing to read back out of the key. */ + WOLFSSL_MSG("AES key wrap"); + break; +#endif #if defined(HAVE_AES_CBC) || defined(WOLFSSL_AES_DIRECT) case WC_AES_128_CBC_TYPE : case WC_AES_192_CBC_TYPE : diff --git a/src/ssl_api_cert.c b/src/ssl_api_cert.c index 497e4f45cb7..a3c0c468381 100644 --- a/src/ssl_api_cert.c +++ b/src/ssl_api_cert.c @@ -1611,6 +1611,162 @@ WOLF_STACK_OF(WOLFSSL_X509_NAME)* wolfSSL_load_client_CA_file(const char* fname) return NULL; #endif } + +#if defined(OPENSSL_EXTRA) && !defined(NO_FILESYSTEM) +/* Add the subject name of each certificate in a file to a list of names. + * + * Names already in the list are not added again. A file holding no + * certificates is not an error, matching OpenSSL. + * + * @param [in, out] list List of X509 names to append to. + * @param [in] fname Name of file holding PEM certificates. + * @return 1 on success. + * @return 0 when the file cannot be read or a name cannot be stored. + */ +static int wolfssl_add_file_subjects(WOLF_STACK_OF(WOLFSSL_X509_NAME)* list, + const char* fname) +{ + int ret = 1; + WOLFSSL_BIO* bio; + WOLFSSL_X509* cert = NULL; + unsigned long error; + + /* Create a file BIO to read. */ + bio = wolfSSL_BIO_new_file(fname, "rb"); + if (bio == NULL) { + WOLFSSL_MSG("wolfSSL_BIO_new_file error"); + return 0; + } + + /* Read each certificate in the chain out of the file. */ + while (wolfSSL_PEM_read_bio_X509(bio, &cert, NULL, NULL) != NULL) { + WOLFSSL_X509_NAME* name = wolfSSL_X509_get_subject_name(cert); + WOLFSSL_X509_NAME* nameCopy; + + if (name == NULL) { + WOLFSSL_MSG("wolfSSL_X509_get_subject_name error"); + ret = 0; + } + /* Skip names already in the list. */ + else if ((wolfSSL_sk_X509_NAME_num(list) > 0) && + (wolfSSL_sk_X509_NAME_find(list, name) >= 0)) { + WOLFSSL_MSG("Subject name already in list"); + } + /* Need a persistent copy of the subject name. */ + else if ((nameCopy = wolfSSL_X509_NAME_dup(name)) == NULL) { + WOLFSSL_MSG("wolfSSL_X509_NAME_dup error"); + ret = 0; + } + else { + /* Certificate read will be freed - clear reference to it. */ + nameCopy->x509 = NULL; + + if (wolfSSL_sk_X509_NAME_push(list, nameCopy) <= 0) { + WOLFSSL_MSG("wolfSSL_sk_X509_NAME_push error"); + /* Name not stored - free now as only place needing to. */ + wolfSSL_X509_NAME_free(nameCopy); + ret = 0; + } + } + + /* Dispose of certificate read. */ + wolfSSL_X509_free(cert); + cert = NULL; + + if (!ret) { + break; + } + } + + /* Clear any error due to no more certificates. */ + CLEAR_ASN_NO_PEM_HEADER_ERROR(error); + + wolfSSL_BIO_free(bio); + return ret; +} + +/* Add the subject name of each certificate in a file to a list of names. + * + * @param [in, out] list List of X509 names to append to. + * @param [in] fname Name of file holding PEM certificates. + * @return 1 on success. + * @return 0 when a parameter is NULL or the file cannot be read. + */ +int wolfSSL_add_file_cert_subjects_to_stack( + WOLF_STACK_OF(WOLFSSL_X509_NAME)* list, const char* fname) +{ + WOLFSSL_ENTER("wolfSSL_add_file_cert_subjects_to_stack"); + + if ((list == NULL) || (fname == NULL)) { + WOLFSSL_MSG("Bad parameter"); + return 0; + } + + return wolfssl_add_file_subjects(list, fname); +} + +#if !defined(NO_WOLFSSL_DIR) && !defined(WOLFSSL_NUCLEUS) && \ + !defined(WOLFSSL_NUCLEUS_1_2) +/* Add the subject name of each certificate in a directory to a list of names. + * + * Used to build the list of acceptable CAs a server sends in its + * CertificateRequest. Subdirectories are skipped by the directory reader. + * + * @param [in, out] list List of X509 names to append to. + * @param [in] dir Directory holding PEM certificates. + * @return 1 on success, including when the directory holds no certificates. + * @return 0 when a parameter is NULL, the directory cannot be read, or a + * certificate file in it cannot be read. + */ +int wolfSSL_add_dir_cert_subjects_to_stack( + WOLF_STACK_OF(WOLFSSL_X509_NAME)* list, const char* dir) +{ + int ret = 1; + int readRet; + char* fname = NULL; + ReadDirCtx* readCtx; + + WOLFSSL_ENTER("wolfSSL_add_dir_cert_subjects_to_stack"); + + if ((list == NULL) || (dir == NULL)) { + WOLFSSL_MSG("Bad parameter"); + return 0; + } + + /* ReadDirCtx holds a full path buffer - keep it off the stack. */ + readCtx = (ReadDirCtx*)XMALLOC(sizeof(ReadDirCtx), NULL, + DYNAMIC_TYPE_TMP_BUFFER); + if (readCtx == NULL) { + WOLFSSL_MSG("Memory error"); + return 0; + } + + /* Only regular files are returned, so subdirectories are skipped. */ + readRet = wc_ReadDirFirst(readCtx, dir, &fname); + if ((readRet != 0) && (readRet != WC_READDIR_NOFILE)) { + /* Directory could not be opened. */ + WOLFSSL_MSG("wc_ReadDirFirst error"); + ret = 0; + } + while ((ret == 1) && (readRet == 0) && (fname != NULL)) { + WOLFSSL_MSG(fname); + + if (!wolfssl_add_file_subjects(list, fname)) { + WOLFSSL_MSG("Failed to add subjects from file in path"); + ret = 0; + } + else { + readRet = wc_ReadDirNext(readCtx, dir, &fname); + } + } + wc_ReadDirClose(readCtx); + XFREE(readCtx, NULL, DYNAMIC_TYPE_TMP_BUFFER); + + return ret; +} +#endif /* !NO_WOLFSSL_DIR && !WOLFSSL_NUCLEUS && !WOLFSSL_NUCLEUS_1_2 */ +#endif /* OPENSSL_EXTRA && !NO_FILESYSTEM */ + #endif /* !NO_BIO */ #endif /* WOLFSSL_NO_CA_NAMES */ diff --git a/src/ssl_api_ext.c b/src/ssl_api_ext.c index 2be0da03757..23b1188455c 100644 --- a/src/ssl_api_ext.c +++ b/src/ssl_api_ext.c @@ -1765,6 +1765,11 @@ int wolfSSL_CTX_set1_sigalgs_list(WOLFSSL_CTX* ctx, const char* list) { int ret = WOLFSSL_SUCCESS; +#if defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL) || \ + defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY) + CtxFreeSuitesStack(ctx); +#endif + WOLFSSL_MSG("wolfSSL_CTX_set1_sigalg_list"); if ((ctx == NULL) || (list == NULL)) { diff --git a/src/ssl_asn1.c b/src/ssl_asn1.c index 0a434a73cb5..7e3efc0e900 100644 --- a/src/ssl_asn1.c +++ b/src/ssl_asn1.c @@ -1647,6 +1647,71 @@ int wolfSSL_a2i_ASN1_INTEGER(WOLFSSL_BIO *bio, WOLFSSL_ASN1_INTEGER *asn1, * @return 0 when bp or a is NULL. * @return 0 DER header in data is invalid. */ +/* Write an ASN.1 string to a BIO as hex digits. + * + * Long values are wrapped with a backslash and newline every 35 bytes, and an + * empty string is written as a single "0", both matching OpenSSL. The type + * argument is accepted for compatibility and, as in OpenSSL, is not used. + * + * @param [in] bp BIO to write to. + * @param [in] a ASN.1 string. + * @param [in] type Ignored. + * @return Number of characters written on success. + * @return 0 when a is NULL. + * @return -1 on error. + */ +int wolfSSL_i2a_ASN1_STRING(WOLFSSL_BIO *bp, const WOLFSSL_ASN1_STRING *a, + int type) +{ + int n = 0; + int i; + + WOLFSSL_ENTER("wolfSSL_i2a_ASN1_STRING"); + + /* OpenSSL ignores the type as well. */ + (void)type; + + if (a == NULL) { + return 0; + } + if ((bp == NULL) || (a->length < 0) || + ((a->length > 0) && (a->data == NULL))) { + return -1; + } + + /* An empty string is reported as a single zero. */ + if (a->length == 0) { + if (wolfSSL_BIO_write(bp, "0", 1) != 1) { + return -1; + } + return 1; + } + + for (i = 0; i < a->length; i++) { + byte hex[3]; + word32 hexLen = (word32)sizeof(hex); + + /* Wrap the line every 35 bytes, as OpenSSL does. */ + if ((i != 0) && ((i % 35) == 0)) { + if (wolfSSL_BIO_write(bp, "\\\n", 2) != 2) { + return -1; + } + n += 2; + } + + if (Base16_Encode((const byte*)a->data + i, 1, hex, &hexLen) != 0) { + return -1; + } + /* Base16_Encode NUL terminates - write just the two digits. */ + if (wolfSSL_BIO_write(bp, hex, 2) != 2) { + return -1; + } + n += 2; + } + + return n; +} + int wolfSSL_i2a_ASN1_INTEGER(WOLFSSL_BIO *bp, const WOLFSSL_ASN1_INTEGER *a) { int err = 0; diff --git a/src/ssl_load.c b/src/ssl_load.c index 4d406db0786..1fe57c4c289 100644 --- a/src/ssl_load.c +++ b/src/ssl_load.c @@ -3310,6 +3310,61 @@ int wolfSSL_CTX_load_verify_locations(WOLFSSL_CTX* ctx, const char* file, return WS_RETURN_CODE(ret, 0); } +#ifdef OPENSSL_EXTRA +/* Load CA certificates from a file. + * + * The file half of wolfSSL_CTX_load_verify_locations(), matching the split + * OpenSSL made in 1.1.1. + * + * @param [in, out] ctx SSL/TLS context. + * @param [in] file Name of file containing PEM CA certificates. + * @return 1 on success. + * @return 0 on failure. + */ +int wolfSSL_CTX_load_verify_file(WOLFSSL_CTX* ctx, const char* file) +{ + WOLFSSL_ENTER("wolfSSL_CTX_load_verify_file"); + + /* Use the compat variant so a bad certificate in the file is + * tolerated the way OpenSSL tolerates it. */ + return wolfSSL_CTX_load_verify_locations_compat(ctx, file, NULL); +} + +#ifndef NO_WOLFSSL_DIR +/* Load CA certificates from every file in a directory. + * + * The directory half of wolfSSL_CTX_load_verify_locations(), matching the + * split OpenSSL made in 1.1.1. + * + * @param [in, out] ctx SSL/TLS context. + * @param [in] path Directory containing PEM CA certificate files. + * @return 1 on success. + * @return 0 on failure. + */ +int wolfSSL_CTX_load_verify_dir(WOLFSSL_CTX* ctx, const char* path) +{ + WOLFSSL_ENTER("wolfSSL_CTX_load_verify_dir"); + + if ((ctx == NULL) || (path == NULL)) { + return WOLFSSL_FAILURE; + } + + /* Load whatever the directory holds, tolerating certificates that cannot + * be used, as the compat variant does. + * + * The result is deliberately not reported. OpenSSL registers the + * directory for lookup rather than reading it here, so it only fails this + * call on a NULL path - a missing, empty or unreadable directory all + * report success. Callers written against that, such as mosquitto's + * capath handling, treat a 0 as a fatal configuration error, so returning + * one where OpenSSL would not would stop them starting. */ + (void)wolfSSL_CTX_load_verify_locations_compat(ctx, NULL, path); + + return WOLFSSL_SUCCESS; +} +#endif /* !NO_WOLFSSL_DIR */ +#endif /* OPENSSL_EXTRA */ + /* Load a file and/or files in path, with OpenSSL-compatible semantics. * * No c_rehash. @@ -6245,6 +6300,48 @@ long wolfSSL_CTX_set_tmp_dh(WOLFSSL_CTX* ctx, WOLFSSL_DH* dh) return ret; } +/* Guarded to match wolfSSL_EVP_PKEY_get0_DH(), which this is a thin wrapper + * over; without it there is no way to reach the DH parameters in the key. */ +#if (defined(OPENSSL_ALL) || defined(WOLFSSL_QT) || \ + defined(WOLFSSL_OPENSSH)) && \ + !defined(NO_DH) && defined(WOLFSSL_DH_EXTRA) && !defined(NO_FILESYSTEM) +/* Set the Diffie-Hellman parameters to use, from an EVP_PKEY. + * + * The context takes ownership of dhpkey on success, matching the "set0" + * contract; on failure the caller keeps it, as OpenSSL does. + * + * Returns WOLFSSL_SUCCESS on success, WOLFSSL_FAILURE otherwise. + */ +int wolfSSL_CTX_set0_tmp_dh_pkey(WOLFSSL_CTX* ctx, WOLFSSL_EVP_PKEY* dhpkey) +{ + WOLFSSL_DH* dh; + + WOLFSSL_ENTER("wolfSSL_CTX_set0_tmp_dh_pkey"); + + if (ctx == NULL || dhpkey == NULL) { + return WOLFSSL_FAILURE; + } + + /* Borrowed from the key, so it must not be freed here. */ + dh = wolfSSL_EVP_PKEY_get0_DH(dhpkey); + if (dh == NULL) { + WOLFSSL_MSG("EVP_PKEY does not hold DH parameters"); + return WOLFSSL_FAILURE; + } + + if (wolfSSL_CTX_set_tmp_dh(ctx, dh) != WOLFSSL_SUCCESS) { + return WOLFSSL_FAILURE; + } + + /* wolfSSL_CTX_set_tmp_dh() copies the parameters, so the key has done its + * job and the ownership just taken is discharged by freeing it. */ + wolfSSL_EVP_PKEY_free(dhpkey); + + return WOLFSSL_SUCCESS; +} +#endif /* (OPENSSL_ALL || WOLFSSL_QT || WOLFSSL_OPENSSH) && !NO_DH && + * WOLFSSL_DH_EXTRA && !NO_FILESYSTEM */ + #endif /* OPENSSL_EXTRA */ #ifndef NO_CERTS diff --git a/src/tls13.c b/src/tls13.c index 7cbaf2f416b..056c9899485 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -7945,6 +7945,27 @@ int DoTls13ClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, } #endif +#if defined(OPENSSL_EXTRA) && defined(HAVE_TLS_EXTENSIONS) && \ + !defined(NO_WOLFSSL_SERVER) + /* Let the application inspect the raw extensions before they are + * processed, so it can pick a context. */ + if (ssl->ctx->chCb != NULL) { + int chAlert = handshake_failure; + int chRet; + + ssl->chExts = input + args->idx; + ssl->chExtsSz = totalExtSz; + chRet = ssl->ctx->chCb(ssl, &chAlert, ssl->ctx->chCbArg); + ssl->chExts = NULL; + ssl->chExtsSz = 0; + + if (chRet != WOLFSSL_CLIENT_HELLO_SUCCESS) { + WOLFSSL_MSG("ClientHello callback failed handshake"); + SendAlert(ssl, alert_fatal, (int)chAlert); + ERROR_OUT(CLIENT_HELLO_CB_E, exit_dch); + } + } +#endif /* Parse extensions */ if ((ret = TLSX_Parse(ssl, input + args->idx, totalExtSz, client_hello, ssl->clSuites))) { diff --git a/src/x509.c b/src/x509.c index 55967dc5c15..876c01ccce6 100644 --- a/src/x509.c +++ b/src/x509.c @@ -3838,31 +3838,69 @@ int wolfSSL_X509_pubkey_digest(const WOLFSSL_X509 *x509, #ifdef OPENSSL_EXTRA - #ifndef NO_WOLFSSL_STUB + /* Get the name of the environment variable that overrides the default CA + * certificate file. + * + * The name matches OpenSSL's X509_CERT_FILE_EVP, and is the variable + * wolfSSL_CTX_load_system_CA_certs() already reads. + * + * @return Environment variable name. + */ const char* wolfSSL_X509_get_default_cert_file_env(void) { - WOLFSSL_STUB("X509_get_default_cert_file_env"); - return ""; + return "SSL_CERT_FILE"; } - const char* wolfSSL_X509_get_default_cert_file(void) + /* Get the name of the environment variable that overrides the default CA + * certificate directory. + * + * The name matches OpenSSL's X509_CERT_DIR_EVP, and is the variable + * wolfSSL_CTX_load_system_CA_certs() already reads. + * + * @return Environment variable name. + */ + const char* wolfSSL_X509_get_default_cert_dir_env(void) { - WOLFSSL_STUB("X509_get_default_cert_file"); - return ""; + return "SSL_CERT_DIR"; } - const char* wolfSSL_X509_get_default_cert_dir_env(void) + #ifndef NO_WOLFSSL_STUB + /* Get the default CA certificate file. + * + * wolfSSL has no single compiled-in CA bundle path to report. + * + * @return Empty string. + */ + const char* wolfSSL_X509_get_default_cert_file(void) { - WOLFSSL_STUB("X509_get_default_cert_dir_env"); + WOLFSSL_STUB("X509_get_default_cert_file"); return ""; } + #endif + /* Get the default CA certificate directory. + * + * Reports the first of the system CA directories wolfSSL would search, + * which is the closest equivalent of OpenSSL's compiled-in X509_CERT_DIR. + * + * @return Default CA certificate directory. + * @return Empty string when the build has no system CA directory list. + */ const char* wolfSSL_X509_get_default_cert_dir(void) { - WOLFSSL_STUB("X509_get_default_cert_dir"); + #if defined(WOLFSSL_SYS_CA_CERTS) && !defined(NO_FILESYSTEM) && \ + !defined(_WIN32) && !defined(USE_WINDOWS_API) && !defined(__APPLE__) + const char** dirs; + word32 num = 0; + + dirs = wolfSSL_get_system_CA_dirs(&num); + if ((dirs != NULL) && (num > 0)) { + return dirs[0]; + } + #endif + WOLFSSL_MSG("No default certificate directory for this build"); return ""; } - #endif #endif /* OPENSSL_EXTRA */ @@ -12149,7 +12187,9 @@ static int CertFromX509(Cert* cert, WOLFSSL_X509* x509) cert->keyUsage = x509->keyUsage; cert->extKeyUsage = x509->extKeyUsage; +#ifndef IGNORE_NETSCAPE_CERT_TYPE cert->nsCertType = x509->nsCertType; +#endif if (x509->rawCRLInfo != NULL) { if (x509->rawCRLInfoSz > CTC_MAX_CRLINFO_SZ) { @@ -16368,6 +16408,446 @@ WOLFSSL_X509* wolfSSL_X509_dup(WOLFSSL_X509 *x) SESSION_CERTS */ #if defined(OPENSSL_EXTRA) +/* Check the netscape-cert-type extension, when the build parses it. + * + * Returns 1 when the extension is absent or names one of the wanted types, + * 0 when it is present and names none of them, which is what OpenSSL's + * ns_reject() decides. + */ +static int wolfssl_x509_check_ns_cert_type(WOLFSSL_X509* x, byte wanted) +{ +#ifndef IGNORE_NETSCAPE_CERT_TYPE + /* A zero field is how an absent extension reads, and an extension with no + * bits set constrains nothing either way. */ + if ((x->nsCertType != 0) && ((x->nsCertType & wanted) == 0)) { + WOLFSSL_MSG("Certificate netscape cert type does not allow purpose"); + return 0; + } +#else + (void)x; + (void)wanted; +#endif + return 1; +} + +/* Check whether a certificate can be used for a given role. + * + * x - the certificate to check. + * id - one of the WOLFSSL_X509_PURPOSE_* values, or -1 to ask only that the + * certificate's extensions be usable, which is how OpenSSL callers + * request the extension cache be populated. + * ca - non-zero to check it as a CA rather than as an end entity. + * + * Only the extensions the certificate carries constrain it: an absent + * extendedKeyUsage or keyUsage places no restriction, as in RFC 5280 + * sec. 4.2.1.12 and OpenSSL. + * + * Returns 1 when the certificate is usable for the role, 0 when it is not, + * and -1 on a bad argument or a role this build does not check. + */ +/* Get the public key algorithm that a signature algorithm has to have been + * produced with. + * + * Mirrors the mapping OpenSSL's check_sig_alg_match() gets from + * OBJ_find_sigid_algs(). + * + * @param [in] sigOID Signature algorithm of a certificate. + * @return 0 when the signature algorithm is not recognized. + * @return Key algorithm identifier otherwise. + */ +static int wolfssl_x509_sig_key_type(int sigOID) +{ + int keyOID; + + switch (sigOID) { + case CTC_MD2wRSA: + case CTC_MD5wRSA: + case CTC_SHAwRSA: + case CTC_SHA224wRSA: + case CTC_SHA256wRSA: + case CTC_SHA384wRSA: + case CTC_SHA512wRSA: + case CTC_SHA3_224wRSA: + case CTC_SHA3_256wRSA: + case CTC_SHA3_384wRSA: + case CTC_SHA3_512wRSA: + keyOID = RSAk; + break; + case CTC_RSASSAPSS: + keyOID = RSAPSSk; + break; + case CTC_SHAwDSA: + case CTC_SHA256wDSA: + keyOID = DSAk; + break; + case CTC_SHAwECDSA: + case CTC_SHA224wECDSA: + case CTC_SHA256wECDSA: + case CTC_SHA384wECDSA: + case CTC_SHA512wECDSA: + case CTC_SHA3_224wECDSA: + case CTC_SHA3_256wECDSA: + case CTC_SHA3_384wECDSA: + case CTC_SHA3_512wECDSA: + keyOID = ECDSAk; + break; + case CTC_SM3wSM2: + keyOID = SM2k; + break; + case CTC_ED25519: + keyOID = ED25519k; + break; + case CTC_ED448: + keyOID = ED448k; + break; + case CTC_FALCON_LEVEL1: + keyOID = FALCON_LEVEL1k; + break; + case CTC_FALCON_LEVEL5: + keyOID = FALCON_LEVEL5k; + break; + case CTC_DILITHIUM_LEVEL2: + keyOID = DILITHIUM_LEVEL2k; + break; + case CTC_DILITHIUM_LEVEL3: + keyOID = DILITHIUM_LEVEL3k; + break; + case CTC_DILITHIUM_LEVEL5: + keyOID = DILITHIUM_LEVEL5k; + break; + case CTC_ML_DSA_44: + keyOID = ML_DSA_44k; + break; + case CTC_ML_DSA_65: + keyOID = ML_DSA_65k; + break; + case CTC_ML_DSA_87: + keyOID = ML_DSA_87k; + break; + case CTC_SLH_DSA_SHA2_128S: + keyOID = SLH_DSA_SHA2_128Sk; + break; + case CTC_SLH_DSA_SHA2_128F: + keyOID = SLH_DSA_SHA2_128Fk; + break; + case CTC_SLH_DSA_SHA2_192S: + keyOID = SLH_DSA_SHA2_192Sk; + break; + case CTC_SLH_DSA_SHA2_192F: + keyOID = SLH_DSA_SHA2_192Fk; + break; + case CTC_SLH_DSA_SHA2_256S: + keyOID = SLH_DSA_SHA2_256Sk; + break; + case CTC_SLH_DSA_SHA2_256F: + keyOID = SLH_DSA_SHA2_256Fk; + break; + case CTC_SLH_DSA_SHAKE_128S: + keyOID = SLH_DSA_SHAKE_128Sk; + break; + case CTC_SLH_DSA_SHAKE_128F: + keyOID = SLH_DSA_SHAKE_128Fk; + break; + case CTC_SLH_DSA_SHAKE_192S: + keyOID = SLH_DSA_SHAKE_192Sk; + break; + case CTC_SLH_DSA_SHAKE_192F: + keyOID = SLH_DSA_SHAKE_192Fk; + break; + case CTC_SLH_DSA_SHAKE_256S: + keyOID = SLH_DSA_SHAKE_256Sk; + break; + case CTC_SLH_DSA_SHAKE_256F: + keyOID = SLH_DSA_SHAKE_256Fk; + break; + case CTC_HSS_LMS: + keyOID = HSS_LMSk; + break; + case CTC_XMSS: + keyOID = XMSSk; + break; + case CTC_XMSSMT: + keyOID = XMSSMTk; + break; + default: + keyOID = 0; + break; + } + + return keyOID; +} + +/* Decide whether a certificate looks self-signed, mirroring the EXFLAG_SS + * that OpenSSL's ossl_x509v3_cache_extensions() sets. + * + * Self-issued is not enough: OpenSSL also requires the signature algorithm to + * match the certificate's own public key algorithm, which rules out a + * certificate that merely shares its issuer's name. Like OpenSSL, the + * signature itself is not verified here. OpenSSL checks the authority key + * identifier as well, but a version 1 certificate - the only kind this is + * asked about - can carry no extensions at all, so there is never one to + * check. + * + * @param [in] x Certificate. + * @return 0 when the certificate does not look self-signed. + * @return 1 when it does. + */ +static int wolfssl_x509_self_signed(WOLFSSL_X509* x) +{ + int keyOID; + + if (wolfSSL_X509_NAME_cmp(&x->issuer, &x->subject) != 0) { + return 0; + } + + keyOID = wolfssl_x509_sig_key_type(x->sigOID); + if (keyOID == 0) { + /* An algorithm that cannot be mapped cannot be shown to match. */ + return 0; + } + /* An RSA key produces both PKCS #1 v1.5 and PSS signatures. */ + if ((keyOID == RSAPSSk) && (x->pubKeyOID == RSAk)) { + return 1; + } + + return keyOID == x->pubKeyOID; +} + +/* Decide whether a certificate may act as a CA, mirroring OpenSSL's + * check_ca(). + * + * A non-zero result records how the decision was reached, because a caller + * has to treat one of them differently: 1 basicConstraints said so, 3 it is a + * version 1 root, 4 there is no basicConstraints but keyUsage allows + * certificate signing, 5 only a netscape certificate type said so. + * + * @param [in] x Certificate. + * @return 0 when the certificate may not act as a CA. + * @return Non-zero when it may. + */ +static int wolfssl_x509_check_ca(WOLFSSL_X509* x) +{ + /* keyUsage, when present, has to allow certificate signing. */ + if (x->keyUsageSet && ((x->keyUsage & KEYUSE_KEY_CERT_SIGN) == 0)) { + return 0; + } + + /* basicConstraints, when present, is the answer. */ + if (x->basicConstSet) { + return x->isCa ? 1 : 0; + } + + /* Without basicConstraints, fall back the way OpenSSL does. */ + if ((x->version == 1) && wolfssl_x509_self_signed(x)) { + /* A version 1 self-signed certificate is taken as a root. */ + return 3; + } + if (x->keyUsageSet) { + /* keyUsage got this far, so it allows certificate signing. */ + return 4; + } +#ifndef IGNORE_NETSCAPE_CERT_TYPE + if ((x->nsCertType & (WC_NS_SSL_CA | WC_NS_SMIME_CA | WC_NS_OBJSIGN_CA)) + != 0) { + return 5; + } +#endif + return 0; +} + +/* Decide whether a certificate may act as a CA for the TLS roles. + * + * As OpenSSL's check_ssl_ca(): a certificate that only a netscape type + * vouched for has to have named the SSL CA type specifically. + * + * @param [in] x Certificate. + * @return 0 when the certificate may not act as a TLS CA, non-zero when it + * may. + */ +static int wolfssl_x509_check_ssl_ca(WOLFSSL_X509* x) +{ + int ret = wolfssl_x509_check_ca(x); + + if (ret == 0) { + return 0; + } +#ifndef IGNORE_NETSCAPE_CERT_TYPE + if ((ret == 5) && ((x->nsCertType & WC_NS_SSL_CA) == 0)) { + return 0; + } +#endif + return ret; +} + +int wolfSSL_X509_check_purpose(WOLFSSL_X509* x, int id, int ca) +{ + byte ekuWanted; + word16 kuWanted; + byte nsWanted; + /* Which of OpenSSL's CA rules this role applies when ca is set. */ + enum { CA_RULE_SSL, CA_RULE_SMIME, CA_RULE_PLAIN } caRule = CA_RULE_PLAIN; + + WOLFSSL_ENTER("wolfSSL_X509_check_purpose"); + + if (x == NULL) { + return WOLFSSL_FATAL_ERROR; + } + + /* -1 asks only that the extensions be readable. wolfSSL parses them when + * the certificate is decoded, so there is nothing to populate. */ + if (id == -1) { + return 1; + } + + switch (id) { + case WOLFSSL_X509_PURPOSE_ANY: + /* Constrains nothing, for a CA as much as for a leaf. */ + return 1; + case WOLFSSL_X509_PURPOSE_OCSP_HELPER: + /* A leaf is left to the OCSP code to check, but a CA still has to + * be a valid one. */ + if (!ca) { + return 1; + } + /* OpenSSL returns check_ca()'s own value here, not a boolean. */ + return wolfssl_x509_check_ca(x); + case WOLFSSL_X509_PURPOSE_SSL_CLIENT: + ekuWanted = EXTKEYUSE_CLIENT_AUTH; + caRule = CA_RULE_SSL; + kuWanted = KEYUSE_DIGITAL_SIG | KEYUSE_KEY_AGREE; + nsWanted = WC_NS_SSL_CLIENT; + break; + case WOLFSSL_X509_PURPOSE_SSL_SERVER: + ekuWanted = EXTKEYUSE_SERVER_AUTH; + caRule = CA_RULE_SSL; + kuWanted = KEYUSE_DIGITAL_SIG | KEYUSE_KEY_ENCIPHER | + KEYUSE_KEY_AGREE; + nsWanted = WC_NS_SSL_SERVER; + break; + case WOLFSSL_X509_PURPOSE_NS_SSL_SERVER: + ekuWanted = EXTKEYUSE_SERVER_AUTH; + caRule = CA_RULE_SSL; + kuWanted = KEYUSE_KEY_ENCIPHER; + nsWanted = WC_NS_SSL_SERVER; + break; + case WOLFSSL_X509_PURPOSE_SMIME_SIGN: + ekuWanted = EXTKEYUSE_EMAILPROT; + caRule = CA_RULE_SMIME; + kuWanted = KEYUSE_DIGITAL_SIG | KEYUSE_CONTENT_COMMIT; + /* SSL_CLIENT is accepted alongside SMIME to match OpenSSL's + * workaround for certificates that set only the former. */ + nsWanted = WC_NS_SMIME | WC_NS_SSL_CLIENT; + break; + case WOLFSSL_X509_PURPOSE_SMIME_ENCRYPT: + ekuWanted = EXTKEYUSE_EMAILPROT; + caRule = CA_RULE_SMIME; + kuWanted = KEYUSE_KEY_ENCIPHER; + nsWanted = WC_NS_SMIME | WC_NS_SSL_CLIENT; + break; + case WOLFSSL_X509_PURPOSE_CRL_SIGN: + /* No extendedKeyUsage or netscape type is defined for CRL + * signing. */ + ekuWanted = 0; + kuWanted = KEYUSE_CRL_SIGN; + nsWanted = 0; + break; + case WOLFSSL_X509_PURPOSE_CODE_SIGN: + /* A CA for code signing is just a CA. */ + if (ca) { + return wolfssl_x509_check_ca(x); + } + /* CA/Browser Forum baseline requirements, as OpenSSL checks them: + * keyUsage has to be there, critical, allow digital signatures and + * allow neither certificate nor CRL signing; extendedKeyUsage has + * to be there, allow code signing, and allow neither any use nor + * server authentication. */ + if ((!x->keyUsageSet) || (!x->keyUsageCrit) || + ((x->keyUsage & KEYUSE_DIGITAL_SIG) == 0) || + ((x->keyUsage & + (KEYUSE_KEY_CERT_SIGN | KEYUSE_CRL_SIGN)) != 0)) { + WOLFSSL_MSG("Key usage does not allow code signing"); + return 0; + } + if ((x->extKeyUsageSrc == NULL) || + ((x->extKeyUsage & EXTKEYUSE_CODESIGN) == 0) || + ((x->extKeyUsage & + (EXTKEYUSE_ANY | EXTKEYUSE_SERVER_AUTH)) != 0)) { + WOLFSSL_MSG("Extended key usage does not allow code signing"); + return 0; + } + return 1; + case WOLFSSL_X509_PURPOSE_TIMESTAMP_SIGN: + /* A CA for timestamping is just a CA; the leaf rule below cannot + * be applied, so it is refused there. */ + if (ca) { + return wolfssl_x509_check_ca(x); + } + /* Needs the extendedKeyUsage to be exactly timeStamping and + * critical, which is not recorded when a certificate is parsed. */ + WOLFSSL_MSG("Cannot check timestamp signing on a leaf"); + return 0; + default: + /* Including TIMESTAMP_SIGN, which needs the extendedKeyUsage to be + * exactly timeStamping. Unrecognised KeyPurposeIds are not + * recorded when a certificate is parsed, so "exactly" cannot be + * decided here. Report "not permitted" rather than an error: + * OpenSSL answers 0 for these, and callers commonly write + * "if (X509_check_purpose(...))", where a negative return would + * read as permission granted. */ + WOLFSSL_MSG("Unsupported X509 purpose"); + return 0; + } + + /* extendedKeyUsage: present and naming none of the accepted uses. This + * is checked before the CA rules, as OpenSSL does, so a CA whose + * extendedKeyUsage excludes the role is refused for it. + * anyExtendedKeyUsage deliberately does not satisfy a specific purpose: + * OpenSSL's xku_reject() treats it as its own bit and rejects such a + * certificate, and this is an authorization decision, so match it. */ + if ((ekuWanted != 0) && (x->extKeyUsageSrc != NULL) && + ((x->extKeyUsage & ekuWanted) == 0)) { + WOLFSSL_MSG("Certificate extended key usage does not allow purpose"); + return 0; + } + + if (ca) { + int caRet; + + if (caRule == CA_RULE_SSL) { + return wolfssl_x509_check_ssl_ca(x) != 0; + } + caRet = wolfssl_x509_check_ca(x); + if (caRet == 0) { + return 0; + } +#ifndef IGNORE_NETSCAPE_CERT_TYPE + /* A certificate only a netscape type vouched for has to have named + * the S/MIME CA type specifically. */ + if ((caRule == CA_RULE_SMIME) && (caRet == 5) && + ((x->nsCertType & WC_NS_SMIME_CA) == 0)) { + return 0; + } +#endif + /* Report how the decision was reached, as OpenSSL does: a caller may + * distinguish a certificate that only a netscape type vouched for + * from one basicConstraints named. */ + return caRet; + } + + /* keyUsage: present and naming none of the accepted uses. */ + if ((kuWanted != 0) && x->keyUsageSet && + ((x->keyUsage & kuWanted) == 0)) { + WOLFSSL_MSG("Certificate key usage does not allow purpose"); + return 0; + } + + if ((nsWanted != 0) && !wolfssl_x509_check_ns_cert_type(x, nsWanted)) { + return 0; + } + + return 1; +} + int wolfSSL_X509_check_ca(WOLFSSL_X509 *x509) { WOLFSSL_ENTER("wolfSSL_X509_check_ca"); diff --git a/tests/api.c b/tests/api.c index 367f4ca9f16..27d74095cef 100644 --- a/tests/api.c +++ b/tests/api.c @@ -2415,7 +2415,8 @@ static int test_wolfSSL_CTX_set_cipher_list_bytes(void) !defined(NO_WOLFSSL_CLIENT) && \ !defined(HAVE_RENEGOTIATION_INDICATION) && \ defined(HAVE_AESGCM) && \ - ((!defined(NO_RSA) && defined(HAVE_ECC)) || !defined(NO_ERROR_STRINGS)) + ((!defined(NO_RSA) && defined(HAVE_ECC)) || !defined(NO_ERROR_STRINGS)) && \ + defined(WOLFSSL_AES_128) /* Helper function to check if TLS 1.3 suites exist in the suites list */ static int suites_has_tls13(const byte* suites, word16 suiteSz) { @@ -2449,7 +2450,8 @@ static int test_wolfSSL_set_cipher_list_tls12_keeps_tls13(void) !defined(WOLFSSL_NO_TLS12) && \ !defined(NO_WOLFSSL_CLIENT) && \ !defined(HAVE_RENEGOTIATION_INDICATION) && \ - defined(HAVE_AESGCM) && defined(HAVE_ECC) && !defined(NO_RSA) + defined(HAVE_AESGCM) && defined(HAVE_ECC) && !defined(NO_RSA) && \ + defined(WOLFSSL_AES_128) WOLFSSL_CTX* ctx = NULL; WOLFSSL* ssl = NULL; @@ -2480,7 +2482,8 @@ static int test_wolfSSL_set_cipher_list_tls13_keeps_tls12(void) !defined(WOLFSSL_NO_TLS12) && \ !defined(NO_WOLFSSL_CLIENT) && \ !defined(HAVE_RENEGOTIATION_INDICATION) && \ - defined(HAVE_AESGCM) && !defined(NO_ERROR_STRINGS) + defined(HAVE_AESGCM) && !defined(NO_ERROR_STRINGS) && \ + defined(WOLFSSL_AES_128) WOLFSSL_CTX* ctx = NULL; WOLFSSL* ssl = NULL; @@ -2511,7 +2514,8 @@ static int test_wolfSSL_set_cipher_list_tls12_with_version(void) !defined(WOLFSSL_NO_TLS12) && \ !defined(NO_WOLFSSL_CLIENT) && \ !defined(HAVE_RENEGOTIATION_INDICATION) && \ - defined(HAVE_AESGCM) && defined(HAVE_ECC) && !defined(NO_RSA) + defined(HAVE_AESGCM) && defined(HAVE_ECC) && !defined(NO_RSA) && \ + defined(WOLFSSL_AES_128) WOLFSSL_CTX* ctx = NULL; WOLFSSL* ssl = NULL; @@ -2547,7 +2551,8 @@ static int test_wolfSSL_set_cipher_list_tls13_with_version(void) !defined(WOLFSSL_NO_TLS12) && \ !defined(NO_WOLFSSL_CLIENT) && \ !defined(HAVE_RENEGOTIATION_INDICATION) && \ - defined(HAVE_AESGCM) && !defined(NO_ERROR_STRINGS) + defined(HAVE_AESGCM) && !defined(NO_ERROR_STRINGS) && \ + defined(WOLFSSL_AES_128) WOLFSSL_CTX* ctx = NULL; WOLFSSL* ssl = NULL; @@ -8613,7 +8618,8 @@ static int test_wolfSSL_CTX_verifyDepth_ServerClient_3(void) #if defined(OPENSSL_ALL) && defined(HAVE_SSL_MEMIO_TESTS_DEPENDENCIES) && \ !defined(WOLFSSL_NO_TLS12) && \ - defined(HAVE_ECC) && !defined(NO_AES) && !defined(NO_SHA256) + defined(HAVE_ECC) && !defined(NO_AES) && !defined(NO_SHA256) && \ + defined(WOLFSSL_AES_128) static int test_wolfSSL_CTX_set_cipher_list_server_ctx_ready(WOLFSSL_CTX* ctx) { EXPECT_DECLS; @@ -8633,7 +8639,8 @@ static int test_wolfSSL_CTX_set_cipher_list(void) { EXPECT_DECLS; #if defined(OPENSSL_ALL) && defined(HAVE_SSL_MEMIO_TESTS_DEPENDENCIES) && \ - defined(HAVE_ECC) && !defined(NO_AES) && !defined(NO_SHA256) + defined(HAVE_ECC) && !defined(NO_AES) && !defined(NO_SHA256) && \ + defined(WOLFSSL_AES_128) #if !defined(WOLFSSL_NO_TLS12) WOLFSSL_CTX* ctxClient = NULL; @@ -15356,7 +15363,8 @@ static int test_wolfSSL_set1_host(void) #if defined(OPENSSL_ALL) && !defined(NO_RSA) && !defined(NO_CERTS) && \ !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \ - defined(HAVE_ECC) && !defined(NO_TLS) && defined(HAVE_AESGCM) + defined(HAVE_ECC) && !defined(NO_TLS) && defined(HAVE_AESGCM) && \ + defined(WOLFSSL_AES_128) static int test_wolfSSL_get_client_ciphers_ctx_ready(WOLFSSL_CTX* ctx) { EXPECT_DECLS; @@ -15399,7 +15407,8 @@ static int test_wolfSSL_get_client_ciphers(void) EXPECT_DECLS; #if defined(OPENSSL_ALL) && !defined(NO_RSA) && !defined(NO_CERTS) && \ !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \ - defined(HAVE_ECC) && !defined(NO_TLS) && defined(HAVE_AESGCM) + defined(HAVE_ECC) && !defined(NO_TLS) && defined(HAVE_AESGCM) && \ + defined(WOLFSSL_AES_128) test_ssl_cbf server_cb; test_ssl_cbf client_cb; @@ -19421,7 +19430,8 @@ static int test_wolfSSL_set1_curves_list(void) } #if defined(HAVE_SSL_MEMIO_TESTS_DEPENDENCIES) && \ - (defined(OPENSSL_EXTRA) || defined(HAVE_CURL)) && defined(HAVE_ECC) + (defined(OPENSSL_EXTRA) || defined(HAVE_CURL)) && defined(HAVE_ECC) && \ + (defined(HAVE_ECC384) || defined(HAVE_ALL_CURVES)) static int test_wolfSSL_curves_mismatch_ctx_ready(WOLFSSL_CTX* ctx) { static int counter = 0; @@ -19453,7 +19463,8 @@ static int test_wolfSSL_curves_mismatch(void) { EXPECT_DECLS; #if defined(HAVE_SSL_MEMIO_TESTS_DEPENDENCIES) && \ - (defined(OPENSSL_EXTRA) || defined(HAVE_CURL)) && defined(HAVE_ECC) + (defined(OPENSSL_EXTRA) || defined(HAVE_CURL)) && defined(HAVE_ECC) && \ + (defined(HAVE_ECC384) || defined(HAVE_ALL_CURVES)) test_ssl_cbf func_cb_client; test_ssl_cbf func_cb_server; size_t i; @@ -20261,7 +20272,7 @@ static int test_wolfSSL_PKCS8_d2i(void) ExpectIntEQ(XMEMCMP(p, pkcs8_buffer, bytes), 0); BIO_free(bio); bio = NULL; -#if !defined(NO_AES) && defined(HAVE_AESGCM) +#if !defined(NO_AES) && defined(HAVE_AESGCM) && defined(WOLFSSL_AES_128) ExpectIntEQ(PEM_write_PKCS8PrivateKey(stderr, pkey, EVP_aes_128_gcm(), NULL, 0, PasswordCallBack, (void*)"yassl123"), 0); #endif @@ -21268,7 +21279,8 @@ static int test_wolfSSL_cert_cb(void) return EXPECT_RESULT(); } -#if defined(OPENSSL_EXTRA) && defined(HAVE_SSL_MEMIO_TESTS_DEPENDENCIES) +#if defined(OPENSSL_EXTRA) && defined(HAVE_SSL_MEMIO_TESTS_DEPENDENCIES) && \ + defined(WOLFSSL_AES_128) static const char* test_wolfSSL_cert_cb_dyn_ciphers_client_cipher = NULL; static const char* test_wolfSSL_cert_cb_dyn_ciphers_client_sigalgs = NULL; @@ -21363,7 +21375,8 @@ static int test_wolfSSL_cert_cb_dyn_ciphers_server_ctx_ready(WOLFSSL_CTX* ctx) static int test_wolfSSL_cert_cb_dyn_ciphers(void) { EXPECT_DECLS; -#if defined(OPENSSL_EXTRA) && defined(HAVE_SSL_MEMIO_TESTS_DEPENDENCIES) +#if defined(OPENSSL_EXTRA) && defined(HAVE_SSL_MEMIO_TESTS_DEPENDENCIES) && \ + defined(WOLFSSL_AES_128) test_ssl_cbf func_cb_client; test_ssl_cbf func_cb_server; struct { @@ -33123,7 +33136,8 @@ static int error_test(void) { -356, -356 }, { -358, -358 }, { -384, -384 }, - { -466, -499 }, + /* -466 is CLIENT_HELLO_CB_E and has a string. */ + { -467, -499 }, { WOLFSSL_LAST_E - 1, WC_SPAN2_FIRST_E + 1 }, { WC_SPAN2_LAST_E - 1, MIN_CODE_E } }; @@ -36979,7 +36993,8 @@ static int test_TLSX_CA_NAMES_bad_extension(void) #if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13) && \ !defined(NO_CERTS) && !defined(WOLFSSL_NO_CA_NAMES) && \ defined(OPENSSL_EXTRA) && defined(BUILD_TLS_CHACHA20_POLY1305_SHA256) && \ - defined(HAVE_ECC) && !defined(WOLFSSL_TLS13_MIDDLEBOX_COMPAT) + defined(HAVE_ECC) && !defined(WOLFSSL_TLS13_MIDDLEBOX_COMPAT) && \ + (defined(HAVE_ECC521) || defined(HAVE_ALL_CURVES)) /* This test should only fail (with BUFFER_ERROR) when we actually try to * parse the CA Names extension. Otherwise it will return other non-related * errors. If CA Names will be parsed in more configurations, that should @@ -41848,6 +41863,7 @@ TEST_CASE testCases[] = { TEST_DECL(test_ocsp_ctx_request_cache), TEST_DECL(test_ocsp_responder), TEST_DECL(test_wolfIO_DecodeUrl_crlf_reject), + TEST_DECL_GROUP("ocsp", test_ocsp_crl_reason_str), TEST_TLS_DECLS, TEST_TLS_BOUNDS_DECLS, TEST_TLS_MSGTYPE_DECLS, diff --git a/tests/api/test_aes.c b/tests/api/test_aes.c index da4db9efe29..d90513908dd 100644 --- a/tests/api/test_aes.c +++ b/tests/api/test_aes.c @@ -8239,7 +8239,8 @@ int test_wc_AesKeyWrapVectors(void) { EXPECT_DECLS; #if !defined(NO_AES) && defined(HAVE_AES_KEYWRAP) && !defined(HAVE_FIPS) && \ - !defined(HAVE_SELFTEST) + !defined(HAVE_SELFTEST) && \ + defined(WOLFSSL_AES_128) static const byte key1[] = { 0x6f, 0x67, 0x48, 0x6d, 0x1e, 0x91, 0x44, 0x19, 0xcb, 0x43, 0xc2, 0x85, 0x09, 0xc7, 0xc1, 0xea @@ -8341,7 +8342,8 @@ int test_wc_AesKeyWrapDecisionCoverage(void) { EXPECT_DECLS; #if !defined(NO_AES) && defined(HAVE_AES_KEYWRAP) && !defined(HAVE_FIPS) && \ - !defined(HAVE_SELFTEST) + !defined(HAVE_SELFTEST) && \ + defined(WOLFSSL_AES_128) static const byte kek[16] = { 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, 0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F @@ -8452,7 +8454,8 @@ int test_wc_AesKeyWrapDecisionCoverage(void) int test_wc_AesGcmDecisionCoverage(void) { EXPECT_DECLS; -#if !defined(NO_AES) && defined(HAVE_AESGCM) +#if !defined(NO_AES) && defined(HAVE_AESGCM) && \ + defined(WOLFSSL_AES_128) static const byte key[16] = { 0xFE,0xFF,0xE9,0x92,0x86,0x65,0x73,0x1C, 0x6D,0x6A,0x8F,0x94,0x67,0x30,0x83,0x08 @@ -8521,7 +8524,8 @@ int test_wc_AesFeatureCoverage(void) * CCM block additionally excludes old FIPS (its AAD-only case diverges there, * see the per-block note); the key-wrap block excludes all FIPS + self-test. * The open MC/DC builds are unaffected. */ -#if !defined(NO_AES) && defined(HAVE_AESGCM) && !defined(HAVE_SELFTEST) +#if !defined(NO_AES) && defined(HAVE_AESGCM) && !defined(HAVE_SELFTEST) && \ + defined(WOLFSSL_AES_128) /* ---- AES-GCM streaming API: multi-chunk AAD and data ---- */ /* Uses a hardcoded 256-bit key, so requires AES-256. */ #if defined(WOLFSSL_AESGCM_STREAM) && defined(WOLFSSL_AES_256) diff --git a/tests/api/test_dtls.c b/tests/api/test_dtls.c index f91ea99eb1c..df5d0b184c5 100644 --- a/tests/api/test_dtls.c +++ b/tests/api/test_dtls.c @@ -8153,4 +8153,3 @@ int test_wolfSSL_set_secret(void) #endif return EXPECT_RESULT(); } - diff --git a/tests/api/test_evp_cipher.c b/tests/api/test_evp_cipher.c index d347fad2636..904934a2512 100644 --- a/tests/api/test_evp_cipher.c +++ b/tests/api/test_evp_cipher.c @@ -67,7 +67,7 @@ int test_wolfSSL_EVP_CIPHER_CTX(void) int test_wolfSSL_EVP_CIPHER_CTX_iv_length(void) { EXPECT_DECLS; -#ifdef OPENSSL_ALL +#if defined(OPENSSL_ALL) && !defined(NO_AES) && defined(WOLFSSL_AES_128) /* This is large enough to be used for all key sizes */ byte key[AES_256_KEY_SIZE] = {0}; byte iv[AES_BLOCK_SIZE] = {0}; @@ -127,7 +127,7 @@ int test_wolfSSL_EVP_CIPHER_CTX_iv_length(void) int test_wolfSSL_EVP_CIPHER_CTX_key_length(void) { EXPECT_DECLS; -#ifdef OPENSSL_ALL +#if defined(OPENSSL_ALL) && !defined(NO_AES) && defined(WOLFSSL_AES_128) byte key[AES_256_KEY_SIZE] = {0}; byte iv[AES_BLOCK_SIZE] = {0}; int i; @@ -677,7 +677,8 @@ int test_wolfSSL_EVP_CIPHER_type_string(void) int test_wolfSSL_EVP_BytesToKey(void) { EXPECT_DECLS; -#if !defined(NO_AES) && defined(HAVE_AES_CBC) && defined(OPENSSL_ALL) +#if !defined(NO_AES) && defined(HAVE_AES_CBC) && defined(OPENSSL_ALL) && \ + defined(WOLFSSL_AES_128) byte key[AES_BLOCK_SIZE] = {0}; byte iv[AES_BLOCK_SIZE] = {0}; int count = 0; @@ -1498,7 +1499,7 @@ int test_wolfssl_EVP_aes_gcm_AAD_2_parts(void) { EXPECT_DECLS; #if defined(OPENSSL_EXTRA) && !defined(NO_AES) && defined(HAVE_AESGCM) && \ - !defined(HAVE_SELFTEST) && !defined(HAVE_FIPS) + defined(WOLFSSL_AES_128) && !defined(HAVE_SELFTEST) && !defined(HAVE_FIPS) const byte iv[12] = { 0 }; const byte key[16] = { 0 }; const byte cleartext[16] = { 0 }; @@ -2956,4 +2957,980 @@ int test_evp_cipher_aead_aad_overflow(void) return EXPECT_RESULT(); } +#if defined(OPENSSL_EXTRA) && defined(WOLFSSL_EVP_AES_KEYWRAP) +/* Wrap in, unwrap back, and check we got the original. Used to cover lengths + * the published vectors do not. */ +static int aesWrapRoundTrip(const WOLFSSL_EVP_CIPHER* cipher, const byte* kek, + const byte* data, int dataLen, int padded) +{ + EXPECT_DECLS; + EVP_CIPHER_CTX* ctx = NULL; + byte wrapped[128]; + byte back[128]; + int wrappedLen = 0; + int backLen = 0; + int finalLen = 0; + int expectLen = padded ? + ((((dataLen + 7) / 8) * 8) + 8) : (dataLen + 8); + + ExpectNotNull(ctx = EVP_CIPHER_CTX_new()); + ExpectIntEQ(EVP_EncryptInit_ex(ctx, cipher, NULL, kek, NULL), 1); + ExpectIntEQ(EVP_EncryptUpdate(ctx, wrapped, &wrappedLen, data, dataLen), + 1); + ExpectIntEQ(wrappedLen, expectLen); + ExpectIntEQ(EVP_EncryptFinal_ex(ctx, wrapped + wrappedLen, &finalLen), 1); + ExpectIntEQ(finalLen, 0); + EVP_CIPHER_CTX_free(ctx); + ctx = NULL; + + ExpectNotNull(ctx = EVP_CIPHER_CTX_new()); + ExpectIntEQ(EVP_DecryptInit_ex(ctx, cipher, NULL, kek, NULL), 1); + ExpectIntEQ(EVP_DecryptUpdate(ctx, back, &backLen, wrapped, wrappedLen), + 1); + /* The padded form reports the original length, not the padded one. */ + ExpectIntEQ(backLen, dataLen); + ExpectIntEQ(EVP_DecryptFinal_ex(ctx, back + backLen, &finalLen), 1); + ExpectIntEQ(XMEMCMP(back, data, (size_t)dataLen), 0); + EVP_CIPHER_CTX_free(ctx); + + return EXPECT_RESULT(); +} +#endif +int test_wolfSSL_EVP_aes_wrap(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && defined(WOLFSSL_EVP_AES_KEYWRAP) + /* RFC 3394 sec. 4.1 / 4.2 / 4.3: a 128 bit key under 128, 192 and 256 bit + * KEKs. */ +#ifdef WOLFSSL_AES_128 + static const byte kek128[] = { + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F + }; +#endif +#ifdef WOLFSSL_AES_192 + static const byte kek192[] = { + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F, + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17 + }; +#endif +#ifdef WOLFSSL_AES_256 + static const byte kek256[] = { + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F, + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, + 0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F + }; +#endif + static const byte keyData[] = { + 0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77, + 0x88,0x99,0xAA,0xBB,0xCC,0xDD,0xEE,0xFF + }; +#ifdef WOLFSSL_AES_128 + static const byte wrap128[] = { + 0x1F,0xA6,0x8B,0x0A,0x81,0x12,0xB4,0x47, + 0xAE,0xF3,0x4B,0xD8,0xFB,0x5A,0x7B,0x82, + 0x9D,0x3E,0x86,0x23,0x71,0xD2,0xCF,0xE5 + }; +#endif +#ifdef WOLFSSL_AES_192 + static const byte wrap192[] = { + 0x96,0x77,0x8B,0x25,0xAE,0x6C,0xA4,0x35, + 0xF9,0x2B,0x5B,0x97,0xC0,0x50,0xAE,0xD2, + 0x46,0x8A,0xB8,0xA1,0x7A,0xD8,0x4E,0x5D + }; +#endif +#ifdef WOLFSSL_AES_256 + static const byte wrap256[] = { + 0x64,0xE8,0xC3,0xF9,0xCE,0x0F,0x5B,0xA2, + 0x63,0xE9,0x77,0x79,0x05,0x81,0x8A,0x2A, + 0x93,0xC8,0x19,0x1E,0x7D,0x6E,0x8A,0xE7 + }; +#endif + struct { + const char* name; + const byte* kek; + int kekLen; + const byte* expect; + int nid; + const WOLFSSL_EVP_CIPHER* (*cipher)(void); + } cases[] = { +#ifdef WOLFSSL_AES_128 + { "id-aes128-wrap", kek128, 16, wrap128, NID_id_aes128_wrap, + wolfSSL_EVP_aes_128_wrap }, +#endif +#ifdef WOLFSSL_AES_192 + { "id-aes192-wrap", kek192, 24, wrap192, NID_id_aes192_wrap, + wolfSSL_EVP_aes_192_wrap }, +#endif +#ifdef WOLFSSL_AES_256 + { "id-aes256-wrap", kek256, 32, wrap256, NID_id_aes256_wrap, + wolfSSL_EVP_aes_256_wrap }, +#endif + { NULL, NULL, 0, NULL, 0, NULL } + }; + /* Lengths the published vectors do not cover; RFC 3394 requires a + * multiple of 8 and at least 16. */ + static const int lengths[] = { 16, 24, 32, 64 }; + int i; + int j; + + for (i = 0; cases[i].kek != NULL; i++) { + EVP_CIPHER_CTX* ctx = NULL; + byte out[128]; + int outl = 0; + int finalLen = 0; + + /* The cipher describes itself the way OpenSSL's does. */ + ExpectNotNull(cases[i].cipher()); + ExpectPtrEq(EVP_get_cipherbyname(cases[i].name), cases[i].cipher()); + ExpectIntEQ(EVP_CIPHER_nid(cases[i].cipher()), cases[i].nid); + + /* Looking the cipher up by name ignores case, which OpenSSL's own + * vectors check with names like "aes256-WRAP". */ + { + char upper[32]; + size_t n; + for (n = 0; n < sizeof(upper) - 1 && cases[i].name[n] != '\0'; + n++) { + upper[n] = (char)XTOUPPER((unsigned char)cases[i].name[n]); + } + upper[n] = '\0'; + ExpectPtrEq(EVP_get_cipherbyname(upper), cases[i].cipher()); + } + + /* Wrap matches the published answer. */ + ExpectNotNull(ctx = EVP_CIPHER_CTX_new()); + ExpectIntEQ(EVP_EncryptInit_ex(ctx, cases[i].cipher(), NULL, + cases[i].kek, NULL), 1); + ExpectIntEQ(EVP_CIPHER_CTX_block_size(ctx), 8); + ExpectIntEQ(EVP_CIPHER_CTX_key_length(ctx), cases[i].kekLen); + ExpectIntEQ(EVP_CIPHER_CTX_nid(ctx), cases[i].nid); + ExpectIntEQ((int)EVP_CIPHER_CTX_mode(ctx), EVP_CIPH_WRAP_MODE); + XMEMSET(out, 0, sizeof(out)); + ExpectIntEQ(EVP_EncryptUpdate(ctx, out, &outl, keyData, + (int)sizeof(keyData)), 1); + ExpectIntEQ(outl, (int)sizeof(keyData) + 8); + ExpectIntEQ(EVP_EncryptFinal_ex(ctx, out + outl, &finalLen), 1); + ExpectIntEQ(finalLen, 0); + ExpectIntEQ(XMEMCMP(out, cases[i].expect, sizeof(keyData) + 8), 0); + EVP_CIPHER_CTX_free(ctx); + ctx = NULL; + + /* Unwrap gives the key back. */ + ExpectNotNull(ctx = EVP_CIPHER_CTX_new()); + ExpectIntEQ(EVP_DecryptInit_ex(ctx, cases[i].cipher(), NULL, + cases[i].kek, NULL), 1); + XMEMSET(out, 0, sizeof(out)); + ExpectIntEQ(EVP_DecryptUpdate(ctx, out, &outl, cases[i].expect, + (int)sizeof(keyData) + 8), 1); + ExpectIntEQ(outl, (int)sizeof(keyData)); + ExpectIntEQ(EVP_DecryptFinal_ex(ctx, out + outl, &finalLen), 1); + ExpectIntEQ(finalLen, 0); + ExpectIntEQ(XMEMCMP(out, keyData, sizeof(keyData)), 0); + EVP_CIPHER_CTX_free(ctx); + ctx = NULL; + + /* Round trips at lengths beyond the published vector. */ + for (j = 0; j < (int)(sizeof(lengths) / sizeof(lengths[0])); j++) { + byte data[64]; + int k; + for (k = 0; k < lengths[j]; k++) { + data[k] = (byte)(k * 7 + j); + } + ExpectIntEQ(aesWrapRoundTrip(cases[i].cipher(), cases[i].kek, + data, lengths[j], 0), TEST_SUCCESS); + } + + /* A caller-supplied integrity check value replaces the RFC 3394 + * default, and only the same value unwraps it again. */ + { + static const byte altIv[] = { + 0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5,0xA5 + }; + byte altWrapped[64]; + int altLen = 0; + + ExpectNotNull(ctx = EVP_CIPHER_CTX_new()); + ExpectIntEQ(EVP_EncryptInit_ex(ctx, cases[i].cipher(), NULL, + cases[i].kek, altIv), 1); + ExpectIntEQ(EVP_EncryptUpdate(ctx, altWrapped, &altLen, keyData, + (int)sizeof(keyData)), 1); + ExpectIntEQ(altLen, (int)sizeof(keyData) + 8); + /* Different check value, so a different wrapping. */ + ExpectIntNE(XMEMCMP(altWrapped, cases[i].expect, + sizeof(keyData) + 8), 0); + EVP_CIPHER_CTX_free(ctx); + ctx = NULL; + + /* The default check value rejects it. */ + ExpectNotNull(ctx = EVP_CIPHER_CTX_new()); + ExpectIntEQ(EVP_DecryptInit_ex(ctx, cases[i].cipher(), NULL, + cases[i].kek, NULL), 1); + ExpectIntNE(EVP_DecryptUpdate(ctx, out, &outl, altWrapped, + altLen), 1); + EVP_CIPHER_CTX_free(ctx); + ctx = NULL; + + /* The same one accepts it. */ + ExpectNotNull(ctx = EVP_CIPHER_CTX_new()); + ExpectIntEQ(EVP_DecryptInit_ex(ctx, cases[i].cipher(), NULL, + cases[i].kek, altIv), 1); + XMEMSET(out, 0, sizeof(out)); + ExpectIntEQ(EVP_DecryptUpdate(ctx, out, &outl, altWrapped, + altLen), 1); + ExpectIntEQ(outl, (int)sizeof(keyData)); + ExpectIntEQ(XMEMCMP(out, keyData, sizeof(keyData)), 0); + EVP_CIPHER_CTX_free(ctx); + ctx = NULL; + } + + /* A corrupted wrapping fails the integrity check. */ + { + byte bad[64]; + XMEMCPY(bad, cases[i].expect, sizeof(keyData) + 8); + bad[0] ^= 0xFF; + ExpectNotNull(ctx = EVP_CIPHER_CTX_new()); + ExpectIntEQ(EVP_DecryptInit_ex(ctx, cases[i].cipher(), NULL, + cases[i].kek, NULL), 1); + ExpectIntNE(EVP_DecryptUpdate(ctx, out, &outl, bad, + (int)sizeof(keyData) + 8), 1); + EVP_CIPHER_CTX_free(ctx); + ctx = NULL; + } + + /* Input that is not a whole number of semiblocks, and input too + * short to carry the check value, are both refused. */ + ExpectNotNull(ctx = EVP_CIPHER_CTX_new()); + ExpectIntEQ(EVP_EncryptInit_ex(ctx, cases[i].cipher(), NULL, + cases[i].kek, NULL), 1); + ExpectIntNE(EVP_EncryptUpdate(ctx, out, &outl, keyData, 15), 1); + EVP_CIPHER_CTX_free(ctx); + ctx = NULL; + + ExpectNotNull(ctx = EVP_CIPHER_CTX_new()); + ExpectIntEQ(EVP_DecryptInit_ex(ctx, cases[i].cipher(), NULL, + cases[i].kek, NULL), 1); + ExpectIntNE(EVP_DecryptUpdate(ctx, out, &outl, cases[i].expect, 8), 1); + EVP_CIPHER_CTX_free(ctx); + ctx = NULL; + + /* The rest of RFC 3394: longer keys, and a longer key under a + * longer KEK. These are the remaining vectors OpenSSL's + * evpciph_aes_wrap.txt checks. */ + { + static const byte pt24[] = { + 0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77, + 0x88,0x99,0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07 + }; + static const byte pt32[] = { + 0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77, + 0x88,0x99,0xAA,0xBB,0xCC,0xDD,0xEE,0xFF, + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F + }; + /* sec. 4.4: 192 bit key under a 192 bit KEK. */ + static const byte ct192_24[] = { + 0x03,0x1D,0x33,0x26,0x4E,0x15,0xD3,0x32, + 0x68,0xF2,0x4E,0xC2,0x60,0x74,0x3E,0xDC, + 0xE1,0xC6,0xC7,0xDD,0xEE,0x72,0x5A,0x93, + 0x6B,0xA8,0x14,0x91,0x5C,0x67,0x62,0xD2 + }; + /* sec. 4.5: 192 bit key under a 256 bit KEK. */ + static const byte ct256_24[] = { + 0xA8,0xF9,0xBC,0x16,0x12,0xC6,0x8B,0x3F, + 0xF6,0xE6,0xF4,0xFB,0xE3,0x0E,0x71,0xE4, + 0x76,0x9C,0x8B,0x80,0xA3,0x2C,0xB8,0x95, + 0x8C,0xD5,0xD1,0x7D,0x6B,0x25,0x4D,0xA1 + }; + /* sec. 4.6: 256 bit key under a 256 bit KEK. */ + static const byte ct256_32[] = { + 0x28,0xC9,0xF4,0x04,0xC4,0xB8,0x10,0xF4, + 0xCB,0xCC,0xB3,0x5C,0xFB,0x87,0xF8,0x26, + 0x3F,0x57,0x86,0xE2,0xD8,0x0E,0xD3,0x26, + 0xCB,0xC7,0xF0,0xE7,0x1A,0x99,0xF4,0x3B, + 0xFB,0x98,0x8B,0x9B,0x7A,0x02,0xDD,0x21 + }; + const byte* pt = NULL; + const byte* ct = NULL; + int ptLen = 0; + + if (cases[i].kekLen == 24) { + pt = pt24; ptLen = (int)sizeof(pt24); ct = ct192_24; + } + else if (cases[i].kekLen == 32) { + /* Two more for the 256 bit KEK. */ + pt = pt24; ptLen = (int)sizeof(pt24); ct = ct256_24; + } + + if (pt != NULL) { + ExpectNotNull(ctx = EVP_CIPHER_CTX_new()); + ExpectIntEQ(EVP_EncryptInit_ex(ctx, cases[i].cipher(), NULL, + cases[i].kek, NULL), 1); + XMEMSET(out, 0, sizeof(out)); + ExpectIntEQ(EVP_EncryptUpdate(ctx, out, &outl, pt, ptLen), 1); + ExpectIntEQ(outl, ptLen + 8); + ExpectIntEQ(XMEMCMP(out, ct, (size_t)ptLen + 8), 0); + EVP_CIPHER_CTX_free(ctx); + ctx = NULL; + } + if (cases[i].kekLen == 32) { + ExpectNotNull(ctx = EVP_CIPHER_CTX_new()); + ExpectIntEQ(EVP_EncryptInit_ex(ctx, cases[i].cipher(), NULL, + cases[i].kek, NULL), 1); + XMEMSET(out, 0, sizeof(out)); + ExpectIntEQ(EVP_EncryptUpdate(ctx, out, &outl, pt32, + (int)sizeof(pt32)), 1); + ExpectIntEQ(outl, (int)sizeof(pt32) + 8); + ExpectIntEQ(XMEMCMP(out, ct256_32, sizeof(ct256_32)), 0); + EVP_CIPHER_CTX_free(ctx); + ctx = NULL; + + /* Unwrapping with the wrong KEK is rejected, and emits no + * plaintext at all - OpenSSL's file calls this one out + * specifically. */ + { + byte wrongKek[32]; + XMEMCPY(wrongKek, cases[i].kek, 32); + wrongKek[31] ^= 0xFF; + XMEMSET(out, 0xEE, sizeof(out)); + outl = -1; + ExpectNotNull(ctx = EVP_CIPHER_CTX_new()); + ExpectIntEQ(EVP_DecryptInit_ex(ctx, cases[i].cipher(), + NULL, wrongKek, NULL), 1); + ExpectIntNE(EVP_DecryptUpdate(ctx, out, &outl, ct256_32, + (int)sizeof(ct256_32)), 1); + ExpectIntEQ(outl, 0); + EVP_CIPHER_CTX_free(ctx); + ctx = NULL; + } + } + } + + /* A context can be reset and used again, and an enc argument of -1 + * leaves the direction it was initialised with alone. */ + { + byte again[64]; + int againLen = 0; + + ExpectNotNull(ctx = EVP_CIPHER_CTX_new()); + ExpectIntEQ(EVP_EncryptInit_ex(ctx, cases[i].cipher(), NULL, + cases[i].kek, NULL), 1); + ExpectIntEQ(EVP_EncryptUpdate(ctx, out, &outl, keyData, + (int)sizeof(keyData)), 1); + ExpectIntEQ(outl, (int)sizeof(keyData) + 8); + + ExpectIntEQ(EVP_CIPHER_CTX_reset(ctx), 1); + ExpectIntEQ(EVP_EncryptInit_ex(ctx, cases[i].cipher(), NULL, + cases[i].kek, NULL), 1); + XMEMSET(again, 0, sizeof(again)); + ExpectIntEQ(EVP_EncryptUpdate(ctx, again, &againLen, keyData, + (int)sizeof(keyData)), 1); + ExpectIntEQ(againLen, (int)sizeof(keyData) + 8); + ExpectIntEQ(XMEMCMP(again, cases[i].expect, + sizeof(keyData) + 8), 0); + EVP_CIPHER_CTX_free(ctx); + ctx = NULL; + + /* Re-initialising with enc == -1 keeps the direction, so this + * still decrypts. */ + ExpectNotNull(ctx = EVP_CIPHER_CTX_new()); + ExpectIntEQ(EVP_CipherInit(ctx, cases[i].cipher(), cases[i].kek, + NULL, 0), 1); + ExpectIntEQ(EVP_CipherInit(ctx, NULL, cases[i].kek, NULL, -1), 1); + XMEMSET(out, 0, sizeof(out)); + ExpectIntEQ(EVP_CipherUpdate(ctx, out, &outl, cases[i].expect, + (int)sizeof(keyData) + 8), 1); + ExpectIntEQ(outl, (int)sizeof(keyData)); + ExpectIntEQ(XMEMCMP(out, keyData, sizeof(keyData)), 0); + EVP_CIPHER_CTX_free(ctx); + ctx = NULL; + } + + /* A NULL output asks only for the size, as with OpenSSL's other + * ciphers. */ + ExpectNotNull(ctx = EVP_CIPHER_CTX_new()); + ExpectIntEQ(EVP_EncryptInit_ex(ctx, cases[i].cipher(), NULL, + cases[i].kek, NULL), 1); + outl = -1; + ExpectIntEQ(EVP_EncryptUpdate(ctx, NULL, &outl, keyData, + (int)sizeof(keyData)), 1); + ExpectIntEQ(outl, (int)sizeof(keyData) + 8); + EVP_CIPHER_CTX_free(ctx); + ctx = NULL; + + /* Wrapping in place is allowed; the output is 8 bytes longer than + * the input, so the buffer has to have room for it. */ + { + byte inPlace[64]; + XMEMSET(inPlace, 0, sizeof(inPlace)); + XMEMCPY(inPlace, keyData, sizeof(keyData)); + ExpectNotNull(ctx = EVP_CIPHER_CTX_new()); + ExpectIntEQ(EVP_EncryptInit_ex(ctx, cases[i].cipher(), NULL, + cases[i].kek, NULL), 1); + ExpectIntEQ(EVP_EncryptUpdate(ctx, inPlace, &outl, inPlace, + (int)sizeof(keyData)), 1); + ExpectIntEQ(outl, (int)sizeof(keyData) + 8); + ExpectIntEQ(XMEMCMP(inPlace, cases[i].expect, + sizeof(keyData) + 8), 0); + EVP_CIPHER_CTX_free(ctx); + ctx = NULL; + } + + /* Key wrapping cannot be streamed, so a second update carrying more + * data is refused rather than wrapping the pieces separately. */ + ExpectNotNull(ctx = EVP_CIPHER_CTX_new()); + ExpectIntEQ(EVP_EncryptInit_ex(ctx, cases[i].cipher(), NULL, + cases[i].kek, NULL), 1); + ExpectIntEQ(EVP_EncryptUpdate(ctx, out, &outl, keyData, + (int)sizeof(keyData)), 1); + ExpectIntNE(EVP_EncryptUpdate(ctx, out, &outl, keyData, + (int)sizeof(keyData)), 1); + EVP_CIPHER_CTX_free(ctx); + } +#endif + return EXPECT_RESULT(); +} + +int test_wolfSSL_EVP_aes_wrap_pad(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && defined(WOLFSSL_EVP_AES_KEYWRAP) && \ + defined(WOLFSSL_AES_KEYWRAP_PADDING) && !defined(NO_AES) + /* RFC 5649 sec. 6 for the 192 bit KEK; the 128 and 256 bit answers are + * the ones wolfCrypt's own aeskeywrap_pad_test() checks. */ +#ifdef WOLFSSL_AES_128 + static const byte kek128[] = { + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f + }; +#endif +#ifdef WOLFSSL_AES_192 + static const byte kek192[] = { + 0x58,0x40,0xdf,0x6e,0x29,0xb0,0x2a,0xf1, + 0xab,0x49,0x3b,0x70,0x5b,0xf1,0x6e,0xa1, + 0xae,0x83,0x38,0xf4,0xdc,0xc1,0x76,0xa8 + }; +#endif +#ifdef WOLFSSL_AES_256 + static const byte kek256[] = { + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, + 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f + }; +#endif + /* 20 octets: padded to 24, so the RFC 3394 loop runs. */ + static const byte data20[] = { + 0xc3,0x7b,0x7e,0x64,0x92,0x58,0x43,0x40, + 0xbe,0xd1,0x22,0x07,0x80,0x89,0x41,0x15, + 0x50,0x68,0xf7,0x38 + }; + /* 7 octets: padded to 8, which RFC 5649 wraps as a single block. */ + static const byte data7[] = { + 0x46,0x6f,0x72,0x50,0x61,0x73,0x69 + }; +#ifdef WOLFSSL_AES_128 + static const byte v128_20[] = { + 0xe1,0xf7,0x17,0x6e,0xcb,0xd7,0x5d,0x42, + 0xe8,0x2b,0x24,0xf9,0x89,0xa2,0x81,0x6c, + 0x20,0x9c,0x6e,0xf2,0xd1,0xaa,0x94,0xd2, + 0xa3,0xe6,0x02,0x84,0x90,0x0d,0x03,0xa2 + }; +#endif +#ifdef WOLFSSL_AES_128 + static const byte v128_7[] = { + 0xbe,0x80,0x53,0x5e,0x12,0xe9,0x39,0x4c, + 0x8f,0x8d,0xf2,0x6b,0xd9,0x52,0x8a,0x35 + }; +#endif +#ifdef WOLFSSL_AES_192 + static const byte v192_20[] = { + 0x13,0x8b,0xde,0xaa,0x9b,0x8f,0xa7,0xfc, + 0x61,0xf9,0x77,0x42,0xe7,0x22,0x48,0xee, + 0x5a,0xe6,0xae,0x53,0x60,0xd1,0xae,0x6a, + 0x5f,0x54,0xf3,0x73,0xfa,0x54,0x3b,0x6a + }; +#endif +#ifdef WOLFSSL_AES_192 + static const byte v192_7[] = { + 0xaf,0xbe,0xb0,0xf0,0x7d,0xfb,0xf5,0x41, + 0x92,0x00,0xf2,0xcc,0xb5,0x0b,0xb2,0x4f + }; +#endif +#ifdef WOLFSSL_AES_256 + static const byte v256_20[] = { + 0x29,0xb7,0xfa,0x19,0x1c,0x21,0x65,0x68, + 0x43,0x74,0xee,0xe9,0xf7,0x45,0x95,0xe2, + 0xa4,0x2b,0xac,0xe7,0x5c,0x42,0x5b,0x30, + 0x53,0xef,0xa2,0x6f,0xfe,0x1b,0xb3,0x2f + }; +#endif +#ifdef WOLFSSL_AES_256 + static const byte v256_7[] = { + 0x44,0x3b,0x17,0x83,0x7b,0xb3,0x93,0x48, + 0x61,0x0d,0x19,0x20,0x2d,0xf8,0xa1,0xf9 + }; +#endif + struct { + const char* name; + const byte* kek; + int kekLen; + const byte* v20; + const byte* v7; + int nid; + const WOLFSSL_EVP_CIPHER* (*cipher)(void); + } cases[] = { +#ifdef WOLFSSL_AES_128 + { "id-aes128-wrap-pad", kek128, 16, v128_20, v128_7, + NID_id_aes128_wrap_pad, wolfSSL_EVP_aes_128_wrap_pad }, +#endif +#ifdef WOLFSSL_AES_192 + { "id-aes192-wrap-pad", kek192, 24, v192_20, v192_7, + NID_id_aes192_wrap_pad, wolfSSL_EVP_aes_192_wrap_pad }, +#endif +#ifdef WOLFSSL_AES_256 + { "id-aes256-wrap-pad", kek256, 32, v256_20, v256_7, + NID_id_aes256_wrap_pad, wolfSSL_EVP_aes_256_wrap_pad }, +#endif + { NULL, NULL, 0, NULL, NULL, 0, NULL } + }; + int i; + int len; + + for (i = 0; cases[i].kek != NULL; i++) { + EVP_CIPHER_CTX* ctx = NULL; + byte out[64]; + int outl = 0; + int finalLen = 0; + int c; + + ExpectNotNull(cases[i].cipher()); + ExpectPtrEq(EVP_get_cipherbyname(cases[i].name), cases[i].cipher()); + ExpectIntEQ(EVP_CIPHER_nid(cases[i].cipher()), cases[i].nid); + + /* Both published lengths: 20 octets takes the loop path, 7 the + * single-block one. */ + for (c = 0; c < 2; c++) { + const byte* data = c ? data7 : data20; + int dataLen = c ? (int)sizeof(data7) : (int)sizeof(data20); + const byte* expect = c ? cases[i].v7 : cases[i].v20; + int expectLen = c ? 16 : 32; + + ExpectNotNull(ctx = EVP_CIPHER_CTX_new()); + ExpectIntEQ(EVP_EncryptInit_ex(ctx, cases[i].cipher(), NULL, + cases[i].kek, NULL), 1); + ExpectIntEQ(EVP_CIPHER_CTX_block_size(ctx), 8); + ExpectIntEQ(EVP_CIPHER_CTX_key_length(ctx), cases[i].kekLen); + ExpectIntEQ(EVP_CIPHER_CTX_nid(ctx), cases[i].nid); + ExpectIntEQ((int)EVP_CIPHER_CTX_mode(ctx), EVP_CIPH_WRAP_MODE); + XMEMSET(out, 0, sizeof(out)); + ExpectIntEQ(EVP_EncryptUpdate(ctx, out, &outl, data, dataLen), 1); + ExpectIntEQ(outl, expectLen); + ExpectIntEQ(EVP_EncryptFinal_ex(ctx, out + outl, &finalLen), 1); + ExpectIntEQ(finalLen, 0); + ExpectIntEQ(XMEMCMP(out, expect, (size_t)expectLen), 0); + EVP_CIPHER_CTX_free(ctx); + ctx = NULL; + + /* Unwrapping reports the original length, not the padded one. */ + ExpectNotNull(ctx = EVP_CIPHER_CTX_new()); + ExpectIntEQ(EVP_DecryptInit_ex(ctx, cases[i].cipher(), NULL, + cases[i].kek, NULL), 1); + XMEMSET(out, 0, sizeof(out)); + ExpectIntEQ(EVP_DecryptUpdate(ctx, out, &outl, expect, expectLen), + 1); + ExpectIntEQ(outl, dataLen); + ExpectIntEQ(EVP_DecryptFinal_ex(ctx, out + outl, &finalLen), 1); + ExpectIntEQ(XMEMCMP(out, data, (size_t)dataLen), 0); + EVP_CIPHER_CTX_free(ctx); + ctx = NULL; + } + + /* Every length from a single octet up to well past the semiblock + * boundary, covering both paths and every amount of padding. */ + for (len = 1; len <= 64; len++) { + byte data[64]; + int k; + for (k = 0; k < len; k++) { + data[k] = (byte)(k + len); + } + ExpectIntEQ(aesWrapRoundTrip(cases[i].cipher(), cases[i].kek, + data, len, 1), TEST_SUCCESS); + } + + /* A corrupted wrapping fails its integrity check. */ + { + byte bad[32]; + XMEMCPY(bad, cases[i].v20, 32); + bad[0] ^= 0xFF; + ExpectNotNull(ctx = EVP_CIPHER_CTX_new()); + ExpectIntEQ(EVP_DecryptInit_ex(ctx, cases[i].cipher(), NULL, + cases[i].kek, NULL), 1); + ExpectIntNE(EVP_DecryptUpdate(ctx, out, &outl, bad, 32), 1); + EVP_CIPHER_CTX_free(ctx); + ctx = NULL; + } + + /* The padded form takes any length, but still only in one go. */ + ExpectNotNull(ctx = EVP_CIPHER_CTX_new()); + ExpectIntEQ(EVP_EncryptInit_ex(ctx, cases[i].cipher(), NULL, + cases[i].kek, NULL), 1); + ExpectIntEQ(EVP_EncryptUpdate(ctx, out, &outl, data20, 3), 1); + ExpectIntEQ(outl, 16); + ExpectIntNE(EVP_EncryptUpdate(ctx, out, &outl, data20, 3), 1); + EVP_CIPHER_CTX_free(ctx); + } +#endif + return EXPECT_RESULT(); +} + +/* EVP_CIPHER_CTX_iv() and EVP_CIPHER_CTX_iv_noconst() hand back the context's + * working IV. cyrus-sasl needs them because its own shim for these is compiled + * only below OpenSSL 1.1.0, and wolfSSL reports 1.1.1. + */ +int test_wolfSSL_EVP_CIPHER_CTX_iv(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && !defined(NO_AES) && defined(HAVE_AES_CBC) && \ + defined(WOLFSSL_AES_128) + EVP_CIPHER_CTX* ctx = NULL; + byte key[AES_128_KEY_SIZE]; + byte iv[AES_BLOCK_SIZE]; + byte got[AES_BLOCK_SIZE]; + const unsigned char* cip = NULL; + unsigned char* mut = NULL; + int i; + + for (i = 0; i < (int)sizeof(key); i++) { + key[i] = (byte)i; + } + for (i = 0; i < (int)sizeof(iv); i++) { + iv[i] = (byte)(0xA0 + i); + } + + /* A NULL context yields NULL rather than dereferencing. */ + ExpectNull(EVP_CIPHER_CTX_iv(NULL)); + ExpectNull(EVP_CIPHER_CTX_iv_noconst(NULL)); + + ExpectNotNull(ctx = EVP_CIPHER_CTX_new()); + ExpectIntEQ(EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv), 1); + + /* The IV handed to init is the one reported back. */ + ExpectNotNull(cip = EVP_CIPHER_CTX_iv(ctx)); + ExpectIntEQ(XMEMCMP(cip, iv, sizeof(iv)), 0); + + /* Both accessors describe the same storage. */ + ExpectNotNull(mut = EVP_CIPHER_CTX_iv_noconst(ctx)); + ExpectPtrEq(mut, cip); + + /* And that storage agrees with the copying getter. */ + ExpectIntEQ(EVP_CIPHER_CTX_get_iv(ctx, got, (int)sizeof(got)), 1); + ExpectIntEQ(XMEMCMP(got, iv, sizeof(iv)), 0); + + /* A write through the mutable pointer is visible to the const one. */ + if (mut != NULL) { + mut[0] ^= 0xFF; + } + ExpectNotNull(cip = EVP_CIPHER_CTX_iv(ctx)); + ExpectIntNE(XMEMCMP(cip, iv, sizeof(iv)), 0); + ExpectIntEQ(cip[0], (byte)(iv[0] ^ 0xFF)); + + EVP_CIPHER_CTX_free(ctx); + ctx = NULL; + + /* The value reported is the chaining value, not the IV that was set: after + * a CBC encryption it is the last ciphertext block, as OpenSSL reports. */ + { + byte in[32]; + byte enc[64]; + int outl = 0; + + XMEMSET(in, 0x5A, sizeof(in)); + ExpectNotNull(ctx = EVP_CIPHER_CTX_new()); + ExpectIntEQ(EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv), + 1); + ExpectIntEQ(EVP_CIPHER_CTX_set_padding(ctx, 0), 1); + ExpectIntEQ(EVP_EncryptUpdate(ctx, enc, &outl, in, (int)sizeof(in)), 1); + ExpectIntEQ(outl, (int)sizeof(in)); + ExpectNotNull(cip = EVP_CIPHER_CTX_iv(ctx)); + ExpectIntEQ(XMEMCMP(cip, enc + 16, AES_BLOCK_SIZE), 0); + /* EVP_CIPHER_CTX_get_iv() is a wolfSSL addition rather than an + * OpenSSL API. It reports the IV as it was set, and reading the + * working value through the new accessor does not disturb it. */ + ExpectIntEQ(EVP_CIPHER_CTX_get_iv(ctx, got, (int)sizeof(got)), 1); + ExpectIntEQ(XMEMCMP(got, iv, AES_BLOCK_SIZE), 0); + EVP_CIPHER_CTX_free(ctx); + ctx = NULL; + } + + + /* Writing a saved chaining value back through the mutable pointer makes + * the next block encrypt as if the context had never moved past it. This + * is how openssh restores cipher state, and OpenSSL behaves the same. */ + { + byte in[16]; + byte one[32]; + byte two[32]; + byte saved[AES_BLOCK_SIZE]; + int outl = 0; + + XMEMSET(in, 0x5A, sizeof(in)); + ExpectNotNull(ctx = EVP_CIPHER_CTX_new()); + ExpectIntEQ(EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv), + 1); + ExpectIntEQ(EVP_CIPHER_CTX_set_padding(ctx, 0), 1); + ExpectIntEQ(EVP_EncryptUpdate(ctx, one, &outl, in, (int)sizeof(in)), 1); + ExpectNotNull(cip = EVP_CIPHER_CTX_iv(ctx)); + XMEMCPY(saved, cip, AES_BLOCK_SIZE); + ExpectIntEQ(EVP_EncryptUpdate(ctx, one, &outl, in, (int)sizeof(in)), 1); + EVP_CIPHER_CTX_free(ctx); + ctx = NULL; + + ExpectNotNull(ctx = EVP_CIPHER_CTX_new()); + ExpectIntEQ(EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv), + 1); + ExpectIntEQ(EVP_CIPHER_CTX_set_padding(ctx, 0), 1); + ExpectNotNull(mut = EVP_CIPHER_CTX_iv_noconst(ctx)); + XMEMCPY(mut, saved, AES_BLOCK_SIZE); + ExpectIntEQ(EVP_EncryptUpdate(ctx, two, &outl, in, (int)sizeof(in)), 1); + ExpectIntEQ(XMEMCMP(one, two, AES_BLOCK_SIZE), 0); + EVP_CIPHER_CTX_free(ctx); + ctx = NULL; + } +#endif + return EXPECT_RESULT(); +} + +/* Test that a size query cannot answer with a negative length. + * + * EVP_DecryptUpdate() with a NULL output is how callers size a buffer. An + * input too short to be a wrapped key must be refused, as OpenSSL refuses it, + * rather than reporting inl - 8. + */ +int test_wolfSSL_EVP_aes_wrap_short_input(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_EVP_AES_KEYWRAP) && defined(WOLFSSL_AES_128) \ + && defined(OPENSSL_EXTRA) + EVP_CIPHER_CTX* ctx = NULL; + byte kek[16]; + byte in[32]; + byte out[64]; + int outl; + int i; + + XMEMSET(kek, 0, sizeof(kek)); + XMEMSET(in, 0, sizeof(in)); + + /* Every length below the RFC 3394 minimum of three semiblocks. */ + for (i = 1; i < 24; i++) { + ExpectNotNull(ctx = EVP_CIPHER_CTX_new()); + ExpectIntEQ(EVP_DecryptInit_ex(ctx, EVP_aes_128_wrap(), NULL, kek, + NULL), 1); + outl = 12345; + ExpectIntNE(EVP_DecryptUpdate(ctx, NULL, &outl, in, i), 1); + ExpectIntEQ(outl, 0); + ExpectIntNE(EVP_DecryptUpdate(ctx, out, &outl, in, i), 1); + EVP_CIPHER_CTX_free(ctx); + ctx = NULL; + } + + /* A length that is not a whole number of semiblocks is refused too. */ + ExpectNotNull(ctx = EVP_CIPHER_CTX_new()); + ExpectIntEQ(EVP_DecryptInit_ex(ctx, EVP_aes_128_wrap(), NULL, kek, NULL), + 1); + outl = 12345; + ExpectIntNE(EVP_DecryptUpdate(ctx, NULL, &outl, in, 25), 1); + EVP_CIPHER_CTX_free(ctx); + + /* The smallest valid wrapped key still reports its size. */ + ExpectNotNull(ctx = EVP_CIPHER_CTX_new()); + ExpectIntEQ(EVP_DecryptInit_ex(ctx, EVP_aes_128_wrap(), NULL, kek, NULL), + 1); + outl = 0; + ExpectIntEQ(EVP_DecryptUpdate(ctx, NULL, &outl, in, 24), 1); + ExpectIntEQ(outl, 16); + EVP_CIPHER_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} + +#if defined(WOLFSSL_EVP_AES_KEYWRAP) && defined(OPENSSL_EXTRA) && \ + defined(WOLFSSL_AES_128) +/* Wrap or unwrap in one shot with the given integrity check value, which may + * be NULL to ask for the RFC default. Returns the output length, or -1 if the + * operation was rejected. */ +static int aesWrapWithIv(const WOLFSSL_EVP_CIPHER* cipher, int enc, + const byte* kek, const byte* iv, const byte* in, int inLen, byte* out) +{ + EVP_CIPHER_CTX* ctx; + int outLen = 0; + int rc; + + ctx = EVP_CIPHER_CTX_new(); + if (ctx == NULL) { + return -1; + } + if (EVP_CipherInit_ex(ctx, cipher, NULL, kek, iv, enc) != 1) { + EVP_CIPHER_CTX_free(ctx); + return -1; + } + rc = EVP_CipherUpdate(ctx, out, &outLen, in, inLen); + EVP_CIPHER_CTX_free(ctx); + + return (rc == 1) ? outLen : -1; +} +#endif + +/* A caller-supplied integrity check value has to be used as given. It cannot + * be recovered from the value itself, because all-zero is a legitimate choice + * that must stay distinct from asking for the RFC default - wrapping with one + * has to differ from the default, and unwrapping it with the default has to be + * refused. OpenSSL 3.5 was the reference for both. */ +int test_wolfSSL_EVP_aes_wrap_icv(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_EVP_AES_KEYWRAP) && defined(OPENSSL_EXTRA) && \ + defined(WOLFSSL_AES_128) + byte kek[16]; + byte data[32]; + byte deflt[64]; + byte custom[64]; + byte back[64]; + byte zeroIcv[8]; + int i; + int defLen = 0; + int cusLen = 0; + + XMEMSET(kek, 0, sizeof(kek)); + XMEMSET(zeroIcv, 0, sizeof(zeroIcv)); + for (i = 0; i < (int)sizeof(data); i++) { + data[i] = (byte)i; + } + + /* An explicit all-zero ICV is not the default one. */ + ExpectIntEQ(defLen = aesWrapWithIv(EVP_aes_128_wrap(), 1, kek, NULL, data, + (int)sizeof(data), deflt), (int)sizeof(data) + 8); + ExpectIntEQ(cusLen = aesWrapWithIv(EVP_aes_128_wrap(), 1, kek, zeroIcv, + data, (int)sizeof(data), custom), (int)sizeof(data) + 8); + ExpectIntNE(XMEMCMP(deflt, custom, (size_t)sizeof(data) + 8), 0); + + /* It round trips with itself and is refused by the default. */ + ExpectIntEQ(aesWrapWithIv(EVP_aes_128_wrap(), 0, kek, zeroIcv, custom, + cusLen, back), (int)sizeof(data)); + ExpectIntEQ(XMEMCMP(back, data, sizeof(data)), 0); + ExpectIntEQ(aesWrapWithIv(EVP_aes_128_wrap(), 0, kek, NULL, custom, + cusLen, back), -1); + + /* A cleaned context has no integrity check value again, so reusing one + * that had an explicit ICV goes back to the default. */ + { + EVP_CIPHER_CTX* ctx = NULL; + int againLen = 0; + + ExpectNotNull(ctx = EVP_CIPHER_CTX_new()); + ExpectIntEQ(EVP_EncryptInit_ex(ctx, EVP_aes_128_wrap(), NULL, kek, + zeroIcv), 1); + ExpectIntEQ(EVP_EncryptUpdate(ctx, back, &againLen, data, + (int)sizeof(data)), 1); + ExpectIntEQ(EVP_CIPHER_CTX_reset(ctx), 1); + ExpectIntEQ(EVP_EncryptInit_ex(ctx, EVP_aes_128_wrap(), NULL, kek, + NULL), 1); + XMEMSET(back, 0, sizeof(back)); + ExpectIntEQ(EVP_EncryptUpdate(ctx, back, &againLen, data, + (int)sizeof(data)), 1); + ExpectIntEQ(againLen, defLen); + ExpectIntEQ(XMEMCMP(back, deflt, (size_t)defLen), 0); + EVP_CIPHER_CTX_free(ctx); + } + +#ifdef WOLFSSL_AES_KEYWRAP_PADDING + { + /* The padded form's 4 byte AIV replaces the constant half only, the + * low half always carrying the length, so it is given the 4 bytes + * that EVP_CIPHER_CTX_iv_length() reports for these ciphers. */ + static const byte aiv[4] = { 0xa1, 0xb2, 0xc3, 0xd4 }; + const int padLen = 20; + + ExpectIntEQ(defLen = aesWrapWithIv(EVP_aes_128_wrap_pad(), 1, kek, + NULL, data, padLen, deflt), 32); + ExpectIntEQ(cusLen = aesWrapWithIv(EVP_aes_128_wrap_pad(), 1, kek, + aiv, data, padLen, custom), 32); + ExpectIntNE(XMEMCMP(deflt, custom, 32), 0); + + ExpectIntEQ(aesWrapWithIv(EVP_aes_128_wrap_pad(), 0, kek, aiv, custom, + cusLen, back), padLen); + ExpectIntEQ(XMEMCMP(back, data, (size_t)padLen), 0); + ExpectIntEQ(aesWrapWithIv(EVP_aes_128_wrap_pad(), 0, kek, NULL, custom, + cusLen, back), -1); + } +#endif +#endif + return EXPECT_RESULT(); +} + +/* Test the IV length reported for the key wrap ciphers. + * + * The unpadded form takes the whole 8 byte RFC 3394 integrity check value. + * The RFC 5649 AIV is 4 bytes of constant followed by the message length, so + * only the constant half can be supplied. OpenSSL reports these same two + * lengths, and a caller sizing a buffer from them must not be over-read. + */ +int test_wolfSSL_EVP_aes_wrap_iv_length(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_EVP_AES_KEYWRAP) && defined(OPENSSL_EXTRA) + EVP_CIPHER_CTX* ctx = NULL; + byte kek[32]; + byte iv[8]; + size_t i; + static const struct { + const char* name; + int len; + } wrap[] = { +#ifdef WOLFSSL_AES_128 + { "id-aes128-wrap", 8 }, +#endif +#ifdef WOLFSSL_AES_192 + { "id-aes192-wrap", 8 }, +#endif +#ifdef WOLFSSL_AES_256 + { "id-aes256-wrap", 8 }, +#endif +#ifdef WOLFSSL_AES_KEYWRAP_PADDING +#ifdef WOLFSSL_AES_128 + { "id-aes128-wrap-pad", 4 }, +#endif +#ifdef WOLFSSL_AES_256 + { "id-aes256-wrap-pad", 4 }, +#endif +#endif + { NULL, 0 } + }; + + XMEMSET(kek, 0, sizeof(kek)); + XMEMSET(iv, 0xA6, sizeof(iv)); + + for (i = 0; wrap[i].name != NULL; i++) { + const EVP_CIPHER* c = NULL; + + ExpectNotNull(c = EVP_get_cipherbyname(wrap[i].name)); + ExpectIntEQ(EVP_CIPHER_iv_length(c), wrap[i].len); + + /* The context agrees, so EVP_CIPHER_CTX_get_iv() accepts that + * length and no other. */ + ExpectNotNull(ctx = EVP_CIPHER_CTX_new()); + ExpectIntEQ(EVP_EncryptInit_ex(ctx, c, NULL, kek, iv), 1); + ExpectIntEQ(EVP_CIPHER_CTX_iv_length(ctx), wrap[i].len); + ExpectIntEQ(EVP_CIPHER_CTX_get_iv(ctx, iv, wrap[i].len), 1); + ExpectIntNE(EVP_CIPHER_CTX_get_iv(ctx, iv, wrap[i].len + 1), 1); + EVP_CIPHER_CTX_free(ctx); + ctx = NULL; + } +#endif + return EXPECT_RESULT(); +} diff --git a/tests/api/test_evp_cipher.h b/tests/api/test_evp_cipher.h index 659fa5abf54..58cdec31e00 100644 --- a/tests/api/test_evp_cipher.h +++ b/tests/api/test_evp_cipher.h @@ -25,9 +25,15 @@ #include int test_wolfSSL_EVP_CIPHER_CTX(void); +int test_wolfSSL_EVP_aes_wrap(void); +int test_wolfSSL_EVP_aes_wrap_pad(void); +int test_wolfSSL_EVP_aes_wrap_short_input(void); +int test_wolfSSL_EVP_aes_wrap_iv_length(void); +int test_wolfSSL_EVP_aes_wrap_icv(void); int test_wolfSSL_EVP_CIPHER_CTX_iv_length(void); int test_wolfSSL_EVP_CIPHER_CTX_key_length(void); int test_wolfSSL_EVP_CIPHER_CTX_set_iv(void); +int test_wolfSSL_EVP_CIPHER_CTX_iv(void); int test_wolfSSL_EVP_get_cipherbynid(void); int test_wolfSSL_EVP_CIPHER_block_size(void); int test_wolfSSL_EVP_CIPHER_iv_length(void); @@ -68,10 +74,16 @@ int test_evp_cipher_pkcs7_pad_zero(void); int test_evp_cipher_aead_aad_overflow(void); #define TEST_EVP_CIPHER_DECLS \ - TEST_DECL_GROUP("evp_cipher", test_wolfSSL_EVP_CIPHER_CTX), \ + TEST_DECL_GROUP("evp_cipher", test_wolfSSL_EVP_CIPHER_CTX), \ + TEST_DECL_GROUP("evp_cipher", test_wolfSSL_EVP_aes_wrap), \ + TEST_DECL_GROUP("evp_cipher", test_wolfSSL_EVP_aes_wrap_pad), \ + TEST_DECL_GROUP("evp_cipher", test_wolfSSL_EVP_aes_wrap_short_input), \ + TEST_DECL_GROUP("evp_cipher", test_wolfSSL_EVP_aes_wrap_iv_length), \ + TEST_DECL_GROUP("evp_cipher", test_wolfSSL_EVP_aes_wrap_icv), \ TEST_DECL_GROUP("evp_cipher", test_wolfSSL_EVP_CIPHER_CTX_iv_length), \ TEST_DECL_GROUP("evp_cipher", test_wolfSSL_EVP_CIPHER_CTX_key_length), \ TEST_DECL_GROUP("evp_cipher", test_wolfSSL_EVP_CIPHER_CTX_set_iv), \ + TEST_DECL_GROUP("evp_cipher", test_wolfSSL_EVP_CIPHER_CTX_iv), \ TEST_DECL_GROUP("evp_cipher", test_wolfSSL_EVP_get_cipherbynid), \ TEST_DECL_GROUP("evp_cipher", test_wolfSSL_EVP_CIPHER_block_size), \ TEST_DECL_GROUP("evp_cipher", test_wolfSSL_EVP_CIPHER_iv_length), \ diff --git a/tests/api/test_evp_digest.c b/tests/api/test_evp_digest.c index e771de9e312..ad56042d921 100644 --- a/tests/api/test_evp_digest.c +++ b/tests/api/test_evp_digest.c @@ -183,6 +183,58 @@ int test_EVP_blake2(void) return EXPECT_RESULT(); } +/* Test the null message digest. + * + * openldap's RFC 5929 channel binding compares the certificate's signature + * digest against EVP_md_null()/EVP_md2()/EVP_md4()/EVP_md5() to downgrade weak + * digests to SHA-256, so the value must exist and be stable to compare. + */ +int test_wolfSSL_EVP_md_null(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) + const EVP_MD* md = NULL; + EVP_MD_CTX* ctx = NULL; + + ExpectNotNull(md = EVP_md_null()); + /* Identity comparison is the whole point - the value must not change. */ + ExpectPtrEq(EVP_md_null(), md); + + /* Distinct from every real digest. */ +#ifndef NO_SHA + /* Only where SHA-1 is built: a lean build has no EVP_sha1() to compare + * against. */ + ExpectPtrNE(md, EVP_sha1()); +#endif + ExpectPtrNE(md, EVP_sha256()); +#ifndef NO_MD5 + ExpectPtrNE(md, EVP_md5()); +#endif +#ifndef OPENSSL_NO_MD4 + ExpectPtrNE(md, EVP_md4()); +#endif + + /* Same answers OpenSSL gives: zero length, NID_undef. */ + ExpectIntEQ(EVP_MD_size(md), 0); + ExpectIntEQ(EVP_MD_type(md), NID_undef); + + /* Not reachable by name, so it cannot shadow a real digest. */ + ExpectNull(EVP_get_digestbyname("NULL")); + + /* Hashing with it is not supported, and is refused rather than quietly + * producing nothing. */ + ExpectNotNull(ctx = EVP_MD_CTX_new()); + ExpectIntNE(EVP_DigestInit_ex(ctx, md, NULL), 1); + EVP_MD_CTX_free(ctx); + + /* MD2 has no EVP wrapper in wolfSSL, so callers must see it as absent. */ +#ifndef OPENSSL_NO_MD2 + Fail(("OPENSSL_NO_MD2 to be defined"), ("it was not")); +#endif +#endif + return EXPECT_RESULT(); +} + int test_wolfSSL_EVP_md4(void) { EXPECT_DECLS; diff --git a/tests/api/test_evp_digest.h b/tests/api/test_evp_digest.h index 0dcb0157fe5..dc967eb1ec3 100644 --- a/tests/api/test_evp_digest.h +++ b/tests/api/test_evp_digest.h @@ -28,6 +28,7 @@ int test_wolfSSL_EVP_shake128(void); int test_wolfSSL_EVP_shake256(void); int test_wolfSSL_EVP_sm3(void); int test_EVP_blake2(void); +int test_wolfSSL_EVP_md_null(void); int test_wolfSSL_EVP_md4(void); int test_wolfSSL_EVP_ripemd160(void); int test_wolfSSL_EVP_get_digestbynid(void); @@ -44,6 +45,7 @@ int test_wolfSSL_EVP_MD_size(void); TEST_DECL_GROUP("evp_digest", test_wolfSSL_EVP_shake256), \ TEST_DECL_GROUP("evp_digest", test_wolfSSL_EVP_sm3), \ TEST_DECL_GROUP("evp_digest", test_EVP_blake2), \ + TEST_DECL_GROUP("evp_digest", test_wolfSSL_EVP_md_null), \ TEST_DECL_GROUP("evp_digest", test_wolfSSL_EVP_md4), \ TEST_DECL_GROUP("evp_digest", test_wolfSSL_EVP_ripemd160), \ TEST_DECL_GROUP("evp_digest", test_wolfSSL_EVP_get_digestbynid), \ diff --git a/tests/api/test_evp_pkey.c b/tests/api/test_evp_pkey.c index 9414b943fda..97efca016a2 100644 --- a/tests/api/test_evp_pkey.c +++ b/tests/api/test_evp_pkey.c @@ -30,12 +30,16 @@ #include #include +#include #ifdef WOLFSSL_HAVE_MLDSA #include #endif #ifdef HAVE_CURVE25519 #include #endif +#ifdef HAVE_CURVE448 + #include +#endif #include #include @@ -3838,3 +3842,469 @@ int test_wolfSSL_CTX_use_PrivateKey_pkcs8_repopulate(void) #endif return EXPECT_RESULT(); } + +#if ((defined(HAVE_CURVE25519) && defined(HAVE_CURVE25519_KEY_EXPORT)) || \ + (defined(HAVE_CURVE448) && defined(HAVE_CURVE448_KEY_EXPORT))) && \ + defined(OPENSSL_EXTRA) && !defined(NO_FILESYSTEM) && !defined(NO_BIO) +/* Check a Montgomery curve key file is read the way RFC 8410 defines it. + * + * The value those encodings carry is the little-endian scalar of RFC 7748, so + * what the raw getter hands back has to equal the trailing key-sized bytes of + * the DER holding the same key. Reading the value with the other endianness + * yields a different - and differently clamped - key, which neither a return + * value nor a length check would notice. + * + * @param [in] pemFile PEM file to read. + * @param [in] derFile DER encoding of the same key. + * @param [in] keySz Size of the raw key in bytes. + * @param [in] priv 1 to read a private key, 0 to read a public key. + * @param [in] useBio 1 to read through a BIO, 0 to read from the file. + * The BIO reader keeps its own copy of the key type + * mapping, so it is exercised rather than assumed to + * follow the file reader. + * @return TEST_SUCCESS on success. + */ +static int test_montgomery_key_file(const char* pemFile, const char* derFile, + int keySz, int priv, int useBio) +{ + EXPECT_DECLS; + XFILE f = XBADFILE; + EVP_PKEY* pkey = NULL; + byte der[128]; + byte raw[64]; + int derSz = 0; + size_t rawLen = sizeof(raw); + + ExpectTrue((f = XFOPEN(derFile, "rb")) != XBADFILE); + if (f != XBADFILE) { + derSz = (int)XFREAD(der, 1, sizeof(der), f); + XFCLOSE(f); + } + ExpectIntGT(derSz, keySz); + + if (useBio) { + WOLFSSL_BIO* bio = NULL; + + ExpectNotNull(bio = wolfSSL_BIO_new_file(pemFile, "rb")); + if (priv) { + ExpectNotNull(pkey = wolfSSL_PEM_read_bio_PrivateKey(bio, NULL, + NULL, NULL)); + } + else { + ExpectNotNull(pkey = wolfSSL_PEM_read_bio_PUBKEY(bio, NULL, NULL, + NULL)); + } + wolfSSL_BIO_free(bio); + } + else { + f = XBADFILE; + ExpectTrue((f = XFOPEN(pemFile, "rb")) != XBADFILE); + if (f != XBADFILE) { + if (priv) { + ExpectNotNull(pkey = wolfSSL_PEM_read_PrivateKey(f, NULL, NULL, + NULL)); + } + else { + ExpectNotNull(pkey = wolfSSL_PEM_read_PUBKEY(f, NULL, NULL, + NULL)); + } + XFCLOSE(f); + } + } + + if (priv) { + ExpectIntEQ(EVP_PKEY_get_raw_private_key(pkey, raw, &rawLen), 1); + } + else { + ExpectIntEQ(EVP_PKEY_get_raw_public_key(pkey, raw, &rawLen), 1); + } + ExpectIntEQ((int)rawLen, keySz); + if (derSz > keySz) { + ExpectIntEQ(XMEMCMP(raw, der + derSz - keySz, (size_t)keySz), 0); + } + + wolfSSL_EVP_PKEY_free(pkey); + return EXPECT_RESULT(); +} +#endif + +int test_wolfSSL_EVP_PKEY_get_raw_key(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) + unsigned char out[128]; + size_t len = 0; +#if (defined(HAVE_CURVE25519) && defined(HAVE_CURVE25519_KEY_EXPORT)) || \ + (defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_EXPORT)) || \ + (defined(HAVE_CURVE448) && defined(HAVE_CURVE448_KEY_EXPORT)) || \ + (defined(HAVE_ED448) && defined(HAVE_ED448_KEY_EXPORT)) + EVP_PKEY* pkey = NULL; +#endif + +#if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_EXPORT) + { + /* RFC 8032 sec. 7.1 test 1: the public key really is derived from + * this seed, so an equality check catches a getter that hands back + * the wrong half or a stale cache. */ + static const byte edPriv[] = { + 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 + }; + static const byte edPub[] = { + 0xd7,0x5a,0x98,0x01,0x82,0xb1,0x0a,0xb7, + 0xd5,0x4b,0xfe,0xd3,0xc9,0x64,0x07,0x3a, + 0x0e,0xe1,0x72,0xf3,0xda,0xa6,0x23,0x25, + 0xaf,0x02,0x1a,0x68,0xf7,0x07,0x51,0x1a + }; + + ExpectNotNull(pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, + NULL, edPriv, sizeof(edPriv))); + /* A NULL buffer asks only for the size. */ + len = 0; + ExpectIntEQ(EVP_PKEY_get_raw_private_key(pkey, NULL, &len), 1); + ExpectIntEQ((int)len, (int)sizeof(edPriv)); + /* Too small a buffer fails and reports what is needed. */ + len = 8; + ExpectIntEQ(EVP_PKEY_get_raw_private_key(pkey, out, &len), 0); + ExpectIntEQ((int)len, (int)sizeof(edPriv)); + /* The seed comes back unchanged. */ + XMEMSET(out, 0, sizeof(out)); + len = sizeof(out); + ExpectIntEQ(EVP_PKEY_get_raw_private_key(pkey, out, &len), 1); + ExpectIntEQ((int)len, (int)sizeof(edPriv)); + ExpectIntEQ(XMEMCMP(out, edPriv, sizeof(edPriv)), 0); + /* And the public key is the one RFC 8032 derives from it, not the + * seed handed back a second time. */ + XMEMSET(out, 0, sizeof(out)); + len = sizeof(out); + ExpectIntEQ(EVP_PKEY_get_raw_public_key(pkey, out, &len), 1); + ExpectIntEQ((int)len, (int)sizeof(edPub)); + ExpectIntEQ(XMEMCMP(out, edPub, sizeof(edPub)), 0); + EVP_PKEY_free(pkey); + pkey = NULL; + + /* A key built from the public alone reports it, and has no private + * to report. */ + ExpectNotNull(pkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, + NULL, edPub, sizeof(edPub))); + XMEMSET(out, 0, sizeof(out)); + len = sizeof(out); + ExpectIntEQ(EVP_PKEY_get_raw_public_key(pkey, out, &len), 1); + ExpectIntEQ(XMEMCMP(out, edPub, sizeof(edPub)), 0); + len = sizeof(out); + ExpectIntEQ(EVP_PKEY_get_raw_private_key(pkey, out, &len), 0); + EVP_PKEY_free(pkey); + pkey = NULL; + } +#endif + +#if defined(HAVE_CURVE25519) && defined(HAVE_CURVE25519_KEY_EXPORT) + { + /* RFC 7748 sec. 6.1: Alice's scalar and her public key. */ + static const byte xPriv[] = { + 0x77,0x07,0x6d,0x0a,0x73,0x18,0xa5,0x7d, + 0x3c,0x16,0xc1,0x72,0x51,0xb2,0x66,0x45, + 0xdf,0x4c,0x2f,0x87,0xeb,0xc0,0x99,0x2a, + 0xb1,0x77,0xfb,0xa5,0x1d,0xb9,0x2c,0x2a + }; + static const byte xPub[] = { + 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 + }; + + ExpectNotNull(pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_X25519, + NULL, xPriv, sizeof(xPriv))); + /* wolfCrypt clamps the scalar on import, so this only matches if the + * getter reports what the caller set rather than re-exporting it. */ + XMEMSET(out, 0, sizeof(out)); + len = sizeof(out); + ExpectIntEQ(EVP_PKEY_get_raw_private_key(pkey, out, &len), 1); + ExpectIntEQ((int)len, (int)sizeof(xPriv)); + ExpectIntEQ(XMEMCMP(out, xPriv, sizeof(xPriv)), 0); + /* The public key is the one RFC 7748 derives. */ + XMEMSET(out, 0, sizeof(out)); + len = sizeof(out); + ExpectIntEQ(EVP_PKEY_get_raw_public_key(pkey, out, &len), 1); + ExpectIntEQ((int)len, (int)sizeof(xPub)); + ExpectIntEQ(XMEMCMP(out, xPub, sizeof(xPub)), 0); + EVP_PKEY_free(pkey); + pkey = NULL; + + ExpectNotNull(pkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_X25519, + NULL, xPub, sizeof(xPub))); + XMEMSET(out, 0, sizeof(out)); + len = sizeof(out); + ExpectIntEQ(EVP_PKEY_get_raw_public_key(pkey, out, &len), 1); + ExpectIntEQ(XMEMCMP(out, xPub, sizeof(xPub)), 0); + len = sizeof(out); + ExpectIntEQ(EVP_PKEY_get_raw_private_key(pkey, out, &len), 0); + EVP_PKEY_free(pkey); + pkey = NULL; + } +#endif + +#if defined(HAVE_ED448) && defined(HAVE_ED448_KEY_EXPORT) + { + /* RFC 8032 sec. 7.4, the 57 octet blank-message key pair. */ + static const byte ed448Priv[] = { + 0x6c,0x82,0xa5,0x62,0xcb,0x80,0x8d,0x10, + 0xd6,0x32,0xbe,0x89,0xc8,0x51,0x3e,0xbf, + 0x6c,0x92,0x9f,0x34,0xdd,0xfa,0x8c,0x9f, + 0x63,0xc9,0x96,0x0e,0xf6,0xe3,0x48,0xa3, + 0x52,0x8c,0x8a,0x3f,0xcc,0x2f,0x04,0x4e, + 0x39,0xa3,0xfc,0x5b,0x94,0x49,0x2f,0x8f, + 0x03,0x2e,0x75,0x49,0xa2,0x00,0x98,0xf9, + 0x5b + }; + static const byte ed448Pub[] = { + 0x5f,0xd7,0x44,0x9b,0x59,0xb4,0x61,0xfd, + 0x2c,0xe7,0x87,0xec,0x61,0x6a,0xd4,0x6a, + 0x1d,0xa1,0x34,0x24,0x85,0xa7,0x0e,0x1f, + 0x8a,0x0e,0xa7,0x5d,0x80,0xe9,0x67,0x78, + 0xed,0xf1,0x24,0x76,0x9b,0x46,0xc7,0x06, + 0x1b,0xd6,0x78,0x3d,0xf1,0xe5,0x0f,0x6c, + 0xd1,0xfa,0x1a,0xbe,0xaf,0xe8,0x25,0x61, + 0x80 + }; + + ExpectNotNull(pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_ED448, + NULL, ed448Priv, sizeof(ed448Priv))); + XMEMSET(out, 0, sizeof(out)); + len = sizeof(out); + ExpectIntEQ(EVP_PKEY_get_raw_private_key(pkey, out, &len), 1); + ExpectIntEQ((int)len, (int)sizeof(ed448Priv)); + ExpectIntEQ(XMEMCMP(out, ed448Priv, sizeof(ed448Priv)), 0); + XMEMSET(out, 0, sizeof(out)); + len = sizeof(out); + ExpectIntEQ(EVP_PKEY_get_raw_public_key(pkey, out, &len), 1); + ExpectIntEQ((int)len, (int)sizeof(ed448Pub)); + ExpectIntEQ(XMEMCMP(out, ed448Pub, sizeof(ed448Pub)), 0); + EVP_PKEY_free(pkey); + pkey = NULL; + + /* A key built from the public alone reports it, and has no private to + * report. With no private key to derive from, the public key is read + * off the key itself - the other of the two branches. */ + ExpectNotNull(pkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_ED448, + NULL, ed448Pub, sizeof(ed448Pub))); + XMEMSET(out, 0, sizeof(out)); + len = sizeof(out); + ExpectIntEQ(EVP_PKEY_get_raw_public_key(pkey, out, &len), 1); + ExpectIntEQ(XMEMCMP(out, ed448Pub, sizeof(ed448Pub)), 0); + len = sizeof(out); + ExpectIntEQ(EVP_PKEY_get_raw_private_key(pkey, out, &len), 0); + EVP_PKEY_free(pkey); + pkey = NULL; + } + +#if !defined(NO_FILESYSTEM) && !defined(NO_BIO) + { + /* The Ed25519 case above covers a key decoded from a file rather than + * imported raw; Ed448 needs the same, because it exports through its + * own branch. The public key is derived from the private one, so it + * has to be right on every call and not only the first - the same + * check the Ed25519 case makes. */ + XFILE f448 = XBADFILE; + EVP_PKEY* filePkey448 = NULL; + unsigned char pub1[57]; + unsigned char pub2[57]; + unsigned char zeros[57]; + size_t len1 = sizeof(pub1); + size_t len2 = sizeof(pub2); + + XMEMSET(zeros, 0, sizeof(zeros)); + ExpectTrue((f448 = XFOPEN("./certs/ed448/server-ed448-priv.pem", + "rb")) != XBADFILE); + if (f448 != XBADFILE) { + ExpectNotNull(filePkey448 = wolfSSL_PEM_read_PrivateKey(f448, NULL, + NULL, NULL)); + XFCLOSE(f448); + } + len = sizeof(out); + ExpectIntEQ(EVP_PKEY_get_raw_private_key(filePkey448, out, &len), 1); + ExpectIntEQ((int)len, 57); + ExpectIntEQ(EVP_PKEY_get_raw_public_key(filePkey448, pub1, &len1), 1); + ExpectIntEQ((int)len1, 57); + ExpectIntEQ(EVP_PKEY_get_raw_public_key(filePkey448, pub2, &len2), 1); + ExpectIntEQ((int)len2, 57); + ExpectIntEQ(XMEMCMP(pub1, pub2, sizeof(pub1)), 0); + ExpectIntNE(XMEMCMP(pub1, zeros, sizeof(pub1)), 0); + EVP_PKEY_free(filePkey448); + } +#endif +#endif + +#if defined(HAVE_CURVE448) && defined(HAVE_CURVE448_KEY_EXPORT) + { + /* RFC 7748 sec. 6.2, party A's key pair. The RFC prints these in + * the little-endian wire order the raw key API takes, so the bytes + * below read the same as the document. + * + * wolfCrypt's own curve448_test() carries the same pair byte + * reversed, because it feeds them to wc_curve448_import_private_raw() + * whose default is big-endian; do not copy those arrays here. */ + static const byte x448Priv[] = { + 0x9a,0x8f,0x49,0x25,0xd1,0x51,0x9f,0x57, + 0x75,0xcf,0x46,0xb0,0x4b,0x58,0x00,0xd4, + 0xee,0x9e,0xe8,0xba,0xe8,0xbc,0x55,0x65, + 0xd4,0x98,0xc2,0x8d,0xd9,0xc9,0xba,0xf5, + 0x74,0xa9,0x41,0x97,0x44,0x89,0x73,0x91, + 0x00,0x63,0x82,0xa6,0xf1,0x27,0xab,0x1d, + 0x9a,0xc2,0xd8,0xc0,0xa5,0x98,0x72,0x6b + }; + static const byte x448Pub[] = { + 0x9b,0x08,0xf7,0xcc,0x31,0xb7,0xe3,0xe6, + 0x7d,0x22,0xd5,0xae,0xa1,0x21,0x07,0x4a, + 0x27,0x3b,0xd2,0xb8,0x3d,0xe0,0x9c,0x63, + 0xfa,0xa7,0x3d,0x2c,0x22,0xc5,0xd9,0xbb, + 0xc8,0x36,0x64,0x72,0x41,0xd9,0x53,0xd4, + 0x0c,0x5b,0x12,0xda,0x88,0x12,0x0d,0x53, + 0x17,0x7f,0x80,0xe5,0x32,0xc4,0x1f,0xa0 + }; + + ExpectNotNull(pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_X448, NULL, + x448Priv, sizeof(x448Priv))); + /* As with X25519 the scalar is clamped on import, so this only holds + * if the getter reports what the caller set. */ + XMEMSET(out, 0, sizeof(out)); + len = sizeof(out); + ExpectIntEQ(EVP_PKEY_get_raw_private_key(pkey, out, &len), 1); + ExpectIntEQ((int)len, (int)sizeof(x448Priv)); + ExpectIntEQ(XMEMCMP(out, x448Priv, sizeof(x448Priv)), 0); + /* And the public key is the one RFC 7748 derives from that scalar. */ + XMEMSET(out, 0, sizeof(out)); + len = sizeof(out); + ExpectIntEQ(EVP_PKEY_get_raw_public_key(pkey, out, &len), 1); + ExpectIntEQ((int)len, (int)sizeof(x448Pub)); + ExpectIntEQ(XMEMCMP(out, x448Pub, sizeof(x448Pub)), 0); + EVP_PKEY_free(pkey); + pkey = NULL; + + /* A key built from the public alone reports it, and has no private + * to report. */ + ExpectNotNull(pkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_X448, NULL, + x448Pub, sizeof(x448Pub))); + XMEMSET(out, 0, sizeof(out)); + len = sizeof(out); + ExpectIntEQ(EVP_PKEY_get_raw_public_key(pkey, out, &len), 1); + ExpectIntEQ(XMEMCMP(out, x448Pub, sizeof(x448Pub)), 0); + len = sizeof(out); + ExpectIntEQ(EVP_PKEY_get_raw_private_key(pkey, out, &len), 0); + EVP_PKEY_free(pkey); + pkey = NULL; + } +#endif + +#if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_EXPORT) && \ + !defined(NO_FILESYSTEM) && !defined(NO_BIO) + { + /* A key decoded from a file, rather than imported raw. Its + * pkey.ptr holds the DER, not raw bytes, so this covers the branch + * that exports from the wolfCrypt key. Reporting the DER here would + * hand back 48 bytes of PKCS#8 instead of the 32 byte seed. */ + XFILE f = XBADFILE; + EVP_PKEY* filePkey = NULL; + + ExpectTrue((f = XFOPEN("./certs/ed25519/server-ed25519-priv.pem", + "rb")) != XBADFILE); + if (f != XBADFILE) { + ExpectNotNull(filePkey = wolfSSL_PEM_read_PrivateKey(f, NULL, + NULL, NULL)); + XFCLOSE(f); + } + len = sizeof(out); + ExpectIntEQ(EVP_PKEY_get_raw_private_key(filePkey, out, &len), 1); + ExpectIntEQ((int)len, 32); + len = sizeof(out); + ExpectIntEQ(EVP_PKEY_get_raw_public_key(filePkey, out, &len), 1); + ExpectIntEQ((int)len, 32); + { + /* The public key is derived from the private one, and has to + * survive being asked for more than once - the usual OpenSSL + * pattern of a size query followed by a fetch asks twice. A + * derivation that marks the key as having a public part without + * storing one answers every later call with zeros, and a check of + * only the return value and the length would not see it. */ + unsigned char pub1[32]; + unsigned char pub2[32]; + unsigned char zeros[32]; + size_t len1 = sizeof(pub1); + size_t len2 = sizeof(pub2); + + XMEMSET(zeros, 0, sizeof(zeros)); + ExpectIntEQ(EVP_PKEY_get_raw_public_key(filePkey, pub1, &len1), 1); + ExpectIntEQ((int)len1, 32); + ExpectIntEQ(EVP_PKEY_get_raw_public_key(filePkey, pub2, &len2), 1); + ExpectIntEQ((int)len2, 32); + ExpectIntEQ(XMEMCMP(pub1, pub2, sizeof(pub1)), 0); + ExpectIntNE(XMEMCMP(pub1, zeros, sizeof(pub1)), 0); + } + EVP_PKEY_free(filePkey); + } +#endif + + /* A key type with no raw form is refused rather than answered with + * something else. */ +#if !defined(NO_RSA) && defined(USE_CERT_BUFFERS_2048) + { + EVP_PKEY* rsa = NULL; + const unsigned char* p = client_key_der_2048; + + ExpectNotNull(rsa = d2i_PrivateKey(EVP_PKEY_RSA, NULL, &p, + (long)sizeof_client_key_der_2048)); + len = sizeof(out); + ExpectIntEQ(EVP_PKEY_get_raw_private_key(rsa, out, &len), 0); + len = sizeof(out); + ExpectIntEQ(EVP_PKEY_get_raw_public_key(rsa, out, &len), 0); + EVP_PKEY_free(rsa); + } +#endif + + /* Bad arguments. */ + len = sizeof(out); + ExpectIntEQ(EVP_PKEY_get_raw_private_key(NULL, out, &len), 0); + ExpectIntEQ(EVP_PKEY_get_raw_public_key(NULL, out, &len), 0); +#if defined(HAVE_CURVE25519) && defined(HAVE_CURVE25519_KEY_EXPORT) + { + byte raw[32]; + XMEMSET(raw, 1, sizeof(raw)); + ExpectNotNull(pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_X25519, + NULL, raw, sizeof(raw))); + ExpectIntEQ(EVP_PKEY_get_raw_private_key(pkey, out, NULL), 0); + ExpectIntEQ(EVP_PKEY_get_raw_public_key(pkey, out, NULL), 0); + EVP_PKEY_free(pkey); + pkey = NULL; + } +#endif + +#if defined(HAVE_CURVE25519) && defined(HAVE_CURVE25519_KEY_EXPORT) && \ + !defined(NO_FILESYSTEM) && !defined(NO_BIO) + ExpectIntEQ(test_montgomery_key_file("./certs/statickeys/x25519.pem", + "./certs/statickeys/x25519.der", CURVE25519_KEYSIZE, 1, 0), + TEST_SUCCESS); + ExpectIntEQ(test_montgomery_key_file("./certs/statickeys/x25519-pub.pem", + "./certs/statickeys/x25519-pub.der", CURVE25519_KEYSIZE, 0, 0), + TEST_SUCCESS); + ExpectIntEQ(test_montgomery_key_file("./certs/statickeys/x25519.pem", + "./certs/statickeys/x25519.der", CURVE25519_KEYSIZE, 1, 1), + TEST_SUCCESS); +#endif +#if defined(HAVE_CURVE448) && defined(HAVE_CURVE448_KEY_EXPORT) && \ + !defined(NO_FILESYSTEM) && !defined(NO_BIO) + ExpectIntEQ(test_montgomery_key_file("./certs/statickeys/x448.pem", + "./certs/statickeys/x448.der", CURVE448_KEY_SIZE, 1, 0), + TEST_SUCCESS); + ExpectIntEQ(test_montgomery_key_file("./certs/statickeys/x448-pub.pem", + "./certs/statickeys/x448-pub.der", CURVE448_KEY_SIZE, 0, 0), + TEST_SUCCESS); + ExpectIntEQ(test_montgomery_key_file("./certs/statickeys/x448.pem", + "./certs/statickeys/x448.der", CURVE448_KEY_SIZE, 1, 1), + TEST_SUCCESS); +#endif +#endif + return EXPECT_RESULT(); +} diff --git a/tests/api/test_evp_pkey.h b/tests/api/test_evp_pkey.h index 8af6d24712e..2acf90ef89b 100644 --- a/tests/api/test_evp_pkey.h +++ b/tests/api/test_evp_pkey.h @@ -76,6 +76,7 @@ int test_wolfSSL_d2i_PrivateKey_mldsa(void); int test_wolfSSL_EVP_PKEY_x25519(void); int test_wolfSSL_EVP_PKEY_x448(void); int test_wolfSSL_EVP_PKEY_encoded_public_key(void); +int test_wolfSSL_EVP_PKEY_get_raw_key(void); int test_wolfSSL_d2i_PrivateKey_reuse_resets_state(void); int test_wolfSSL_CTX_use_PrivateKey_pkcs8_repopulate(void); @@ -133,6 +134,7 @@ int test_wolfSSL_CTX_use_PrivateKey_pkcs8_repopulate(void); TEST_DECL_GROUP("evp_pkey", test_wolfSSL_EVP_PKEY_x25519), \ TEST_DECL_GROUP("evp_pkey", test_wolfSSL_EVP_PKEY_x448), \ TEST_DECL_GROUP("evp_pkey", test_wolfSSL_EVP_PKEY_encoded_public_key), \ + TEST_DECL_GROUP("evp_pkey", test_wolfSSL_EVP_PKEY_get_raw_key), \ TEST_DECL_GROUP("evp_pkey", test_wolfSSL_d2i_PrivateKey_reuse_resets_state),\ TEST_DECL_GROUP("evp_pkey", \ test_wolfSSL_CTX_use_PrivateKey_pkcs8_repopulate) diff --git a/tests/api/test_ocsp.c b/tests/api/test_ocsp.c index ccac1491fb9..622777495e4 100644 --- a/tests/api/test_ocsp.c +++ b/tests/api/test_ocsp.c @@ -29,6 +29,9 @@ #include #include #include +#if defined(OPENSSL_EXTRA) +#include +#endif #if defined(HAVE_OCSP) && !defined(NO_SHA) && !defined(NO_RSA) struct ocsp_cb_ctx { @@ -2468,3 +2471,40 @@ int test_wolfIO_DecodeUrl_crlf_reject(void) return TEST_SKIPPED; } #endif /* HAVE_HTTP_CLIENT */ + +/* OCSP_crl_reason_str() maps RFC 5280 revocation reason codes to the same + * strings OpenSSL returns, and reports anything unrecognized as "(UNKNOWN)". + */ +int test_ocsp_crl_reason_str(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_ALL) && defined(HAVE_OCSP) + static const struct { + long code; + const char* str; + } reasons[] = { + { 0, "unspecified" }, + { 1, "keyCompromise" }, + { 2, "cACompromise" }, + { 3, "affiliationChanged" }, + { 4, "superseded" }, + { 5, "cessationOfOperation" }, + { 6, "certificateHold" }, + { 8, "removeFromCRL" }, + { 9, "privilegeWithdrawn" }, + { 10, "aACompromise" } + }; + /* Code 7 is unassigned; the rest are simply out of range. */ + static const long unknown[] = { -2, -1, 7, 11, 255, 0x7FFFFFFF }; + size_t i; + + for (i = 0; i < XELEM_CNT(reasons); i++) { + ExpectNotNull(OCSP_crl_reason_str(reasons[i].code)); + ExpectStrEQ(OCSP_crl_reason_str(reasons[i].code), reasons[i].str); + } + for (i = 0; i < XELEM_CNT(unknown); i++) { + ExpectStrEQ(OCSP_crl_reason_str(unknown[i]), "(UNKNOWN)"); + } +#endif + return EXPECT_RESULT(); +} diff --git a/tests/api/test_ocsp.h b/tests/api/test_ocsp.h index 5b1b4000c8b..7e56efd3d63 100644 --- a/tests/api/test_ocsp.h +++ b/tests/api/test_ocsp.h @@ -40,5 +40,6 @@ int test_ocsp_responder(void); int test_ocsp_ancestor_responder_rejected(void); int test_ocsp_forged_responder_cert_rejected(void); int test_wolfIO_DecodeUrl_crlf_reject(void); +int test_ocsp_crl_reason_str(void); #endif /* WOLFSSL_TEST_OCSP_H */ diff --git a/tests/api/test_ossl_asn1.c b/tests/api/test_ossl_asn1.c index c646141ed7d..dfd2202531a 100644 --- a/tests/api/test_ossl_asn1.c +++ b/tests/api/test_ossl_asn1.c @@ -2872,3 +2872,68 @@ int test_ASN1_strings(void) return EXPECT_RESULT(); } +/* Test writing an ASN.1 string to a BIO as hex. + * + * haproxy (src/ssl_ocsp.c) prints OCSP certificate ID fields this way. The + * wrapping and the empty-string form are matched to OpenSSL byte for byte. + */ +int test_wolfSSL_i2a_ASN1_STRING(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && !defined(NO_BIO) + WOLFSSL_BIO* bio = NULL; + WOLFSSL_ASN1_STRING* str = NULL; + unsigned char data[71]; + char out[256]; + int i; + + for (i = 0; i < (int)sizeof(data); i++) { + data[i] = (unsigned char)(i * 7 + 1); + } + + ExpectNotNull(bio = BIO_new(BIO_s_mem())); + ExpectNotNull(str = ASN1_STRING_new()); + + /* A NULL string writes nothing, a NULL BIO is an error. */ + ExpectIntEQ(i2a_ASN1_STRING(bio, NULL, 0), 0); + ExpectIntEQ(i2a_ASN1_STRING(NULL, str, 0), -1); + + /* An empty string is written as a single zero. */ + ExpectIntEQ(ASN1_STRING_set(str, data, 0), 1); + ExpectIntEQ(i2a_ASN1_STRING(bio, str, 0), 1); + XMEMSET(out, 0, sizeof(out)); + ExpectIntEQ(BIO_read(bio, out, sizeof(out) - 1), 1); + ExpectIntEQ(XMEMCMP(out, "0", 1), 0); + + /* Two bytes become four upper case hex digits. */ + ExpectIntEQ(ASN1_STRING_set(str, data, 2), 1); + ExpectIntEQ(i2a_ASN1_STRING(bio, str, 0), 4); + XMEMSET(out, 0, sizeof(out)); + ExpectIntEQ(BIO_read(bio, out, sizeof(out) - 1), 4); + ExpectIntEQ(XMEMCMP(out, "0108", 4), 0); + + /* 35 bytes is the last length that fits on one line. */ + ExpectIntEQ(ASN1_STRING_set(str, data, 35), 1); + ExpectIntEQ(i2a_ASN1_STRING(bio, str, 0), 70); + XMEMSET(out, 0, sizeof(out)); + ExpectIntEQ(BIO_read(bio, out, sizeof(out) - 1), 70); + ExpectNull(XSTRSTR(out, "\\")); + + /* 36 bytes wraps once, adding a backslash and a newline. */ + ExpectIntEQ(ASN1_STRING_set(str, data, 36), 1); + ExpectIntEQ(i2a_ASN1_STRING(bio, str, 0), 74); + XMEMSET(out, 0, sizeof(out)); + ExpectIntEQ(BIO_read(bio, out, sizeof(out) - 1), 74); + ExpectIntEQ(XMEMCMP(out + 70, "\\\n", 2), 0); + + /* 71 bytes wraps twice. */ + ExpectIntEQ(ASN1_STRING_set(str, data, 71), 1); + ExpectIntEQ(i2a_ASN1_STRING(bio, str, 0), 146); + XMEMSET(out, 0, sizeof(out)); + ExpectIntEQ(BIO_read(bio, out, sizeof(out) - 1), 146); + + ASN1_STRING_free(str); + BIO_free(bio); +#endif + return EXPECT_RESULT(); +} diff --git a/tests/api/test_ossl_asn1.h b/tests/api/test_ossl_asn1.h index 7ae66eb0672..f75c6dc2b37 100644 --- a/tests/api/test_ossl_asn1.h +++ b/tests/api/test_ossl_asn1.h @@ -31,6 +31,7 @@ int test_wolfSSL_ASN1_INTEGER_BN(void); int test_wolfSSL_ASN1_INTEGER_get_set(void); int test_wolfSSL_d2i_ASN1_INTEGER(void); int test_wolfSSL_a2i_ASN1_INTEGER(void); +int test_wolfSSL_i2a_ASN1_STRING(void); int test_wolfSSL_i2c_ASN1_INTEGER(void); int test_wolfSSL_ASN1_OBJECT(void); int test_wolfSSL_ASN1_get_object(void); @@ -70,6 +71,7 @@ int test_ASN1_strings(void); TEST_DECL_GROUP("ossl_asn1_int", test_wolfSSL_ASN1_INTEGER_get_set), \ TEST_DECL_GROUP("ossl_asn1_int", test_wolfSSL_d2i_ASN1_INTEGER), \ TEST_DECL_GROUP("ossl_asn1_int", test_wolfSSL_a2i_ASN1_INTEGER), \ + TEST_DECL_GROUP("ossl_asn1", test_wolfSSL_i2a_ASN1_STRING), \ TEST_DECL_GROUP("ossl_asn1_int", test_wolfSSL_i2c_ASN1_INTEGER) #define TEST_OSSL_ASN1_OBJECT_DECLS \ diff --git a/tests/api/test_ossl_obj.c b/tests/api/test_ossl_obj.c index 0f56cffffd0..a869650e22c 100644 --- a/tests/api/test_ossl_obj.c +++ b/tests/api/test_ossl_obj.c @@ -106,7 +106,9 @@ int test_wolfSSL_OBJ(void) #endif #ifdef HAVE_ECC "./certs/ca-ecc-cert.der", + #if (defined(HAVE_ECC384) || defined(HAVE_ALL_CURVES)) "./certs/ca-ecc384-cert.der", + #endif #endif NULL}; ASN1_OBJECT *field_name_obj = NULL; diff --git a/tests/api/test_ossl_rsa.c b/tests/api/test_ossl_rsa.c index 7c30e5ce07e..2be2edd391c 100644 --- a/tests/api/test_ossl_rsa.c +++ b/tests/api/test_ossl_rsa.c @@ -1625,7 +1625,8 @@ int test_wolfSSL_PEM_write_RSAPrivateKey(void) size_t privDerSz = sizeof_client_key_der_2048; #endif const unsigned char* der; -#ifndef NO_AES +/* Only used to encrypt the key, which needs AES-128-CBC. */ +#if !defined(NO_AES) && defined(HAVE_AES_CBC) && defined(WOLFSSL_AES_128) unsigned char passwd[] = "password"; #endif @@ -1645,7 +1646,8 @@ int test_wolfSSL_PEM_write_RSAPrivateKey(void) ExpectIntEQ(wolfSSL_PEM_write_RSAPrivateKey(stderr, rsa, NULL, NULL, 0, NULL, NULL), 1); -#if !defined(NO_AES) && defined(HAVE_AES_CBC) +#if !defined(NO_AES) && defined(HAVE_AES_CBC) && \ + defined(WOLFSSL_AES_128) ExpectIntEQ(wolfSSL_PEM_write_RSAPrivateKey(stderr, rsa, EVP_aes_128_cbc(), NULL, 0, NULL, NULL), 1); ExpectIntEQ(wolfSSL_PEM_write_RSAPrivateKey(stderr, rsa, EVP_aes_128_cbc(), @@ -1671,7 +1673,8 @@ int test_wolfSSL_PEM_write_mem_RSAPrivateKey(void) size_t privDerSz = sizeof_client_key_der_2048; #endif const unsigned char* der; -#ifndef NO_AES +/* Only used to encrypt the key, which needs AES-128-CBC. */ +#if !defined(NO_AES) && defined(HAVE_AES_CBC) && defined(WOLFSSL_AES_128) unsigned char passwd[] = "password"; #endif unsigned char* pem = NULL; @@ -1697,7 +1700,8 @@ int test_wolfSSL_PEM_write_mem_RSAPrivateKey(void) &plen), 1); XFREE(pem, NULL, DYNAMIC_TYPE_KEY); pem = NULL; -#if !defined(NO_AES) && defined(HAVE_AES_CBC) +#if !defined(NO_AES) && defined(HAVE_AES_CBC) && \ + defined(WOLFSSL_AES_128) ExpectIntEQ(wolfSSL_PEM_write_mem_RSAPrivateKey(rsa, EVP_aes_128_cbc(), NULL, 0, &pem, &plen), 1); XFREE(pem, NULL, DYNAMIC_TYPE_KEY); diff --git a/tests/api/test_ossl_x509.c b/tests/api/test_ossl_x509.c index dabbe81a29c..842f29988a6 100644 --- a/tests/api/test_ossl_x509.c +++ b/tests/api/test_ossl_x509.c @@ -29,6 +29,8 @@ #endif #include +#include +#include #include #include #include @@ -575,11 +577,21 @@ int test_wolfSSL_X509(void) ExpectIntEQ(X509_STORE_CTX_init(ctx, store, x509, NULL), SSL_SUCCESS); ExpectIntEQ(X509_verify_cert(ctx), SSL_SUCCESS); + /* The environment variable names wolfSSL_CTX_load_system_CA_certs() + * reads, which are the same names OpenSSL reports. */ + ExpectStrEQ(X509_get_default_cert_file_env(), "SSL_CERT_FILE"); + ExpectStrEQ(X509_get_default_cert_dir_env(), "SSL_CERT_DIR"); +#if defined(WOLFSSL_SYS_CA_CERTS) && !defined(NO_FILESYSTEM) && \ + !defined(_WIN32) && !defined(USE_WINDOWS_API) && !defined(__APPLE__) + /* First of the system CA directories wolfSSL would search. */ + ExpectNotNull(X509_get_default_cert_dir()); + ExpectIntGT(XSTRLEN(X509_get_default_cert_dir()), 0); +#else + ExpectStrEQ(X509_get_default_cert_dir(), ""); +#endif #ifndef NO_WOLFSSL_STUB - ExpectStrEQ(X509_get_default_cert_file_env(), ""); + /* wolfSSL has no single compiled-in CA bundle path. */ ExpectStrEQ(X509_get_default_cert_file(), ""); - ExpectStrEQ(X509_get_default_cert_dir_env(), ""); - ExpectStrEQ(X509_get_default_cert_dir(), ""); #endif ExpectNull(wolfSSL_X509_get_der(NULL, NULL)); @@ -2157,3 +2169,584 @@ int test_wolfSSL_X509_cmp(void) #endif return EXPECT_RESULT(); } + +int test_wolfSSL_X509_check_purpose(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && !defined(NO_RSA) && !defined(NO_FILESYSTEM) + WOLFSSL_X509* serverOnly = NULL; + WOLFSSL_X509* both = NULL; + WOLFSSL_X509* ca = NULL; + WOLFSSL_X509* inter = NULL; + WOLFSSL_X509* anyEku = NULL; + WOLFSSL_X509* nsType = NULL; + WOLFSSL_X509* nsCaOnly = NULL; + WOLFSSL_X509* codeSign = NULL; + WOLFSSL_X509* v1Root = NULL; +#ifdef HAVE_ECC + WOLFSSL_X509* v1SelfIssued = NULL; +#endif + + /* leaf-cert.pem: extendedKeyUsage of serverAuth only, keyUsage of + * digitalSignature + keyEncipherment. */ + ExpectNotNull(serverOnly = wolfSSL_X509_load_certificate_file( + "./certs/intermediate/untrusted_anchor/leaf-cert.pem", + WOLFSSL_FILETYPE_PEM)); + /* server-cert.pem: serverAuth + clientAuth. */ + ExpectNotNull(both = wolfSSL_X509_load_certificate_file(svrCertFile, + WOLFSSL_FILETYPE_PEM)); + ExpectNotNull(ca = wolfSSL_X509_load_certificate_file(caCertFile, + WOLFSSL_FILETYPE_PEM)); + /* ca-int-cert.pem: keyUsage but no extendedKeyUsage. */ + ExpectNotNull(inter = wolfSSL_X509_load_certificate_file( + "./certs/intermediate/ca-int-cert.pem", WOLFSSL_FILETYPE_PEM)); + /* any-eku-cert.pem: extendedKeyUsage of anyExtendedKeyUsage. */ + ExpectNotNull(anyEku = wolfSSL_X509_load_certificate_file( + "./certs/any-eku-cert.pem", WOLFSSL_FILETYPE_PEM)); + /* ns-cert-type-cert.pem: netscape-cert-type of SSL client, with an + * extendedKeyUsage that allows both TLS roles. */ + ExpectNotNull(nsType = wolfSSL_X509_load_certificate_file( + "./certs/ns-cert-type-cert.pem", WOLFSSL_FILETYPE_PEM)); + /* ns-ca-only-cert.pem: a netscape-cert-type of SSL CA and nothing else - + * no basicConstraints and no keyUsage - so the netscape type is the only + * thing that can say whether it is a CA. */ + ExpectNotNull(nsCaOnly = wolfSSL_X509_load_certificate_file( + "./certs/ns-ca-only-cert.pem", WOLFSSL_FILETYPE_PEM)); + /* code-sign-cert.pem: the one shape that satisfies the code signing rule - + * a critical keyUsage of digitalSignature alone, and an extendedKeyUsage + * of codeSigning alone. */ + ExpectNotNull(codeSign = wolfSSL_X509_load_certificate_file( + "./certs/code-sign-cert.pem", WOLFSSL_FILETYPE_PEM)); + /* v1-root-cert.pem: a version 1 certificate, so no extensions at all, that + * is self-signed - its RSA signature matches its own RSA key. */ + ExpectNotNull(v1Root = wolfSSL_X509_load_certificate_file( + "./certs/v1-root-cert.pem", WOLFSSL_FILETYPE_PEM)); +#ifdef HAVE_ECC + /* v1-self-issued-cert.pem: the same shape, but only self-issued - it + * carries an RSA key under an ECDSA signature, so it cannot have signed + * itself. Parsing it needs ECDSA to be built in, since that is the + * algorithm its signature names. */ + ExpectNotNull(v1SelfIssued = wolfSSL_X509_load_certificate_file( + "./certs/v1-self-issued-cert.pem", WOLFSSL_FILETYPE_PEM)); +#endif + + /* This is how OpenVPN's --ns-cert-type check reads the answer; with the + * old constant-0 macro both of these were always 0, so the check could + * never pass. */ + ExpectIntEQ(X509_check_purpose(serverOnly, X509_PURPOSE_SSL_SERVER, 0), 1); + ExpectIntEQ(X509_check_purpose(serverOnly, X509_PURPOSE_SSL_CLIENT, 0), 0); + ExpectIntEQ(X509_check_purpose(both, X509_PURPOSE_SSL_SERVER, 0), 1); + ExpectIntEQ(X509_check_purpose(both, X509_PURPOSE_SSL_CLIENT, 0), 1); + + /* An absent extension constrains nothing: ca-cert.pem carries no + * keyUsage, so even CRL signing is allowed. */ + ExpectIntEQ(X509_check_purpose(ca, X509_PURPOSE_CRL_SIGN, 0), 1); + /* leaf-cert.pem does carry keyUsage, and it has no cRLSign bit. */ + ExpectIntEQ(X509_check_purpose(serverOnly, X509_PURPOSE_CRL_SIGN, 0), 0); + /* Nor emailProtection in its extendedKeyUsage. */ + ExpectIntEQ(X509_check_purpose(serverOnly, X509_PURPOSE_SMIME_SIGN, 0), 0); + + /* Roles that constrain nothing. */ + ExpectIntEQ(X509_check_purpose(serverOnly, X509_PURPOSE_ANY, 0), 1); + ExpectIntEQ(X509_check_purpose(serverOnly, X509_PURPOSE_OCSP_HELPER, 0), + 1); + ExpectIntEQ(X509_check_purpose(ca, X509_PURPOSE_ANY, 0), 1); + + /* The two extensions are checked independently, so a certificate can + * satisfy one requirement and fail the other. + * + * leaf-cert.pem: extendedKeyUsage serverAuth, keyUsage + * digitalSignature + keyEncipherment + * ca-int-cert.pem: no extendedKeyUsage at all, keyUsage + * digitalSignature + keyCertSign + cRLSign + * ca-cert.pem: extendedKeyUsage serverAuth + clientAuth, no keyUsage */ + + /* Netscape's server role wants encipherment, which the leaf has. */ + ExpectIntEQ(X509_check_purpose(serverOnly, X509_PURPOSE_NS_SSL_SERVER, 0), + 1); + /* The intermediate has no encipherment bit, so it fails that, even + * though its absent extendedKeyUsage constrains nothing. */ + ExpectIntEQ(X509_check_purpose(inter, X509_PURPOSE_NS_SSL_SERVER, 0), 0); + /* But its keyUsage does allow CRL signing, unlike the leaf's. */ + ExpectIntEQ(X509_check_purpose(inter, X509_PURPOSE_CRL_SIGN, 0), 1); + /* And with no extendedKeyUsage to object, the TLS roles come down to + * keyUsage alone, which digitalSignature satisfies. */ + ExpectIntEQ(X509_check_purpose(inter, X509_PURPOSE_SSL_SERVER, 0), 1); + ExpectIntEQ(X509_check_purpose(inter, X509_PURPOSE_SSL_CLIENT, 0), 1); + /* S/MIME needs an emailProtection it does not forbid but does not name; + * with no extendedKeyUsage the check falls to keyUsage, which allows + * signing but not encipherment. */ + ExpectIntEQ(X509_check_purpose(inter, X509_PURPOSE_SMIME_SIGN, 0), 1); + ExpectIntEQ(X509_check_purpose(inter, X509_PURPOSE_SMIME_ENCRYPT, 0), 0); + /* ca-cert.pem has no keyUsage, so only its extendedKeyUsage decides: + * both TLS roles pass, S/MIME does not. */ + ExpectIntEQ(X509_check_purpose(ca, X509_PURPOSE_SSL_CLIENT, 0), 1); + ExpectIntEQ(X509_check_purpose(ca, X509_PURPOSE_SMIME_ENCRYPT, 0), 0); + ExpectIntEQ(X509_check_purpose(ca, X509_PURPOSE_NS_SSL_SERVER, 0), 1); + + /* -1 asks only that the extensions be readable, which is how hostap and + * sssd call it; OpenSSL answers 1. */ + ExpectIntEQ(X509_check_purpose(serverOnly, -1, 0), 1); + ExpectIntEQ(X509_check_purpose(ca, -1, -1), 1); + + /* Checked as a CA. */ + ExpectIntEQ(X509_check_purpose(ca, X509_PURPOSE_SSL_SERVER, 1), 1); + ExpectIntEQ(X509_check_purpose(serverOnly, X509_PURPOSE_SSL_SERVER, 1), 0); + + /* anyExtendedKeyUsage does NOT stand in for a specific role. OpenSSL's + * xku_reject() treats it as its own bit, so a certificate naming only + * anyExtendedKeyUsage is refused for every specific purpose; verified + * against OpenSSL 3.5 on this certificate. */ + ExpectIntEQ(X509_check_purpose(anyEku, X509_PURPOSE_SSL_SERVER, 0), 0); + ExpectIntEQ(X509_check_purpose(anyEku, X509_PURPOSE_SSL_CLIENT, 0), 0); + ExpectIntEQ(X509_check_purpose(anyEku, X509_PURPOSE_NS_SSL_SERVER, 0), 0); + ExpectIntEQ(X509_check_purpose(anyEku, X509_PURPOSE_SMIME_SIGN, 0), 0); + ExpectIntEQ(X509_check_purpose(anyEku, X509_PURPOSE_SMIME_ENCRYPT, 0), 0); + /* The roles that constrain no extendedKeyUsage are still allowed. */ + ExpectIntEQ(X509_check_purpose(anyEku, X509_PURPOSE_ANY, 0), 1); + ExpectIntEQ(X509_check_purpose(anyEku, X509_PURPOSE_OCSP_HELPER, 0), 1); + /* CRL signing consults only keyUsage, and there is no cRLSign bit. */ + ExpectIntEQ(X509_check_purpose(anyEku, X509_PURPOSE_CRL_SIGN, 0), 0); + /* And it is not a CA. */ + ExpectIntEQ(X509_check_purpose(anyEku, X509_PURPOSE_SSL_SERVER, 1), 0); + /* The OCSP helper role constrains nothing on a leaf, but a CA still has + * to be a valid one - verified against OpenSSL 3.5. */ + ExpectIntEQ(X509_check_purpose(anyEku, X509_PURPOSE_OCSP_HELPER, 1), 0); + + /* With ca set, the extendedKeyUsage is checked before the CA rules, so a + * CA whose extendedKeyUsage excludes the role is refused for it even + * though it is a valid CA. ca-cert names only the TLS roles. */ + ExpectIntEQ(X509_check_purpose(ca, X509_PURPOSE_SSL_SERVER, 1), 1); + ExpectIntEQ(X509_check_purpose(ca, X509_PURPOSE_SSL_CLIENT, 1), 1); + ExpectIntEQ(X509_check_purpose(ca, X509_PURPOSE_SMIME_SIGN, 1), 0); + ExpectIntEQ(X509_check_purpose(ca, X509_PURPOSE_SMIME_ENCRYPT, 1), 0); + /* Roles with no extendedKeyUsage of their own just need a valid CA. */ + ExpectIntEQ(X509_check_purpose(ca, X509_PURPOSE_CRL_SIGN, 1), 1); + ExpectIntEQ(X509_check_purpose(ca, X509_PURPOSE_OCSP_HELPER, 1), 1); + ExpectIntEQ(X509_check_purpose(ca, X509_PURPOSE_TIMESTAMP_SIGN, 1), 1); + /* Any Purpose constrains nothing, for a CA as much as a leaf. */ + ExpectIntEQ(X509_check_purpose(ca, X509_PURPOSE_ANY, 1), 1); + + /* Code signing. Every value here was taken from OpenSSL 3.5. + * + * The leaf rule follows the CA/Browser Forum baseline the way OpenSSL + * applies it: keyUsage present and critical, allowing digital signatures + * and neither certificate nor CRL signing, and an extendedKeyUsage that + * allows code signing but neither any use nor server authentication. + * None of the certificates here name code signing, so each is refused as + * a leaf; a CA for the role is just a CA. */ + ExpectIntEQ(X509_check_purpose(ca, X509_PURPOSE_CODE_SIGN, 0), 0); + ExpectIntEQ(X509_check_purpose(ca, X509_PURPOSE_CODE_SIGN, 1), 1); + ExpectIntEQ(X509_check_purpose(serverOnly, X509_PURPOSE_CODE_SIGN, 0), 0); + /* anyExtendedKeyUsage is explicitly disqualifying for this role. */ + ExpectIntEQ(X509_check_purpose(anyEku, X509_PURPOSE_CODE_SIGN, 0), 0); + ExpectIntEQ(X509_check_purpose(anyEku, X509_PURPOSE_CODE_SIGN, 1), 0); + + /* A certificate that does satisfy the code signing rule. Every value + * here was taken from OpenSSL 3.5. */ + ExpectIntEQ(X509_check_purpose(codeSign, X509_PURPOSE_CODE_SIGN, 0), 1); + /* It is not a CA, so it cannot be one for the role either. */ + ExpectIntEQ(X509_check_purpose(codeSign, X509_PURPOSE_CODE_SIGN, 1), 0); + /* Its extendedKeyUsage names code signing and nothing else, so the other + * roles that constrain one are refused. */ + ExpectIntEQ(X509_check_purpose(codeSign, X509_PURPOSE_SSL_CLIENT, 0), 0); + ExpectIntEQ(X509_check_purpose(codeSign, X509_PURPOSE_SSL_SERVER, 0), 0); + ExpectIntEQ(X509_check_purpose(codeSign, X509_PURPOSE_SMIME_SIGN, 0), 0); + /* Its keyUsage has no cRLSign bit. */ + ExpectIntEQ(X509_check_purpose(codeSign, X509_PURPOSE_CRL_SIGN, 0), 0); + /* The roles that constrain nothing on a leaf still allow it. */ + ExpectIntEQ(X509_check_purpose(codeSign, X509_PURPOSE_ANY, 0), 1); + ExpectIntEQ(X509_check_purpose(codeSign, X509_PURPOSE_OCSP_HELPER, 0), 1); + + /* A certificate whose only CA evidence is a netscape type. OpenSSL + * reports how it decided rather than a plain 1, and refuses the roles the + * type did not name. Every value here was taken from OpenSSL 3.5. */ +#ifndef IGNORE_NETSCAPE_CERT_TYPE + /* The type named the SSL CA role, so the TLS roles accept it. */ + ExpectIntEQ(X509_check_purpose(nsCaOnly, X509_PURPOSE_SSL_CLIENT, 1), 1); + ExpectIntEQ(X509_check_purpose(nsCaOnly, X509_PURPOSE_SSL_SERVER, 1), 1); + ExpectIntEQ(X509_check_purpose(nsCaOnly, X509_PURPOSE_NS_SSL_SERVER, 1), + 1); + /* It did not name the S/MIME CA role, so those are refused. */ + ExpectIntEQ(X509_check_purpose(nsCaOnly, X509_PURPOSE_SMIME_SIGN, 1), 0); + ExpectIntEQ(X509_check_purpose(nsCaOnly, X509_PURPOSE_SMIME_ENCRYPT, 1), + 0); + /* The roles that just need a CA get the value that says a netscape type + * was the only evidence. */ + ExpectIntEQ(X509_check_purpose(nsCaOnly, X509_PURPOSE_CRL_SIGN, 1), 5); + ExpectIntEQ(X509_check_purpose(nsCaOnly, X509_PURPOSE_OCSP_HELPER, 1), 5); + ExpectIntEQ(X509_check_purpose(nsCaOnly, X509_PURPOSE_TIMESTAMP_SIGN, 1), + 5); + ExpectIntEQ(X509_check_purpose(nsCaOnly, X509_PURPOSE_ANY, 1), 1); + ExpectIntEQ(X509_check_purpose(nsCaOnly, X509_PURPOSE_CODE_SIGN, 1), 5); + /* As a leaf the netscape type decides, and it names no leaf role. */ + ExpectIntEQ(X509_check_purpose(nsCaOnly, X509_PURPOSE_SSL_CLIENT, 0), 0); + ExpectIntEQ(X509_check_purpose(nsCaOnly, X509_PURPOSE_SSL_SERVER, 0), 0); + ExpectIntEQ(X509_check_purpose(nsCaOnly, X509_PURPOSE_CRL_SIGN, 0), 1); +#endif + + /* A purpose that cannot be decided is reported as "not permitted", the + * answer OpenSSL gives. A negative return would read as permission + * granted to a caller writing "if (X509_check_purpose(...))". */ + ExpectIntEQ(X509_check_purpose(anyEku, X509_PURPOSE_TIMESTAMP_SIGN, 0), 0); + + /* The netscape-cert-type extension is consulted after the key usages. + * This certificate's extendedKeyUsage allows both TLS roles and its + * keyUsage allows both, so the netscape type is the only thing that can + * separate them: it names the client role and not the server one. */ +#ifndef IGNORE_NETSCAPE_CERT_TYPE + ExpectIntEQ(X509_check_purpose(nsType, X509_PURPOSE_SSL_CLIENT, 0), 1); + ExpectIntEQ(X509_check_purpose(nsType, X509_PURPOSE_SSL_SERVER, 0), 0); + ExpectIntEQ(X509_check_purpose(nsType, X509_PURPOSE_NS_SSL_SERVER, 0), 0); + /* OpenSSL accepts an SSL client type for S/MIME as a workaround for + * certificates that set only that bit. */ + ExpectIntEQ(X509_check_purpose(nsType, X509_PURPOSE_SMIME_SIGN, 0), 0); + /* CRL signing consults no netscape type, so only keyUsage decides, and + * this certificate has no cRLSign bit. */ + ExpectIntEQ(X509_check_purpose(nsType, X509_PURPOSE_CRL_SIGN, 0), 0); + /* A role that constrains nothing is unaffected. */ + ExpectIntEQ(X509_check_purpose(nsType, X509_PURPOSE_ANY, 0), 1); +#endif + + /* Version 1 certificates. OpenSSL takes a version 1 certificate for a CA + * root when it has no basicConstraints and looks self-signed, and reports + * that it decided so this way rather than a plain 1. Every value here was + * taken from OpenSSL 3.5. + * + * Looking self-signed is more than sharing the issuer and subject names: + * OpenSSL also requires the signature algorithm to match the certificate's + * own public key algorithm. Neither certificate's signature is verified + * to reach this answer, by OpenSSL or here. + * + * The two differ in nothing else, so as leaves they are indistinguishable + * - with no keyUsage and no extendedKeyUsage to constrain them, every role + * that does not demand one accepts them both. */ + ExpectIntEQ(X509_check_purpose(v1Root, X509_PURPOSE_SSL_CLIENT, 0), 1); + ExpectIntEQ(X509_check_purpose(v1Root, X509_PURPOSE_CRL_SIGN, 0), 1); + ExpectIntEQ(X509_check_purpose(v1Root, X509_PURPOSE_ANY, 0), 1); + ExpectIntEQ(X509_check_purpose(v1Root, X509_PURPOSE_TIMESTAMP_SIGN, 0), 0); + ExpectIntEQ(X509_check_purpose(v1Root, X509_PURPOSE_CODE_SIGN, 0), 0); +#ifdef HAVE_ECC + ExpectIntEQ(X509_check_purpose(v1SelfIssued, X509_PURPOSE_SSL_CLIENT, 0), + 1); + ExpectIntEQ(X509_check_purpose(v1SelfIssued, X509_PURPOSE_CRL_SIGN, 0), 1); + ExpectIntEQ(X509_check_purpose(v1SelfIssued, X509_PURPOSE_ANY, 0), 1); + ExpectIntEQ(X509_check_purpose(v1SelfIssued, + X509_PURPOSE_TIMESTAMP_SIGN, 0), 0); + ExpectIntEQ(X509_check_purpose(v1SelfIssued, X509_PURPOSE_CODE_SIGN, 0), + 0); +#endif + + /* As a CA the self-signed one is a version 1 root. The roles that only + * need a CA get the value that says so; the TLS roles report a plain 1 + * because no extendedKeyUsage limits them. */ + ExpectIntEQ(X509_check_purpose(v1Root, X509_PURPOSE_SSL_CLIENT, 1), 1); + ExpectIntEQ(X509_check_purpose(v1Root, X509_PURPOSE_SSL_SERVER, 1), 1); + ExpectIntEQ(X509_check_purpose(v1Root, X509_PURPOSE_NS_SSL_SERVER, 1), 1); + ExpectIntEQ(X509_check_purpose(v1Root, X509_PURPOSE_SMIME_SIGN, 1), 3); + ExpectIntEQ(X509_check_purpose(v1Root, X509_PURPOSE_SMIME_ENCRYPT, 1), 3); + ExpectIntEQ(X509_check_purpose(v1Root, X509_PURPOSE_CRL_SIGN, 1), 3); + ExpectIntEQ(X509_check_purpose(v1Root, X509_PURPOSE_OCSP_HELPER, 1), 3); + ExpectIntEQ(X509_check_purpose(v1Root, X509_PURPOSE_TIMESTAMP_SIGN, 1), 3); + ExpectIntEQ(X509_check_purpose(v1Root, X509_PURPOSE_CODE_SIGN, 1), 3); + ExpectIntEQ(X509_check_purpose(v1Root, X509_PURPOSE_ANY, 1), 1); + +#ifdef HAVE_ECC + /* The self-issued one is not, because its ECDSA signature could not have + * come from its own RSA key. Sharing the issuer and subject names is not + * enough, and nothing else here says it is a CA. */ + ExpectIntEQ(X509_check_purpose(v1SelfIssued, X509_PURPOSE_SSL_CLIENT, 1), + 0); + ExpectIntEQ(X509_check_purpose(v1SelfIssued, X509_PURPOSE_SSL_SERVER, 1), + 0); + ExpectIntEQ(X509_check_purpose(v1SelfIssued, + X509_PURPOSE_NS_SSL_SERVER, 1), 0); + ExpectIntEQ(X509_check_purpose(v1SelfIssued, X509_PURPOSE_SMIME_SIGN, 1), + 0); + ExpectIntEQ(X509_check_purpose(v1SelfIssued, + X509_PURPOSE_SMIME_ENCRYPT, 1), 0); + ExpectIntEQ(X509_check_purpose(v1SelfIssued, X509_PURPOSE_CRL_SIGN, 1), 0); + ExpectIntEQ(X509_check_purpose(v1SelfIssued, X509_PURPOSE_OCSP_HELPER, 1), + 0); + ExpectIntEQ(X509_check_purpose(v1SelfIssued, + X509_PURPOSE_TIMESTAMP_SIGN, 1), 0); + ExpectIntEQ(X509_check_purpose(v1SelfIssued, X509_PURPOSE_CODE_SIGN, 1), + 0); + /* Any Purpose constrains nothing, so it accepts it as it accepts + * anything. */ + ExpectIntEQ(X509_check_purpose(v1SelfIssued, X509_PURPOSE_ANY, 1), 1); +#endif + + /* A NULL certificate is an error. A role that cannot be decided is not + * an error but a refusal, which is what OpenSSL answers and what a caller + * testing the result for truth needs. */ + ExpectIntEQ(X509_check_purpose(NULL, X509_PURPOSE_SSL_SERVER, 0), -1); + ExpectIntEQ(X509_check_purpose(serverOnly, X509_PURPOSE_TIMESTAMP_SIGN, + 0), 0); + ExpectIntEQ(X509_check_purpose(serverOnly, 999, 0), 0); + + wolfSSL_X509_free(serverOnly); + wolfSSL_X509_free(both); + wolfSSL_X509_free(ca); + wolfSSL_X509_free(inter); + wolfSSL_X509_free(anyEku); + wolfSSL_X509_free(nsType); + wolfSSL_X509_free(nsCaOnly); + wolfSSL_X509_free(codeSign); + wolfSSL_X509_free(v1Root); +#ifdef HAVE_ECC + wolfSSL_X509_free(v1SelfIssued); +#endif +#endif + return EXPECT_RESULT(); +} + +int test_wolfSSL_CTX_set0_tmp_dh_pkey(void) +{ + EXPECT_DECLS; +#if (defined(OPENSSL_ALL) || defined(WOLFSSL_QT) || \ + defined(WOLFSSL_OPENSSH)) && \ + !defined(NO_DH) && defined(WOLFSSL_DH_EXTRA) && !defined(NO_FILESYSTEM) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_BIO) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL_BIO* bio = NULL; + WOLFSSL_DH* dh = NULL; + WOLFSSL_EVP_PKEY* pkey = NULL; + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_client_method())); + + ExpectNotNull(bio = wolfSSL_BIO_new_file(dhParamFile, "rb")); + ExpectNotNull(dh = wolfSSL_PEM_read_bio_DHparams(bio, NULL, NULL, NULL)); + wolfSSL_BIO_free(bio); + bio = NULL; + + ExpectNotNull(pkey = wolfSSL_EVP_PKEY_new()); + ExpectIntEQ(wolfSSL_EVP_PKEY_assign(pkey, WC_EVP_PKEY_DH, dh), 1); + if (EXPECT_SUCCESS()) { + /* the key owns dh now */ + dh = NULL; + } + + /* The context takes ownership of the key on success, so the test must + * not free it afterwards. */ + ExpectIntEQ(wolfSSL_CTX_set0_tmp_dh_pkey(ctx, pkey), WOLFSSL_SUCCESS); + if (EXPECT_SUCCESS()) { + pkey = NULL; + } + + /* Bad arguments leave ownership with the caller. */ + ExpectIntEQ(wolfSSL_CTX_set0_tmp_dh_pkey(NULL, NULL), WOLFSSL_FAILURE); + ExpectIntEQ(wolfSSL_CTX_set0_tmp_dh_pkey(ctx, NULL), WOLFSSL_FAILURE); + + /* A key that holds no DH parameters is refused, and stays the + * caller's. */ + { + WOLFSSL_EVP_PKEY* empty = NULL; + ExpectNotNull(empty = wolfSSL_EVP_PKEY_new()); + ExpectIntEQ(wolfSSL_CTX_set0_tmp_dh_pkey(ctx, empty), + WOLFSSL_FAILURE); + wolfSSL_EVP_PKEY_free(empty); + } + + wolfSSL_EVP_PKEY_free(pkey); + wolfSSL_DH_free(dh); + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} + +#if defined(HAVE_SSL_MEMIO_TESTS_DEPENDENCIES) && defined(OPENSSL_EXTRA) && \ + !defined(NO_RSA) && !defined(NO_FILESYSTEM) +static int get1PeerCertClientCb(WOLFSSL* ssl) +{ + /* Called after the handshake, so the peer certificate is available. */ + X509* first = NULL; + X509* second = NULL; + WOLFSSL_X509_NAME* name = NULL; + int ok = TEST_FAIL; + + first = SSL_get1_peer_certificate(ssl); + if (first == NULL) { + return TEST_FAIL; + } + + /* It is a get1: the caller owns it, so asking again gives an independent + * object rather than the same pointer. */ + second = SSL_get1_peer_certificate(ssl); + if (second != NULL && second != first) { + name = wolfSSL_X509_get_subject_name(first); + if (name != NULL && + wolfSSL_X509_NAME_cmp(name, + wolfSSL_X509_get_subject_name(second)) == 0) { + ok = TEST_SUCCESS; + } + } + + /* Both have to be released; a leak here shows up under the memory + * tracking builds. */ + X509_free(first); + X509_free(second); + + return ok; +} +#endif + +#if defined(HAVE_SSL_MEMIO_TESTS_DEPENDENCIES) && defined(OPENSSL_EXTRA) && \ + !defined(NO_RSA) && !defined(NO_FILESYSTEM) +/* The server side asks for the client's certificate, which only arrives when + * client authentication was requested. */ +static int get1PeerCertServerCtxCb(WOLFSSL_CTX* ctx) +{ + wolfSSL_CTX_set_verify(ctx, WOLFSSL_VERIFY_PEER | + WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); + return TEST_SUCCESS; +} + +static int get1PeerCertServerCb(WOLFSSL* ssl) +{ + X509* cert = SSL_get1_peer_certificate(ssl); + + if (cert == NULL) { + return TEST_FAIL; + } + /* Owned by the caller here too. */ + X509_free(cert); + + return TEST_SUCCESS; +} +#endif + +/* SSL_get1_peer_certificate is the OpenSSL 3.0 spelling of + * SSL_get_peer_certificate; both hand the caller a certificate to free. */ +int test_wolfSSL_SSL_get1_peer_certificate(void) +{ + EXPECT_DECLS; +#if defined(HAVE_SSL_MEMIO_TESTS_DEPENDENCIES) && defined(OPENSSL_EXTRA) && \ + !defined(NO_RSA) && !defined(NO_FILESYSTEM) + test_ssl_cbf client_cb; + test_ssl_cbf server_cb; + + XMEMSET(&client_cb, 0, sizeof(client_cb)); + XMEMSET(&server_cb, 0, sizeof(server_cb)); + client_cb.on_result = get1PeerCertClientCb; + ExpectIntEQ(test_wolfSSL_client_server_nofail_memio(&client_cb, + &server_cb, NULL), TEST_SUCCESS); + + /* The other direction: a server reading the client's certificate. */ + XMEMSET(&client_cb, 0, sizeof(client_cb)); + XMEMSET(&server_cb, 0, sizeof(server_cb)); + server_cb.ctx_ready = get1PeerCertServerCtxCb; + server_cb.on_result = get1PeerCertServerCb; + ExpectIntEQ(test_wolfSSL_client_server_nofail_memio(&client_cb, + &server_cb, NULL), TEST_SUCCESS); + + /* Before a handshake there is no peer certificate to hand back. */ + { + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_client_method())); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + ExpectNull(SSL_get1_peer_certificate(ssl)); + ExpectNull(SSL_get1_peer_certificate(NULL)); + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); + } +#endif + return EXPECT_RESULT(); +} + +#if (defined(OPENSSL_ALL) || defined(WOLFSSL_QT) || \ + defined(WOLFSSL_OPENSSH)) && \ + !defined(NO_DH) && defined(WOLFSSL_DH_EXTRA) && !defined(NO_FILESYSTEM) && \ + !defined(NO_BIO) && defined(HAVE_SSL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(NO_RSA) && !defined(WOLFSSL_NO_TLS12) && \ + !defined(NO_AES) && defined(WOLFSSL_AES_128) +/* Hand the server its DH parameters through an EVP_PKEY, then insist on a + * DHE suite so the handshake can only succeed if those parameters were + * actually installed. */ +static int set0DhPkeyServerCb(WOLFSSL_CTX* ctx) +{ + WOLFSSL_BIO* bio = NULL; + WOLFSSL_DH* dh = NULL; + WOLFSSL_EVP_PKEY* pkey = NULL; + int ret = TEST_FAIL; + + bio = wolfSSL_BIO_new_file(dhParamFile, "rb"); + if (bio == NULL) { + return TEST_FAIL; + } + dh = wolfSSL_PEM_read_bio_DHparams(bio, NULL, NULL, NULL); + wolfSSL_BIO_free(bio); + if (dh == NULL) { + return TEST_FAIL; + } + + pkey = wolfSSL_EVP_PKEY_new(); + if (pkey == NULL) { + wolfSSL_DH_free(dh); + return TEST_FAIL; + } + if (wolfSSL_EVP_PKEY_assign(pkey, WC_EVP_PKEY_DH, dh) != 1) { + wolfSSL_DH_free(dh); + wolfSSL_EVP_PKEY_free(pkey); + return TEST_FAIL; + } + /* the key owns dh now */ + + if (wolfSSL_CTX_set0_tmp_dh_pkey(ctx, pkey) == WOLFSSL_SUCCESS) { + /* the ctx owns pkey now */ + ret = TEST_SUCCESS; + } + else { + wolfSSL_EVP_PKEY_free(pkey); + } + + if (ret == TEST_SUCCESS) { + if (wolfSSL_CTX_set_cipher_list(ctx, "DHE-RSA-AES128-SHA") != + WOLFSSL_SUCCESS) { + ret = TEST_FAIL; + } + } + + return ret; +} + +static int set0DhPkeyClientCb(WOLFSSL_CTX* ctx) +{ + if (wolfSSL_CTX_set_cipher_list(ctx, "DHE-RSA-AES128-SHA") != + WOLFSSL_SUCCESS) { + return TEST_FAIL; + } + return TEST_SUCCESS; +} +#endif + +/* The parameters handed over by SSL_CTX_set0_tmp_dh_pkey are the ones the + * server actually uses, not merely stored. */ +int test_wolfSSL_CTX_set0_tmp_dh_pkey_handshake(void) +{ + EXPECT_DECLS; +#if (defined(OPENSSL_ALL) || defined(WOLFSSL_QT) || \ + defined(WOLFSSL_OPENSSH)) && \ + !defined(NO_DH) && defined(WOLFSSL_DH_EXTRA) && !defined(NO_FILESYSTEM) && \ + !defined(NO_BIO) && defined(HAVE_SSL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(NO_RSA) && !defined(WOLFSSL_NO_TLS12) && \ + !defined(NO_AES) && defined(WOLFSSL_AES_128) + test_ssl_cbf client_cb; + test_ssl_cbf server_cb; + + XMEMSET(&client_cb, 0, sizeof(client_cb)); + XMEMSET(&server_cb, 0, sizeof(server_cb)); + client_cb.method = wolfTLSv1_2_client_method; + server_cb.method = wolfTLSv1_2_server_method; + client_cb.ctx_ready = set0DhPkeyClientCb; + server_cb.ctx_ready = set0DhPkeyServerCb; + + /* Only a DHE suite is offered, so a handshake proves the server had + * usable DH parameters. */ + ExpectIntEQ(test_wolfSSL_client_server_nofail_memio(&client_cb, + &server_cb, NULL), TEST_SUCCESS); +#endif + return EXPECT_RESULT(); +} diff --git a/tests/api/test_ossl_x509.h b/tests/api/test_ossl_x509.h index 1d3771ca5e5..f75223a19f8 100644 --- a/tests/api/test_ossl_x509.h +++ b/tests/api/test_ossl_x509.h @@ -45,6 +45,10 @@ int test_wolfSSL_get_tbs(void); int test_wolfSSL_X509_ext_get_critical_by_NID(void); int test_wolfSSL_X509_CRL_distribution_points(void); int test_wolfSSL_X509_check_ip_asc(void); +int test_wolfSSL_X509_check_purpose(void); +int test_wolfSSL_CTX_set0_tmp_dh_pkey(void); +int test_wolfSSL_SSL_get1_peer_certificate(void); +int test_wolfSSL_CTX_set0_tmp_dh_pkey_handshake(void); int test_wolfSSL_X509_bad_altname(void); int test_wolfSSL_X509_name_match1(void); int test_wolfSSL_X509_name_match2(void); @@ -81,6 +85,12 @@ int test_wolfSSL_X509_cmp(void); TEST_DECL_GROUP("ossl_x509", test_wolfSSL_X509_ext_get_critical_by_NID), \ TEST_DECL_GROUP("ossl_x509", test_wolfSSL_X509_CRL_distribution_points), \ TEST_DECL_GROUP("ossl_x509", test_wolfSSL_X509_check_ip_asc), \ + TEST_DECL_GROUP("ossl_x509", test_wolfSSL_X509_check_purpose), \ + TEST_DECL_GROUP("ossl_x509", test_wolfSSL_CTX_set0_tmp_dh_pkey), \ + TEST_DECL_GROUP("ossl_x509", \ + test_wolfSSL_SSL_get1_peer_certificate), \ + TEST_DECL_GROUP("ossl_x509", \ + test_wolfSSL_CTX_set0_tmp_dh_pkey_handshake), \ TEST_DECL_GROUP("ossl_x509", test_wolfSSL_X509_bad_altname), \ TEST_DECL_GROUP("ossl_x509", test_wolfSSL_X509_name_match1), \ TEST_DECL_GROUP("ossl_x509", test_wolfSSL_X509_name_match2), \ diff --git a/tests/api/test_ossl_x509_io.c b/tests/api/test_ossl_x509_io.c index 8e3cd7673e9..773d458bdae 100644 --- a/tests/api/test_ossl_x509_io.c +++ b/tests/api/test_ossl_x509_io.c @@ -241,7 +241,13 @@ int test_wolfSSL_PEM_write_bio_X509(void) * information */ /* Here we copy the validity struct from the original */ +#ifndef IGNORE_NETSCAPE_CERT_TYPE expectedLen = 1688; +#else + /* The netscape certificate type is not carried into the re-encoded + * certificate when it is compiled out, so its extension is absent. */ + expectedLen = 1663; +#endif ExpectIntEQ(wolfSSL_BIO_get_len(output), expectedLen); /* Reset buffers and x509 */ diff --git a/tests/api/test_ssl_cert.c b/tests/api/test_ssl_cert.c index 44073b5f611..6960f7fbf12 100644 --- a/tests/api/test_ssl_cert.c +++ b/tests/api/test_ssl_cert.c @@ -975,6 +975,72 @@ int test_wolfSSL_load_client_CA_file(void) return EXPECT_RESULT(); } +/* Test appending CA names to an existing list from a file and a directory. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_add_cert_subjects_to_stack(void) +{ + EXPECT_DECLS; +#if !defined(NO_CERTS) && !defined(WOLFSSL_NO_CA_NAMES) && \ + !defined(NO_BIO) && defined(OPENSSL_EXTRA) && !defined(NO_FILESYSTEM) && \ + !defined(NO_RSA) && defined(WOLFSSL_PEM_TO_DER) + WOLF_STACK_OF(WOLFSSL_X509_NAME)* names = NULL; + int num = 0; + + ExpectNotNull(names = wolfSSL_sk_X509_NAME_new(NULL)); + + /* NULL parameters are rejected. */ + ExpectIntEQ(wolfSSL_add_file_cert_subjects_to_stack(NULL, caCertFile), 0); + ExpectIntEQ(wolfSSL_add_file_cert_subjects_to_stack(names, NULL), 0); + + /* A file that cannot be opened is an error and adds nothing. */ + ExpectIntEQ(wolfSSL_add_file_cert_subjects_to_stack(names, + "does/not/exist.pem"), 0); + ExpectIntEQ(wolfSSL_sk_X509_NAME_num(names), 0); + + /* Every certificate in the file contributes its subject name. */ + ExpectIntEQ(wolfSSL_add_file_cert_subjects_to_stack(names, caCertFile), 1); + ExpectIntGT(num = wolfSSL_sk_X509_NAME_num(names), 0); + + /* The same file again is not an error, but adds no duplicates. */ + ExpectIntEQ(wolfSSL_add_file_cert_subjects_to_stack(names, caCertFile), 1); + ExpectIntEQ(wolfSSL_sk_X509_NAME_num(names), num); + + wolfSSL_sk_X509_NAME_pop_free(names, NULL); + names = NULL; + +#if !defined(NO_WOLFSSL_DIR) && !defined(WOLFSSL_NUCLEUS) && \ + !defined(WOLFSSL_NUCLEUS_1_2) + ExpectNotNull(names = wolfSSL_sk_X509_NAME_new(NULL)); + + /* NULL parameters are rejected. */ + ExpectIntEQ(wolfSSL_add_dir_cert_subjects_to_stack(NULL, "./certs"), 0); + ExpectIntEQ(wolfSSL_add_dir_cert_subjects_to_stack(names, NULL), 0); + + /* A directory that cannot be opened is an error. */ + ExpectIntEQ(wolfSSL_add_dir_cert_subjects_to_stack(names, + "./does-not-exist"), 0); + ExpectIntEQ(wolfSSL_sk_X509_NAME_num(names), 0); + + /* Certificates in the directory contribute their subject names. Files + * holding no certificate, and subdirectories, are skipped rather than + * failing the walk. */ + ExpectIntEQ(wolfSSL_add_dir_cert_subjects_to_stack(names, "./certs/1024"), + 1); + ExpectIntGT(num = wolfSSL_sk_X509_NAME_num(names), 0); + + /* A second pass over the same directory adds no duplicates. */ + ExpectIntEQ(wolfSSL_add_dir_cert_subjects_to_stack(names, "./certs/1024"), + 1); + ExpectIntEQ(wolfSSL_sk_X509_NAME_num(names), num); + + wolfSSL_sk_X509_NAME_pop_free(names, NULL); +#endif +#endif + return EXPECT_RESULT(); +} + /* Test requiring mutual authentication. * * @return TEST_SUCCESS on success. @@ -1651,6 +1717,65 @@ int test_wolfSSL_verify_mode_options(void) return EXPECT_RESULT(); } +/* Test the file and directory halves of load_verify_locations. + * + * mosquitto (src/net.c) uses this pair for its cafile/capath options and + * treats a 0 return as a fatal configuration error. + */ +int test_wolfSSL_CTX_load_verify_file_dir(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && !defined(NO_CERTS) && !defined(NO_FILESYSTEM) && \ + !defined(NO_RSA) && !defined(NO_TLS) && !defined(NO_WOLFSSL_CLIENT) + WOLFSSL_CTX* ctx = NULL; + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_client_method())); + + /* Bad parameters are rejected rather than dereferenced. */ + ExpectIntEQ(wolfSSL_CTX_load_verify_file(NULL, caCertFile), 0); + ExpectIntEQ(wolfSSL_CTX_load_verify_file(ctx, NULL), 0); +#ifdef WOLFSSL_ERROR_CODE_OPENSSL + ExpectIntEQ(wolfSSL_CTX_load_verify_file(ctx, "does/not/exist.pem"), 0); +#else + /* Without WOLFSSL_ERROR_CODE_OPENSSL - which --enable-opensslall defines + * and --enable-opensslextra alone does not - WS_RETURN_CODE() passes the + * loader's error code through instead of turning it into 0. That is how + * the wolfSSL_CTX_load_verify_locations_compat() this is built on has + * always behaved, so only the failure is asserted here. */ + ExpectIntNE(wolfSSL_CTX_load_verify_file(ctx, "does/not/exist.pem"), 1); +#endif + + /* A readable CA file loads. */ + ExpectIntEQ(wolfSSL_CTX_load_verify_file(ctx, caCertFile), 1); + +#ifndef NO_WOLFSSL_DIR + ExpectIntEQ(wolfSSL_CTX_load_verify_dir(NULL, "./certs"), 0); + ExpectIntEQ(wolfSSL_CTX_load_verify_dir(ctx, NULL), 0); + + /* A directory holding certificates loads, and files in it that cannot be + * used are skipped rather than failing the whole call. */ + ExpectIntEQ(wolfSSL_CTX_load_verify_dir(ctx, "./certs"), 1); + + /* OpenSSL registers the directory for lookup rather than reading it, so + * it only fails this call on a NULL path: a missing or unreadable + * directory still reports success, and callers depend on that. */ + ExpectIntEQ(wolfSSL_CTX_load_verify_dir(ctx, "./does-not-exist"), 1); + /* wolfSSL's own entry point keeps its stricter answer for the same + * path - only the OpenSSL-named function follows OpenSSL here. */ +#ifdef WOLFSSL_ERROR_CODE_OPENSSL + ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx, NULL, + "./does-not-exist"), 0); +#else + ExpectIntNE(wolfSSL_CTX_load_verify_locations(ctx, NULL, + "./does-not-exist"), 1); +#endif +#endif + + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} + /* Test that WOLFSSL_VERIFY_CLIENT_ONCE is accepted and ignored. * * The flag exists for source compatibility with OpenSSL. wolfSSL stores no diff --git a/tests/api/test_ssl_cert.h b/tests/api/test_ssl_cert.h index 151895bbab6..b0d16e06340 100644 --- a/tests/api/test_ssl_cert.h +++ b/tests/api/test_ssl_cert.h @@ -39,6 +39,8 @@ int test_wolfSSL_get0_verified_chain(void); int test_wolfSSL_CA_list_add(void); int test_wolfSSL_CA_list_get(void); int test_wolfSSL_load_client_CA_file(void); +int test_wolfSSL_add_cert_subjects_to_stack(void); +int test_wolfSSL_CTX_load_verify_file_dir(void); int test_wolfSSL_mutual_auth(void); int test_wolfSSL_post_handshake_auth(void); int test_wolfSSL_verify_cert_store(void); @@ -76,6 +78,8 @@ int test_wolfSSL_verify_post_handshake_defers(void); TEST_DECL_GROUP("ssl_cert", test_wolfSSL_CA_list_add), \ TEST_DECL_GROUP("ssl_cert", test_wolfSSL_CA_list_get), \ TEST_DECL_GROUP("ssl_cert", test_wolfSSL_load_client_CA_file), \ + TEST_DECL_GROUP("ssl_cert", test_wolfSSL_add_cert_subjects_to_stack), \ + TEST_DECL_GROUP("ssl_cert", test_wolfSSL_CTX_load_verify_file_dir), \ TEST_DECL_GROUP("ssl_cert", test_wolfSSL_mutual_auth), \ TEST_DECL_GROUP("ssl_cert", test_wolfSSL_post_handshake_auth), \ TEST_DECL_GROUP("ssl_cert", test_wolfSSL_verify_cert_store), \ diff --git a/tests/api/test_ssl_ext.c b/tests/api/test_ssl_ext.c index 132485c2be9..6e8470c118c 100644 --- a/tests/api/test_ssl_ext.c +++ b/tests/api/test_ssl_ext.c @@ -1362,3 +1362,107 @@ int test_wolfSSL_ticket_key_cb_renew_ext(void) #endif return EXPECT_RESULT(); } + +#if (defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL)) && !defined(NO_TLS) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_AES) && defined(WOLFSSL_AES_128) +/* Join the names of the suites a stack reports, so two lists can be compared + * as a whole. Comparing one entry is not enough: how many suites a name + * selects, and whether the TLS 1.3 suites are always present whatever is + * asked for, are properties of the build rather than of the call. + * + * @param [in] sk Stack of ciphers to read. + * @param [out] buf Buffer to write the joined names to. + * @param [in] bufSz Size of buf in bytes. + */ +static void test_ciphers_join(WOLF_STACK_OF(WOLFSSL_CIPHER)* sk, char* buf, + size_t bufSz) +{ + int i; + int num = (sk == NULL) ? 0 : wolfSSL_sk_SSL_CIPHER_num(sk); + + buf[0] = '\0'; + for (i = 0; i < num; i++) { + const char* name = wolfSSL_CIPHER_get_name( + wolfSSL_sk_SSL_CIPHER_value(sk, i)); + + if (name == NULL) { + continue; + } + if (XSTRLEN(buf) + XSTRLEN(name) + 2 >= bufSz) { + break; + } + if (buf[0] != '\0') { + XSTRNCAT(buf, ":", bufSz - XSTRLEN(buf) - 1); + } + XSTRNCAT(buf, name, bufSz - XSTRLEN(buf) - 1); + } +} +#endif + +/* Test reading back the cipher suites configured on a context. + * + * haproxy (src/fips.c) and stunnel (src/ctx.c) both enumerate a context's + * suites this way; stunnel reads the list again after changing it, so the + * cached stack must not go stale. + */ +int test_wolfSSL_CTX_get_ciphers_ext(void) +{ + EXPECT_DECLS; +#if (defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL)) && !defined(NO_TLS) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_AES) && defined(WOLFSSL_AES_128) + WOLFSSL_CTX* ctx = NULL; + WOLF_STACK_OF(WOLFSSL_CIPHER)* sk = NULL; + WOLF_STACK_OF(WOLFSSL_CIPHER)* sk2 = NULL; + const char* first = NULL; + const char* second = NULL; + char firstList[512]; + char secondList[512]; + + /* Two suites this build actually has, rather than any named here: a + * restricted build may not carry a given suite at all, and the names + * these come back as are the ones set_cipher_list() takes. */ + first = wolfSSL_get_cipher_list(0); + second = wolfSSL_get_cipher_list(1); + ExpectNotNull(first); + ExpectNotNull(second); + + ExpectNull(wolfSSL_CTX_get_ciphers(NULL)); + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_client_method())); + /* A context with no cipher list set has no suites to report. */ + ExpectNull(wolfSSL_CTX_get_ciphers(ctx)); + + ExpectIntEQ(wolfSSL_CTX_set_cipher_list(ctx, first), WOLFSSL_SUCCESS); + ExpectNotNull(sk = wolfSSL_CTX_get_ciphers(ctx)); + /* How many suites one name selects is a property of the build, not of + * this call - what matters is that the list is reported and rebuilt. */ + ExpectIntGE(wolfSSL_sk_SSL_CIPHER_num(sk), 1); + test_ciphers_join(sk, firstList, sizeof(firstList)); + ExpectIntGT((int)XSTRLEN(firstList), 0); + + /* The stack belongs to the context, so it is handed back unchanged. */ + ExpectPtrEq(wolfSSL_CTX_get_ciphers(ctx), sk); + + /* Changing the list must invalidate what was handed out before: a + * different suite was asked for, so a different one is reported rather + * than the cached stack coming back. */ + ExpectIntEQ(wolfSSL_CTX_set_cipher_list(ctx, second), WOLFSSL_SUCCESS); + ExpectNotNull(sk2 = wolfSSL_CTX_get_ciphers(ctx)); + ExpectIntGE(wolfSSL_sk_SSL_CIPHER_num(sk2), 1); + test_ciphers_join(sk2, secondList, sizeof(secondList)); + ExpectStrNE(secondList, firstList); + + /* Loading a certificate must not invalidate a stack already handed out: + * the caller may still hold it, and OpenSSL does not rebuild its list + * there either. */ +#if !defined(NO_CERTS) && !defined(NO_FILESYSTEM) && !defined(NO_RSA) + ExpectNotNull(sk2 = wolfSSL_CTX_get_ciphers(ctx)); + ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx, svrCertFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectPtrEq(wolfSSL_CTX_get_ciphers(ctx), sk2); +#endif + + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} diff --git a/tests/api/test_ssl_ext.h b/tests/api/test_ssl_ext.h index ee3da3a2108..3979c89bf03 100644 --- a/tests/api/test_ssl_ext.h +++ b/tests/api/test_ssl_ext.h @@ -56,6 +56,7 @@ int test_wolfSSL_ALPN_GetPeerProtocol_badlen_ext(void); int test_wolfSSL_get_secure_renegotiation_support_ext(void); int test_wolfSSL_set_alpn_protos_badlen_ext(void); int test_wolfSSL_ticket_key_cb_renew_ext(void); +int test_wolfSSL_CTX_get_ciphers_ext(void); #define TEST_SSL_EXT_DECLS \ TEST_DECL_GROUP("ssl_ext", test_wolfSSL_NoTicketTLSv12_ext), \ @@ -106,6 +107,7 @@ int test_wolfSSL_ticket_key_cb_renew_ext(void); TEST_DECL_GROUP("ssl_ext", \ test_wolfSSL_get_secure_renegotiation_support_ext), \ TEST_DECL_GROUP("ssl_ext", test_wolfSSL_set_alpn_protos_badlen_ext), \ - TEST_DECL_GROUP("ssl_ext", test_wolfSSL_ticket_key_cb_renew_ext) + TEST_DECL_GROUP("ssl_ext", test_wolfSSL_ticket_key_cb_renew_ext), \ + TEST_DECL_GROUP("ssl_ext", test_wolfSSL_CTX_get_ciphers_ext) #endif /* TESTS_API_SSL_EXT_H */ diff --git a/tests/api/test_tls.c b/tests/api/test_tls.c index c9c0c8a44b6..89c85ca6157 100644 --- a/tests/api/test_tls.c +++ b/tests/api/test_tls.c @@ -387,7 +387,8 @@ int test_tls12_dhe_rsa_pss_sigalg(void) #if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ !defined(WOLFSSL_NO_TLS12) && !defined(NO_DH) && !defined(NO_RSA) && \ defined(WC_RSA_PSS) && !defined(NO_SHA256) && defined(HAVE_AESGCM) && \ - !defined(WOLFSSL_HARDEN_TLS) && defined(OPENSSL_EXTRA) + !defined(WOLFSSL_HARDEN_TLS) && defined(OPENSSL_EXTRA) && \ + defined(WOLFSSL_AES_128) /* Regression test for S1: SendServerKeyExchange had an inverted guard * (#ifndef WC_RSA_PSS) that compiled out the rsa_pss_sa_algo case in the * server-side signature self-check for the DHE key exchange path. This @@ -429,7 +430,8 @@ int test_tls12_ske_sig_param_binding(void) #if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ !defined(WOLFSSL_NO_TLS12) && !defined(NO_DH) && !defined(NO_RSA) && \ !defined(NO_SHA256) && defined(HAVE_AESGCM) && \ - !defined(WOLFSSL_HARDEN_TLS) && defined(OPENSSL_EXTRA) + !defined(WOLFSSL_HARDEN_TLS) && defined(OPENSSL_EXTRA) && \ + defined(WOLFSSL_AES_128) /* Negative test for the client-side ServerKeyExchange signature-content * check in DoServerKeyExchange (classic rsa_sa_algo / PKCS#1 v1.5 path). */ WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; @@ -530,7 +532,8 @@ int test_tls12_bad_cv_sig_content(void) !defined(WOLFSSL_NO_TLS12) && !defined(NO_DH) && !defined(NO_RSA) && \ !defined(NO_SHA256) && defined(HAVE_AESGCM) && \ !defined(WOLFSSL_HARDEN_TLS) && defined(OPENSSL_EXTRA) && \ - !defined(WOLFSSL_NO_CLIENT_AUTH) + !defined(WOLFSSL_NO_CLIENT_AUTH) && \ + defined(WOLFSSL_AES_128) /* Negative test for the server-side CertificateVerify signature-content * check in DoCertificateVerify (classic rsa_sa_algo / PKCS#1 v1.5 path). * That XMEMCMP is the only proof the client holds the private key for the @@ -2001,7 +2004,8 @@ int test_tls12_etm_failed_resumption(void) !defined(WOLFSSL_NO_TLS12) && defined(HAVE_ENCRYPT_THEN_MAC) && \ !defined(WOLFSSL_AEAD_ONLY) && !defined(NO_RSA) && !defined(NO_AES) && \ defined(HAVE_AES_CBC) && !defined(NO_SHA256) && \ - defined(HAVE_SESSION_TICKET) && defined(HAVE_ECC) + defined(HAVE_SESSION_TICKET) && defined(HAVE_ECC) && \ + defined(WOLFSSL_AES_128) /* TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 - a CBC suite, where ETM applies. */ const char* cbcSuite = "ECDHE-RSA-AES128-SHA256"; WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; @@ -2960,7 +2964,8 @@ int test_tls12_ecdhe_ecdsa_rsa_client_cert(void) && defined(HAVE_AESGCM) && defined(KEEP_PEER_CERT) \ && !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) \ && !defined(WOLFSSL_NO_CLIENT_AUTH) \ - && !defined(NO_FILESYSTEM) && !defined(NO_CERTS) + && !defined(NO_FILESYSTEM) && !defined(NO_CERTS) && \ + defined(WOLFSSL_AES_128) WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; WOLFSSL *ssl_c = NULL, *ssl_s = NULL; struct test_memio_ctx test_ctx; @@ -3018,7 +3023,8 @@ int test_tls12_ecdhe_rsa_ecdsa_client_cert(void) && defined(HAVE_AESGCM) && defined(KEEP_PEER_CERT) \ && !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) \ && !defined(WOLFSSL_NO_CLIENT_AUTH) \ - && !defined(NO_FILESYSTEM) && !defined(NO_CERTS) + && !defined(NO_FILESYSTEM) && !defined(NO_CERTS) && \ + defined(WOLFSSL_AES_128) WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; WOLFSSL *ssl_c = NULL, *ssl_s = NULL; struct test_memio_ctx test_ctx; @@ -3378,3 +3384,319 @@ int test_record_size_cache_invalidated_on_renegotiation(void) #endif return EXPECT_RESULT(); } + +#if defined(OPENSSL_EXTRA) && defined(HAVE_TLS_EXTENSIONS) && \ + defined(HAVE_SNI) && !defined(NO_WOLFSSL_SERVER) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(WOLFSSL_NO_TLS12) && \ + !defined(NO_RSA) && !defined(NO_TLS) +static int test_ch_cb_calls = 0; +static int test_ch_cb_sni_ok = 0; +static int test_ch_cb_absent_ok = 0; +static int test_ch_cb_reject = 0; +static int test_ch_cb_retry = 0; + +static int test_client_hello_cb(WOLFSSL* ssl, int* al, void* arg) +{ + const unsigned char* ext = NULL; + size_t extLen = 0; + const char* want = (const char*)arg; + + test_ch_cb_calls++; + + /* ServerNameList: 2 byte list length, 1 byte type, 2 byte name length, + * then the name itself. */ + /* TLSX_SERVER_NAME, not the TLSEXT_TYPE_server_name of the compatibility + * layer: that name is only defined for OPENSSL_ALL, while the API under + * test is available for OPENSSL_EXTRA. Both are extension type 0. */ + if ((wolfSSL_client_hello_get0_ext(ssl, TLSX_SERVER_NAME, &ext, + &extLen) == 1) && (ext != NULL) && + (extLen == XSTRLEN(want) + 5) && + (XMEMCMP(ext + 5, want, XSTRLEN(want)) == 0)) { + test_ch_cb_sni_ok = 1; + } + + /* An extension that was not sent is reported as absent. */ + ext = NULL; + extLen = 0; + if ((wolfSSL_client_hello_get0_ext(ssl, 0xFEED, &ext, &extLen) == 0) && + (ext == NULL)) { + test_ch_cb_absent_ok = 1; + } + + if (test_ch_cb_reject) { + *al = 40; /* handshake_failure */ + return WOLFSSL_CLIENT_HELLO_ERROR; + } + if (test_ch_cb_retry) { + return WOLFSSL_CLIENT_HELLO_RETRY; + } + return WOLFSSL_CLIENT_HELLO_SUCCESS; +} +#endif + +#if defined(OPENSSL_EXTRA) && defined(HAVE_TLS_EXTENSIONS) && \ + !defined(NO_WOLFSSL_SERVER) && !defined(WOLFSSL_NO_TLS12) && \ + !defined(NO_RSA) && !defined(NO_TLS) && !defined(NO_CERTS) && \ + !defined(NO_FILESYSTEM) +static int test_ch_cb_noext_calls = 0; +static int test_ch_cb_noext_absent = 0; + +static int test_client_hello_cb_noext(WOLFSSL* ssl, int* al, void* arg) +{ + const unsigned char* ext = NULL; + size_t extLen = 0; + + (void)al; + (void)arg; + test_ch_cb_noext_calls++; + + /* With no extension block there is nothing to find, and looking is still + * safe. TLSXT_SERVER_NAME is the extension number itself, which + * HAVE_TLS_EXTENSIONS defines; the TLSX_ enum member of the same meaning + * exists only under HAVE_SNI, which this test does not need. */ + if (wolfSSL_client_hello_get0_ext(ssl, TLSXT_SERVER_NAME, &ext, + &extLen) == 0) { + test_ch_cb_noext_absent = 1; + } + + return WOLFSSL_CLIENT_HELLO_SUCCESS; +} + +/* A hand built TLS 1.2 ClientHello that stops after the compression list, so + * it carries no extension block at all - which a wolfSSL client never sends, + * hence the raw bytes. */ +static const unsigned char test_ch_noext[] = { + /* record: handshake, TLS 1.0 version, length */ + 0x16, 0x03, 0x01, 0x00, 0x2d, + /* handshake: client_hello, length */ + 0x01, 0x00, 0x00, 0x29, + /* client_version TLS 1.2 */ + 0x03, 0x03, + /* random */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + /* session id: empty */ + 0x00, + /* cipher suites: TLS_RSA_WITH_AES_128_CBC_SHA */ + 0x00, 0x02, 0x00, 0x2f, + /* compression methods: null */ + 0x01, 0x00 + /* and nothing further: no extensions */ +}; + +static unsigned int test_ch_noext_off = 0; + +static int test_ch_noext_recv(WOLFSSL* ssl, char* buf, int sz, void* ctx) +{ + unsigned int avail = (unsigned int)sizeof(test_ch_noext) - + test_ch_noext_off; + + (void)ssl; + (void)ctx; + if (avail == 0) { + return WOLFSSL_CBIO_ERR_WANT_READ; + } + if ((unsigned int)sz > avail) { + sz = (int)avail; + } + XMEMCPY(buf, test_ch_noext + test_ch_noext_off, (size_t)sz); + test_ch_noext_off += (unsigned int)sz; + + return sz; +} + +static int test_ch_noext_send(WOLFSSL* ssl, char* buf, int sz, void* ctx) +{ + (void)ssl; + (void)buf; + (void)ctx; + /* Whatever the server replies is of no interest here. */ + return sz; +} +#endif + +/* Extensions are optional in a TLS 1.2 ClientHello, and OpenSSL runs the + * callback for one that has none. The callback is the point at which an + * application picks a context, so it has to see every ClientHello, not only + * the ones that happen to carry extensions. + */ +int test_wolfSSL_client_hello_cb_no_extensions(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && defined(HAVE_TLS_EXTENSIONS) && \ + !defined(NO_WOLFSSL_SERVER) && !defined(WOLFSSL_NO_TLS12) && \ + !defined(NO_RSA) && !defined(NO_TLS) && !defined(NO_CERTS) && \ + !defined(NO_FILESYSTEM) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + + test_ch_cb_noext_calls = 0; + test_ch_cb_noext_absent = 0; + test_ch_noext_off = 0; + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_server_method())); + ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx, svrCertFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile, + WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); + wolfSSL_CTX_set_client_hello_cb(ctx, test_client_hello_cb_noext, NULL); + + ExpectNotNull(ssl = wolfSSL_new(ctx)); + wolfSSL_SSLSetIORecv(ssl, test_ch_noext_recv); + wolfSSL_SSLSetIOSend(ssl, test_ch_noext_send); + + /* The handshake cannot finish - there is no client on the other end - but + * the ClientHello is parsed, which is all this is about. */ + wolfSSL_accept(ssl); + ExpectIntEQ(test_ch_cb_noext_calls, 1); + ExpectIntEQ(test_ch_cb_noext_absent, 1); + + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} + +/* Test the ClientHello callback and raw extension access. + * + * haproxy (src/ssl_sock.c) and nginx (src/event/ngx_event_openssl.c) use this + * pair to pick a certificate from the ClientHello before it is processed. + */ +int test_wolfSSL_client_hello_cb(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && defined(HAVE_TLS_EXTENSIONS) && \ + defined(HAVE_SNI) && !defined(NO_WOLFSSL_SERVER) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(WOLFSSL_NO_TLS12) && \ + !defined(NO_RSA) && !defined(NO_TLS) + WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL; + WOLFSSL *ssl_c = NULL, *ssl_s = NULL; + struct test_memio_ctx test_ctx; + char sni[] = "hello-cb.example"; + + /* The callback runs, sees the SNI extension, and lets the handshake + * continue. */ + test_ch_cb_calls = 0; + test_ch_cb_sni_ok = 0; + test_ch_cb_absent_ok = 0; + test_ch_cb_reject = 0; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + wolfSSL_CTX_set_client_hello_cb(ctx_s, test_client_hello_cb, sni); + ExpectIntEQ(wolfSSL_UseSNI(ssl_c, WOLFSSL_SNI_HOST_NAME, + sni, (word16)XSTRLEN(sni)), WOLFSSL_SUCCESS); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectIntEQ(test_ch_cb_calls, 1); + ExpectIntEQ(test_ch_cb_sni_ok, 1); + ExpectIntEQ(test_ch_cb_absent_ok, 1); + + wolfSSL_free(ssl_c); ssl_c = NULL; + wolfSSL_free(ssl_s); ssl_s = NULL; + wolfSSL_CTX_free(ctx_c); ctx_c = NULL; + wolfSSL_CTX_free(ctx_s); ctx_s = NULL; + + /* A callback that rejects fails the handshake. */ + test_ch_cb_calls = 0; + test_ch_cb_reject = 1; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + wolfSSL_CTX_set_client_hello_cb(ctx_s, test_client_hello_cb, sni); + ExpectIntEQ(wolfSSL_UseSNI(ssl_c, WOLFSSL_SNI_HOST_NAME, + sni, (word16)XSTRLEN(sni)), WOLFSSL_SUCCESS); + ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectIntEQ(test_ch_cb_calls, 1); + test_ch_cb_reject = 0; + + wolfSSL_free(ssl_c); ssl_c = NULL; + wolfSSL_free(ssl_s); ssl_s = NULL; + wolfSSL_CTX_free(ctx_c); ctx_c = NULL; + wolfSSL_CTX_free(ctx_s); ctx_s = NULL; + + /* OpenSSL lets the callback return SSL_CLIENT_HELLO_RETRY to pause the + * handshake and be called again. wolfSSL cannot pause it, so the value is + * documented as unsupported and treated as a failure - anything other + * than success stops the handshake. Pinned here so a later change cannot + * quietly start reading it as success and let the ClientHello through. */ + test_ch_cb_calls = 0; + test_ch_cb_retry = 1; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0); + wolfSSL_CTX_set_client_hello_cb(ctx_s, test_client_hello_cb, sni); + ExpectIntEQ(wolfSSL_UseSNI(ssl_c, WOLFSSL_SNI_HOST_NAME, + sni, (word16)XSTRLEN(sni)), WOLFSSL_SUCCESS); + ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectIntEQ(test_ch_cb_calls, 1); + test_ch_cb_retry = 0; + + wolfSSL_free(ssl_c); ssl_c = NULL; + wolfSSL_free(ssl_s); ssl_s = NULL; + wolfSSL_CTX_free(ctx_c); ctx_c = NULL; + wolfSSL_CTX_free(ctx_s); ctx_s = NULL; + +#ifdef WOLFSSL_TLS13 + /* The same callback runs on the TLS 1.3 ClientHello, which is parsed by a + * different path. This is the one haproxy and nginx actually hit. */ + test_ch_cb_calls = 0; + test_ch_cb_sni_ok = 0; + test_ch_cb_absent_ok = 0; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); + wolfSSL_CTX_set_client_hello_cb(ctx_s, test_client_hello_cb, sni); + ExpectIntEQ(wolfSSL_UseSNI(ssl_c, WOLFSSL_SNI_HOST_NAME, + sni, (word16)XSTRLEN(sni)), WOLFSSL_SUCCESS); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + /* Exactly once for a ClientHello the server can act on immediately. */ + ExpectIntEQ(test_ch_cb_calls, 1); + ExpectIntEQ(test_ch_cb_sni_ok, 1); + ExpectIntEQ(test_ch_cb_absent_ok, 1); + + wolfSSL_free(ssl_c); ssl_c = NULL; + wolfSSL_free(ssl_s); ssl_s = NULL; + wolfSSL_CTX_free(ctx_c); ctx_c = NULL; + wolfSSL_CTX_free(ctx_s); ctx_s = NULL; + +/* The retry is driven by offering P-256 and having the server insist on + * P-384, so both curves have to be usable in this build. */ +#if defined(HAVE_ECC) && !defined(NO_ECC_SECP) && \ + defined(HAVE_SUPPORTED_CURVES) && \ + (!defined(NO_ECC256) || defined(HAVE_ALL_CURVES)) && \ + (defined(HAVE_ECC384) || defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 256 + /* A HelloRetryRequest makes the client send a second ClientHello, and the + * callback runs on each one. OpenSSL 3.5 behaves the same way, so a + * consumer selecting a certificate here must expect to be asked twice. */ + test_ch_cb_calls = 0; + test_ch_cb_sni_ok = 0; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0); + wolfSSL_CTX_set_client_hello_cb(ctx_s, test_client_hello_cb, sni); + ExpectIntEQ(wolfSSL_UseSNI(ssl_c, WOLFSSL_SNI_HOST_NAME, + sni, (word16)XSTRLEN(sni)), WOLFSSL_SUCCESS); + /* The client key shares P-256; the server will only accept P-384. */ + ExpectIntEQ(wolfSSL_UseKeyShare(ssl_c, WOLFSSL_ECC_SECP256R1), + WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_set1_groups_list(ssl_s, "P-384"), WOLFSSL_SUCCESS); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectIntEQ(test_ch_cb_calls, 2); + ExpectIntEQ(test_ch_cb_sni_ok, 1); + + wolfSSL_free(ssl_c); ssl_c = NULL; + wolfSSL_free(ssl_s); ssl_s = NULL; + wolfSSL_CTX_free(ctx_c); ctx_c = NULL; + wolfSSL_CTX_free(ctx_s); ctx_s = NULL; +#endif +#endif +#endif + return EXPECT_RESULT(); +} diff --git a/tests/api/test_tls.h b/tests/api/test_tls.h index 6c3fda89962..989b50b809b 100644 --- a/tests/api/test_tls.h +++ b/tests/api/test_tls.h @@ -66,6 +66,8 @@ int test_record_size_matches_build_message(void); int test_record_size_preserves_build_msg_state(void); int test_record_size_cache_invalidated_on_renegotiation(void); int test_wolfSSL_get_shared_ciphers(void); +int test_wolfSSL_client_hello_cb(void); +int test_wolfSSL_client_hello_cb_no_extensions(void); #define TEST_TLS_DECLS \ TEST_DECL_GROUP("tls", test_utils_memio_move_message), \ @@ -114,6 +116,8 @@ int test_wolfSSL_get_shared_ciphers(void); test_record_size_preserves_build_msg_state), \ TEST_DECL_GROUP("tls", \ test_record_size_cache_invalidated_on_renegotiation), \ - TEST_DECL_GROUP("tls", test_wolfSSL_get_shared_ciphers) + TEST_DECL_GROUP("tls", test_wolfSSL_get_shared_ciphers), \ + TEST_DECL_GROUP("tls", test_wolfSSL_client_hello_cb), \ + TEST_DECL_GROUP("tls", test_wolfSSL_client_hello_cb_no_extensions) #endif /* TESTS_API_TEST_TLS_H */ diff --git a/tests/api/test_tls13.c b/tests/api/test_tls13.c index b387e5beb92..f042360f905 100644 --- a/tests/api/test_tls13.c +++ b/tests/api/test_tls13.c @@ -6494,7 +6494,8 @@ int test_key_share_mismatch(void) EXPECT_DECLS; #if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13) && \ defined(HAVE_SUPPORTED_CURVES) && defined(HAVE_ECC) && \ - defined(BUILD_TLS_AES_128_GCM_SHA256) && (!defined(WOLFSSL_SP_MATH) || \ + defined(BUILD_TLS_AES_128_GCM_SHA256) && \ + (defined(HAVE_ECC384) || defined(HAVE_ALL_CURVES)) && (defined(HAVE_ECC521) || defined(HAVE_ALL_CURVES)) && (!defined(WOLFSSL_SP_MATH) || \ (defined(WOLFSSL_SP_521) && !defined(WOLFSSL_SP_NO_256) && \ defined(WOLFSSL_SP_384))) /* Taken from payload in https://github.com/wolfSSL/wolfssl/issues/9362 */ diff --git a/tests/api/test_tls_bounds.c b/tests/api/test_tls_bounds.c index da3befb3784..d23e7e22163 100644 --- a/tests/api/test_tls_bounds.c +++ b/tests/api/test_tls_bounds.c @@ -2101,7 +2101,8 @@ int test_TLSX_CSR2_Parse_bounds(void) { #if defined(WOLFSSL_TEST_STATIC_BUILD) && defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2) && !defined(NO_WOLFSSL_SERVER) && \ defined(HAVE_TLS_EXTENSIONS) && \ - !defined(WOLFSSL_NO_TLS12) + !defined(WOLFSSL_NO_TLS12) && \ + !defined(NO_CERTS) && !defined(NO_RSA) && !defined(NO_FILESYSTEM) EXPECT_DECLS; WOLFSSL_CTX* ctx = NULL; WOLFSSL* ssl = NULL; @@ -2342,7 +2343,8 @@ int test_TLSX_WriteRequest_length_prefix_bounds(void) int test_TLSX_WriteResponse_bounds(void) { #if defined(WOLFSSL_TEST_STATIC_BUILD) && defined(HAVE_EXTENDED_MASTER) && !defined(NO_WOLFSSL_SERVER) && !defined(WOLFSSL_NO_TLS12) && \ - defined(HAVE_TLS_EXTENSIONS) + defined(HAVE_TLS_EXTENSIONS) && \ + !defined(NO_CERTS) && !defined(NO_RSA) && !defined(NO_FILESYSTEM) EXPECT_DECLS; WOLFSSL_CTX* ctx = NULL; WOLFSSL* ssl = NULL; diff --git a/tests/api/test_tls_msgtype.c b/tests/api/test_tls_msgtype.c index c480696fe47..501bd00e3aa 100644 --- a/tests/api/test_tls_msgtype.c +++ b/tests/api/test_tls_msgtype.c @@ -1181,7 +1181,8 @@ int test_tls_msgtype_server_cert_type_tls12(void) int test_tls_msgtype_connection_id(void) { EXPECT_DECLS; -#if defined(WOLFSSL_DTLS_CID) && !defined(NO_WOLFSSL_CLIENT) && !defined(NO_TLS) && \ +#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_DTLS_CID) && \ + !defined(NO_WOLFSSL_CLIENT) && !defined(NO_TLS) && \ defined(HAVE_TLS_EXTENSIONS) WOLFSSL_CTX* ctx = NULL; WOLFSSL* ssl = NULL; diff --git a/tests/api/test_tls_parse.c b/tests/api/test_tls_parse.c index baff3de2ee6..119d3e0cc07 100644 --- a/tests/api/test_tls_parse.c +++ b/tests/api/test_tls_parse.c @@ -2500,7 +2500,7 @@ int test_TLSX_KeyShare_gen(void) EXPECT_DECLS; #if defined(WOLFSSL_TLS13) && defined(WOLFSSL_TEST_STATIC_BUILD) && \ defined(HAVE_TLS_EXTENSIONS) && \ - defined(USE_WOLFSSL_MEMORY) + defined(USE_WOLFSSL_MEMORY) && !defined(NO_WOLFSSL_CLIENT) WOLFSSL_CTX* ctx = NULL; WOLFSSL* ssl = NULL; @@ -2721,7 +2721,7 @@ int test_TLSX_KeyShare_freesizewrite(void) { EXPECT_DECLS; #if defined(WOLFSSL_TLS13) && defined(HAVE_SUPPORTED_CURVES) && !defined(NO_DH) && defined(HAVE_FFDHE_2048) && defined(WOLFSSL_TEST_STATIC_BUILD) && \ - defined(HAVE_TLS_EXTENSIONS) + defined(HAVE_TLS_EXTENSIONS) && !defined(NO_WOLFSSL_CLIENT) WOLFSSL_CTX* ctx = NULL; WOLFSSL* ssl = NULL; @@ -2873,7 +2873,7 @@ int test_TLSX_KeyShare_process(void) { EXPECT_DECLS; #if defined(WOLFSSL_TLS13) && defined(HAVE_SUPPORTED_CURVES) && defined(WOLFSSL_TEST_STATIC_BUILD) && \ - defined(HAVE_TLS_EXTENSIONS) + defined(HAVE_TLS_EXTENSIONS) && !defined(NO_WOLFSSL_CLIENT) WOLFSSL_CTX* ctx = NULL; WOLFSSL* ssl = NULL; diff --git a/tests/api/test_wc_encrypt.c b/tests/api/test_wc_encrypt.c index fea5c5ecb0f..4064cd65ad8 100644 --- a/tests/api/test_wc_encrypt.c +++ b/tests/api/test_wc_encrypt.c @@ -164,7 +164,8 @@ int test_wc_Des_CbcEncryptDecryptWithKey(void) int test_wc_AesCbcEncryptDecryptWithKey(void) { EXPECT_DECLS; -#if !defined(NO_AES) && defined(HAVE_AES_CBC) && defined(HAVE_AES_DECRYPT) +#if !defined(NO_AES) && defined(HAVE_AES_CBC) && defined(HAVE_AES_DECRYPT) && \ + defined(WOLFSSL_AES_128) const byte key[16] = { 0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef, 0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10 @@ -221,7 +222,8 @@ int test_wc_BufferKeyEncryptDecryptDecisionCoverage(void) { EXPECT_DECLS; #if !defined(NO_ASN) && defined(WOLFSSL_ENCRYPTED_KEYS) && \ - !defined(NO_PWDBASED) && !defined(NO_SHA) + !defined(NO_PWDBASED) && !defined(NO_SHA) && \ + defined(WOLFSSL_AES_128) const byte pw[] = { 'p','a','s','s','w','o','r','d' }; EncryptedInfo info; byte der[32]; diff --git a/tests/suites.c b/tests/suites.c index f63de1d2880..059bde7c9ee 100644 --- a/tests/suites.c +++ b/tests/suites.c @@ -215,7 +215,8 @@ static int IsKyberLevelAvailable(const char* line) #endif /* WOLFSSL_TLS_NO_MLKEM_STANDALONE */ #ifdef WOLFSSL_PQC_HYBRIDS #if !defined(WOLFSSL_NO_ML_KEM_768) && defined(HAVE_ECC) && \ - (!defined(WOLFSSL_SP_MATH) || !defined(WOLFSSL_SP_NO_256)) + (!defined(WOLFSSL_SP_MATH) || !defined(WOLFSSL_SP_NO_256)) && \ + (!defined(NO_ECC256) || defined(HAVE_ALL_CURVES)) if (MATCH_PQC(begin, "SecP256r1MLKEM768", len)) { available = 1; } @@ -226,14 +227,16 @@ static int IsKyberLevelAvailable(const char* line) } #endif #if !defined(WOLFSSL_NO_ML_KEM_1024) && defined(HAVE_ECC) && \ - (!defined(WOLFSSL_SP_MATH) || defined(WOLFSSL_SP_384)) + (!defined(WOLFSSL_SP_MATH) || defined(WOLFSSL_SP_384)) && \ + (defined(HAVE_ECC384) || defined(HAVE_ALL_CURVES)) if (MATCH_PQC(begin, "SecP384r1MLKEM1024", len)) { available = 1; } #endif #endif /* WOLFSSL_PQC_HYBRIDS */ #ifdef WOLFSSL_EXTRA_PQC_HYBRIDS - #if !defined(WOLFSSL_NO_ML_KEM_512) && defined(HAVE_ECC) + #if !defined(WOLFSSL_NO_ML_KEM_512) && defined(HAVE_ECC) && \ + (!defined(NO_ECC256) || defined(HAVE_ALL_CURVES)) if (MATCH_PQC(begin, "SecP256r1MLKEM512", len)) { available = 1; } @@ -249,7 +252,8 @@ static int IsKyberLevelAvailable(const char* line) } #endif #if !defined(WOLFSSL_NO_ML_KEM_768) && defined(HAVE_ECC) && \ - (!defined(WOLFSSL_SP_MATH) || defined(WOLFSSL_SP_384)) + (!defined(WOLFSSL_SP_MATH) || defined(WOLFSSL_SP_384)) && \ + (defined(HAVE_ECC384) || defined(HAVE_ALL_CURVES)) if (MATCH_PQC(begin, "SecP384r1MLKEM768", len)) { available = 1; } diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index 6623eb858bb..ee394d8207d 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -29668,8 +29668,10 @@ static int EncodeExtensions(Cert* cert, byte* output, word32 maxSz, static const byte kuOID[] = { 0x55, 0x1d, 0x0f }; static const byte ekuOID[] = { 0x55, 0x1d, 0x25 }; static const byte cpOID[] = { 0x55, 0x1d, 0x20 }; +#ifndef IGNORE_NETSCAPE_CERT_TYPE static const byte nsCertOID[] = { 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x42, 0x01, 0x01 }; +#endif static const byte crlInfoOID[] = { 0x55, 0x1D, 0x1F }; #ifdef WOLFSSL_ACME_OID static const byte acmeIdOID[] = { 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, diff --git a/wolfcrypt/src/evp.c b/wolfcrypt/src/evp.c index 1744c754571..067a44f1817 100644 --- a/wolfcrypt/src/evp.c +++ b/wolfcrypt/src/evp.c @@ -166,6 +166,30 @@ static const struct s_ent { #endif /* WOLFSSL_AES_XTS && (!defined(HAVE_FIPS) || FIPS_VERSION_GE(5,3)) */ + #if defined(WOLFSSL_EVP_AES_KEYWRAP) + /* OpenSSL's short names for these ciphers. */ + #ifdef WOLFSSL_AES_128 + static const char EVP_AES_128_WRAP[] = "id-aes128-wrap"; + #endif + #ifdef WOLFSSL_AES_192 + static const char EVP_AES_192_WRAP[] = "id-aes192-wrap"; + #endif + #ifdef WOLFSSL_AES_256 + static const char EVP_AES_256_WRAP[] = "id-aes256-wrap"; + #endif + #ifdef WOLFSSL_AES_KEYWRAP_PADDING + #ifdef WOLFSSL_AES_128 + static const char EVP_AES_128_WRAP_PAD[] = "id-aes128-wrap-pad"; + #endif + #ifdef WOLFSSL_AES_192 + static const char EVP_AES_192_WRAP_PAD[] = "id-aes192-wrap-pad"; + #endif + #ifdef WOLFSSL_AES_256 + static const char EVP_AES_256_WRAP_PAD[] = "id-aes256-wrap-pad"; + #endif + #endif /* WOLFSSL_AES_KEYWRAP_PADDING */ + #endif /* WOLFSSL_EVP_AES_KEYWRAP */ + #ifdef WOLFSSL_AES_CFB #ifndef WOLFSSL_NO_AES_CFB_1_8 #ifdef WOLFSSL_AES_128 @@ -383,6 +407,17 @@ int wolfSSL_EVP_Cipher_key_length(const WOLFSSL_EVP_CIPHER* c) case WC_AES_128_XTS_TYPE: return 16 * 2; case WC_AES_256_XTS_TYPE: return 32 * 2; #endif + #if defined(WOLFSSL_EVP_AES_KEYWRAP) + case WC_AES_128_WRAP_TYPE: return 16; + case WC_AES_192_WRAP_TYPE: return 24; + case WC_AES_256_WRAP_TYPE: return 32; + #endif + #if defined(WOLFSSL_EVP_AES_KEYWRAP) && \ + defined(WOLFSSL_AES_KEYWRAP_PADDING) + case WC_AES_128_WRAP_PAD_TYPE: return 16; + case WC_AES_192_WRAP_PAD_TYPE: return 24; + case WC_AES_256_WRAP_PAD_TYPE: return 32; + #endif #if defined(HAVE_AESGCM) case WC_AES_128_GCM_TYPE: return 16; case WC_AES_192_GCM_TYPE: return 24; @@ -515,6 +550,23 @@ int wolfSSL_EVP_CIPHER_CTX_reset(WOLFSSL_EVP_CIPHER_CTX *ctx) unsigned long wolfSSL_EVP_CIPHER_CTX_mode(const WOLFSSL_EVP_CIPHER_CTX *ctx) { if (ctx == NULL) return 0; +#if defined(WOLFSSL_EVP_AES_KEYWRAP) + /* WOLFSSL_EVP_CIPH_MODE only covers the low three bits, so the wrap mode + * does not survive the mask; report it from the cipher type instead. */ + switch (ctx->cipherType) { + case WC_AES_128_WRAP_TYPE: + case WC_AES_192_WRAP_TYPE: + case WC_AES_256_WRAP_TYPE: + #ifdef WOLFSSL_AES_KEYWRAP_PADDING + case WC_AES_128_WRAP_PAD_TYPE: + case WC_AES_192_WRAP_PAD_TYPE: + case WC_AES_256_WRAP_PAD_TYPE: + #endif + return WOLFSSL_EVP_CIPH_WRAP_MODE; + default: + break; + } +#endif return ctx->flags & WOLFSSL_EVP_CIPH_MODE; } @@ -1069,6 +1121,129 @@ static int wolfSSL_EVP_CipherUpdate_AriaGCM(WOLFSSL_EVP_CIPHER_CTX *ctx, /* returns WOLFSSL_SUCCESS on success and WOLFSSL_FAILURE on failure */ +#if defined(WOLFSSL_EVP_AES_KEYWRAP) +/* Wrap or unwrap a key with AES-KW (RFC 3394). + * + * Key wrapping is not a streaming operation: the whole key has to be present + * at once, so this does the work here and CipherFinal has nothing left to + * emit. OpenSSL's wrap ciphers behave the same way, producing their output + * from the update call. + * + * A second call carrying more data is refused rather than quietly wrapping + * the pieces separately, which would produce something that does not unwrap. + * + * Returns WOLFSSL_SUCCESS on success, WOLFSSL_FAILURE otherwise. + */ +static int wolfSSL_EVP_CipherUpdate_Wrap(WOLFSSL_EVP_CIPHER_CTX *ctx, + unsigned char *out, int *outl, const unsigned char *in, int inl) +{ + int ret; + const byte* iv = NULL; + int pad = 0; + + *outl = 0; + + if (ctx->bufUsed != 0) { + WOLFSSL_MSG("AES key wrap takes all of its input at once"); + return WOLFSSL_FAILURE; + } + +#ifdef WOLFSSL_AES_KEYWRAP_PADDING + switch (ctx->cipherType) { + case WC_AES_128_WRAP_PAD_TYPE: + case WC_AES_192_WRAP_PAD_TYPE: + case WC_AES_256_WRAP_PAD_TYPE: + pad = 1; + break; + default: + break; + } +#endif + + if (in == NULL || inl <= 0) { + WOLFSSL_MSG("Bad argument"); + return WOLFSSL_FAILURE; + } + + /* Unwrapping needs at least the integrity check value plus one semiblock, + * and RFC 3394 needs two. Reject short input here as well as on the real + * call, so a size query cannot answer with a negative length. */ + if (!ctx->enc) { + int minIn = pad ? (2 * KEYWRAP_BLOCK_SIZE) : (3 * KEYWRAP_BLOCK_SIZE); + + if ((inl < minIn) || ((inl % KEYWRAP_BLOCK_SIZE) != 0)) { + WOLFSSL_MSG("AES key wrap input too short"); + return WOLFSSL_FAILURE; + } + } + + /* Report the size needed, as OpenSSL does for a NULL output. RFC 5649 + * rounds the plaintext up to a semiblock before adding the header, and on + * the way back the true length is only known once the header has been + * checked, so this is the largest it can be. */ + if (out == NULL) { + if (ctx->enc) { + *outl = pad ? + (((inl + KEYWRAP_BLOCK_SIZE - 1) / KEYWRAP_BLOCK_SIZE) * + KEYWRAP_BLOCK_SIZE) + KEYWRAP_BLOCK_SIZE : + (inl + KEYWRAP_BLOCK_SIZE); + } + else { + *outl = inl - KEYWRAP_BLOCK_SIZE; + } + return WOLFSSL_SUCCESS; + } + + /* Whether an integrity check value was given is recorded at init rather + * than guessed from the value: all-zero is a legitimate ICV and has to + * stay distinct from asking for the RFC default. The unpadded form takes + * the whole 8 byte value; the padded form's is 4 bytes, overriding only + * the AIV constant, since the low half carries the length. */ + if (ctx->kwIvSupplied) { + iv = ctx->iv; + } + +#ifdef WOLFSSL_AES_KEYWRAP_PADDING + if (pad) { + word32 outSz = ctx->enc ? + (word32)((((inl + KEYWRAP_BLOCK_SIZE - 1) / KEYWRAP_BLOCK_SIZE) * + KEYWRAP_BLOCK_SIZE) + KEYWRAP_BLOCK_SIZE) : + (word32)(inl - KEYWRAP_BLOCK_SIZE); + + if (ctx->enc) { + ret = wc_AesKeyWrap_Pad_ex(&ctx->cipher.aes, in, (word32)inl, out, + outSz, iv); + } + else { + ret = wc_AesKeyUnWrap_Pad_ex(&ctx->cipher.aes, in, (word32)inl, + out, outSz, iv); + } + } + else +#endif + if (ctx->enc) { + ret = wc_AesKeyWrap_ex(&ctx->cipher.aes, in, (word32)inl, out, + (word32)(inl + KEYWRAP_BLOCK_SIZE), iv); + } + else { + ret = wc_AesKeyUnWrap_ex(&ctx->cipher.aes, in, (word32)inl, out, + (word32)(inl - KEYWRAP_BLOCK_SIZE), iv); + } + + if (ret < 0) { + WOLFSSL_MSG("AES key wrap failed"); + return WOLFSSL_FAILURE; + } + + *outl = ret; + /* Remember that the one shot has been taken, so a second update with + * data is caught above. */ + ctx->bufUsed = 1; + + return WOLFSSL_SUCCESS; +} +#endif /* WOLFSSL_EVP_AES_KEYWRAP */ + int wolfSSL_EVP_CipherUpdate(WOLFSSL_EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl, const unsigned char *in, int inl) @@ -1094,6 +1269,20 @@ int wolfSSL_EVP_CipherUpdate(WOLFSSL_EVP_CIPHER_CTX *ctx, } switch (ctx->cipherType) { +#if defined(WOLFSSL_EVP_AES_KEYWRAP) + case WC_AES_128_WRAP_TYPE: + case WC_AES_192_WRAP_TYPE: + case WC_AES_256_WRAP_TYPE: + WOLFSSL_MSG("AES key wrap"); + return wolfSSL_EVP_CipherUpdate_Wrap(ctx, out, outl, in, inl); +#ifdef WOLFSSL_AES_KEYWRAP_PADDING + case WC_AES_128_WRAP_PAD_TYPE: + case WC_AES_192_WRAP_PAD_TYPE: + case WC_AES_256_WRAP_PAD_TYPE: + WOLFSSL_MSG("AES key wrap with padding"); + return wolfSSL_EVP_CipherUpdate_Wrap(ctx, out, outl, in, inl); +#endif +#endif /* WOLFSSL_EVP_AES_KEYWRAP */ case WC_NULL_CIPHER_TYPE: if (out == NULL) { WOLFSSL_MSG("Bad argument"); @@ -1320,6 +1509,21 @@ int wolfSSL_EVP_CipherFinal(WOLFSSL_EVP_CIPHER_CTX *ctx, unsigned char *out, WOLFSSL_ENTER("wolfSSL_EVP_CipherFinal"); switch (ctx->cipherType) { +#if defined(WOLFSSL_EVP_AES_KEYWRAP) + case WC_AES_128_WRAP_TYPE: + case WC_AES_192_WRAP_TYPE: + case WC_AES_256_WRAP_TYPE: + /* The wrap was done by CipherUpdate, so there is nothing left to + * emit here, as with OpenSSL's wrap ciphers. */ +#ifdef WOLFSSL_AES_KEYWRAP_PADDING + case WC_AES_128_WRAP_PAD_TYPE: + case WC_AES_192_WRAP_PAD_TYPE: + case WC_AES_256_WRAP_PAD_TYPE: +#endif + *outl = 0; + ret = WOLFSSL_SUCCESS; + break; +#endif /* WOLFSSL_EVP_AES_KEYWRAP */ #if defined(HAVE_AESGCM) && ((!defined(HAVE_FIPS) && !defined(HAVE_SELFTEST)) \ || FIPS_VERSION_GE(2,0)) case WC_AES_128_GCM_TYPE: @@ -1913,6 +2117,19 @@ int wolfSSL_EVP_CIPHER_CTX_block_size(const WOLFSSL_EVP_CIPHER_CTX *ctx) case WC_SM4_CCM_TYPE: #endif return ctx->block_size; +#if defined(WOLFSSL_EVP_AES_KEYWRAP) + case WC_AES_128_WRAP_TYPE: + case WC_AES_192_WRAP_TYPE: + case WC_AES_256_WRAP_TYPE: + return ctx->block_size; +#endif +#if defined(WOLFSSL_EVP_AES_KEYWRAP) && \ + defined(WOLFSSL_AES_KEYWRAP_PADDING) + case WC_AES_128_WRAP_PAD_TYPE: + case WC_AES_192_WRAP_PAD_TYPE: + case WC_AES_256_WRAP_PAD_TYPE: + return ctx->block_size; +#endif #endif /* !NO_AES || !NO_DES3 || WOLFSSL_SM4 */ default: return 0; @@ -2015,6 +2232,35 @@ static unsigned int cipherType(const WOLFSSL_EVP_CIPHER *cipher) return WC_AES_256_XTS_TYPE; #endif #endif /* WOLFSSL_AES_XTS */ +#if defined(WOLFSSL_EVP_AES_KEYWRAP) + #ifdef WOLFSSL_AES_128 + else if (EVP_CIPHER_TYPE_MATCHES(cipher, EVP_AES_128_WRAP)) + return WC_AES_128_WRAP_TYPE; + #endif + #ifdef WOLFSSL_AES_192 + else if (EVP_CIPHER_TYPE_MATCHES(cipher, EVP_AES_192_WRAP)) + return WC_AES_192_WRAP_TYPE; + #endif + #ifdef WOLFSSL_AES_256 + else if (EVP_CIPHER_TYPE_MATCHES(cipher, EVP_AES_256_WRAP)) + return WC_AES_256_WRAP_TYPE; + #endif +#endif /* WOLFSSL_EVP_AES_KEYWRAP */ +#if defined(WOLFSSL_EVP_AES_KEYWRAP) && \ + defined(WOLFSSL_AES_KEYWRAP_PADDING) + #ifdef WOLFSSL_AES_128 + else if (EVP_CIPHER_TYPE_MATCHES(cipher, EVP_AES_128_WRAP_PAD)) + return WC_AES_128_WRAP_PAD_TYPE; + #endif + #ifdef WOLFSSL_AES_192 + else if (EVP_CIPHER_TYPE_MATCHES(cipher, EVP_AES_192_WRAP_PAD)) + return WC_AES_192_WRAP_PAD_TYPE; + #endif + #ifdef WOLFSSL_AES_256 + else if (EVP_CIPHER_TYPE_MATCHES(cipher, EVP_AES_256_WRAP_PAD)) + return WC_AES_256_WRAP_PAD_TYPE; + #endif +#endif /* ... WOLFSSL_AES_KEYWRAP_PADDING */ #if defined(WOLFSSL_AES_CFB) #ifndef WOLFSSL_NO_AES_CFB_1_8 #ifdef WOLFSSL_AES_128 @@ -2197,6 +2443,29 @@ const char* wolfSSL_EVP_CIPHER_type_string(unsigned int cipherType) case WC_AES_256_XTS_TYPE: return EVP_AES_256_XTS; #endif #endif /* WOLFSSL_AES_XTS && (!defined(HAVE_FIPS) || FIPS_VERSION_GE(5,3)) */ + #if defined(WOLFSSL_EVP_AES_KEYWRAP) + #ifdef WOLFSSL_AES_128 + case WC_AES_128_WRAP_TYPE: return EVP_AES_128_WRAP; + #endif + #ifdef WOLFSSL_AES_192 + case WC_AES_192_WRAP_TYPE: return EVP_AES_192_WRAP; + #endif + #ifdef WOLFSSL_AES_256 + case WC_AES_256_WRAP_TYPE: return EVP_AES_256_WRAP; + #endif + #endif /* WOLFSSL_EVP_AES_KEYWRAP */ + #if defined(WOLFSSL_EVP_AES_KEYWRAP) && \ + defined(WOLFSSL_AES_KEYWRAP_PADDING) + #ifdef WOLFSSL_AES_128 + case WC_AES_128_WRAP_PAD_TYPE: return EVP_AES_128_WRAP_PAD; + #endif + #ifdef WOLFSSL_AES_192 + case WC_AES_192_WRAP_PAD_TYPE: return EVP_AES_192_WRAP_PAD; + #endif + #ifdef WOLFSSL_AES_256 + case WC_AES_256_WRAP_PAD_TYPE: return EVP_AES_256_WRAP_PAD; + #endif + #endif /* ... WOLFSSL_AES_KEYWRAP_PADDING */ #if defined(HAVE_AESGCM) #ifdef WOLFSSL_AES_128 case WC_AES_128_GCM_TYPE: return EVP_AES_128_GCM; @@ -2338,6 +2607,19 @@ int wolfSSL_EVP_CIPHER_block_size(const WOLFSSL_EVP_CIPHER *cipher) case WC_AES_256_XTS_TYPE: return 1; #endif + #if defined(WOLFSSL_EVP_AES_KEYWRAP) + case WC_AES_128_WRAP_TYPE: + case WC_AES_192_WRAP_TYPE: + case WC_AES_256_WRAP_TYPE: + return 8; + #endif + #if defined(WOLFSSL_EVP_AES_KEYWRAP) && \ + defined(WOLFSSL_AES_KEYWRAP_PADDING) + case WC_AES_128_WRAP_PAD_TYPE: + case WC_AES_192_WRAP_PAD_TYPE: + case WC_AES_256_WRAP_PAD_TYPE: + return 8; + #endif /* ... WOLFSSL_AES_KEYWRAP_PADDING */ #endif /* NO_AES */ #ifndef NO_RC4 @@ -2448,6 +2730,19 @@ unsigned long WOLFSSL_CIPHER_mode(const WOLFSSL_EVP_CIPHER *cipher) case WC_AES_256_XTS_TYPE: return WOLFSSL_EVP_CIPH_XTS_MODE; #endif + #if defined(WOLFSSL_EVP_AES_KEYWRAP) + case WC_AES_128_WRAP_TYPE: + case WC_AES_192_WRAP_TYPE: + case WC_AES_256_WRAP_TYPE: + return WOLFSSL_EVP_CIPH_WRAP_MODE; + #endif + #if defined(WOLFSSL_EVP_AES_KEYWRAP) && \ + defined(WOLFSSL_AES_KEYWRAP_PADDING) + case WC_AES_128_WRAP_PAD_TYPE: + case WC_AES_192_WRAP_PAD_TYPE: + case WC_AES_256_WRAP_PAD_TYPE: + return WOLFSSL_EVP_CIPH_WRAP_MODE; + #endif /* ... WOLFSSL_AES_KEYWRAP_PADDING */ case WC_AES_128_ECB_TYPE: case WC_AES_192_ECB_TYPE: case WC_AES_256_ECB_TYPE: @@ -5674,6 +5969,32 @@ static const struct cipher{ {WC_AES_256_XTS_TYPE, EVP_AES_256_XTS, WC_NID_aes_256_xts}, #endif #endif + #if defined(WOLFSSL_EVP_AES_KEYWRAP) + #ifdef WOLFSSL_AES_128 + {WC_AES_128_WRAP_TYPE, EVP_AES_128_WRAP, WC_NID_id_aes128_wrap}, + #endif + #ifdef WOLFSSL_AES_192 + {WC_AES_192_WRAP_TYPE, EVP_AES_192_WRAP, WC_NID_id_aes192_wrap}, + #endif + #ifdef WOLFSSL_AES_256 + {WC_AES_256_WRAP_TYPE, EVP_AES_256_WRAP, WC_NID_id_aes256_wrap}, + #endif + #endif /* WOLFSSL_EVP_AES_KEYWRAP */ + #if defined(WOLFSSL_EVP_AES_KEYWRAP) && \ + defined(WOLFSSL_AES_KEYWRAP_PADDING) + #ifdef WOLFSSL_AES_128 + {WC_AES_128_WRAP_PAD_TYPE, EVP_AES_128_WRAP_PAD, + WC_NID_id_aes128_wrap_pad}, + #endif + #ifdef WOLFSSL_AES_192 + {WC_AES_192_WRAP_PAD_TYPE, EVP_AES_192_WRAP_PAD, + WC_NID_id_aes192_wrap_pad}, + #endif + #ifdef WOLFSSL_AES_256 + {WC_AES_256_WRAP_PAD_TYPE, EVP_AES_256_WRAP_PAD, + WC_NID_id_aes256_wrap_pad}, + #endif + #endif /* ... WOLFSSL_AES_KEYWRAP_PADDING */ #ifdef HAVE_AESGCM #ifdef WOLFSSL_AES_128 @@ -6440,6 +6761,53 @@ void wolfSSL_EVP_init(void) #endif /* WOLFSSL_AES_XTS && (!defined(HAVE_FIPS) || FIPS_VERSION_GE(5,3)) */ + #if defined(WOLFSSL_EVP_AES_KEYWRAP) + #ifdef WOLFSSL_AES_128 + const WOLFSSL_EVP_CIPHER* wolfSSL_EVP_aes_128_wrap(void) + { + WOLFSSL_ENTER("wolfSSL_EVP_aes_128_wrap"); + return EVP_AES_128_WRAP; + } + #endif /* WOLFSSL_AES_128 */ + #ifdef WOLFSSL_AES_192 + const WOLFSSL_EVP_CIPHER* wolfSSL_EVP_aes_192_wrap(void) + { + WOLFSSL_ENTER("wolfSSL_EVP_aes_192_wrap"); + return EVP_AES_192_WRAP; + } + #endif /* WOLFSSL_AES_192 */ + #ifdef WOLFSSL_AES_256 + const WOLFSSL_EVP_CIPHER* wolfSSL_EVP_aes_256_wrap(void) + { + WOLFSSL_ENTER("wolfSSL_EVP_aes_256_wrap"); + return EVP_AES_256_WRAP; + } + #endif /* WOLFSSL_AES_256 */ + #ifdef WOLFSSL_AES_KEYWRAP_PADDING + #ifdef WOLFSSL_AES_128 + const WOLFSSL_EVP_CIPHER* wolfSSL_EVP_aes_128_wrap_pad(void) + { + WOLFSSL_ENTER("wolfSSL_EVP_aes_128_wrap_pad"); + return EVP_AES_128_WRAP_PAD; + } + #endif /* WOLFSSL_AES_128 */ + #ifdef WOLFSSL_AES_192 + const WOLFSSL_EVP_CIPHER* wolfSSL_EVP_aes_192_wrap_pad(void) + { + WOLFSSL_ENTER("wolfSSL_EVP_aes_192_wrap_pad"); + return EVP_AES_192_WRAP_PAD; + } + #endif /* WOLFSSL_AES_192 */ + #ifdef WOLFSSL_AES_256 + const WOLFSSL_EVP_CIPHER* wolfSSL_EVP_aes_256_wrap_pad(void) + { + WOLFSSL_ENTER("wolfSSL_EVP_aes_256_wrap_pad"); + return EVP_AES_256_WRAP_PAD; + } + #endif /* WOLFSSL_AES_256 */ + #endif /* WOLFSSL_AES_KEYWRAP_PADDING */ + #endif /* WOLFSSL_EVP_AES_KEYWRAP */ + #ifdef HAVE_AESGCM #ifdef WOLFSSL_AES_128 const WOLFSSL_EVP_CIPHER* wolfSSL_EVP_aes_128_gcm(void) @@ -6946,7 +7314,8 @@ void wolfSSL_EVP_init(void) defined(HAVE_AES_ECB) || \ defined(WOLFSSL_AES_CFB) || \ defined(WOLFSSL_AES_OFB) || \ - defined(WOLFSSL_AES_XTS) + defined(WOLFSSL_AES_XTS) || \ + defined(WOLFSSL_EVP_AES_KEYWRAP) #if defined(HAVE_AESGCM) case WC_AES_128_GCM_TYPE: @@ -6988,6 +7357,18 @@ void wolfSSL_EVP_init(void) case WC_AES_128_OFB_TYPE: case WC_AES_192_OFB_TYPE: case WC_AES_256_OFB_TYPE: + #endif + #ifdef WOLFSSL_EVP_AES_KEYWRAP + /* Key wrap keys an Aes of its own in EVP_CipherInit(), so it + * is released here like every other AES mode. */ + case WC_AES_128_WRAP_TYPE: + case WC_AES_192_WRAP_TYPE: + case WC_AES_256_WRAP_TYPE: + #ifdef WOLFSSL_AES_KEYWRAP_PADDING + case WC_AES_128_WRAP_PAD_TYPE: + case WC_AES_192_WRAP_PAD_TYPE: + case WC_AES_256_WRAP_PAD_TYPE: + #endif #endif wc_AesFree(&ctx->cipher.aes); ctx->flags &= @@ -7069,6 +7450,9 @@ void wolfSSL_EVP_init(void) ctx->authInSz = 0; ctx->authIvGenEnable = 0; ctx->authIncIv = 0; +#endif +#ifdef WOLFSSL_EVP_AES_KEYWRAP + ctx->kwIvSupplied = 0; #endif } @@ -8401,6 +8785,135 @@ void wolfSSL_EVP_init(void) #endif /* WOLFSSL_AES_256 */ #endif /* WOLFSSL_AES_XTS && (!defined(HAVE_FIPS) || FIPS_VERSION_GE(5,3)) */ + + #if defined(WOLFSSL_EVP_AES_KEYWRAP) + /* AES Key Wrap (RFC 3394). The KEK is an ordinary AES key; the + * wrapping itself is done in one shot by CipherUpdate below, so all + * that is needed here is the key schedule in the right direction. */ + { + int wrapKeyLen = 0; + + #ifdef WOLFSSL_AES_128 + if (ctx->cipherType == WC_AES_128_WRAP_TYPE || + (type && EVP_CIPHER_TYPE_MATCHES(type, EVP_AES_128_WRAP))) { + WOLFSSL_MSG("EVP_AES_128_WRAP"); + ctx->cipherType = WC_AES_128_WRAP_TYPE; + wrapKeyLen = 16; + } + #endif + #ifdef WOLFSSL_AES_192 + if (ctx->cipherType == WC_AES_192_WRAP_TYPE || + (type && EVP_CIPHER_TYPE_MATCHES(type, EVP_AES_192_WRAP))) { + WOLFSSL_MSG("EVP_AES_192_WRAP"); + ctx->cipherType = WC_AES_192_WRAP_TYPE; + wrapKeyLen = 24; + } + #endif + #ifdef WOLFSSL_AES_256 + if (ctx->cipherType == WC_AES_256_WRAP_TYPE || + (type && EVP_CIPHER_TYPE_MATCHES(type, EVP_AES_256_WRAP))) { + WOLFSSL_MSG("EVP_AES_256_WRAP"); + ctx->cipherType = WC_AES_256_WRAP_TYPE; + wrapKeyLen = 32; + } + #endif + #ifdef WOLFSSL_AES_KEYWRAP_PADDING + #ifdef WOLFSSL_AES_128 + if (ctx->cipherType == WC_AES_128_WRAP_PAD_TYPE || + (type && + EVP_CIPHER_TYPE_MATCHES(type, EVP_AES_128_WRAP_PAD))) { + WOLFSSL_MSG("EVP_AES_128_WRAP_PAD"); + ctx->cipherType = WC_AES_128_WRAP_PAD_TYPE; + wrapKeyLen = 16; + } + #endif + #ifdef WOLFSSL_AES_192 + if (ctx->cipherType == WC_AES_192_WRAP_PAD_TYPE || + (type && + EVP_CIPHER_TYPE_MATCHES(type, EVP_AES_192_WRAP_PAD))) { + WOLFSSL_MSG("EVP_AES_192_WRAP_PAD"); + ctx->cipherType = WC_AES_192_WRAP_PAD_TYPE; + wrapKeyLen = 24; + } + #endif + #ifdef WOLFSSL_AES_256 + if (ctx->cipherType == WC_AES_256_WRAP_PAD_TYPE || + (type && + EVP_CIPHER_TYPE_MATCHES(type, EVP_AES_256_WRAP_PAD))) { + WOLFSSL_MSG("EVP_AES_256_WRAP_PAD"); + ctx->cipherType = WC_AES_256_WRAP_PAD_TYPE; + wrapKeyLen = 32; + } + #endif + #endif /* WOLFSSL_AES_KEYWRAP_PADDING */ + + if (wrapKeyLen != 0) { + ctx->flags &= (unsigned long)~WOLFSSL_EVP_CIPH_MODE; + ctx->flags |= WOLFSSL_EVP_CIPH_WRAP_MODE; + ctx->keyLen = wrapKeyLen; + /* RFC 3394 works in 8 byte semiblocks. */ + ctx->block_size = 8; + /* The unpadded form takes the whole 8 byte integrity check + * value. The padded form's AIV is 4 bytes of constant + * followed by the message length, so only the constant half + * can be given; OpenSSL reports these same two lengths. */ + ctx->ivSz = KEYWRAP_BLOCK_SIZE; + #ifdef WOLFSSL_AES_KEYWRAP_PADDING + if ((ctx->cipherType == WC_AES_128_WRAP_PAD_TYPE) || + (ctx->cipherType == WC_AES_192_WRAP_PAD_TYPE) || + (ctx->cipherType == WC_AES_256_WRAP_PAD_TYPE)) { + ctx->ivSz = KEYWRAP_BLOCK_SIZE / 2; + } + #endif + + /* Remember whether an integrity check value was given, so + * that CipherUpdate can tell "use the RFC default" from an + * explicit value that happens to be all zeros. Only a call + * that names a cipher gets here, so a later partial init + * passing a NULL iv leaves the recorded one alone. */ + if (iv == NULL) { + XMEMSET(ctx->iv, 0, (size_t)ctx->ivSz); + ctx->kwIvSupplied = 0; + } + else if (iv != ctx->iv) { + XMEMCPY(ctx->iv, iv, (size_t)ctx->ivSz); + ctx->kwIvSupplied = 1; + } + /* Otherwise the context's own buffer was handed back, which + * is what a NULL iv becomes further up when one is already + * stored, so what is recorded still describes it. */ + + if (enc == 0 || enc == 1) { + ctx->enc = enc ? 1 : 0; + } + + /* Only once per context: a second wc_AesInit() on the same + * Aes would leave whatever the first one allocated with no + * owner, and the context may be initialised again to change + * key or direction. */ + if (!(ctx->flags & WOLFSSL_EVP_CIPH_LOW_LEVEL_INITED)) { + if (wc_AesInit(&ctx->cipher.aes, NULL, + INVALID_DEVID) != 0) { + WOLFSSL_MSG("wc_AesInit() failed"); + return WOLFSSL_FAILURE; + } + ctx->flags |= WOLFSSL_EVP_CIPH_LOW_LEVEL_INITED; + } + + if (key != NULL) { + /* Wrapping uses the forward AES function and unwrapping + * the inverse, so the schedule direction follows enc. */ + ret = AesSetKey_ex(&ctx->cipher.aes, key, + (word32)ctx->keyLen, NULL, + ctx->enc ? AES_ENCRYPTION : AES_DECRYPTION, 1); + if (ret != 0) { + WOLFSSL_MSG("AesSetKey_ex() failed"); + return WOLFSSL_FAILURE; + } + } + } + } + #endif /* WOLFSSL_EVP_AES_KEYWRAP */ #endif /* NO_AES */ #if defined(HAVE_ARIA) if (ctx->cipherType == WC_ARIA_128_GCM_TYPE || @@ -8757,6 +9270,23 @@ void wolfSSL_EVP_init(void) case WC_AES_256_CBC_TYPE : return WC_NID_aes_256_cbc; #endif +#if defined(WOLFSSL_EVP_AES_KEYWRAP) + case WC_AES_128_WRAP_TYPE : + return WC_NID_id_aes128_wrap; + case WC_AES_192_WRAP_TYPE : + return WC_NID_id_aes192_wrap; + case WC_AES_256_WRAP_TYPE : + return WC_NID_id_aes256_wrap; +#endif +#if defined(WOLFSSL_EVP_AES_KEYWRAP) && \ + defined(WOLFSSL_AES_KEYWRAP_PADDING) + case WC_AES_128_WRAP_PAD_TYPE : + return WC_NID_id_aes128_wrap_pad; + case WC_AES_192_WRAP_PAD_TYPE : + return WC_NID_id_aes192_wrap_pad; + case WC_AES_256_WRAP_PAD_TYPE : + return WC_NID_id_aes256_wrap_pad; +#endif #ifdef HAVE_AESGCM case WC_AES_128_GCM_TYPE : return WC_NID_aes_128_gcm; @@ -8954,6 +9484,129 @@ void wolfSSL_EVP_init(void) } #endif /* !NO_AES || !NO_DES3 */ +/* Guard matches the condition on the iv field of WOLFSSL_EVP_CIPHER_CTX. */ +#if !defined(NO_AES) || defined(WOLFSSL_SM4) || \ + (defined(HAVE_CHACHA) && defined(HAVE_POLY1305)) || !defined(NO_DES3) + /* Locate the working IV of a cipher context. + * + * OpenSSL keeps the chaining value in the context itself, so a caller can + * both read it and restore it through EVP_CIPHER_CTX_iv(). wolfCrypt + * keeps it inside the cipher, so point at it there rather than copying: + * copying would have to write into ctx->iv, which would change what the + * separate EVP_CIPHER_CTX_get_iv() reports. + * + * The chaining modes keep that value in the register: CBC the previous + * ciphertext block, CTR the counter, OFB and CFB the keystream block. + * Partially consumed keystream lives in tmp/left and is not described by + * the register, so restoring a value mid-stream only makes sense on a + * block boundary. ECB has no IV and the AEAD modes take a fixed nonce, + * so for those the IV as it was set is reported, which is what wolfSSL + * has always done. + * + * @param [in] ctx Cipher context. + * @return The working IV for the chaining modes, otherwise the IV as it + * was set. + */ + static unsigned char* wolfssl_evp_cipher_working_iv( + WOLFSSL_EVP_CIPHER_CTX* ctx) + { + switch (ctx->cipherType) { + /* Every mode below keeps its working IV in the same place, but each is + * optional: with none of them built there is no case label left and the + * return would be unreachable. */ + #if !defined(NO_AES) && (defined(HAVE_AES_CBC) || \ + defined(WOLFSSL_AES_COUNTER) || defined(WOLFSSL_AES_OFB) || \ + defined(WOLFSSL_AES_CFB)) + #ifdef HAVE_AES_CBC + case WC_AES_128_CBC_TYPE: + case WC_AES_192_CBC_TYPE: + case WC_AES_256_CBC_TYPE: + #endif + #ifdef WOLFSSL_AES_COUNTER + case WC_AES_128_CTR_TYPE: + case WC_AES_192_CTR_TYPE: + case WC_AES_256_CTR_TYPE: + #endif + #ifdef WOLFSSL_AES_OFB + case WC_AES_128_OFB_TYPE: + case WC_AES_192_OFB_TYPE: + case WC_AES_256_OFB_TYPE: + #endif + #ifdef WOLFSSL_AES_CFB + case WC_AES_128_CFB1_TYPE: + case WC_AES_192_CFB1_TYPE: + case WC_AES_256_CFB1_TYPE: + case WC_AES_128_CFB8_TYPE: + case WC_AES_192_CFB8_TYPE: + case WC_AES_256_CFB8_TYPE: + case WC_AES_128_CFB128_TYPE: + case WC_AES_192_CFB128_TYPE: + case WC_AES_256_CFB128_TYPE: + #endif + return (unsigned char*)ctx->cipher.aes.reg; + #endif + #ifndef NO_DES3 + case WC_DES_CBC_TYPE: + return (unsigned char*)ctx->cipher.des.reg; + case WC_DES_EDE3_CBC_TYPE: + return (unsigned char*)ctx->cipher.des3.reg; + #endif + default: + return ctx->iv; + } + } + + /* Get a pointer to the working IV of a cipher context. + * + * For a chaining mode this is the value the next block will be combined + * with, so it changes as data is processed - after a CBC encryption it is + * the last ciphertext block, matching OpenSSL. ECB and the AEAD modes + * report the IV as it was set; see wolfssl_evp_cipher_working_iv(). + * + * The pointer is into the cipher's own state, so writing a saved chaining + * value back through EVP_CIPHER_CTX_iv_noconst() takes effect on the next + * block, as it does with OpenSSL. + * + * @param [in] ctx Cipher context. + * @return Pointer to the working IV on success. + * @return NULL when ctx is NULL. + */ + const unsigned char* wolfSSL_EVP_CIPHER_CTX_iv( + const WOLFSSL_EVP_CIPHER_CTX* ctx) + { + WOLFSSL_ENTER("wolfSSL_EVP_CIPHER_CTX_iv"); + + if (ctx == NULL) { + WOLFSSL_MSG("Bad parameter"); + return NULL; + } + + return wolfssl_evp_cipher_working_iv( + (WOLFSSL_EVP_CIPHER_CTX*)ctx); + } + + /* Get a modifiable pointer to the working IV of a cipher context. + * + * @param [in] ctx Cipher context. + * @return Pointer to the working IV on success. + * @return NULL when ctx is NULL. + */ + unsigned char* wolfSSL_EVP_CIPHER_CTX_iv_noconst( + WOLFSSL_EVP_CIPHER_CTX* ctx) + { + WOLFSSL_ENTER("wolfSSL_EVP_CIPHER_CTX_iv_noconst"); + + if (ctx == NULL) { + WOLFSSL_MSG("Bad parameter"); + return NULL; + } + + return wolfssl_evp_cipher_working_iv( + (WOLFSSL_EVP_CIPHER_CTX*)ctx); + } +#endif /* !NO_AES || WOLFSSL_SM4 || (HAVE_CHACHA && HAVE_POLY1305) || + * !NO_DES3 */ + static int IsCipherTypeAEAD(unsigned int type) { switch (type) { @@ -10531,6 +11184,21 @@ int wolfSSL_EVP_CIPHER_CTX_iv_length(const WOLFSSL_EVP_CIPHER_CTX* ctx) WOLFSSL_MSG("AES CBC"); return WC_AES_BLOCK_SIZE; #endif +#if defined(WOLFSSL_EVP_AES_KEYWRAP) + case WC_AES_128_WRAP_TYPE : + case WC_AES_192_WRAP_TYPE : + case WC_AES_256_WRAP_TYPE : + WOLFSSL_MSG("AES key wrap"); + return KEYWRAP_BLOCK_SIZE; +#ifdef WOLFSSL_AES_KEYWRAP_PADDING + case WC_AES_128_WRAP_PAD_TYPE : + case WC_AES_192_WRAP_PAD_TYPE : + case WC_AES_256_WRAP_PAD_TYPE : + WOLFSSL_MSG("AES key wrap with padding"); + /* Only the constant half of the AIV can be given. */ + return KEYWRAP_BLOCK_SIZE / 2; +#endif +#endif #if (!defined(HAVE_FIPS) && !defined(HAVE_SELFTEST)) || \ (defined(HAVE_FIPS_VERSION) && (HAVE_FIPS_VERSION >= 2)) #ifdef HAVE_AESGCM @@ -10671,6 +11339,44 @@ int wolfSSL_EVP_CIPHER_iv_length(const WOLFSSL_EVP_CIPHER* cipher) WOLFSSL_MSG("wolfSSL_EVP_CIPHER_iv_length"); #ifndef NO_AES +#ifdef WOLFSSL_EVP_AES_KEYWRAP + /* The unpadded form takes the whole 8 byte integrity check value; the + * padded form's AIV is 4 bytes of constant followed by the message + * length, so only the constant half can be given. These are the lengths + * OpenSSL reports for the same ciphers. */ + #ifdef WOLFSSL_AES_128 + if (XSTRCMP(name, EVP_AES_128_WRAP) == 0) { + return KEYWRAP_BLOCK_SIZE; + } + #endif + #ifdef WOLFSSL_AES_192 + if (XSTRCMP(name, EVP_AES_192_WRAP) == 0) { + return KEYWRAP_BLOCK_SIZE; + } + #endif + #ifdef WOLFSSL_AES_256 + if (XSTRCMP(name, EVP_AES_256_WRAP) == 0) { + return KEYWRAP_BLOCK_SIZE; + } + #endif + #ifdef WOLFSSL_AES_KEYWRAP_PADDING + #ifdef WOLFSSL_AES_128 + if (XSTRCMP(name, EVP_AES_128_WRAP_PAD) == 0) { + return KEYWRAP_BLOCK_SIZE / 2; + } + #endif + #ifdef WOLFSSL_AES_192 + if (XSTRCMP(name, EVP_AES_192_WRAP_PAD) == 0) { + return KEYWRAP_BLOCK_SIZE / 2; + } + #endif + #ifdef WOLFSSL_AES_256 + if (XSTRCMP(name, EVP_AES_256_WRAP_PAD) == 0) { + return KEYWRAP_BLOCK_SIZE / 2; + } + #endif + #endif +#endif /* WOLFSSL_EVP_AES_KEYWRAP */ #if defined(HAVE_AES_CBC) || defined(WOLFSSL_AES_DIRECT) #ifdef WOLFSSL_AES_128 if (XSTRCMP(name, EVP_AES_128_CBC) == 0) @@ -11179,6 +11885,23 @@ int wolfSSL_EVP_MD_type(const WOLFSSL_EVP_MD* type) return WC_NID_undef; } +/* Return the "null" message digest. + * + * A WOLFSSL_EVP_MD is a digest name, and this name matches no real digest, so + * the value is only useful for identity comparison against another EVP_MD - + * which is what callers use it for. Hashing with it is not supported and the + * digest routines reject it rather than producing a zero length hash. + * + * @return The null message digest. + */ +const WOLFSSL_EVP_MD* wolfSSL_EVP_md_null(void) +{ + static const WOLFSSL_EVP_MD wolfssl_evp_md_null[] = "NULL"; + + WOLFSSL_ENTER("EVP_md_null"); + return wolfssl_evp_md_null; +} + #ifndef NO_MD4 /* return a pointer to MD4 EVP type */ diff --git a/wolfcrypt/src/evp_pk.c b/wolfcrypt/src/evp_pk.c index c0347f95b14..2e433c36416 100644 --- a/wolfcrypt/src/evp_pk.c +++ b/wolfcrypt/src/evp_pk.c @@ -426,6 +426,204 @@ static int d2iTryEd448Key(WOLFSSL_EVP_PKEY** out, const unsigned char* mem, } #endif /* HAVE_ED448 */ +#if defined(HAVE_CURVE25519) && defined(HAVE_CURVE25519_KEY_IMPORT) +/** + * Try to make an X25519 EVP PKEY from data. + * + * @param [in, out] out On in, an EVP PKEY or NULL. + * On out, an EVP PKEY or NULL. + * @param [in] mem Memory containing key data. + * @param [in] memSz Size of key data in bytes. + * @param [in] priv 1 means private key, 0 means public key. + * @param [in] prePopulated 1 means *out already holds the input bytes + * so the d2i_make_pkey allocate/copy is skipped. + * @return 1 on success. + * @return 0 when input was recognized as this key type but object + * creation/import failed. + * @return WOLFSSL_FATAL_ERROR when input is not this key type. + */ +static int d2iTryX25519Key(WOLFSSL_EVP_PKEY** out, const unsigned char* mem, + long memSz, int priv, int prePopulated) +{ + curve25519_key* cKey = NULL; + word32 keyIdx = 0; + int isCurveKey; + int ret = 1; + void* heap = NULL; + + if (*out != NULL) { + heap = (*out)->heap; + } + + cKey = (curve25519_key*)XMALLOC(sizeof(curve25519_key), heap, + DYNAMIC_TYPE_CURVE25519); + if (cKey == NULL) { + return 0; + } + if (wc_curve25519_init_ex(cKey, heap, INVALID_DEVID) != 0) { + XFREE(cKey, heap, DYNAMIC_TYPE_CURVE25519); + return 0; + } + + /* Decode data as an X25519 key in DER form (SubjectPublicKeyInfo for + * public keys, PKCS#8 PrivateKeyInfo for private keys). + * + * The value is imported here rather than left to + * wc_Curve25519PrivateKeyDecode(), which takes the scalar big-endian to + * match the encoder beside it. RFC 8410 carries the little-endian value + * of RFC 7748, as OpenSSL reads and writes it, and a big-endian import + * would also clamp the wrong end of it. The decode hands back where the + * key actually sits: an RFC 5958 v2 key carries the public key after the + * private one, so the position cannot be assumed from the length. */ + if (priv) { + const byte* privKey = NULL; + const byte* pubKey = NULL; + word32 privKeyLen = 0; + word32 pubKeyLen = 0; + int keyType = X25519k; + + /* The public key outputs are required even though only the private + * key is wanted: a v2 OneAsymmetricKey carries a public key too, and + * the decode refuses to run with nowhere to report it. */ + isCurveKey = (DecodeAsymKey_Assign(mem, &keyIdx, (word32)memSz, NULL, + NULL, &privKey, &privKeyLen, &pubKey, &pubKeyLen, &keyType) == 0); + if (isCurveKey && (privKeyLen == CURVE25519_KEYSIZE)) { + isCurveKey = (wc_curve25519_import_private_ex(privKey, privKeyLen, + cKey, EC25519_LITTLE_ENDIAN) == 0); + } + else { + isCurveKey = 0; + } + } + else { + const byte* pubKey = NULL; + word32 pubKeyLen = 0; + int keyType = X25519k; + + isCurveKey = (DecodeAsymKeyPublic_Assign(mem, &keyIdx, (word32)memSz, + &pubKey, &pubKeyLen, &keyType) == 0); + if (isCurveKey && (pubKeyLen == CURVE25519_KEYSIZE)) { + isCurveKey = (wc_curve25519_import_public_ex(pubKey, pubKeyLen, + cKey, EC25519_LITTLE_ENDIAN) == 0); + } + else { + isCurveKey = 0; + } + } + + if (!isCurveKey) { + wc_curve25519_free(cKey); + XFREE(cKey, heap, DYNAMIC_TYPE_CURVE25519); + return WOLFSSL_FATAL_ERROR; + } + + /* Copy the consumed DER into pkey->pkey.ptr, unless the caller + * pre-filled the EVP PKEY with the input bytes (d2i_evp_pkey()). */ + if (!prePopulated) { + ret = d2i_make_pkey(out, mem, keyIdx, priv, WC_EVP_PKEY_X25519); + } + if (ret == 1) { + (*out)->ownCurve25519 = 1; + (*out)->curve25519 = cKey; + } + else { + wc_curve25519_free(cKey); + XFREE(cKey, heap, DYNAMIC_TYPE_CURVE25519); + } + + return ret; +} +#endif /* HAVE_CURVE25519 && HAVE_CURVE25519_KEY_IMPORT */ + +#if defined(HAVE_CURVE448) && defined(HAVE_CURVE448_KEY_IMPORT) +/** + * Try to make an X448 EVP PKEY from data. + * + * See d2iTryX25519Key() for the parameters and return values. + */ +static int d2iTryX448Key(WOLFSSL_EVP_PKEY** out, const unsigned char* mem, + long memSz, int priv, int prePopulated) +{ + curve448_key* cKey = NULL; + word32 keyIdx = 0; + int isCurveKey; + int ret = 1; + void* heap = NULL; + + if (*out != NULL) { + heap = (*out)->heap; + } + + cKey = (curve448_key*)XMALLOC(sizeof(curve448_key), heap, + DYNAMIC_TYPE_CURVE448); + if (cKey == NULL) { + return 0; + } + if (wc_curve448_init(cKey) != 0) { + XFREE(cKey, heap, DYNAMIC_TYPE_CURVE448); + return 0; + } + + /* Decode data as an X448 key in DER form (SubjectPublicKeyInfo for public + * keys, PKCS#8 PrivateKeyInfo for private keys). See d2iTryX25519Key() + * for why the value is imported here, with an explicit endianness, from + * where the decode says the key sits. */ + if (priv) { + const byte* privKey = NULL; + const byte* pubKey = NULL; + word32 privKeyLen = 0; + word32 pubKeyLen = 0; + int keyType = X448k; + + /* See d2iTryX25519Key() for why the public key outputs are given. */ + isCurveKey = (DecodeAsymKey_Assign(mem, &keyIdx, (word32)memSz, NULL, + NULL, &privKey, &privKeyLen, &pubKey, &pubKeyLen, &keyType) == 0); + if (isCurveKey && (privKeyLen == CURVE448_KEY_SIZE)) { + isCurveKey = (wc_curve448_import_private_ex(privKey, privKeyLen, + cKey, EC448_LITTLE_ENDIAN) == 0); + } + else { + isCurveKey = 0; + } + } + else { + const byte* pubKey = NULL; + word32 pubKeyLen = 0; + int keyType = X448k; + + isCurveKey = (DecodeAsymKeyPublic_Assign(mem, &keyIdx, (word32)memSz, + &pubKey, &pubKeyLen, &keyType) == 0); + if (isCurveKey && (pubKeyLen == CURVE448_KEY_SIZE)) { + isCurveKey = (wc_curve448_import_public_ex(pubKey, pubKeyLen, + cKey, EC448_LITTLE_ENDIAN) == 0); + } + else { + isCurveKey = 0; + } + } + + if (!isCurveKey) { + wc_curve448_free(cKey); + XFREE(cKey, heap, DYNAMIC_TYPE_CURVE448); + return WOLFSSL_FATAL_ERROR; + } + + if (!prePopulated) { + ret = d2i_make_pkey(out, mem, keyIdx, priv, WC_EVP_PKEY_X448); + } + if (ret == 1) { + (*out)->ownCurve448 = 1; + (*out)->curve448 = cKey; + } + else { + wc_curve448_free(cKey); + XFREE(cKey, heap, DYNAMIC_TYPE_CURVE448); + } + + return ret; +} +#endif /* HAVE_CURVE448 && HAVE_CURVE448_KEY_IMPORT */ + /* Create a new EVP_PKEY from raw Ed25519 or Ed448 key material. * * Used for for callers who already have the raw key bytes and shouldn't need @@ -582,6 +780,289 @@ WOLFSSL_EVP_PKEY* wolfSSL_EVP_PKEY_new_raw_public_key(int type, /* Private-key counterpart to wolfSSL_EVP_PKEY_new_raw_public_key. The raw * input is the 32-byte seed (Ed25519) or 57-byte seed (Ed448). */ +/* Raw key forms exist only for the curves below; without any of them the + * getters have nothing to report. */ +#if (defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_EXPORT)) || \ + (defined(HAVE_ED448) && defined(HAVE_ED448_KEY_EXPORT)) || \ + (defined(HAVE_CURVE25519) && defined(HAVE_CURVE25519_KEY_EXPORT)) || \ + (defined(HAVE_CURVE448) && defined(HAVE_CURVE448_KEY_EXPORT)) + #define WOLFSSL_EVP_HAVE_RAW_KEYS +#endif + +#ifdef WOLFSSL_EVP_HAVE_RAW_KEYS +/* Copy a raw key out to the caller's buffer, honouring OpenSSL's + * query-then-fetch protocol. + * + * out/outLen - when out is NULL, *outLen receives the size needed and nothing + * is copied. Otherwise *outLen is the buffer size on entry and + * the size written on exit. + * + * Returns 1 on success, 0 when the buffer is too small. + */ +static int wolfssl_evp_pkey_raw_out(const byte* raw, word32 rawLen, + unsigned char* out, size_t* outLen) +{ + if (out == NULL) { + *outLen = (size_t)rawLen; + return 1; + } + if (*outLen < (size_t)rawLen) { + WOLFSSL_MSG("buffer too small for raw key"); + *outLen = (size_t)rawLen; + return 0; + } + XMEMCPY(out, raw, rawLen); + *outLen = (size_t)rawLen; + return 1; +} +#endif /* WOLFSSL_EVP_HAVE_RAW_KEYS */ + +#ifdef WOLFSSL_EVP_HAVE_RAW_KEYS +/* Decide whether pkey->pkey.ptr holds the raw private key. + * + * That field is the key's DER for anything decoded from a file or a buffer, + * and only wolfSSL_EVP_PKEY_new_raw_private_key() puts raw bytes there. The + * two are told apart by size: a raw private key is exactly as long as the one + * the wolfCrypt key exports, while a DER encoding never is. + * + * The cache matters because wolfCrypt clamps an X25519/X448 scalar on import + * (RFC 7748), so re-exporting one would not give back what the caller set, + * while OpenSSL hands back exactly what it was given. + */ +static int wolfssl_evp_pkey_raw_cached(const WOLFSSL_EVP_PKEY* pkey, + word32 rawLen) +{ + return (pkey->pkey.ptr != NULL) && (pkey->pkey_sz > 0) && + ((word32)pkey->pkey_sz == rawLen); +} +#endif /* WOLFSSL_EVP_HAVE_RAW_KEYS */ + +int wolfSSL_EVP_PKEY_get_raw_private_key(const WOLFSSL_EVP_PKEY* pkey, + unsigned char* priv, size_t* len) +{ +#ifdef WOLFSSL_EVP_HAVE_RAW_KEYS + int ret = 0; + byte buf[128]; + word32 bufLen = (word32)sizeof(buf); +#endif + + WOLFSSL_ENTER("wolfSSL_EVP_PKEY_get_raw_private_key"); + + if (pkey == NULL || len == NULL) { + return 0; + } + +#ifndef WOLFSSL_EVP_HAVE_RAW_KEYS + (void)priv; + WOLFSSL_MSG("build has no key type with a raw form"); + return 0; +#else + + switch (pkey->type) { +#if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_EXPORT) + case WC_EVP_PKEY_ED25519: + if (pkey->ed25519 == NULL) { + break; + } + if (wc_ed25519_export_private_only(pkey->ed25519, buf, &bufLen) + != 0) { + WOLFSSL_MSG("wc_ed25519_export_private_only failed"); + break; + } + if (wolfssl_evp_pkey_raw_cached(pkey, bufLen)) { + ret = wolfssl_evp_pkey_raw_out((const byte*)pkey->pkey.ptr, + bufLen, priv, len); + } + else { + ret = wolfssl_evp_pkey_raw_out(buf, bufLen, priv, len); + } + break; +#endif +#if defined(HAVE_ED448) && defined(HAVE_ED448_KEY_EXPORT) + case WC_EVP_PKEY_ED448: + if (pkey->ed448 == NULL) { + break; + } + if (wc_ed448_export_private_only(pkey->ed448, buf, &bufLen) != 0) { + WOLFSSL_MSG("wc_ed448_export_private_only failed"); + break; + } + if (wolfssl_evp_pkey_raw_cached(pkey, bufLen)) { + ret = wolfssl_evp_pkey_raw_out((const byte*)pkey->pkey.ptr, + bufLen, priv, len); + } + else { + ret = wolfssl_evp_pkey_raw_out(buf, bufLen, priv, len); + } + break; +#endif +#if defined(HAVE_CURVE25519) && defined(HAVE_CURVE25519_KEY_EXPORT) + case WC_EVP_PKEY_X25519: + if (pkey->curve25519 == NULL) { + break; + } + /* Raw X25519 keys are little-endian (RFC 7748). */ + if (wc_curve25519_export_private_raw_ex(pkey->curve25519, buf, + &bufLen, EC25519_LITTLE_ENDIAN) != 0) { + WOLFSSL_MSG("wc_curve25519_export_private_raw_ex failed"); + break; + } + if (wolfssl_evp_pkey_raw_cached(pkey, bufLen)) { + ret = wolfssl_evp_pkey_raw_out((const byte*)pkey->pkey.ptr, + bufLen, priv, len); + } + else { + ret = wolfssl_evp_pkey_raw_out(buf, bufLen, priv, len); + } + break; +#endif +#if defined(HAVE_CURVE448) && defined(HAVE_CURVE448_KEY_EXPORT) + case WC_EVP_PKEY_X448: + if (pkey->curve448 == NULL) { + break; + } + if (wc_curve448_export_private_raw_ex(pkey->curve448, buf, &bufLen, + EC448_LITTLE_ENDIAN) != 0) { + WOLFSSL_MSG("wc_curve448_export_private_raw_ex failed"); + break; + } + if (wolfssl_evp_pkey_raw_cached(pkey, bufLen)) { + ret = wolfssl_evp_pkey_raw_out((const byte*)pkey->pkey.ptr, + bufLen, priv, len); + } + else { + ret = wolfssl_evp_pkey_raw_out(buf, bufLen, priv, len); + } + break; +#endif + default: + WOLFSSL_MSG("key type has no raw private form"); + break; + } + + ForceZero(buf, sizeof(buf)); + + return ret; +#endif /* WOLFSSL_EVP_HAVE_RAW_KEYS */ +} + +/* Get the raw public key of a key type that has one. + * + * See wolfSSL_EVP_PKEY_get_raw_private_key() on where the bytes come from. + * + * Returns 1 on success, 0 otherwise. + */ +int wolfSSL_EVP_PKEY_get_raw_public_key(const WOLFSSL_EVP_PKEY* pkey, + unsigned char* pub, size_t* len) +{ +#ifdef WOLFSSL_EVP_HAVE_RAW_KEYS + int ret = 0; + byte buf[128]; + word32 bufLen = (word32)sizeof(buf); +#endif + + WOLFSSL_ENTER("wolfSSL_EVP_PKEY_get_raw_public_key"); + + if (pkey == NULL || len == NULL) { + return 0; + } + +#ifndef WOLFSSL_EVP_HAVE_RAW_KEYS + (void)pub; + WOLFSSL_MSG("build has no key type with a raw form"); + return 0; +#else + + switch (pkey->type) { +#if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_EXPORT) + case WC_EVP_PKEY_ED25519: + if (pkey->ed25519 == NULL) { + break; + } + #ifdef HAVE_ED25519_MAKE_KEY + /* A key carrying only its private half has no public bytes to + * export, so derive them - into the key's own buffer, and only + * when it does not already hold them. wc_ed25519_make_public() + * writes to whatever buffer it is handed but sets pubKeySet on the + * key, so deriving into scratch would leave the key claiming a + * public key it does not hold and every later export or signature + * would use zeros. It wants exactly the public key size, not the + * capacity of the buffer. */ + if (pkey->ed25519->privKeySet && (!pkey->ed25519->pubKeySet)) { + if (wc_ed25519_make_public(pkey->ed25519, pkey->ed25519->p, + ED25519_PUB_KEY_SIZE) != 0) { + WOLFSSL_MSG("wc_ed25519_make_public failed"); + break; + } + } + #endif /* HAVE_ED25519_MAKE_KEY: a verify only build cannot derive one, + * and the export below reports it missing. */ + bufLen = (word32)sizeof(buf); + if (wc_ed25519_export_public(pkey->ed25519, buf, &bufLen) != 0) { + WOLFSSL_MSG("wc_ed25519_export_public failed"); + break; + } + ret = wolfssl_evp_pkey_raw_out(buf, bufLen, pub, len); + break; +#endif +#if defined(HAVE_ED448) && defined(HAVE_ED448_KEY_EXPORT) + case WC_EVP_PKEY_ED448: + if (pkey->ed448 == NULL) { + break; + } + /* See the Ed25519 case: derive into the key's own buffer so the + * key is never left flagged as holding a public key it does not. + * wc_ed448_make_public() has no build option of its own. */ + if (pkey->ed448->privKeySet && (!pkey->ed448->pubKeySet)) { + if (wc_ed448_make_public(pkey->ed448, pkey->ed448->p, + ED448_PUB_KEY_SIZE) != 0) { + WOLFSSL_MSG("wc_ed448_make_public failed"); + break; + } + } + bufLen = (word32)sizeof(buf); + if (wc_ed448_export_public(pkey->ed448, buf, &bufLen) != 0) { + WOLFSSL_MSG("wc_ed448_export_public failed"); + break; + } + ret = wolfssl_evp_pkey_raw_out(buf, bufLen, pub, len); + break; +#endif +#if defined(HAVE_CURVE25519) && defined(HAVE_CURVE25519_KEY_EXPORT) + case WC_EVP_PKEY_X25519: + if (pkey->curve25519 == NULL) { + break; + } + if (wc_curve25519_export_public_ex(pkey->curve25519, buf, &bufLen, + EC25519_LITTLE_ENDIAN) != 0) { + WOLFSSL_MSG("wc_curve25519_export_public_ex failed"); + break; + } + ret = wolfssl_evp_pkey_raw_out(buf, bufLen, pub, len); + break; +#endif +#if defined(HAVE_CURVE448) && defined(HAVE_CURVE448_KEY_EXPORT) + case WC_EVP_PKEY_X448: + if (pkey->curve448 == NULL) { + break; + } + if (wc_curve448_export_public_ex(pkey->curve448, buf, &bufLen, + EC448_LITTLE_ENDIAN) != 0) { + WOLFSSL_MSG("wc_curve448_export_public_ex failed"); + break; + } + ret = wolfssl_evp_pkey_raw_out(buf, bufLen, pub, len); + break; +#endif + default: + WOLFSSL_MSG("key type has no raw public form"); + break; + } + + return ret; +#endif /* WOLFSSL_EVP_HAVE_RAW_KEYS */ +} + WOLFSSL_EVP_PKEY* wolfSSL_EVP_PKEY_new_raw_private_key(int type, WOLFSSL_ENGINE* e, const unsigned char* priv, size_t len) { @@ -1252,6 +1733,18 @@ static WOLFSSL_EVP_PKEY* d2i_evp_pkey_try(WOLFSSL_EVP_PKEY** out, } else #endif /* HAVE_ED448 && HAVE_ED448_KEY_IMPORT */ +#if defined(HAVE_CURVE25519) && defined(HAVE_CURVE25519_KEY_IMPORT) + if (d2iTryX25519Key(&pkey, *in, inSz, priv, 0) >= 0) { + found = 1; + } + else +#endif /* HAVE_CURVE25519 && HAVE_CURVE25519_KEY_IMPORT */ +#if defined(HAVE_CURVE448) && defined(HAVE_CURVE448_KEY_IMPORT) + if (d2iTryX448Key(&pkey, *in, inSz, priv, 0) >= 0) { + found = 1; + } + else +#endif /* HAVE_CURVE448 && HAVE_CURVE448_KEY_IMPORT */ #ifdef HAVE_FALCON if (d2iTryFalconKey(&pkey, *in, inSz, priv) >= 0) { found = 1; @@ -1497,6 +1990,12 @@ static WOLFSSL_EVP_PKEY* d2i_evp_pkey(int type, WOLFSSL_EVP_PKEY** out, #ifdef HAVE_ED448 || (type == WC_EVP_PKEY_ED448 && algId != ED448k) #endif + #ifdef HAVE_CURVE25519 + || (type == WC_EVP_PKEY_X25519 && algId != X25519k) + #endif + #ifdef HAVE_CURVE448 + || (type == WC_EVP_PKEY_X448 && algId != X448k) + #endif #ifdef WOLFSSL_HAVE_MLDSA || (type == WC_EVP_PKEY_DILITHIUM && algId != ML_DSA_44k && algId != ML_DSA_65k && @@ -1635,6 +2134,24 @@ static WOLFSSL_EVP_PKEY* d2i_evp_pkey(int type, WOLFSSL_EVP_PKEY** out, } break; #endif /* HAVE_ED448 */ +#if defined(HAVE_CURVE25519) && defined(HAVE_CURVE25519_KEY_IMPORT) + case WC_EVP_PKEY_X25519: + /* See WC_EVP_PKEY_ED25519 case above. */ + if (d2iTryX25519Key(&local, p, local->pkey_sz, priv, 1) != 1) { + wolfSSL_EVP_PKEY_free(local); + return NULL; + } + break; +#endif /* HAVE_CURVE25519 && HAVE_CURVE25519_KEY_IMPORT */ +#if defined(HAVE_CURVE448) && defined(HAVE_CURVE448_KEY_IMPORT) + case WC_EVP_PKEY_X448: + /* See WC_EVP_PKEY_ED25519 case above. */ + if (d2iTryX448Key(&local, p, local->pkey_sz, priv, 1) != 1) { + wolfSSL_EVP_PKEY_free(local); + return NULL; + } + break; +#endif /* HAVE_CURVE448 && HAVE_CURVE448_KEY_IMPORT */ #if defined(WOLFSSL_HAVE_MLDSA) case WC_EVP_PKEY_DILITHIUM: /* local already holds the input bytes: prePopulated=1. */ diff --git a/wolfssl/error-ssl.h b/wolfssl/error-ssl.h index 4c6893fbeb3..bc72d044497 100644 --- a/wolfssl/error-ssl.h +++ b/wolfssl/error-ssl.h @@ -213,6 +213,7 @@ enum wolfSSL_ErrorCodes { WOLFSSL_BAD_FILE = -463, /* Input/output error on file */ WOLFSSL_NOT_IMPLEMENTED = -464, /* Function not implemented */ WOLFSSL_UNKNOWN = -465, /* Unknown algorithm (EVP) */ + CLIENT_HELLO_CB_E = -466, /* ClientHello callback failed */ /* negotiation parameter errors */ UNSUPPORTED_SUITE = -500, /* unsupported cipher suite */ diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 18ab83ca268..70e67aa1fe7 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -2532,6 +2532,10 @@ WOLFSSL_TEST_VIS void InitSuitesHashSigAlgo(byte* hashSigAlgo, int have, int tls1_2, int tls1_3, int keySz, word16* len); WOLFSSL_LOCAL int AllocateCtxSuites(WOLFSSL_CTX* ctx); +#if defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL) || \ + defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY) +WOLFSSL_LOCAL void CtxFreeSuitesStack(WOLFSSL_CTX* ctx); +#endif WOLFSSL_LOCAL int InitCtxSuitesWithMutex(WOLFSSL_CTX* ctx); WOLFSSL_LOCAL int AllocateSuites(WOLFSSL* ssl); WOLFSSL_LOCAL void InitSuites(Suites* suites, ProtocolVersion pv, int keySz, @@ -4214,6 +4218,16 @@ struct WOLFSSL_CTX { int ownOurCert; /* Dispose of certificate if we own */ #endif Suites* suites; /* make dynamic, user may not need/set */ +#if defined(OPENSSL_EXTRA) && defined(HAVE_TLS_EXTENSIONS) && \ + !defined(NO_WOLFSSL_SERVER) + CallbackClientHello chCb; /* called on receiving a ClientHello */ + void* chCbArg; /* user context for chCb */ +#endif +#if defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL) || \ + defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY) + WOLF_STACK_OF(WOLFSSL_CIPHER)* suitesStack; /* stack of configured + * cipher suites */ +#endif void* heap; /* for user memory overrides */ byte verifyDepth; byte verifyPeer:1; @@ -6930,6 +6944,11 @@ struct WOLFSSL { #endif #if defined(OPENSSL_EXTRA) WOLFSSL_STACK* supportedCiphers; /* Used in wolfSSL_get_ciphers_compat */ +#if defined(HAVE_TLS_EXTENSIONS) && !defined(NO_WOLFSSL_SERVER) + const byte* chExts; /* raw ClientHello extension block, only valid + * for the duration of the ClientHello callback */ + word16 chExtsSz; +#endif WOLFSSL_STACK* peerCertChain; /* Used in wolfSSL_get_peer_cert_chain */ WOLFSSL_STACK* verifiedChain; /* peer cert chain to CA */ #ifdef KEEP_OUR_CERT diff --git a/wolfssl/openssl/evp.h b/wolfssl/openssl/evp.h index 74e8a8ba126..c236ef4f88e 100644 --- a/wolfssl/openssl/evp.h +++ b/wolfssl/openssl/evp.h @@ -81,6 +81,20 @@ #endif +/* Feature macros for the digests OpenSSL can be built without. Callers use + * these to decide whether EVP_md2()/EVP_md4()/EVP_md5() may be referenced. + * MD2 has no EVP wrapper in wolfSSL at all, so it is always absent. */ +#ifndef OPENSSL_NO_MD2 + #define OPENSSL_NO_MD2 +#endif +#if defined(NO_MD4) && !defined(OPENSSL_NO_MD4) + #define OPENSSL_NO_MD4 +#endif +#if defined(NO_MD5) && !defined(OPENSSL_NO_MD5) + #define OPENSSL_NO_MD5 +#endif + +WOLFSSL_API const WOLFSSL_EVP_MD* wolfSSL_EVP_md_null(void); #ifndef NO_MD4 WOLFSSL_API const WOLFSSL_EVP_MD* wolfSSL_EVP_md4(void); #endif @@ -136,6 +150,26 @@ WOLFSSL_API const WOLFSSL_EVP_CIPHER* wolfSSL_EVP_aes_256_ofb(void); WOLFSSL_API const WOLFSSL_EVP_CIPHER* wolfSSL_EVP_aes_128_xts(void); WOLFSSL_API const WOLFSSL_EVP_CIPHER* wolfSSL_EVP_aes_256_xts(void); #endif +/* The EVP AES key wrap ciphers are built on wc_AesKeyWrap_ex() and the other + * _ex entry points, which take an Aes already keyed with the KEK. Those are + * recent additions that a frozen crypto module does not carry - neither the + * self-test build nor any released FIPS module - so the ciphers are left out + * of those builds rather than failing to link against them. */ +#if defined(HAVE_AES_KEYWRAP) && !defined(NO_AES) && \ + !defined(HAVE_SELFTEST) && !defined(HAVE_FIPS) + #define WOLFSSL_EVP_AES_KEYWRAP +#endif + +#ifdef WOLFSSL_EVP_AES_KEYWRAP +WOLFSSL_API const WOLFSSL_EVP_CIPHER* wolfSSL_EVP_aes_128_wrap(void); +WOLFSSL_API const WOLFSSL_EVP_CIPHER* wolfSSL_EVP_aes_192_wrap(void); +WOLFSSL_API const WOLFSSL_EVP_CIPHER* wolfSSL_EVP_aes_256_wrap(void); +#ifdef WOLFSSL_AES_KEYWRAP_PADDING +WOLFSSL_API const WOLFSSL_EVP_CIPHER* wolfSSL_EVP_aes_128_wrap_pad(void); +WOLFSSL_API const WOLFSSL_EVP_CIPHER* wolfSSL_EVP_aes_192_wrap_pad(void); +WOLFSSL_API const WOLFSSL_EVP_CIPHER* wolfSSL_EVP_aes_256_wrap_pad(void); +#endif +#endif #endif /* NO_AES */ #if !defined(NO_AES) && defined(HAVE_AESGCM) WOLFSSL_API const WOLFSSL_EVP_CIPHER* wolfSSL_EVP_aes_128_gcm(void); @@ -320,6 +354,12 @@ typedef union { #define WC_NID_aes_256_ofb 428 #define WC_NID_aes_128_xts 913 #define WC_NID_aes_256_xts 914 +#define WC_NID_id_aes128_wrap 788 +#define WC_NID_id_aes192_wrap 789 +#define WC_NID_id_aes256_wrap 790 +#define WC_NID_id_aes128_wrap_pad 897 +#define WC_NID_id_aes192_wrap_pad 900 +#define WC_NID_id_aes256_wrap_pad 903 #define WC_NID_camellia_128_cbc 751 #define WC_NID_camellia_256_cbc 753 #define WC_NID_chacha20_poly1305 1018 @@ -488,7 +528,13 @@ enum { WC_SM4_CCM_TYPE = 47, WC_ARIA_128_GCM_TYPE = 48, WC_ARIA_192_GCM_TYPE = 49, - WC_ARIA_256_GCM_TYPE = 50 + WC_ARIA_256_GCM_TYPE = 50, + WC_AES_128_WRAP_TYPE = 51, + WC_AES_192_WRAP_TYPE = 52, + WC_AES_256_WRAP_TYPE = 53, + WC_AES_128_WRAP_PAD_TYPE = 54, + WC_AES_192_WRAP_PAD_TYPE = 55, + WC_AES_256_WRAP_PAD_TYPE = 56 }; #define WOLFSSL_EVP_PKEY_PRINT_INDENT_MAX 128 @@ -556,6 +602,12 @@ enum { #define AES_128_XTS_TYPE WC_AES_128_XTS_TYPE #define AES_256_XTS_TYPE WC_AES_256_XTS_TYPE #define CHACHA20_POLY1305_TYPE WC_CHACHA20_POLY1305_TYPE +#define AES_128_WRAP_TYPE WC_AES_128_WRAP_TYPE +#define AES_192_WRAP_TYPE WC_AES_192_WRAP_TYPE +#define AES_256_WRAP_TYPE WC_AES_256_WRAP_TYPE +#define AES_128_WRAP_PAD_TYPE WC_AES_128_WRAP_PAD_TYPE +#define AES_192_WRAP_PAD_TYPE WC_AES_192_WRAP_PAD_TYPE +#define AES_256_WRAP_PAD_TYPE WC_AES_256_WRAP_PAD_TYPE #define CHACHA20_TYPE WC_CHACHA20_TYPE #define AES_128_CCM_TYPE WC_AES_128_CCM_TYPE #define AES_192_CCM_TYPE WC_AES_192_CCM_TYPE @@ -582,6 +634,12 @@ enum { #define NID_aes_192_ctr WC_NID_aes_192_ctr #define NID_aes_256_ctr WC_NID_aes_256_ctr #define NID_aes_128_ecb WC_NID_aes_128_ecb +#define NID_id_aes128_wrap WC_NID_id_aes128_wrap +#define NID_id_aes192_wrap WC_NID_id_aes192_wrap +#define NID_id_aes256_wrap WC_NID_id_aes256_wrap +#define NID_id_aes128_wrap_pad WC_NID_id_aes128_wrap_pad +#define NID_id_aes192_wrap_pad WC_NID_id_aes192_wrap_pad +#define NID_id_aes256_wrap_pad WC_NID_id_aes256_wrap_pad #define NID_aes_192_ecb WC_NID_aes_192_ecb #define NID_aes_256_ecb WC_NID_aes_256_ecb #define NID_des_cbc WC_NID_des_cbc @@ -772,6 +830,12 @@ struct WOLFSSL_EVP_CIPHER_CTX { WC_BITFIELD authIncIv:1; #endif #endif +#ifdef WOLFSSL_EVP_AES_KEYWRAP + /* Whether an integrity check value was given at init. Key wrap cannot + * read that from the value itself: an all-zero ICV is a legitimate + * choice, distinct from asking for the RFC default. */ + WC_BITFIELD kwIvSupplied:1; +#endif }; struct WOLFSSL_EVP_PKEY_CTX { @@ -932,6 +996,13 @@ WOLFSSL_API int wolfSSL_EVP_CIPHER_CTX_set_iv(WOLFSSL_EVP_CIPHER_CTX* ctx, byte int ivLen); WOLFSSL_API int wolfSSL_EVP_CIPHER_CTX_get_iv(WOLFSSL_EVP_CIPHER_CTX* ctx, byte* iv, int ivLen); +#if !defined(NO_AES) || defined(WOLFSSL_SM4) || \ + (defined(HAVE_CHACHA) && defined(HAVE_POLY1305)) || !defined(NO_DES3) +WOLFSSL_API const unsigned char* wolfSSL_EVP_CIPHER_CTX_iv( + const WOLFSSL_EVP_CIPHER_CTX* ctx); +WOLFSSL_API unsigned char* wolfSSL_EVP_CIPHER_CTX_iv_noconst( + WOLFSSL_EVP_CIPHER_CTX* ctx); +#endif WOLFSSL_API int wolfSSL_EVP_Cipher(WOLFSSL_EVP_CIPHER_CTX* ctx, unsigned char* dst, const unsigned char* src, unsigned int len); @@ -1017,6 +1088,10 @@ WOLFSSL_API WOLFSSL_EVP_PKEY *wolfSSL_EVP_PKEY_new(void); WOLFSSL_API WOLFSSL_EVP_PKEY* wolfSSL_EVP_PKEY_new_ex(void* heap); WOLFSSL_API WOLFSSL_EVP_PKEY* wolfSSL_EVP_PKEY_new_raw_public_key(int type, WOLFSSL_ENGINE* e, const unsigned char* pub, size_t len); +WOLFSSL_API int wolfSSL_EVP_PKEY_get_raw_private_key( + const WOLFSSL_EVP_PKEY* pkey, unsigned char* priv, size_t* len); +WOLFSSL_API int wolfSSL_EVP_PKEY_get_raw_public_key( + const WOLFSSL_EVP_PKEY* pkey, unsigned char* pub, size_t* len); WOLFSSL_API WOLFSSL_EVP_PKEY* wolfSSL_EVP_PKEY_new_raw_private_key(int type, WOLFSSL_ENGINE* e, const unsigned char* priv, size_t len); WOLFSSL_API void wolfSSL_EVP_PKEY_free(WOLFSSL_EVP_PKEY* key); @@ -1122,6 +1197,7 @@ WOLFSSL_API int wolfSSL_EVP_PKEY_CTX_hkdf_mode(WOLFSSL_EVP_PKEY_CTX* ctx, #define WOLFSSL_EVP_CIPH_GCM_MODE 0x6 #define WOLFSSL_EVP_CIPH_CCM_MODE 0x7 #define WOLFSSL_EVP_CIPH_XTS_MODE 0x10 +#define WOLFSSL_EVP_CIPH_WRAP_MODE 0x40 #define WOLFSSL_EVP_CIPH_FLAG_AEAD_CIPHER 0x20 #define WOLFSSL_EVP_CIPH_NO_PADDING 0x100 #define WOLFSSL_EVP_CIPH_VARIABLE_LENGTH 0x200 @@ -1221,9 +1297,11 @@ WOLFSSL_API int wolfSSL_EVP_SignInit_ex(WOLFSSL_EVP_MD_CTX* ctx, #define EVP_CIPH_GCM_MODE WOLFSSL_EVP_CIPH_GCM_MODE #define EVP_CIPH_CCM_MODE WOLFSSL_EVP_CIPH_CCM_MODE #define EVP_CIPH_XTS_MODE WOLFSSL_EVP_CIPH_XTS_MODE +#define EVP_CIPH_WRAP_MODE WOLFSSL_EVP_CIPH_WRAP_MODE #define EVP_CIPH_FLAG_AEAD_CIPHER WOLFSSL_EVP_CIPH_FLAG_AEAD_CIPHER +#define EVP_md_null wolfSSL_EVP_md_null #ifndef NO_MD4 #define EVP_md4 wolfSSL_EVP_md4 #endif @@ -1270,6 +1348,12 @@ WOLFSSL_API int wolfSSL_EVP_SignInit_ex(WOLFSSL_EVP_MD_CTX* ctx, #define EVP_aes_256_ofb wolfSSL_EVP_aes_256_ofb #define EVP_aes_128_xts wolfSSL_EVP_aes_128_xts #define EVP_aes_256_xts wolfSSL_EVP_aes_256_xts +#define EVP_aes_128_wrap wolfSSL_EVP_aes_128_wrap +#define EVP_aes_192_wrap wolfSSL_EVP_aes_192_wrap +#define EVP_aes_256_wrap wolfSSL_EVP_aes_256_wrap +#define EVP_aes_128_wrap_pad wolfSSL_EVP_aes_128_wrap_pad +#define EVP_aes_192_wrap_pad wolfSSL_EVP_aes_192_wrap_pad +#define EVP_aes_256_wrap_pad wolfSSL_EVP_aes_256_wrap_pad #define EVP_aes_128_gcm wolfSSL_EVP_aes_128_gcm #define EVP_aes_192_gcm wolfSSL_EVP_aes_192_gcm #define EVP_aes_256_gcm wolfSSL_EVP_aes_256_gcm @@ -1438,6 +1522,8 @@ WOLFSSL_API int wolfSSL_EVP_SignInit_ex(WOLFSSL_EVP_MD_CTX* ctx, #define EVP_PKEY_new wolfSSL_EVP_PKEY_new #define EVP_PKEY_new_raw_public_key wolfSSL_EVP_PKEY_new_raw_public_key #define EVP_PKEY_new_raw_private_key wolfSSL_EVP_PKEY_new_raw_private_key +#define EVP_PKEY_get_raw_private_key wolfSSL_EVP_PKEY_get_raw_private_key +#define EVP_PKEY_get_raw_public_key wolfSSL_EVP_PKEY_get_raw_public_key #define EVP_PKEY_free wolfSSL_EVP_PKEY_free #define EVP_PKEY_up_ref wolfSSL_EVP_PKEY_up_ref #define EVP_PKEY_size wolfSSL_EVP_PKEY_size @@ -1469,6 +1555,8 @@ WOLFSSL_API int wolfSSL_EVP_SignInit_ex(WOLFSSL_EVP_MD_CTX* ctx, #define EVP_CIPHER_CTX_flags wolfSSL_EVP_CIPHER_CTX_flags #define EVP_CIPHER_CTX_set_iv wolfSSL_EVP_CIPHER_CTX_set_iv #define EVP_CIPHER_CTX_get_iv wolfSSL_EVP_CIPHER_CTX_get_iv +#define EVP_CIPHER_CTX_iv wolfSSL_EVP_CIPHER_CTX_iv +#define EVP_CIPHER_CTX_iv_noconst wolfSSL_EVP_CIPHER_CTX_iv_noconst #define EVP_add_digest wolfSSL_EVP_add_digest #define EVP_add_cipher wolfSSL_EVP_add_cipher #define EVP_cleanup wolfSSL_EVP_cleanup diff --git a/wolfssl/openssl/ssl.h b/wolfssl/openssl/ssl.h index 762eb5833fa..a0ab76fb2c1 100644 --- a/wolfssl/openssl/ssl.h +++ b/wolfssl/openssl/ssl.h @@ -905,7 +905,9 @@ wolfSSL_X509_STORE_set_verify_cb((WOLFSSL_X509_STORE *)(s), (WOLFSSL_X509_STORE_ #define X509_REVOKED_get_ext_count wolfSSL_X509_REVOKED_get_ext_count #define X509_REVOKED_get_ext wolfSSL_X509_REVOKED_get_ext -#define X509_check_purpose(x, id, ca) 0 +/* Was a macro expanding to the constant 0, i.e. "suitable for nothing", which + * is not what OpenSSL's X509_check_purpose() reports. */ +#define X509_check_purpose wolfSSL_X509_check_purpose #define OCSP_parse_url wolfSSL_OCSP_parse_url @@ -1023,6 +1025,7 @@ wolfSSL_X509_STORE_set_verify_cb((WOLFSSL_X509_STORE *)(s), (WOLFSSL_X509_STORE_ #define a2i_ASN1_INTEGER wolfSSL_a2i_ASN1_INTEGER #define i2a_ASN1_INTEGER wolfSSL_i2a_ASN1_INTEGER +#define i2a_ASN1_STRING wolfSSL_i2a_ASN1_STRING #define i2c_ASN1_INTEGER wolfSSL_i2c_ASN1_INTEGER #define ASN1_INTEGER_new wolfSSL_ASN1_INTEGER_new #define ASN1_INTEGER_free wolfSSL_ASN1_INTEGER_free @@ -1112,6 +1115,10 @@ wolfSSL_X509_STORE_set_verify_cb((WOLFSSL_X509_STORE *)(s), (WOLFSSL_X509_STORE_ #define ASN1_BOOLEAN WOLFSSL_ASN1_BOOLEAN #define SSL_load_client_CA_file wolfSSL_load_client_CA_file +#define SSL_add_file_cert_subjects_to_stack \ + wolfSSL_add_file_cert_subjects_to_stack +#define SSL_add_dir_cert_subjects_to_stack \ + wolfSSL_add_dir_cert_subjects_to_stack #define SSL_CTX_get_client_CA_list wolfSSL_CTX_get_client_CA_list #define SSL_CTX_set_client_CA_list wolfSSL_CTX_set_client_CA_list @@ -1275,6 +1282,9 @@ typedef wolfSSL_custom_ext_parse_cb custom_ext_parse_cb; #define SSL_CTX_set_session_id_context wolfSSL_CTX_set_session_id_context #define SSL_get_peer_certificate wolfSSL_get_peer_certificate +/* OpenSSL 3.0 renamed SSL_get_peer_certificate(); both return a certificate + * the caller has to free, which is what wolfSSL_get_peer_certificate() does. */ +#define SSL_get1_peer_certificate wolfSSL_get_peer_certificate #define SSL_get_peer_cert_chain wolfSSL_get_peer_cert_chain #define SSL_want wolfSSL_want @@ -1383,6 +1393,20 @@ typedef WOLFSSL_SRTP_PROTECTION_PROFILE SRTP_PROTECTION_PROFILE; #define SSL_get_wbio wolfSSL_SSL_get_wbio #define SSL_do_handshake wolfSSL_SSL_do_handshake #define SSL_get_ciphers(x) wolfSSL_get_ciphers_compat(x) +#define SSL_CTX_get_ciphers wolfSSL_CTX_get_ciphers +#define SSL_CTX_load_verify_file wolfSSL_CTX_load_verify_file +#define SSL_CTX_load_verify_dir wolfSSL_CTX_load_verify_dir +#define SSL_CTX_set_client_hello_cb wolfSSL_CTX_set_client_hello_cb +#define SSL_client_hello_get0_ext wolfSSL_client_hello_get0_ext + +/* Return values for the ClientHello callback. RETRY is not supported - the + * handshake cannot be paused - and is treated as an error. */ +#define SSL_CLIENT_HELLO_SUCCESS WOLFSSL_CLIENT_HELLO_SUCCESS +#define SSL_CLIENT_HELLO_ERROR WOLFSSL_CLIENT_HELLO_ERROR +#define SSL_CLIENT_HELLO_RETRY WOLFSSL_CLIENT_HELLO_RETRY +#ifndef TLSEXT_TYPE_server_name + #define TLSEXT_TYPE_server_name 0 +#endif #define SSL_SESSION_get_id wolfSSL_SESSION_get_id #define SSL_get_cipher_bits(s,np) \ wolfSSL_CIPHER_get_bits(SSL_get_current_cipher(s),np) @@ -1437,6 +1461,7 @@ typedef WOLFSSL_SRTP_PROTECTION_PROFILE SRTP_PROTECTION_PROFILE; #define SSL_CTX_set_dh_auto wolfSSL_CTX_set_dh_auto #define SSL_CTX_set_tmp_dh wolfSSL_CTX_set_tmp_dh +#define SSL_CTX_set0_tmp_dh_pkey wolfSSL_CTX_set0_tmp_dh_pkey #define TLSEXT_STATUSTYPE_ocsp WOLFSSL_TLSEXT_STATUSTYPE_ocsp diff --git a/wolfssl/openssl/x509v3.h b/wolfssl/openssl/x509v3.h index 480242c325b..f8e4454077a 100644 --- a/wolfssl/openssl/x509v3.h +++ b/wolfssl/openssl/x509v3.h @@ -49,8 +49,23 @@ #define WOLFSSL_XKU_DVCS 0x80 #define WOLFSSL_XKU_ANYEKU 0x100 -#define WOLFSSL_X509_PURPOSE_SSL_CLIENT 0 -#define WOLFSSL_X509_PURPOSE_SSL_SERVER 1 +/* Purpose identifiers. These match OpenSSL's values, so an application that + * stores or compares the number behaves the same either way. + * + * NOTE: SSL_CLIENT and SSL_SERVER used to be 0 and 1 here, which matched + * neither OpenSSL nor X509_check_purpose()'s reserved -1 "no purpose". */ +#define WOLFSSL_X509_PURPOSE_SSL_CLIENT 1 +#define WOLFSSL_X509_PURPOSE_SSL_SERVER 2 +#define WOLFSSL_X509_PURPOSE_NS_SSL_SERVER 3 +#define WOLFSSL_X509_PURPOSE_SMIME_SIGN 4 +#define WOLFSSL_X509_PURPOSE_SMIME_ENCRYPT 5 +#define WOLFSSL_X509_PURPOSE_CRL_SIGN 6 +#define WOLFSSL_X509_PURPOSE_ANY 7 +#define WOLFSSL_X509_PURPOSE_OCSP_HELPER 8 +#define WOLFSSL_X509_PURPOSE_TIMESTAMP_SIGN 9 +#define WOLFSSL_X509_PURPOSE_CODE_SIGN 10 +#define WOLFSSL_X509_PURPOSE_MIN 1 +#define WOLFSSL_X509_PURPOSE_MAX 10 #if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x0090801fL typedef void *(*WOLFSSL_X509V3_EXT_D2I)(void *, const unsigned char **, long); @@ -184,6 +199,16 @@ WOLFSSL_API WOLFSSL_ASN1_STRING* wolfSSL_a2i_IPADDRESS(const char* ipa); #define X509_PURPOSE_SSL_CLIENT WOLFSSL_X509_PURPOSE_SSL_CLIENT #define X509_PURPOSE_SSL_SERVER WOLFSSL_X509_PURPOSE_SSL_SERVER +#define X509_PURPOSE_NS_SSL_SERVER WOLFSSL_X509_PURPOSE_NS_SSL_SERVER +#define X509_PURPOSE_SMIME_SIGN WOLFSSL_X509_PURPOSE_SMIME_SIGN +#define X509_PURPOSE_SMIME_ENCRYPT WOLFSSL_X509_PURPOSE_SMIME_ENCRYPT +#define X509_PURPOSE_CRL_SIGN WOLFSSL_X509_PURPOSE_CRL_SIGN +#define X509_PURPOSE_ANY WOLFSSL_X509_PURPOSE_ANY +#define X509_PURPOSE_OCSP_HELPER WOLFSSL_X509_PURPOSE_OCSP_HELPER +#define X509_PURPOSE_TIMESTAMP_SIGN WOLFSSL_X509_PURPOSE_TIMESTAMP_SIGN +#define X509_PURPOSE_CODE_SIGN WOLFSSL_X509_PURPOSE_CODE_SIGN +#define X509_PURPOSE_MIN WOLFSSL_X509_PURPOSE_MIN +#define X509_PURPOSE_MAX WOLFSSL_X509_PURPOSE_MAX #define NS_SSL_CLIENT WC_NS_SSL_CLIENT #define NS_SSL_SERVER WC_NS_SSL_SERVER diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h index 30b0d2908fe..a6b073aebc1 100644 --- a/wolfssl/ssl.h +++ b/wolfssl/ssl.h @@ -1396,6 +1396,14 @@ WOLFSSL_API int wolfSSL_CTX_load_verify_locations_ex( WOLFSSL_CTX* ctx, const char* file, const char* path, word32 flags); WOLFSSL_ABI WOLFSSL_API int wolfSSL_CTX_load_verify_locations( WOLFSSL_CTX* ctx, const char* file, const char* path); +#ifdef OPENSSL_EXTRA +WOLFSSL_API int wolfSSL_CTX_load_verify_file(WOLFSSL_CTX* ctx, + const char* file); +#ifndef NO_WOLFSSL_DIR +WOLFSSL_API int wolfSSL_CTX_load_verify_dir(WOLFSSL_CTX* ctx, + const char* path); +#endif +#endif WOLFSSL_API int wolfSSL_CTX_load_verify_locations_compat( WOLFSSL_CTX* ctx, const char* file, const char* path); #ifndef _WIN32 @@ -2659,6 +2667,10 @@ WOLFSSL_API WOLFSSL_BIGNUM *wolfSSL_ASN1_INTEGER_to_BN(const WOLFSSL_ASN1_INTEGE #endif WOLFSSL_API WOLF_STACK_OF(WOLFSSL_X509_NAME)* wolfSSL_load_client_CA_file(const char* fname); +WOLFSSL_API int wolfSSL_add_file_cert_subjects_to_stack( + WOLF_STACK_OF(WOLFSSL_X509_NAME)* list, const char* fname); +WOLFSSL_API int wolfSSL_add_dir_cert_subjects_to_stack( + WOLF_STACK_OF(WOLFSSL_X509_NAME)* list, const char* dir); #ifndef WOLFSSL_NO_CA_NAMES WOLFSSL_API WOLF_STACK_OF(WOLFSSL_X509_NAME)* wolfSSL_CTX_get_client_CA_list( @@ -4034,6 +4046,17 @@ WOLFSSL_API void wolfSSL_SetFuzzerCb(WOLFSSL* ssl, CallbackFuzzer cbf, void* fCt #endif +/* Called when a ClientHello has been received. Return + * WOLFSSL_CLIENT_HELLO_SUCCESS to continue the handshake, or + * WOLFSSL_CLIENT_HELLO_ERROR to fail it with the alert stored in al. */ +#define WOLFSSL_CLIENT_HELLO_SUCCESS 1 +#define WOLFSSL_CLIENT_HELLO_ERROR 0 +#define WOLFSSL_CLIENT_HELLO_RETRY (-1) +typedef int (*CallbackClientHello)(WOLFSSL* ssl, int* al, void* arg); +WOLFSSL_API void wolfSSL_CTX_set_client_hello_cb(WOLFSSL_CTX* ctx, + CallbackClientHello cb, void* arg); +WOLFSSL_API int wolfSSL_client_hello_get0_ext(WOLFSSL* ssl, unsigned int type, + const unsigned char** out, size_t* outLen); WOLFSSL_API int wolfSSL_DTLS_SetCookieSecret(WOLFSSL* ssl, const byte* secret, word32 secretSz); WOLFSSL_API int wolfSSL_DTLS_SetCookieSecretSecondary(WOLFSSL* ssl, const byte* secret, word32 secretSz); @@ -5628,6 +5651,8 @@ WOLFSSL_API unsigned char *wolfSSL_SHA512(const unsigned char *d, size_t n, unsi WOLFSSL_API int wolfSSL_X509_check_private_key(WOLFSSL_X509* x509, WOLFSSL_EVP_PKEY* pkey); WOLFSSL_API WOLF_STACK_OF(WOLFSSL_X509_NAME) *wolfSSL_dup_CA_list( WOLF_STACK_OF(WOLFSSL_X509_NAME) *sk ); WOLFSSL_API int wolfSSL_X509_check_ca(WOLFSSL_X509 *x509); +WOLFSSL_API int wolfSSL_X509_check_purpose(WOLFSSL_X509* x, int id, + int ca); #ifndef NO_FILESYSTEM WOLFSSL_API long wolfSSL_BIO_set_fp(WOLFSSL_BIO *bio, XFILE fp, int c); @@ -5658,6 +5683,12 @@ WOLFSSL_API WOLFSSL_BIO* wolfSSL_BIO_new_fp(XFILE fp, int c); #define X509_BUFFER_SZ 8192 WOLFSSL_API long wolfSSL_CTX_set_tmp_dh(WOLFSSL_CTX* ctx, WOLFSSL_DH* dh); +#if (defined(OPENSSL_ALL) || defined(WOLFSSL_QT) || \ + defined(WOLFSSL_OPENSSH)) && \ + !defined(NO_DH) && defined(WOLFSSL_DH_EXTRA) && !defined(NO_FILESYSTEM) +WOLFSSL_API int wolfSSL_CTX_set0_tmp_dh_pkey(WOLFSSL_CTX* ctx, + WOLFSSL_EVP_PKEY* dhpkey); +#endif WOLFSSL_API WOLFSSL_DH *wolfSSL_PEM_read_bio_DHparams(WOLFSSL_BIO *bp, WOLFSSL_DH **x, wc_pem_password_cb *cb, void *u); #ifndef NO_FILESYSTEM @@ -6088,6 +6119,8 @@ WOLFSSL_API void wolfSSL_CTX_set_ocsp_status_verify_cb(WOLFSSL_CTX* ctx, #if defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY) \ || defined(OPENSSL_EXTRA) || defined(HAVE_LIGHTY) WOLFSSL_API WOLF_STACK_OF(WOLFSSL_CIPHER) *wolfSSL_get_ciphers_compat(const WOLFSSL *ssl); +WOLFSSL_API WOLF_STACK_OF(WOLFSSL_CIPHER) *wolfSSL_CTX_get_ciphers( + const WOLFSSL_CTX *ctx); WOLFSSL_API int wolfSSL_X509_NAME_digest(const WOLFSSL_X509_NAME *data, const WOLFSSL_EVP_MD *type, unsigned char *md, unsigned int *len); WOLFSSL_API int wolfSSL_SSL_CTX_set_tmp_ecdh(WOLFSSL_CTX *ctx, @@ -6115,6 +6148,8 @@ WOLFSSL_API int wolfSSL_SSL_in_connect_init(WOLFSSL* ssl); WOLFSSL_API WOLFSSL_SESSION *wolfSSL_SSL_get0_session(const WOLFSSL *s); #endif +WOLFSSL_API int wolfSSL_i2a_ASN1_STRING(WOLFSSL_BIO *bp, + const WOLFSSL_ASN1_STRING *a, int type); WOLFSSL_API int wolfSSL_i2a_ASN1_INTEGER(WOLFSSL_BIO *bp, const WOLFSSL_ASN1_INTEGER *a);