diff --git a/.gitignore b/.gitignore index 0ef9644175..591b9d4230 100644 --- a/.gitignore +++ b/.gitignore @@ -104,6 +104,10 @@ ecc-key.der ecc-key.pem certreq.der certreq.pem +crlRsaOut.pem +crlRsaOut.der +crlEccOut.pem +crlEccOut.der pkcs7cert.der pkcs7authEnvelopedDataAES128GCM.der pkcs7authEnvelopedDataAES128GCM_ECDH_SHA1KDF.der @@ -470,3 +474,6 @@ wolfssl/debug-trace-error-codes.h wolfssl/debug-untrace-error-codes.h AGENTS.md + +# Code navigation files +compile_commands.json diff --git a/certs/client-ca-cert.der b/certs/client-ca-cert.der new file mode 100644 index 0000000000..8b5ba8ddea Binary files /dev/null and b/certs/client-ca-cert.der differ diff --git a/certs/client-ca-cert.pem b/certs/client-ca-cert.pem new file mode 100644 index 0000000000..b7eea645de --- /dev/null +++ b/certs/client-ca-cert.pem @@ -0,0 +1,92 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 4661 (0x1235) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C = US, ST = Montana, L = Bozeman, O = Sawtooth, OU = Consulting, CN = www.wolfssl.com, emailAddress = info@wolfssl.com + Validity + Not Before: Jan 24 20:31:13 2026 GMT + Not After : Oct 20 20:31:13 2028 GMT + Subject: C = US, ST = Montana, L = Bozeman, O = wolfSSL_2048, OU = Programming-2048, CN = www.wolfssl.com, emailAddress = info@wolfssl.com + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:c3:03:d1:2b:fe:39:a4:32:45:3b:53:c8:84:2b: + 2a:7c:74:9a:bd:aa:2a:52:07:47:d6:a6:36:b2:07: + 32:8e:d0:ba:69:7b:c6:c3:44:9e:d4:81:48:fd:2d: + 68:a2:8b:67:bb:a1:75:c8:36:2c:4a:d2:1b:f7:8b: + ba:cf:0d:f9:ef:ec:f1:81:1e:7b:9b:03:47:9a:bf: + 65:cc:7f:65:24:69:a6:e8:14:89:5b:e4:34:f7:c5: + b0:14:93:f5:67:7b:3a:7a:78:e1:01:56:56:91:a6: + 13:42:8d:d2:3c:40:9c:4c:ef:d1:86:df:37:51:1b: + 0c:a1:3b:f5:f1:a3:4a:35:e4:e1:ce:96:df:1b:7e: + bf:4e:97:d0:10:e8:a8:08:30:81:af:20:0b:43:14: + c5:74:67:b4:32:82:6f:8d:86:c2:88:40:99:36:83: + ba:1e:40:72:22:17:d7:52:65:24:73:b0:ce:ef:19: + cd:ae:ff:78:6c:7b:c0:12:03:d4:4e:72:0d:50:6d: + 3b:a3:3b:a3:99:5e:9d:c8:d9:0c:85:b3:d9:8a:d9: + 54:26:db:6d:fa:ac:bb:ff:25:4c:c4:d1:79:f4:71: + d3:86:40:18:13:b0:63:b5:72:4e:30:c4:97:84:86: + 2d:56:2f:d7:15:f7:7f:c0:ae:f5:fc:5b:e5:fb:a1: + ba:d3 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:FALSE + X509v3 Key Usage: critical + Digital Signature, Key Encipherment + X509v3 Extended Key Usage: + TLS Web Client Authentication + X509v3 Subject Key Identifier: + 33:D8:45:66:D7:68:87:18:7E:54:0D:70:27:91:C7:26:D7:85:65:C0 + 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=info@wolfssl.com + serial:3F:29:11:20:57:71:E7:8E:F9:18:0D:CA:70:4D:5B:15:2A:43:D6:24 + Signature Algorithm: sha256WithRSAEncryption + Signature Value: + 7f:71:41:b2:72:c1:a9:ba:c9:52:40:ea:6d:3d:3d:c0:ae:1e: + 44:cd:f9:a5:d6:ac:34:5f:8b:ed:cd:91:81:0f:05:0f:5e:4b: + b3:18:bf:33:7a:61:a5:25:f1:91:55:c1:12:66:b7:26:3b:9c: + bb:21:1a:0c:72:78:88:57:fa:49:22:e7:80:2f:c1:40:01:66: + 3d:20:63:e7:e3:38:a9:54:39:52:42:d2:b6:38:6b:08:7d:45: + 49:1a:de:b5:64:70:c9:65:ce:0b:94:24:ee:b4:46:67:3c:74: + f0:2a:61:4d:b2:fc:6e:ca:c0:36:a9:b0:d3:5a:e2:15:72:f5: + a4:90:73:b2:37:58:b4:10:39:d3:85:5f:56:91:7e:cf:54:5d: + c6:a7:40:36:bd:ed:f2:af:e5:ce:b6:ea:38:be:47:32:6f:ed: + d2:ba:9d:70:e1:74:2e:f0:27:e4:72:53:75:43:ce:0a:07:b4: + 7e:74:17:00:55:b5:d1:92:e4:42:39:ca:84:51:84:f8:23:a6: + 41:27:fb:20:e2:43:e3:74:d3:ce:95:4e:1f:06:de:65:5e:e3: + 38:e2:eb:f1:a6:ca:6b:7c:56:51:c0:02:1e:6e:3f:51:c1:d5: + 04:c0:3d:57:56:15:65:76:a4:f4:eb:43:27:2c:c3:58:29:5c: + 18:da:e8:fd +-----BEGIN CERTIFICATE----- +MIIE3zCCA8egAwIBAgICEjUwDQYJKoZIhvcNAQELBQAwgZQxCzAJBgNVBAYTAlVT +MRAwDgYDVQQIDAdNb250YW5hMRAwDgYDVQQHDAdCb3plbWFuMREwDwYDVQQKDAhT +YXd0b290aDETMBEGA1UECwwKQ29uc3VsdGluZzEYMBYGA1UEAwwPd3d3LndvbGZz +c2wuY29tMR8wHQYJKoZIhvcNAQkBFhBpbmZvQHdvbGZzc2wuY29tMB4XDTI2MDEy +NDIwMzExM1oXDTI4MTAyMDIwMzExM1owgZ4xCzAJBgNVBAYTAlVTMRAwDgYDVQQI +DAdNb250YW5hMRAwDgYDVQQHDAdCb3plbWFuMRUwEwYDVQQKDAx3b2xmU1NMXzIw +NDgxGTAXBgNVBAsMEFByb2dyYW1taW5nLTIwNDgxGDAWBgNVBAMMD3d3dy53b2xm +c3NsLmNvbTEfMB0GCSqGSIb3DQEJARYQaW5mb0B3b2xmc3NsLmNvbTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMMD0Sv+OaQyRTtTyIQrKnx0mr2qKlIH +R9amNrIHMo7Quml7xsNEntSBSP0taKKLZ7uhdcg2LErSG/eLus8N+e/s8YEee5sD +R5q/Zcx/ZSRppugUiVvkNPfFsBST9Wd7Onp44QFWVpGmE0KN0jxAnEzv0YbfN1Eb +DKE79fGjSjXk4c6W3xt+v06X0BDoqAgwga8gC0MUxXRntDKCb42GwohAmTaDuh5A +ciIX11JlJHOwzu8Zza7/eGx7wBID1E5yDVBtO6M7o5lencjZDIWz2YrZVCbbbfqs +u/8lTMTRefRx04ZAGBOwY7VyTjDEl4SGLVYv1xX3f8Cu9fxb5fuhutMCAwEAAaOC +AS0wggEpMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoG +CCsGAQUFBwMCMB0GA1UdDgQWBBQz2EVm12iHGH5UDXAnkccm14VlwDCB1AYDVR0j +BIHMMIHJgBQnjmcRdMMmHT/tM2OzpNgdMOXo1aGBmqSBlzCBlDELMAkGA1UEBhMC +VVMxEDAOBgNVBAgMB01vbnRhbmExEDAOBgNVBAcMB0JvemVtYW4xETAPBgNVBAoM +CFNhd3Rvb3RoMRMwEQYDVQQLDApDb25zdWx0aW5nMRgwFgYDVQQDDA93d3cud29s +ZnNzbC5jb20xHzAdBgkqhkiG9w0BCQEWEGluZm9Ad29sZnNzbC5jb22CFD8pESBX +ceeO+RgNynBNWxUqQ9YkMA0GCSqGSIb3DQEBCwUAA4IBAQB/cUGycsGpuslSQOpt +PT3Arh5Ezfml1qw0X4vtzZGBDwUPXkuzGL8zemGlJfGRVcESZrcmO5y7IRoMcniI +V/pJIueAL8FAAWY9IGPn4zipVDlSQtK2OGsIfUVJGt61ZHDJZc4LlCTutEZnPHTw +KmFNsvxuysA2qbDTWuIVcvWkkHOyN1i0EDnThV9WkX7PVF3Gp0A2ve3yr+XOtuo4 +vkcyb+3Sup1w4XQu8CfkclN1Q84KB7R+dBcAVbXRkuRCOcqEUYT4I6ZBJ/sg4kPj +dNPOlU4fBt5lXuM44uvxpsprfFZRwAIebj9RwdUEwD1XVhVldqT060MnLMNYKVwY +2uj9 +-----END CERTIFICATE----- diff --git a/certs/client-ecc-ca-cert.der b/certs/client-ecc-ca-cert.der new file mode 100644 index 0000000000..5fa80d3c07 Binary files /dev/null and b/certs/client-ecc-ca-cert.der differ diff --git a/certs/client-ecc-ca-cert.pem b/certs/client-ecc-ca-cert.pem new file mode 100644 index 0000000000..9059e12507 --- /dev/null +++ b/certs/client-ecc-ca-cert.pem @@ -0,0 +1,54 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 4660 (0x1234) + Signature Algorithm: ecdsa-with-SHA256 + Issuer: C = US, ST = Washington, L = Seattle, O = wolfSSL, OU = Development, CN = www.wolfssl.com, emailAddress = info@wolfssl.com + Validity + Not Before: Jan 24 20:12:22 2026 GMT + Not After : Oct 20 20:12:22 2028 GMT + Subject: C = US, ST = Oregon, L = Salem, O = Client ECC, OU = Fast, CN = www.wolfssl.com, emailAddress = info@wolfssl.com + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:55:bf:f4:0f:44:50:9a:3d:ce:9b:b7:f0:c5:4d: + f5:70:7b:d4:ec:24:8e:19:80:ec:5a:4c:a2:24:03: + 62:2c:9b:da:ef:a2:35:12:43:84:76:16:c6:56:95: + 06:cc:01:a9:bd:f6:75:1a:42:f7:bd:a9:b2:36:22: + 5f:c7:5d:7f:b4 + ASN1 OID: prime256v1 + NIST CURVE: P-256 + X509v3 extensions: + X509v3 Subject Key Identifier: + EB:D4:4B:59:6B:95:61:3F:51:57:B6:04:4D:89:41:88:44:5C:AB:F2 + X509v3 Authority Key Identifier: + 56:8E:9A:C3:F0:42:DE:18:B9:45:55:6E:F9:93:CF:EA:C3:F3:A5:21 + X509v3 Basic Constraints: critical + CA:FALSE + X509v3 Key Usage: critical + Digital Signature, Key Encipherment, Key Agreement + X509v3 Extended Key Usage: + TLS Web Client Authentication + Signature Algorithm: ecdsa-with-SHA256 + Signature Value: + 30:45:02:21:00:9d:4d:72:4a:fb:f7:19:96:e3:d3:c2:75:ed: + b5:39:18:44:e7:61:7d:5e:31:d0:3c:eb:45:b3:6f:38:68:f9: + 1d:02:20:57:c1:19:e8:c8:8a:14:e7:37:d1:93:b3:46:f5:eb: + 8f:24:31:6c:78:d7:cd:b7:c9:8e:09:54:6e:4d:3b:7b:7b +-----BEGIN CERTIFICATE----- +MIICizCCAjGgAwIBAgICEjQwCgYIKoZIzj0EAwIwgZcxCzAJBgNVBAYTAlVTMRMw +EQYDVQQIDApXYXNoaW5ndG9uMRAwDgYDVQQHDAdTZWF0dGxlMRAwDgYDVQQKDAd3 +b2xmU1NMMRQwEgYDVQQLDAtEZXZlbG9wbWVudDEYMBYGA1UEAwwPd3d3LndvbGZz +c2wuY29tMR8wHQYJKoZIhvcNAQkBFhBpbmZvQHdvbGZzc2wuY29tMB4XDTI2MDEy +NDIwMTIyMloXDTI4MTAyMDIwMTIyMlowgY0xCzAJBgNVBAYTAlVTMQ8wDQYDVQQI +DAZPcmVnb24xDjAMBgNVBAcMBVNhbGVtMRMwEQYDVQQKDApDbGllbnQgRUNDMQ0w +CwYDVQQLDARGYXN0MRgwFgYDVQQDDA93d3cud29sZnNzbC5jb20xHzAdBgkqhkiG +9w0BCQEWEGluZm9Ad29sZnNzbC5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC +AARVv/QPRFCaPc6bt/DFTfVwe9TsJI4ZgOxaTKIkA2Ism9rvojUSQ4R2FsZWlQbM +Aam99nUaQve9qbI2Il/HXX+0o3UwczAdBgNVHQ4EFgQU69RLWWuVYT9RV7YETYlB +iERcq/IwHwYDVR0jBBgwFoAUVo6aw/BC3hi5RVVu+ZPP6sPzpSEwDAYDVR0TAQH/ +BAIwADAOBgNVHQ8BAf8EBAMCA6gwEwYDVR0lBAwwCgYIKwYBBQUHAwIwCgYIKoZI +zj0EAwIDSAAwRQIhAJ1Nckr79xmW49PCde21ORhE52F9XjHQPOtFs284aPkdAiBX +wRnoyIoU5zfRk7NG9euPJDFseNfNt8mOCVRuTTt7ew== +-----END CERTIFICATE----- diff --git a/certs/include.am b/certs/include.am index 68fcd1e2ea..a4c85eb69d 100644 --- a/certs/include.am +++ b/certs/include.am @@ -32,7 +32,9 @@ EXTRA_DIST += \ certs/ecc-client-keyPub.pem \ certs/empty-issuer-cert.pem \ certs/client-ecc-cert.pem \ + certs/client-ecc-ca-cert.pem \ certs/client-ca.pem \ + certs/client-ca-cert.pem \ certs/dh2048.pem \ certs/server-cert.pem \ certs/server-ecc.pem \ @@ -91,6 +93,8 @@ EXTRA_DIST += \ certs/client-cert.der \ certs/client-key.der \ certs/client-ecc-cert.der \ + certs/client-ecc-ca-cert.der \ + certs/client-ca-cert.der \ certs/client-keyPub.der \ certs/client-keyPub.pem \ certs/dh2048.der \ @@ -154,4 +158,3 @@ include certs/sphincs/include.am include certs/rpk/include.am include certs/acert/include.am include certs/mldsa/include.am - diff --git a/certs/renewcerts.sh b/certs/renewcerts.sh index 5aed648817..3521f401e5 100755 --- a/certs/renewcerts.sh +++ b/certs/renewcerts.sh @@ -21,6 +21,10 @@ # 1024/client-cert.pem # server-ecc-comp.pem # client-ca.pem +# client-ca-cert.der +# client-ca-cert.pem +# client-ecc-ca-cert.der +# client-ecc-ca-cert.pem # test/digsigku.pem # ecc-privOnlyCert.pem # client-uri-cert.pem @@ -896,6 +900,73 @@ run_renewcerts(){ echo "End of section" echo "---------------------------------------------------------------------" + ############################################################ + ########## update and sign client-ca-cert.pem ############## + ############################################################ + echo "Updating client-ca-cert.pem" + echo "" + cat > client-ca-ext.cnf <<'EOF' +[ client_ca ] +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid:always,issuer:always +basicConstraints=critical, CA:FALSE +keyUsage=critical, digitalSignature, keyEncipherment +extendedKeyUsage=clientAuth +EOF + check_result $? "Step 1" + + #pipe the following arguments to openssl req... + echo -e "US\\nMontana\\nBozeman\\nwolfSSL_2048\\nProgramming-2048\\n" \ + "www.wolfssl.com\\ninfo@wolfssl.com\\n.\\n.\\n" | \ + openssl req -new -key client-key.pem -config ./wolfssl.cnf -nodes \ + > client-ca-cert-req.pem + check_result $? "Step 2" + + openssl x509 -req -in client-ca-cert-req.pem -extfile client-ca-ext.cnf \ + -extensions client_ca -days 1000 -CA ca-cert.pem -CAkey ca-key.pem \ + -set_serial 0x1235 > client-ca-cert.pem + check_result $? "Step 3" + rm client-ca-cert-req.pem + + openssl x509 -in client-ca-cert.pem -text > tmp.pem + check_result $? "Step 4" + mv tmp.pem client-ca-cert.pem + + openssl x509 -inform PEM -in client-ca-cert.pem -outform DER \ + -out client-ca-cert.der + check_result $? "Step 5" + rm client-ca-ext.cnf + echo "End of section" + echo "---------------------------------------------------------------------" + + ############################################################ + ####### update and sign client-ecc-ca-cert.pem ############# + ############################################################ + echo "Updating client-ecc-ca-cert.pem" + echo "" + #pipe the following arguments to openssl req... + echo -e "US\\nOregon\\nSalem\\nClient ECC\\nFast\\nwww.wolfssl.com\\n" \ + "info@wolfssl.com\\n.\\n.\\n" | \ + openssl req -new -key ecc-client-key.pem -config ./wolfssl.cnf -nodes \ + > client-ecc-ca-cert-req.pem + check_result $? "Step 1" + + openssl x509 -req -in client-ecc-ca-cert-req.pem -extfile wolfssl.cnf \ + -extensions client_ecc -days 1000 -CA ca-ecc-cert.pem \ + -CAkey ca-ecc-key.pem -set_serial 0x1234 > client-ecc-ca-cert.pem + check_result $? "Step 2" + rm client-ecc-ca-cert-req.pem + + openssl x509 -in client-ecc-ca-cert.pem -text > tmp.pem + check_result $? "Step 3" + mv tmp.pem client-ecc-ca-cert.pem + + openssl x509 -inform PEM -in client-ecc-ca-cert.pem -outform DER \ + -out client-ecc-ca-cert.der + check_result $? "Step 4" + echo "End of section" + echo "---------------------------------------------------------------------" + #cleanup the file system now that we're done echo "Performing final steps, cleaning up the file system..." echo "" diff --git a/scripts/crl-gen-openssl.test b/scripts/crl-gen-openssl.test new file mode 100755 index 0000000000..b2b356d88f --- /dev/null +++ b/scripts/crl-gen-openssl.test @@ -0,0 +1,141 @@ +#!/usr/bin/env bash + +set -euo pipefail + +# Verifies CRLs generated by the C API unit tests (tests/api.c). +# Uses OpenSSL to validate CRL structure, signature, and revocation behavior. +# RSA: ca-cert.pem + server-cert.pem (revoked) + client-ca-cert.pem (good). +# ECC: ca-ecc-cert.pem + server-ecc.pem (revoked) + client-ecc-ca-cert.pem +# (good). + +OPENSSL=${OPENSSL:-openssl} + +if ! command -v "$OPENSSL" >/dev/null 2>&1; then + echo "skipping crl-gen-openssl.test: openssl not found" + exit 77 +fi + +normalize_dn() { + sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' \ + -e 's/^issuer=//' -e 's/^subject=//' \ + -e 's/[[:space:]]*=[[:space:]]*/=/g' \ + -e 's/[[:space:]]*,[[:space:]]*/,/g' +} + +check_crl() { + local crl="$1" + local ca_cert="$2" + local revoked_cert="$3" + local good_cert="$4" + local label="$5" + + echo "Checking $label CRL: $crl" + + local issuer subject + issuer=$("$OPENSSL" crl -in "$crl" -noout -issuer | normalize_dn) + subject=$("$OPENSSL" x509 -in "$ca_cert" -noout -subject | normalize_dn) + if [ "$issuer" != "$subject" ]; then + echo "issuer mismatch for $label CRL" + echo "issuer : $issuer" + echo "subject: $subject" + return 1 + fi + + local last_update next_update + last_update=$("$OPENSSL" crl -in "$crl" -noout -lastupdate) + next_update=$("$OPENSSL" crl -in "$crl" -noout -nextupdate) + if [ -z "$last_update" ] || [ -z "$next_update" ]; then + echo "missing lastUpdate/nextUpdate for $label CRL" + return 1 + fi + + if ! "$OPENSSL" crl -in "$crl" -noout -verify -CAfile "$ca_cert" \ + >/dev/null 2>&1; then + echo "CRL signature verification failed for $label" + return 1 + fi + + local revoked_count + revoked_count=$("$OPENSSL" crl -in "$crl" -text -noout | \ + grep -c "Serial Number" || true) + if [ "$revoked_count" -ne 4 ]; then + echo "unexpected revoked count for $label CRL: $revoked_count " \ + "(expected 4)" + return 1 + fi + + local serial found + serial=$("$OPENSSL" x509 -in "$revoked_cert" -noout -serial | cut -d= -f2 \ + | tr 'A-F' 'a-f' | tr -d ':' | sed 's/^0*//') + if [ -z "$serial" ]; then + serial=0 + fi + + found=0 + while IFS= read -r s; do + s=$(printf '%s' "$s" | tr 'A-F' 'a-f' | tr -d '[:space:]:' \ + | sed 's/^0*//') + if [ -z "$s" ]; then + s=0 + fi + if [ "$s" = "$serial" ]; then + found=1 + break + fi + done < <("$OPENSSL" crl -in "$crl" -text -noout | \ + awk -F: '/Serial Number/ {print $2}') + + if [ "$found" -ne 1 ]; then + echo "revoked serial not found in $label CRL: $serial" + return 1 + fi + + local verify_out verify_rc + verify_out=$("$OPENSSL" verify -CAfile "$ca_cert" -crl_check \ + -CRLfile "$crl" \ + "$revoked_cert" 2>&1) || verify_rc=$? + verify_rc=${verify_rc:-0} + if ! echo "$verify_out" | grep -qi "revoked"; then + echo "expected revoked verification failure for $label CRL" + echo "$verify_out" + return 1 + fi + if [ "$verify_rc" -eq 0 ]; then + # Some OpenSSL builds return success even when reporting revocation. + # Treat revoked output as authoritative. + : + fi + + if [ -n "$good_cert" ]; then + if ! "$OPENSSL" verify -CAfile "$ca_cert" -crl_check -CRLfile "$crl" \ + "$good_cert" >/dev/null 2>&1; then + echo "expected successful verification for $label CRL with " \ + "$good_cert" + return 1 + fi + fi +} + +crl_rsa="certs/crl/crlRsaOut.pem" +crl_ecc="certs/crl/crlEccOut.pem" + +if [ ! -f "$crl_rsa" ] && [ ! -f "$crl_ecc" ]; then + echo "skipping crl-gen-openssl.test: CRL outputs not found" + exit 77 +fi + +if [ -f "$crl_rsa" ]; then + ca_rsa="certs/ca-cert.pem" + revoked_rsa="certs/server-cert.pem" + good_rsa="certs/client-ca-cert.pem" + check_crl "$crl_rsa" "$ca_rsa" "$revoked_rsa" "$good_rsa" "RSA" +fi + +if [ -f "$crl_ecc" ]; then + ca_ecc="certs/ca-ecc-cert.pem" + revoked_ecc="certs/server-ecc.pem" + good_ecc="certs/client-ecc-ca-cert.pem" + check_crl "$crl_ecc" "$ca_ecc" "$revoked_ecc" "${good_ecc:-}" "ECC" +fi + +echo "crl-gen-openssl.test: OK" diff --git a/scripts/include.am b/scripts/include.am index 7325d3b668..b2dd157ea8 100644 --- a/scripts/include.am +++ b/scripts/include.am @@ -18,7 +18,9 @@ if BUILD_RSA if BUILD_CRL # make revoked test rely on completion of resume test dist_noinst_SCRIPTS+= scripts/crl-revoked.test +dist_noinst_SCRIPTS+= scripts/crl-gen-openssl.test scripts/crl-revoked.log: scripts/resume.log +scripts/crl-gen-openssl.log: scripts/crl-revoked.log endif # arrange to serialize ocsp.test, ocsp-stapling.test, ocsp-stapling-with-ca-as-responder.test, ocsp-stapling2.test, and testsuite, diff --git a/src/crl.c b/src/crl.c index 85cd71b365..283e8c59e9 100644 --- a/src/crl.c +++ b/src/crl.c @@ -40,6 +40,8 @@ CRL Options: #include #include #include +#include +#include #ifndef NO_STRING_H #include @@ -817,6 +819,227 @@ int BufferLoadCRL(WOLFSSL_CRL* crl, const byte* buff, long sz, int type, return ret ? ret : WOLFSSL_SUCCESS; /* convert 0 to WOLFSSL_SUCCESS */ } +/* Store CRL into a buffer in DER or PEM format. + * If buff is NULL, updates inOutSz with required size and returns success. + * Returns WOLFSSL_SUCCESS on success, negative on failure. + */ +int BufferStoreCRL(WOLFSSL_CRL* crl, byte* buff, long* inOutSz, int type) +{ + CRL_Entry* ent = NULL; + const byte* tbs = NULL; + word32 tbsSz = 0; + const byte* sig = NULL; + word32 sigSz = 0; + word32 sigOID = 0; +#ifdef WC_RSA_PSS + const byte* sigParams = NULL; + word32 sigParamsSz = 0; +#endif + word32 algoLen = 0; + word32 bitHdrLen = 0; + word32 totalContentLen = 0; + word32 outerHdrLen = 0; + word32 derNeeded = 0; + long outSz = 0; + + WOLFSSL_ENTER("BufferStoreCRL"); + + if (crl == NULL || inOutSz == NULL) { + return BAD_FUNC_ARG; + } + + outSz = *inOutSz; + + /* Access the first CRL entry. */ + if (wc_LockRwLock_Rd(&crl->crlLock) != 0) { + WOLFSSL_MSG("wc_LockRwLock_Rd failed"); + return BAD_MUTEX_E; + } + ent = crl->crlList; + if (ent != NULL) { + tbs = ent->toBeSigned; + tbsSz = ent->tbsSz; + sig = ent->signature; + sigSz = ent->signatureSz; + sigOID = ent->signatureOID; +#ifdef WC_RSA_PSS + sigParams = ent->sigParams; + sigParamsSz = ent->sigParamsSz; +#endif + } + wc_UnLockRwLock(&crl->crlLock); + + if (ent == NULL || tbs == NULL || tbsSz == 0 || sig == NULL || sigSz == 0) { + WOLFSSL_MSG("CRL entry missing toBeSigned/signature data"); + return BAD_FUNC_ARG; + } + + /* Calculate encoded lengths for AlgorithmIdentifier. */ +#ifdef WC_RSA_PSS + if (sigParams != NULL && sigParamsSz > 0) { + /* OID + explicit parameters inside SEQUENCE */ + word32 oidSz = 0; + word32 idLen; + const byte* oid = OidFromId(sigOID, oidSigType, &oidSz); + if (oid == NULL) { + WOLFSSL_MSG("Unknown signature OID for CRL"); + return WOLFSSL_FATAL_ERROR; + } + /* OBJECT IDENTIFIER header */ + idLen = (word32)SetObjectId((int)oidSz, NULL); + algoLen = SetSequence(idLen + oidSz + sigParamsSz, NULL) + + idLen + oidSz + sigParamsSz; + } + else +#endif + { + algoLen = SetAlgoID((int)sigOID, NULL, oidSigType, 0); + if (algoLen == 0) { + WOLFSSL_MSG("SetAlgoID failed"); + return WOLFSSL_FATAL_ERROR; + } + } + + /* BIT STRING header for signature */ + bitHdrLen = SetBitString(sigSz, 0, NULL); + + /* Compute total DER size. */ + totalContentLen = tbsSz + algoLen + bitHdrLen + sigSz; + outerHdrLen = SetSequence(totalContentLen, NULL); + derNeeded = outerHdrLen + totalContentLen; + + if (type == WOLFSSL_FILETYPE_ASN1) { + if (buff == NULL) { + *inOutSz = (long)derNeeded; + return WOLFSSL_SUCCESS; + } + if ((long)derNeeded > outSz) { + WOLFSSL_MSG("Output buffer too small for DER CRL"); + return BUFFER_E; + } + + /* Encode DER CRL directly into caller buffer. */ + { + word32 pos = 0; +#ifdef WC_RSA_PSS + word32 oidSz = 0; + const byte* oid = NULL; +#endif + /* Outer SEQUENCE header */ + pos += SetSequence(totalContentLen, buff + pos); + /* tbsCertList */ + XMEMCPY(buff + pos, tbs, tbsSz); + pos += tbsSz; + + /* signatureAlgorithm AlgorithmIdentifier */ +#ifdef WC_RSA_PSS + if (sigParams != NULL && sigParamsSz > 0) { + /* Lookup OID bytes for signature algorithm. */ + oid = OidFromId(sigOID, oidSigType, &oidSz); + if (oid == NULL) { + WOLFSSL_MSG("Unknown signature OID for CRL"); + return WOLFSSL_FATAL_ERROR; + } + /* SEQUENCE header for AlgorithmIdentifier */ + pos += SetSequence((word32)SetObjectId((int)oidSz, NULL) + + oidSz + sigParamsSz, buff + pos); + /* OBJECT IDENTIFIER header and content */ + pos += (word32)SetObjectId((int)oidSz, buff + pos); + XMEMCPY(buff + pos, oid, oidSz); + pos += oidSz; + /* Parameters as captured (already DER encoded) */ + XMEMCPY(buff + pos, sigParams, sigParamsSz); + pos += sigParamsSz; + } + else +#endif + { + pos += SetAlgoID((int)sigOID, buff + pos, oidSigType, 0); + } + + /* signature BIT STRING and bytes */ + pos += SetBitString(sigSz, 0, buff + pos); + XMEMCPY(buff + pos, sig, sigSz); + (void)pos; /* pos not used after this point */ + } + + *inOutSz = (long)derNeeded; + return WOLFSSL_SUCCESS; + } +#ifdef WOLFSSL_DER_TO_PEM + else if (type == WOLFSSL_FILETYPE_PEM) { + byte* derTmp = NULL; + int pemSz; + /* Build DER first in a temporary buffer. */ + derTmp = (byte*)XMALLOC(derNeeded, crl->heap, DYNAMIC_TYPE_TMP_BUFFER); + if (derTmp == NULL) { + return MEMORY_E; + } + /* Encode DER CRL into temporary buffer. */ + { + word32 pos = 0; +#ifdef WC_RSA_PSS + word32 oidSz = 0; + const byte* oid = NULL; +#endif + pos += SetSequence(totalContentLen, derTmp + pos); + XMEMCPY(derTmp + pos, tbs, tbsSz); + pos += tbsSz; +#ifdef WC_RSA_PSS + if (sigParams != NULL && sigParamsSz > 0) { + oid = OidFromId(sigOID, oidSigType, &oidSz); + if (oid == NULL) { + XFREE(derTmp, crl->heap, DYNAMIC_TYPE_TMP_BUFFER); + return WOLFSSL_FATAL_ERROR; + } + pos += SetSequence((word32)SetObjectId((int)oidSz, NULL) + + oidSz + sigParamsSz, derTmp + pos); + pos += (word32)SetObjectId((int)oidSz, derTmp + pos); + XMEMCPY(derTmp + pos, oid, oidSz); + pos += oidSz; + XMEMCPY(derTmp + pos, sigParams, sigParamsSz); + pos += sigParamsSz; + } + else +#endif + { + pos += SetAlgoID((int)sigOID, derTmp + pos, oidSigType, 0); + } + pos += SetBitString(sigSz, 0, derTmp + pos); + XMEMCPY(derTmp + pos, sig, sigSz); + (void)pos; /* pos not used after this point */ + } + + /* Determine required PEM size. */ + pemSz = wc_DerToPemEx(derTmp, derNeeded, NULL, 0, NULL, CRL_TYPE); + if (pemSz < 0) { + XFREE(derTmp, crl->heap, DYNAMIC_TYPE_TMP_BUFFER); + return WOLFSSL_FATAL_ERROR; + } + if (buff == NULL) { + *inOutSz = pemSz; + XFREE(derTmp, crl->heap, DYNAMIC_TYPE_TMP_BUFFER); + return WOLFSSL_SUCCESS; + } + if (outSz < pemSz) { + XFREE(derTmp, crl->heap, DYNAMIC_TYPE_TMP_BUFFER); + WOLFSSL_MSG("Output buffer too small for PEM CRL"); + return BUFFER_E; + } + if (wc_DerToPemEx(derTmp, derNeeded, buff, (word32)pemSz, NULL, + CRL_TYPE) < 0) { + XFREE(derTmp, crl->heap, DYNAMIC_TYPE_TMP_BUFFER); + return WOLFSSL_FATAL_ERROR; + } + *inOutSz = pemSz; + XFREE(derTmp, crl->heap, DYNAMIC_TYPE_TMP_BUFFER); + return WOLFSSL_SUCCESS; + } +#endif /* WOLFSSL_DER_TO_PEM */ + else { + return BAD_FUNC_ARG; + } +} #ifdef HAVE_CRL_UPDATE_CB /* Fill out CRL info structure, WOLFSSL_SUCCESS on ok */ @@ -1894,5 +2117,693 @@ int LoadCRL(WOLFSSL_CRL* crl, const char* path, int type, int monitor) } #endif /* !NO_FILESYSTEM && !NO_WOLFSSL_DIR */ +#ifndef NO_FILESYSTEM +/* Store CRL to a file in DER or PEM format. + * Returns WOLFSSL_SUCCESS on success, negative on failure. + * @param [in] crl CRL object. + * @param [in] path Path to the file to store the CRL. + * @param [in] type Format of encoding. Valid values: + * WOLFSSL_FILETYPE_ASN1, WOLFSSL_FILETYPE_PEM. + * @return WOLFSSL_SUCCESS on success, or negative on failure. + */ +int StoreCRL(WOLFSSL_CRL* crl, const char* path, int type) +{ + XFILE fp = XBADFILE; + int ret = WOLFSSL_SUCCESS; + long sz = 0; + byte* mem = NULL; + + WOLFSSL_ENTER("StoreCRL"); + + if (crl == NULL || path == NULL) + return BAD_FUNC_ARG; + + /* Determine required size. */ + ret = BufferStoreCRL(crl, NULL, &sz, type); + if (ret != WOLFSSL_SUCCESS) { + return ret; + } + + /* Allocate temporary buffer. */ + mem = (byte*)XMALLOC((size_t)sz, crl->heap, DYNAMIC_TYPE_TMP_BUFFER); + if (mem == NULL) { + return MEMORY_E; + } + + /* Encode CRL into buffer. */ + ret = BufferStoreCRL(crl, mem, &sz, type); + if (ret == WOLFSSL_SUCCESS) { + /* Open destination file for writing. */ + fp = XFOPEN(path, "wb"); + if (fp == XBADFILE) { + ret = WOLFSSL_BAD_FILE; + } + else { + size_t wrote = XFWRITE(mem, 1, (size_t)sz, fp); + if (wrote != (size_t)sz) { + WOLFSSL_MSG("CRL file write failed"); + ret = FWRITE_ERROR; + } + XFCLOSE(fp); + } + } + + XFREE(mem, crl->heap, DYNAMIC_TYPE_TMP_BUFFER); + return ret; +} +#else +int StoreCRL(WOLFSSL_CRL* crl, const char* file, int type) +{ + (void)crl; + (void)file; + (void)type; + return NOT_COMPILED_IN; +} +#endif /* NO_FILESYSTEM */ + +#if defined(OPENSSL_EXTRA) +/* Create a new empty CRL object for generation. + * Version is set to 2 by default. Use wolfSSL_X509_CRL_set_version() to + * change it. + * lastUpdate set to current time, nextUpdate set to 500 days from now. + * Returns a new CRL or NULL on failure. + */ +WOLFSSL_X509_CRL* wolfSSL_X509_CRL_new(void) +{ + WOLFSSL_X509_CRL* crl; + CRL_Entry* entry; + WOLFSSL_ASN1_TIME asnTime; + + WOLFSSL_ENTER("wolfSSL_X509_CRL_new"); + + crl = (WOLFSSL_X509_CRL*)XMALLOC(sizeof(WOLFSSL_X509_CRL), NULL, + DYNAMIC_TYPE_CRL); + if (crl == NULL) { + WOLFSSL_MSG("Memory allocation failed for CRL"); + return NULL; + } + + if (InitCRL(crl, NULL) < 0) { + WOLFSSL_MSG("Init CRL failed"); + XFREE(crl, NULL, DYNAMIC_TYPE_CRL); + return NULL; + } + + /* Allocate empty CRL entry for setting fields */ + entry = (CRL_Entry*)XMALLOC(sizeof(CRL_Entry), NULL, + DYNAMIC_TYPE_CRL_ENTRY); + if (entry == NULL) { + WOLFSSL_MSG("Memory allocation failed for CRL entry"); + FreeCRL(crl, 1); + return NULL; + } + XMEMSET(entry, 0, sizeof(CRL_Entry)); + + if (wc_InitMutex(&entry->verifyMutex) != 0) { + XFREE(entry, NULL, DYNAMIC_TYPE_CRL_ENTRY); + FreeCRL(crl, 1); + return NULL; + } + + crl->crlList = entry; + + /* Set thisUpdate to current time */ + if (wolfSSL_ASN1_TIME_adj(&asnTime, XTIME(NULL), 0, 0) == NULL) { + WOLFSSL_MSG("Failed to get current time"); + wc_FreeMutex(&entry->verifyMutex); + XFREE(entry, NULL, DYNAMIC_TYPE_CRL_ENTRY); + FreeCRL(crl, 1); + return NULL; + } + if (wolfSSL_X509_CRL_set_lastUpdate(crl, &asnTime) != WOLFSSL_SUCCESS) { + WOLFSSL_MSG("Failed to set last update"); + wc_FreeMutex(&entry->verifyMutex); + XFREE(entry, NULL, DYNAMIC_TYPE_CRL_ENTRY); + FreeCRL(crl, 1); + return NULL; + } + + /* Set next update date to 500 days from now, + * following convention from wc_InitCert() */ + if (wolfSSL_ASN1_TIME_adj(&asnTime, XTIME(NULL), 500, 0) == NULL) { + WOLFSSL_MSG("Failed to get next update time"); + wc_FreeMutex(&entry->verifyMutex); + XFREE(entry, NULL, DYNAMIC_TYPE_CRL_ENTRY); + FreeCRL(crl, 1); + return NULL; + } + if (wolfSSL_X509_CRL_set_nextUpdate(crl, &asnTime) != WOLFSSL_SUCCESS) { + WOLFSSL_MSG("Failed to set next update"); + wc_FreeMutex(&entry->verifyMutex); + XFREE(entry, NULL, DYNAMIC_TYPE_CRL_ENTRY); + FreeCRL(crl, 1); + return NULL; + } + + /* Set default version to v2 (required for extensions) */ + entry->version = 2; + + return crl; +} + +#ifdef WOLFSSL_CERT_GEN +/* Add a revoked certificate entry to CRL. + * crl: target CRL + * rev: serial number of revoked certificate + * Returns WOLFSSL_SUCCESS on success. + * TODO: support other fields for OpenSSL compatibility: revocationDate, + * extensions, issuer, etc. + */ +int wolfSSL_X509_CRL_add_revoked(WOLFSSL_X509_CRL* crl, + WOLFSSL_X509_REVOKED* rev) +{ + CRL_Entry* entry; + RevokedCert* rc; + RevokedCert* curr; + WOLFSSL_ASN1_TIME revDate = {0}; + + WOLFSSL_ENTER("wolfSSL_X509_CRL_add_revoked"); + + if (crl == NULL || rev == NULL || rev->serialNumber == NULL) { + return BAD_FUNC_ARG; + } + + entry = crl->crlList; + if (entry == NULL) { + return BAD_FUNC_ARG; + } + + /* Set the revocation date to the current time */ + if (wolfSSL_ASN1_TIME_adj(&revDate, XTIME(NULL), 0, 0) == NULL) { + WOLFSSL_MSG("Failed to get current time"); + return BAD_STATE_E; + } + + { + const byte* serial = rev->serialNumber->data; + int serialSz = rev->serialNumber->length; + + if (serial == NULL || serialSz <= 0) { + return BAD_FUNC_ARG; + } + + /* If the serial is DER-encoded ASN.1 INTEGER, strip tag/length. */ + if (serialSz >= 3 && serial[0] == ASN_INTEGER && + serial[1] == serialSz - 2) { + serial += 2; + serialSz -= 2; + } + + if (serialSz <= 0 || serialSz > EXTERNAL_SERIAL_SIZE) { + return BAD_FUNC_ARG; + } + + /* All zero serial numbers are invalid per rfc5280 and not supported */ + rc = (RevokedCert*)XMALLOC(sizeof(RevokedCert), crl->heap, + DYNAMIC_TYPE_REVOKED); + if (rc == NULL) { + return MEMORY_E; + } + XMEMSET(rc, 0, sizeof(RevokedCert)); + + XMEMCPY(rc->serialNumber, serial, (size_t)serialSz); + rc->serialSz = serialSz; + } + + XMEMCPY(rc->revDate, revDate.data, revDate.length); + rc->revDateFormat = (byte)revDate.type; + rc->next = NULL; + + /* Add to end of list */ + if (entry->certs == NULL) { + entry->certs = rc; + } + else { + for (curr = entry->certs; curr->next != NULL; curr = curr->next) + ; + curr->next = rc; + } + entry->totalCerts++; + + WOLFSSL_LEAVE("wolfSSL_X509_CRL_add_revoked", WOLFSSL_SUCCESS); + return WOLFSSL_SUCCESS; +} + +/* Add a revoked certificate entry to CRL by parsing a certificate buffer. + * crl: target CRL + * certBuf: DER-encoded certificate buffer + * certSz: size of certificate buffer + * revDate: revocation date (ASN.1 format), or NULL for + * current time + * revDateFmt: date format (ASN_UTC_TIME or ASN_GENERALIZED_TIME), ignored if + * revDate is NULL + * Returns WOLFSSL_SUCCESS on success. + * Note: this function is only available when WOLFSSL_CERT_GEN is defined. + */ +int wolfSSL_X509_CRL_add_revoked_cert(WOLFSSL_X509_CRL* crl, + const unsigned char* certBuf, int certSz) +{ + int ret; + DecodedCert cert[1]; + WOLFSSL_X509_REVOKED revoked; + WOLFSSL_ASN1_INTEGER* serialInt = NULL; + int i = 0; + + WOLFSSL_ENTER("wolfSSL_X509_CRL_add_revoked_cert"); + + if (crl == NULL || certBuf == NULL || certSz <= 0) { + return BAD_FUNC_ARG; + } + + /* Initialize and parse the certificate */ + InitDecodedCert(cert, certBuf, (word32)certSz, NULL); + ret = ParseCertRelative(cert, CERT_TYPE, NO_VERIFY, NULL, NULL); + if (ret != 0) { + WOLFSSL_MSG("Failed to parse certificate"); + FreeDecodedCert(cert); + return ret; + } + + serialInt = wolfSSL_ASN1_INTEGER_new(); + if (serialInt == NULL) { + FreeDecodedCert(cert); + return MEMORY_E; + } + + if (cert->serialSz > (WOLFSSL_ASN1_INTEGER_MAX - 2)) { + serialInt->data = (unsigned char*)XMALLOC(cert->serialSz + 2, NULL, + DYNAMIC_TYPE_OPENSSL); + if (serialInt->data == NULL) { + wolfSSL_ASN1_INTEGER_free(serialInt); + FreeDecodedCert(cert); + return MEMORY_E; + } + serialInt->dataMax = (unsigned int)cert->serialSz + 2; + serialInt->isDynamic = 1; + } + else { + serialInt->data = serialInt->intData; + serialInt->dataMax = WOLFSSL_ASN1_INTEGER_MAX; + } + + serialInt->data[i++] = ASN_INTEGER; + i += SetLength(cert->serialSz, serialInt->data + i); + XMEMCPY(&serialInt->data[i], cert->serial, cert->serialSz); + serialInt->length = cert->serialSz + 2; + + revoked.serialNumber = serialInt; + + /* Add the revoked certificate entry */ + ret = wolfSSL_X509_CRL_add_revoked(crl, &revoked); + + FreeDecodedCert(cert); + wolfSSL_ASN1_INTEGER_free(serialInt); + + return ret; +} + +static int GetCrlSignBufSz(int tbsSz, int sigType, RsaKey* rsaKey, + ecc_key* eccKey) +{ + int sigSz = 0; + int ret; + byte sigDummy = 0; + + if (tbsSz <= 0) + return BAD_FUNC_ARG; + +#ifndef NO_RSA + if (rsaKey != NULL) { + sigSz = wc_RsaEncryptSize(rsaKey); + } +#endif +#ifdef HAVE_ECC + if (sigSz <= 0 && eccKey != NULL) { + sigSz = wc_ecc_sig_size(eccKey); + } +#endif + if (sigSz <= 0) { + /* Fallback for unexpected key sizes */ + sigSz = 1024; + } + + /* Estimate total CRL size by asking AddSignature for the DER wrapper + * size (sequence + algo OID + BIT STRING headers). If it fails (e.g., + * unknown sigType), fall back to a conservative headroom of 64 bytes for + * those headers. This is defensive (size-estimate only); the real sign + * path will still report any unsupported sigType. */ + ret = AddSignature(NULL, tbsSz, &sigDummy, sigSz, sigType); + if (ret < 0) { + ret = tbsSz + sigSz + 64; + } + return ret; +} + +/* Sign a CRL with a private key, rebuilding TBS from fields. + * crl: CRL with fields set via setter functions + * pkey: private key for signing + * md: digest algorithm (e.g., EVP_sha256()) + * Note: only one entry is supported in the CRL list. + * Returns WOLFSSL_SUCCESS on success. + */ +int wolfSSL_X509_CRL_sign(WOLFSSL_X509_CRL* crl, WOLFSSL_EVP_PKEY* pkey, + const WOLFSSL_EVP_MD* md) +{ + int ret = WOLFSSL_SUCCESS; + CRL_Entry* entry; + byte* issuerDer = NULL; + int issuerSz = 0; + int sigType; + int tbsSz = 0; + int totalSz = 0; + byte* buf = NULL; + int bufSz = 0; + RsaKey* rsaKey = NULL; + ecc_key* eccKey = NULL; + WC_RNG rng; + int rngInit = 0; + + WOLFSSL_ENTER("wolfSSL_X509_CRL_sign"); + + if (crl == NULL || pkey == NULL || md == NULL) { + return BAD_FUNC_ARG; + } + + /* Fetch only the first entry in the CRL list */ + entry = crl->crlList; + if (entry == NULL) { + WOLFSSL_MSG("CRL has no entry"); + return BAD_FUNC_ARG; + } + + /* Determine signature type from digest and key type */ +#ifndef NO_RSA + if (ret == WOLFSSL_SUCCESS) { + if (pkey->type == WC_EVP_PKEY_RSA) { + if (md == wolfSSL_EVP_sha256()) { + sigType = CTC_SHA256wRSA; + } + #ifdef WOLFSSL_SHA384 + else if (md == wolfSSL_EVP_sha384()) { + sigType = CTC_SHA384wRSA; + } + #endif + #ifdef WOLFSSL_SHA512 + else if (md == wolfSSL_EVP_sha512()) { + sigType = CTC_SHA512wRSA; + } + #endif + else if (md == wolfSSL_EVP_sha1()) { + sigType = CTC_SHAwRSA; + } + else { + WOLFSSL_MSG("Unsupported digest for RSA"); + return BAD_FUNC_ARG; + } + rsaKey = (RsaKey*)pkey->rsa->internal; + } + else +#endif +#ifdef HAVE_ECC + if (pkey->type == WC_EVP_PKEY_EC) { + if (md == wolfSSL_EVP_sha256()) { + sigType = CTC_SHA256wECDSA; + } + #ifdef WOLFSSL_SHA384 + else if (md == wolfSSL_EVP_sha384()) { + sigType = CTC_SHA384wECDSA; + } + #endif + #ifdef WOLFSSL_SHA512 + else if (md == wolfSSL_EVP_sha512()) { + sigType = CTC_SHA512wECDSA; + } + #endif + else if (md == wolfSSL_EVP_sha1()) { + sigType = CTC_SHAwECDSA; + } + else { + WOLFSSL_MSG("Unsupported digest for ECDSA"); + return BAD_FUNC_ARG; + } + eccKey = (ecc_key*)pkey->ecc->internal; + } + else +#endif + { + WOLFSSL_MSG("Unsupported key type"); + return BAD_FUNC_ARG; + } + } + + /* Get issuer name DER */ + if (ret == WOLFSSL_SUCCESS) { + if (entry->issuer != NULL) { + issuerSz = wolfSSL_i2d_X509_NAME(entry->issuer, &issuerDer); + if (issuerSz <= 0) { + WOLFSSL_MSG("Failed to encode issuer name"); + ret = WOLFSSL_FAILURE; + } + } + else { + WOLFSSL_MSG("CRL has no issuer set"); + ret = BAD_FUNC_ARG; + } + } + + /* Copy dates from ASN1 time structures to raw fields if needed */ + if (ret == WOLFSSL_SUCCESS) { + if (entry->lastDateAsn1.length > 0 && entry->lastDateFormat == 0) { + XMEMCPY(entry->lastDate, entry->lastDateAsn1.data, + (size_t)entry->lastDateAsn1.length); + entry->lastDateFormat = (byte)entry->lastDateAsn1.type; + } + if (entry->nextDateAsn1.length > 0 && entry->nextDateFormat == 0) { + XMEMCPY(entry->nextDate, entry->nextDateAsn1.data, + (size_t)entry->nextDateAsn1.length); + entry->nextDateFormat = (byte)entry->nextDateAsn1.type; + } + } + + /* Verify we have valid dates */ + if (ret == WOLFSSL_SUCCESS) { + if (entry->lastDateFormat == 0) { + WOLFSSL_MSG("CRL has no lastUpdate date set"); + ret = BAD_FUNC_ARG; + } + } + + /* Initialize RNG */ + if (ret == WOLFSSL_SUCCESS) { + if (wc_InitRng(&rng) != 0) { + WOLFSSL_MSG("RNG init failed"); + ret = WOLFSSL_FAILURE; + } + else { + rngInit = 1; + } + } + + if (ret == WOLFSSL_SUCCESS) { + const byte* crlNumber = NULL; + word32 crlNumberSz = 0; + + if (entry->crlNumberSet) { + crlNumber = (const byte*)entry->crlNumber; + crlNumberSz = CRL_MAX_NUM_SZ; + } + + /* Determine TBS size, but this does not include the outer signature + * wrapper (AlgorithmIdentifier, BIT STRING and outer SEQUENCE) */ + bufSz = wc_MakeCRL_ex(issuerDer, (word32)issuerSz, + entry->lastDate, entry->lastDateFormat, + entry->nextDate, entry->nextDateFormat, + entry->certs, entry->totalCerts, + crlNumber, crlNumberSz, sigType, + entry->version, NULL, 0); + if (bufSz < 0) { + WOLFSSL_MSG("wc_MakeCRL_ex size check failed"); + ret = bufSz; + } + } + + if (ret == WOLFSSL_SUCCESS) { + bufSz = GetCrlSignBufSz(bufSz, sigType, rsaKey, eccKey); + if (bufSz <= 0) { + WOLFSSL_MSG("CRL buffer size calc failed"); + ret = bufSz; + } + } + + /* Allocate working buffer for TBS + signature */ + if (ret == WOLFSSL_SUCCESS) { + buf = (byte*)XMALLOC(bufSz, crl->heap, DYNAMIC_TYPE_TMP_BUFFER); + if (buf == NULL) { + ret = MEMORY_E; + } + } + + /* Build to-be-signed (TBS) portion of the CRL buffer. + * Note that we pass the fields rather than the CRL_entry struct so + * wolfcrypt need not know about the openSSL-compatible CRL_entry struct. + */ + if (ret == WOLFSSL_SUCCESS) { + const byte* crlNumber = NULL; + word32 crlNumberSz = 0; + + if (entry->crlNumberSet) { + crlNumber = (const byte*)entry->crlNumber; + crlNumberSz = CRL_MAX_NUM_SZ; + } + + tbsSz = wc_MakeCRL_ex(issuerDer, (word32)issuerSz, + entry->lastDate, entry->lastDateFormat, + entry->nextDate, entry->nextDateFormat, + entry->certs, entry->totalCerts, + crlNumber, crlNumberSz, sigType, + entry->version, buf, bufSz); + if (tbsSz < 0) { + WOLFSSL_MSG("wc_MakeCRL_ex failed"); + ret = tbsSz; + } + } + + /* Sign and complete CRL. Note that the output buffer is the same as the + * input buffer. The signature is added to the end of the buffer. + */ + if (ret == WOLFSSL_SUCCESS) { + totalSz = wc_SignCRL_ex(buf, tbsSz, sigType, buf, bufSz, + rsaKey, eccKey, &rng); + if (totalSz < 0) { + WOLFSSL_MSG("wc_SignCRL_ex failed"); + ret = totalSz; + } + } + + /* Update CRL entry with new toBeSigned and signature */ + /* Free old buffers if present */ + if (ret == WOLFSSL_SUCCESS) { + if (entry->toBeSigned != NULL) { + XFREE(entry->toBeSigned, crl->heap, DYNAMIC_TYPE_CRL_ENTRY); + } + if (entry->signature != NULL) { + XFREE(entry->signature, crl->heap, DYNAMIC_TYPE_CRL_ENTRY); + } + + /* Extract TBS and signature from the complete CRL buffer. + * This bookkeeping ensures the CRL_entry is updated with the results + * from the complete CRL buffer. + * After AddSignature, the buffer layout is: + * [outer SEQUENCE header][TBS][AlgorithmIdentifier] + * [BIT STRING signature] + */ + { + word32 idx = 0; + int len; + word32 tbsStart = 0; + word32 tbsLen = 0; + int sigLen; + + /* Parse outer SEQUENCE */ + if (GetSequence(buf, &idx, &len, (word32)totalSz) < 0) { + ret = ASN_PARSE_E; + } + + /* TBS starts here */ + if (ret == WOLFSSL_SUCCESS) + tbsStart = idx; + + /* Parse TBS SEQUENCE to get its length */ + if (ret == WOLFSSL_SUCCESS) { + if (GetSequence(buf, &idx, &len, (word32)totalSz) < 0) { + ret = ASN_PARSE_E; + } + } + if (ret == WOLFSSL_SUCCESS) { + tbsLen = idx + (word32)len - tbsStart; + idx = tbsStart + tbsLen; /* Move past TBS */ + } + + /* Allocate and copy TBS */ + if (ret == WOLFSSL_SUCCESS) { + entry->toBeSigned = (byte*)XMALLOC(tbsLen, crl->heap, + DYNAMIC_TYPE_CRL_ENTRY); + if (entry->toBeSigned == NULL) { + ret = MEMORY_E; + } + } + if (ret == WOLFSSL_SUCCESS) { + XMEMCPY(entry->toBeSigned, buf + tbsStart, tbsLen); + entry->tbsSz = tbsLen; + } + + /* Skip AlgorithmIdentifier */ + if (ret == WOLFSSL_SUCCESS) { + if (GetAlgoId(buf, &idx, (word32*)&len, oidSigType, + (word32)totalSz) < 0) { + XFREE(entry->toBeSigned, crl->heap, DYNAMIC_TYPE_CRL_ENTRY); + entry->toBeSigned = NULL; + ret = ASN_PARSE_E; + } + } + + /* Get BIT STRING */ + if (ret == WOLFSSL_SUCCESS) { + if (GetASNHeader(buf, ASN_BIT_STRING, &idx, &sigLen, + (word32)totalSz) < 0) { + XFREE(entry->toBeSigned, crl->heap, DYNAMIC_TYPE_CRL_ENTRY); + entry->toBeSigned = NULL; + ret = ASN_PARSE_E; + } + } + + /* Skip unused bits byte */ + if (ret == WOLFSSL_SUCCESS) { + if (buf[idx] != 0) { + XFREE(entry->toBeSigned, crl->heap, DYNAMIC_TYPE_CRL_ENTRY); + entry->toBeSigned = NULL; + ret = ASN_PARSE_E; + } + } + if (ret == WOLFSSL_SUCCESS) { + idx++; + sigLen--; + } + + if (ret == WOLFSSL_SUCCESS) { + entry->signature = (byte*)XMALLOC((word32)sigLen, crl->heap, + DYNAMIC_TYPE_CRL_ENTRY); + if (entry->signature == NULL) { + XFREE(entry->toBeSigned, crl->heap, DYNAMIC_TYPE_CRL_ENTRY); + entry->toBeSigned = NULL; + ret = MEMORY_E; + } + } + if (ret == WOLFSSL_SUCCESS) { + XMEMCPY(entry->signature, buf + idx, (size_t)sigLen); + entry->signatureSz = (word32)sigLen; + entry->signatureOID = (word32)sigType; + } + } + } + + /* Mark the CRL as verified/signed for future reference. */ + if (ret == WOLFSSL_SUCCESS) { + entry->verified = 1; + } + + if (issuerDer) + XFREE(issuerDer, NULL, DYNAMIC_TYPE_OPENSSL); + if (buf) + XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (rngInit) + wc_FreeRng(&rng); + + return ret; +} +#endif /* WOLFSSL_CERT_GEN */ + +#endif /* OPENSSL_EXTRA */ + #endif /* HAVE_CRL */ #endif /* !WOLFCRYPT_ONLY */ diff --git a/src/pk.c b/src/pk.c index 8f07a679c1..3f7ab4ee5d 100644 --- a/src/pk.c +++ b/src/pk.c @@ -63,7 +63,7 @@ #define ASN_LEN_SIZE(l) \ (((l) < 128) ? 1 : (((l) < 256) ? 2 : 3)) -#if defined(OPENSSL_EXTRA) +#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) #ifndef NO_ASN @@ -127,7 +127,7 @@ static int pem_mem_to_der(const char* pem, int pemSz, wc_pem_password_cb* cb, } #endif -#if !defined(NO_RSA) || !defined(WOLFCRYPT_ONLY) +#if defined(OPENSSL_EXTRA) && (!defined(NO_RSA) || !defined(WOLFCRYPT_ONLY)) #ifndef NO_BIO /* Read PEM data from a BIO and decode to DER in a new buffer. * @@ -306,9 +306,10 @@ static int der_write_to_bio_as_pem(const unsigned char* der, int derSz, #endif #endif -#if (!defined(NO_RSA) && defined(WOLFSSL_KEY_GEN)) || \ +#if defined(OPENSSL_EXTRA) && \ + ((!defined(NO_RSA) && defined(WOLFSSL_KEY_GEN)) || \ (!defined(NO_DH) && defined(WOLFSSL_DH_EXTRA)) || \ - (defined(HAVE_ECC) && defined(WOLFSSL_KEY_GEN)) + (defined(HAVE_ECC) && defined(WOLFSSL_KEY_GEN))) #if !defined(NO_FILESYSTEM) /* Write the DER data as PEM into file pointer. * @@ -342,7 +343,8 @@ static int der_write_to_file_as_pem(const unsigned char* der, int derSz, #endif #endif -#if defined(WOLFSSL_KEY_GEN) && defined(WOLFSSL_PEM_TO_DER) +#if defined(OPENSSL_EXTRA) && defined(WOLFSSL_KEY_GEN) && \ + defined(WOLFSSL_PEM_TO_DER) /* Encrypt private key into PEM format. * * DER is encrypted in place. @@ -464,10 +466,10 @@ int EncryptDerKey(byte *der, int *derSz, const WOLFSSL_EVP_CIPHER* cipher, WC_FREE_VAR_EX(info, NULL, DYNAMIC_TYPE_ENCRYPTEDINFO); return ret == 0; } -#endif /* WOLFSSL_KEY_GEN || WOLFSSL_PEM_TO_DER */ +#endif /* OPENSSL_EXTRA && WOLFSSL_KEY_GEN && WOLFSSL_PEM_TO_DER */ -#if defined(WOLFSSL_KEY_GEN) && \ +#if defined(OPENSSL_EXTRA) && defined(WOLFSSL_KEY_GEN) && \ (defined(WOLFSSL_PEM_TO_DER) || defined(WOLFSSL_DER_TO_PEM)) && \ (!defined(NO_RSA) || defined(HAVE_ECC)) /* Encrypt the DER in PEM format. @@ -696,7 +698,8 @@ static int pk_bn_field_print_fp(XFILE fp, int indent, const char* field, #endif /* !NO_CERTS && XFPRINTF && !NO_FILESYSTEM && !NO_STDIO_FILESYSTEM && * (!NO_DSA || !NO_RSA || HAVE_ECC) */ -#if defined(XSNPRINTF) && !defined(NO_BIO) && !defined(NO_RSA) +#if defined(OPENSSL_EXTRA) && defined(XSNPRINTF) && !defined(NO_BIO) && \ + !defined(NO_RSA) /* snprintf() must be available */ /* Maximum number of extra indent spaces on each line. */ @@ -905,7 +908,7 @@ static int wolfssl_print_number(WOLFSSL_BIO* bio, mp_int* num, const char* name, return ret; } -#endif /* XSNPRINTF && !NO_BIO && !NO_RSA */ +#endif /* OPENSSL_EXTRA && XSNPRINTF && !NO_BIO && !NO_RSA */ #endif /* OPENSSL_EXTRA */ @@ -16721,4 +16724,3 @@ int wolfSSL_PEM_write_PKCS8PrivateKey(XFILE f, WOLFSSL_EVP_PKEY* pkey, ******************************************************************************/ #endif /* !WOLFSSL_PK_INCLUDED */ - diff --git a/src/ssl.c b/src/ssl.c index ff0a4eb0e5..f54b22319a 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -22962,6 +22962,10 @@ int wolfSSL_set_alpn_protos(WOLFSSL* ssl, #include "src/bio.c" #endif +#endif /* OPENSSL_EXTRA */ + +#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) + word32 nid2oid(int nid, int grp) { /* get OID type */ @@ -23729,6 +23733,10 @@ int oid2nid(word32 oid, int grp) return WOLFSSL_FATAL_ERROR; } +#endif /* OPENSSL_EXTRA || OPENSSL_EXTRA_X509_SMALL */ + +#if defined(OPENSSL_EXTRA) + /* frees all nodes in the current threads error queue * * id thread id. ERR_remove_state is depreciated and id is ignored. The diff --git a/src/ssl_asn1.c b/src/ssl_asn1.c index 2fe3cab470..7f5d44b925 100644 --- a/src/ssl_asn1.c +++ b/src/ssl_asn1.c @@ -722,7 +722,8 @@ void* wolfSSL_ASN1_item_d2i(void** dst, const byte **src, long len, * ASN1_BIT_STRING APIs ******************************************************************************/ -#if defined(OPENSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL) +#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) || \ + defined(WOLFSSL_WPAS_SMALL) /* Create a new ASN.1 BIT_STRING object. * * @return ASN.1 BIT_STRING object on success. @@ -4549,7 +4550,8 @@ int wolfSSL_ASN1_UTCTIME_print(WOLFSSL_BIO* bio, const WOLFSSL_ASN1_UTCTIME* a) * ASN1_TYPE APIs ******************************************************************************/ -#if defined(OPENSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL) +#if defined(OPENSSL_ALL) || defined(OPENSSL_EXTRA) || \ + defined(OPENSSL_EXTRA_X509_SMALL) || defined(WOLFSSL_WPAS_SMALL) /** * Allocate a new ASN.1 TYPE object. @@ -4623,6 +4625,11 @@ void wolfSSL_ASN1_TYPE_free(WOLFSSL_ASN1_TYPE* at) XFREE(at, NULL, DYNAMIC_TYPE_OPENSSL); } +#endif /* OPENSSL_ALL || OPENSSL_EXTRA || OPENSSL_EXTRA_X509_SMALL || + WOLFSSL_WPAS_SMALL */ + +#if defined(OPENSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL) + int wolfSSL_i2d_ASN1_TYPE(WOLFSSL_ASN1_TYPE* at, unsigned char** pp) { int ret = WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR); @@ -4660,8 +4667,8 @@ int wolfSSL_i2d_ASN1_TYPE(WOLFSSL_ASN1_TYPE* at, unsigned char** pp) #endif /* OPENSSL_EXTRA || WOLFSSL_WPAS_SMALL */ -#if defined(OPENSSL_ALL) || defined(OPENSSL_EXTRA) || defined(WOLFSSL_WPAS) || \ - defined(WOLFSSL_WPAS_SMALL) +#if defined(OPENSSL_ALL) || defined(OPENSSL_EXTRA) || \ + defined(OPENSSL_EXTRA_X509_SMALL) || defined(WOLFSSL_WPAS_SMALL) /** * Set ASN.1 TYPE object with a type and value. * @@ -4723,9 +4730,9 @@ int wolfSSL_ASN1_TYPE_get(const WOLFSSL_ASN1_TYPE *a) return 0; } -#endif /* OPENSSL_ALL || OPENSSL_EXTRA || WOLFSSL_WPAS */ +#endif /* OPENSSL_ALL || OPENSSL_EXTRA || OPENSSL_EXTRA_X509_SMALL || + WOLFSSL_WPAS_SMALL */ #endif /* !NO_ASN */ #endif /* !WOLFSSL_SSL_ASN1_INCLUDED */ - diff --git a/src/x509.c b/src/x509.c index 291cd37ba2..345562f76e 100644 --- a/src/x509.c +++ b/src/x509.c @@ -9292,7 +9292,7 @@ WOLFSSL_X509_CRL* wolfSSL_d2i_X509_CRL(WOLFSSL_X509_CRL** crl, * return X509_NAME* on success * return NULL on failure */ -WOLFSSL_X509_NAME* wolfSSL_X509_CRL_get_issuer_name(WOLFSSL_X509_CRL* crl) +WOLFSSL_X509_NAME* wolfSSL_X509_CRL_get_issuer_name(const WOLFSSL_X509_CRL* crl) { if (crl == NULL || crl->crlList == NULL) return NULL; @@ -9300,6 +9300,31 @@ WOLFSSL_X509_NAME* wolfSSL_X509_CRL_get_issuer_name(WOLFSSL_X509_CRL* crl) return crl->crlList->issuer; } +/* Set issuer name of CRL + * return WOLFSSL_SUCCESS on success + * return WOLFSSL_FAILURE on failure + */ +int wolfSSL_X509_CRL_set_issuer_name(WOLFSSL_X509_CRL* crl, + const WOLFSSL_X509_NAME* name) +{ + WOLFSSL_X509_NAME* newName; + + if (crl == NULL || crl->crlList == NULL || name == NULL) + return WOLFSSL_FAILURE; + + newName = wolfSSL_X509_NAME_dup(name); + if (newName == NULL) + return WOLFSSL_FAILURE; + + if (crl->crlList->issuer != NULL) { + FreeX509Name(crl->crlList->issuer); + XFREE(crl->crlList->issuer, crl->heap, DYNAMIC_TYPE_X509); + } + crl->crlList->issuer = newName; + + return WOLFSSL_SUCCESS; +} + /* Retrieve version from CRL * return version on success * return 0 on failure @@ -9312,6 +9337,20 @@ int wolfSSL_X509_CRL_version(WOLFSSL_X509_CRL* crl) return crl->crlList->version; } +/* Set version of CRL + * RFC 5280: 5.2.1. CRL version is 0 for v1, 1 for v2, etc + * return WOLFSSL_SUCCESS on success + * return WOLFSSL_FAILURE on failure + */ +int wolfSSL_X509_CRL_set_version(WOLFSSL_X509_CRL* crl, long version) +{ + if (crl == NULL || crl->crlList == NULL) + return WOLFSSL_FAILURE; + + crl->crlList->version = (int)version; + return WOLFSSL_SUCCESS; +} + /* Retrieve sig OID from CRL * return OID on success * return 0 on failure @@ -9324,6 +9363,20 @@ int wolfSSL_X509_CRL_get_signature_type(WOLFSSL_X509_CRL* crl) return crl->crlList->signatureOID; } +/* Set signature type of CRL + * return WOLFSSL_SUCCESS on success + * return WOLFSSL_FAILURE on failure + */ +int wolfSSL_X509_CRL_set_signature_type(WOLFSSL_X509_CRL* crl, + int signatureType) +{ + if (crl == NULL || crl->crlList == NULL) + return WOLFSSL_FAILURE; + + crl->crlList->signatureOID = signatureType; + return WOLFSSL_SUCCESS; +} + /* Retrieve sig NID from CRL * return NID on success * return 0 on failure @@ -9336,6 +9389,19 @@ int wolfSSL_X509_CRL_get_signature_nid(const WOLFSSL_X509_CRL* crl) return oid2nid(crl->crlList->signatureOID, oidSigType); } +/* Set signature NID of CRL + * return OID on success + * return negative value on failure + */ +int wolfSSL_X509_CRL_set_signature_nid(WOLFSSL_X509_CRL* crl, int nid) +{ + if (crl == NULL || crl->crlList == NULL || nid <= 0) + return BAD_FUNC_ARG; + + crl->crlList->signatureOID = nid2oid(nid, oidSigType); + return WOLFSSL_SUCCESS; +} + /* Retrieve signature from CRL * return WOLFSSL_SUCCESS on success and negative values on failure */ @@ -9362,6 +9428,39 @@ int wolfSSL_X509_CRL_get_signature(WOLFSSL_X509_CRL* crl, return WOLFSSL_SUCCESS; } +int wolfSSL_X509_CRL_set_signature(WOLFSSL_X509_CRL* crl, + unsigned char* buf, int bufSz) +{ + byte* newSig; + + if (crl == NULL || crl->crlList == NULL || buf == NULL || bufSz <= 0) + return BAD_FUNC_ARG; + + /* Ensure signature buffer is allocated and large enough. */ + if (crl->crlList->signature == NULL) { + crl->crlList->signature = (byte*)XMALLOC((word32)bufSz, crl->heap, + DYNAMIC_TYPE_CRL_ENTRY); + if (crl->crlList->signature == NULL) { + return MEMORY_E; + } + crl->crlList->signatureSz = (word32)bufSz; + } + else if ((word32)bufSz > crl->crlList->signatureSz) { + newSig = (byte*)XMALLOC((word32)bufSz, crl->heap, + DYNAMIC_TYPE_CRL_ENTRY); + if (newSig == NULL) { + return MEMORY_E; + } + XFREE(crl->crlList->signature, crl->heap, DYNAMIC_TYPE_CRL_ENTRY); + crl->crlList->signature = newSig; + crl->crlList->signatureSz = (word32)bufSz; + } + + XMEMCPY(crl->crlList->signature, buf, bufSz); + crl->crlList->signatureSz = (word32)bufSz; + return WOLFSSL_SUCCESS; +} + /* Retrieve serial number from RevokedCert * return WOLFSSL_SUCCESS on success and negative values on failure */ @@ -9843,8 +9942,17 @@ WOLFSSL_ASN1_TIME* wolfSSL_X509_CRL_get_lastUpdate(WOLFSSL_X509_CRL* crl) (crl->crlList->lastDateAsn1.data[0] != 0)) { return &crl->crlList->lastDateAsn1; } - else - return NULL; + return NULL; +} + +int wolfSSL_X509_CRL_set_lastUpdate(WOLFSSL_X509_CRL* crl, + const WOLFSSL_ASN1_TIME* time) +{ + if (crl != NULL && crl->crlList != NULL && time != NULL) { + crl->crlList->lastDateAsn1 = *time; + return WOLFSSL_SUCCESS; + } + return WOLFSSL_FAILURE; } WOLFSSL_ASN1_TIME* wolfSSL_X509_CRL_get_nextUpdate(WOLFSSL_X509_CRL* crl) @@ -9853,8 +9961,17 @@ WOLFSSL_ASN1_TIME* wolfSSL_X509_CRL_get_nextUpdate(WOLFSSL_X509_CRL* crl) (crl->crlList->nextDateAsn1.data[0] != 0)) { return &crl->crlList->nextDateAsn1; } - else - return NULL; + return NULL; +} + +int wolfSSL_X509_CRL_set_nextUpdate(WOLFSSL_X509_CRL* crl, + const WOLFSSL_ASN1_TIME* time) +{ + if (crl != NULL && crl->crlList != NULL && time != NULL) { + crl->crlList->nextDateAsn1 = *time; + return WOLFSSL_SUCCESS; + } + return WOLFSSL_FAILURE; } #ifndef NO_WOLFSSL_STUB @@ -9866,6 +9983,70 @@ int wolfSSL_X509_CRL_verify(WOLFSSL_X509_CRL* crl, WOLFSSL_EVP_PKEY* key) return 0; } #endif + +/* Encode CRL to DER format in memory. + * + * If *out is NULL, allocates memory and returns it via *out. + * If *out is not NULL, writes DER data starting at *out. + * + * @param crl CRL to encode + * @param out Pointer to output buffer pointer + * @return Size of DER encoding on success, negative on failure + */ +int wolfSSL_i2d_X509_CRL(WOLFSSL_X509_CRL* crl, unsigned char** out) +{ + int ret; + long derSz = 0; + byte* der = NULL; + int alloced = 0; + + WOLFSSL_ENTER("wolfSSL_i2d_X509_CRL"); + + if (crl == NULL) { + return BAD_FUNC_ARG; + } + + /* Get required size */ + ret = BufferStoreCRL(crl, NULL, &derSz, WOLFSSL_FILETYPE_ASN1); + if (ret != WOLFSSL_SUCCESS || derSz <= 0) { + WOLFSSL_MSG("BufferStoreCRL failed to get size"); + return WOLFSSL_FAILURE; + } + + if (out == NULL) { + /* Just return size */ + return (int)derSz; + } + + if (*out == NULL) { + /* Allocate output buffer */ + der = (byte*)XMALLOC((size_t)derSz, NULL, DYNAMIC_TYPE_OPENSSL); + if (der == NULL) { + WOLFSSL_MSG("Memory allocation failed"); + return MEMORY_E; + } + alloced = 1; + } + else { + der = *out; + } + + /* Encode CRL to DER */ + ret = BufferStoreCRL(crl, der, &derSz, WOLFSSL_FILETYPE_ASN1); + if (ret != WOLFSSL_SUCCESS) { + WOLFSSL_MSG("BufferStoreCRL failed to encode"); + if (alloced) { + XFREE(der, NULL, DYNAMIC_TYPE_OPENSSL); + } + return WOLFSSL_FAILURE; + } + + if (alloced) { + *out = der; + } + + return (int)derSz; +} #endif /* HAVE_CRL && OPENSSL_EXTRA */ #ifdef OPENSSL_EXTRA @@ -10446,10 +10627,11 @@ WOLFSSL_ASN1_INTEGER* wolfSSL_X509_get_serialNumber(WOLFSSL_X509* x509) #endif /* OPENSSL_EXTRA || WOLFSSL_WPAS_SMALL */ -#ifdef OPENSSL_EXTRA +#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) -#if defined(OPENSSL_ALL) || defined(WOLFSSL_APACHE_HTTPD) \ - || defined(WOLFSSL_HAPROXY) || defined(WOLFSSL_WPAS) +#if defined(OPENSSL_ALL) || defined(OPENSSL_EXTRA) || \ + defined(WOLFSSL_APACHE_HTTPD) || defined(WOLFSSL_HAPROXY) || \ + defined(WOLFSSL_WPAS) WOLFSSL_X509_ALGOR* wolfSSL_X509_ALGOR_new(void) { WOLFSSL_X509_ALGOR* ret; @@ -10864,10 +11046,11 @@ int wolfSSL_X509_PUBKEY_set(WOLFSSL_X509_PUBKEY **x, WOLFSSL_EVP_PKEY *key) return WOLFSSL_FAILURE; } -#endif /* OPENSSL_ALL || WOLFSSL_APACHE_HTTPD || WOLFSSL_HAPROXY || - * WOLFSSL_WPAS */ +#endif /* OPENSSL_ALL || OPENSSL_EXTRA || WOLFSSL_APACHE_HTTPD || + * WOLFSSL_HAPROXY || WOLFSSL_WPAS */ -#if !defined(NO_CERTS) && !defined(NO_ASN) && !defined(NO_PWDBASED) +#if defined(OPENSSL_EXTRA) && !defined(NO_CERTS) && !defined(NO_ASN) && \ + !defined(NO_PWDBASED) int wolfSSL_i2d_X509_PUBKEY(WOLFSSL_X509_PUBKEY* x509_PubKey, unsigned char** der) @@ -10877,9 +11060,9 @@ int wolfSSL_i2d_X509_PUBKEY(WOLFSSL_X509_PUBKEY* x509_PubKey, return wolfSSL_i2d_PublicKey(x509_PubKey->pkey, der); } -#endif /* !NO_CERTS && !NO_ASN && !NO_PWDBASED */ +#endif /* OPENSSL_EXTRA && !NO_CERTS && !NO_ASN && !NO_PWDBASED */ -#endif /* OPENSSL_EXTRA */ +#endif /* OPENSSL_EXTRA || OPENSSL_EXTRA_X509_SMALL */ #if defined(OPENSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL) WOLFSSL_BASIC_CONSTRAINTS* wolfSSL_BASIC_CONSTRAINTS_new(void) @@ -11030,7 +11213,7 @@ WOLF_STACK_OF(WOLFSSL_X509_OBJECT)* wolfSSL_sk_X509_OBJECT_deep_copy( /* Creates a duplicate of a WOLFSSL_X509_NAME structure. Returns a new WOLFSSL_X509_NAME structure or NULL on failure */ - WOLFSSL_X509_NAME* wolfSSL_X509_NAME_dup(WOLFSSL_X509_NAME *name) + WOLFSSL_X509_NAME* wolfSSL_X509_NAME_dup(const WOLFSSL_X509_NAME *name) { WOLFSSL_X509_NAME* copy = NULL; @@ -12909,6 +13092,19 @@ WOLFSSL_X509_CRL* wolfSSL_PEM_read_X509_CRL(XFILE fp, return (WOLFSSL_X509_CRL* )wolfSSL_PEM_read_X509_ex(fp, (void **)crl, cb, u, CRL_TYPE); } + +/* Convert CRL to DER or PEM format. + * Returns WOLFSSL_SUCCESS on success, negative on failure. + * The caller is responsible for freeing the buffer using XFREE. + */ +int wolfSSL_write_X509_CRL(WOLFSSL_X509_CRL* crl, const char* path, int type) +{ + int ret; + WOLFSSL_ENTER("wolfSSL_write_X509_CRL"); + ret = StoreCRL(crl, path, type); + WOLFSSL_LEAVE("wolfSSL_write_X509_CRL", ret); + return ret; +} #endif #ifdef WOLFSSL_CERT_GEN @@ -13593,7 +13789,7 @@ WOLFSSL_ASN1_OBJECT* wolfSSL_X509_NAME_ENTRY_get_object( else { /* iterate through and find first open spot */ for (i = 0; i < MAX_NAME_ENTRIES; i++) { - if (name->entry[i].set != 1) { /* not set so overwritten */ + if (name->entry[i].set == 0) { /* not set so overwritten */ WOLFSSL_MSG("Found place for name entry"); break; } @@ -13636,6 +13832,15 @@ WOLFSSL_ASN1_OBJECT* wolfSSL_X509_NAME_ENTRY_get_object( return WOLFSSL_FAILURE; } +#ifdef WOLFSSL_PYTHON + /* Set name index for OpenSSL stack index position and so Python can + * generate tuples/sets from the list. */ + for (i = 0; i < MAX_NAME_ENTRIES; i++) { + if (name->entry[i].set != 0) + name->entry[i].set = i + 1; + } +#endif + if (RebuildFullName(name) != 0) return WOLFSSL_FAILURE; @@ -13707,6 +13912,17 @@ WOLFSSL_ASN1_OBJECT* wolfSSL_X509_NAME_ENTRY_get_object( return NULL; } name->entry[loc].set = 0; +#ifdef WOLFSSL_PYTHON + { + int i; + /* Set name index for OpenSSL stack index position and so Python can + * generate tuples/sets from the list. */ + for (i = 0; i < MAX_NAME_ENTRIES; i++) { + if (name->entry[i].set != 0) + name->entry[i].set = i + 1; + } + } +#endif return ret; } @@ -13757,7 +13973,7 @@ WOLFSSL_ASN1_OBJECT* wolfSSL_X509_NAME_ENTRY_get_object( /* returns a pointer to the internal entry at location 'loc' on success, * a null pointer is returned in fail cases */ WOLFSSL_X509_NAME_ENTRY *wolfSSL_X509_NAME_get_entry( - WOLFSSL_X509_NAME *name, int loc) + const WOLFSSL_X509_NAME *name, int loc) { #ifdef WOLFSSL_DEBUG_OPENSSL WOLFSSL_ENTER("wolfSSL_X509_NAME_get_entry"); @@ -13773,15 +13989,7 @@ WOLFSSL_ASN1_OBJECT* wolfSSL_X509_NAME_ENTRY_get_object( } if (name->entry[loc].set) { -#ifdef WOLFSSL_PYTHON - /* "set" is not only flag use, but also stack index position use in - * OpenSSL. Python makes tuple based on this number. Therefore, - * updating "set" by position + 1. "plus 1" means to avoid "not set" - * zero. - */ - name->entry[loc].set = loc + 1; -#endif - return &name->entry[loc]; + return (WOLFSSL_X509_NAME_ENTRY*)&name->entry[loc]; } else { return NULL; @@ -15179,7 +15387,7 @@ int wolfSSL_sk_X509_OBJECT_push(WOLFSSL_STACK* sk, WOLFSSL_X509_OBJECT* obj) /* unlike wolfSSL_X509_NAME_dup this does not malloc a duplicate, only deep * copy. "to" is expected to be a fresh blank name, if not pointers could be * lost */ -int wolfSSL_X509_NAME_copy(WOLFSSL_X509_NAME* from, WOLFSSL_X509_NAME* to) +int wolfSSL_X509_NAME_copy(const WOLFSSL_X509_NAME* from, WOLFSSL_X509_NAME* to) { int i; diff --git a/tests/api.c b/tests/api.c index 95b5f37713..d4be404a08 100644 --- a/tests/api.c +++ b/tests/api.c @@ -20115,11 +20115,11 @@ static int test_sk_X509(void) return EXPECT_RESULT(); } -static int test_sk_X509_CRL(void) +static int test_sk_X509_CRL_decode(void) { EXPECT_DECLS; -#if defined(OPENSSL_ALL) && !defined(NO_CERTS) && defined(HAVE_CRL) && \ - !defined(NO_RSA) +#if (defined(OPENSSL_ALL) || defined(OPENSSL_EXTRA)) && !defined(NO_CERTS) && \ + defined(HAVE_CRL) && !defined(NO_RSA) X509_CRL* crl = NULL; XFILE fp = XBADFILE; STACK_OF(X509_CRL)* s = NULL; @@ -20211,11 +20211,17 @@ static int test_sk_X509_CRL(void) ExpectIntEQ(wolfSSL_X509_CRL_get_signature_type(&empty), 0); ExpectIntEQ(wolfSSL_X509_CRL_get_signature_nid(NULL), 0); ExpectIntEQ(wolfSSL_X509_CRL_get_signature_nid(&empty), 0); - ExpectIntEQ(wolfSSL_X509_CRL_get_signature(NULL, NULL, NULL), BAD_FUNC_ARG); - ExpectIntEQ(wolfSSL_X509_CRL_get_signature(crl , NULL, NULL), BAD_FUNC_ARG); - ExpectIntEQ(wolfSSL_X509_CRL_get_signature(NULL, NULL, &len), BAD_FUNC_ARG); - ExpectIntEQ(wolfSSL_X509_CRL_get_signature(&empty, NULL, &len), - BAD_FUNC_ARG); + { + int sigSz = 0; + ExpectIntEQ(wolfSSL_X509_CRL_get_signature(NULL, NULL, NULL), + BAD_FUNC_ARG); + ExpectIntEQ(wolfSSL_X509_CRL_get_signature(crl, NULL, NULL), + BAD_FUNC_ARG); + ExpectIntEQ(wolfSSL_X509_CRL_get_signature(NULL, NULL, &sigSz), + BAD_FUNC_ARG); + ExpectIntEQ(wolfSSL_X509_CRL_get_signature(&empty, NULL, &sigSz), + BAD_FUNC_ARG); + } ExpectIntEQ(wolfSSL_X509_REVOKED_get_serial_number(NULL, NULL, NULL), BAD_FUNC_ARG); ExpectIntEQ(wolfSSL_X509_REVOKED_get_serial_number(rev , NULL, NULL), @@ -20232,15 +20238,12 @@ static int test_sk_X509_CRL(void) ExpectIntEQ(wolfSSL_X509_CRL_get_signature_type(crl), CTC_SHA256wRSA); ExpectIntEQ(wolfSSL_X509_CRL_get_signature_nid(crl), WC_NID_sha256WithRSAEncryption); - ExpectIntEQ(wolfSSL_X509_CRL_get_signature(crl, NULL, &len), - WOLFSSL_SUCCESS); - ExpectIntEQ(len, 256); - len--; - ExpectIntEQ(wolfSSL_X509_CRL_get_signature(crl, buff, &len), BUFFER_E); - len += 2; - ExpectIntEQ(wolfSSL_X509_CRL_get_signature(crl, buff, &len), - WOLFSSL_SUCCESS); - ExpectIntEQ(len, 256); + { + int sigSz = 0; + ExpectIntEQ(wolfSSL_X509_CRL_get_signature(crl, NULL, &sigSz), + WOLFSSL_SUCCESS); + ExpectIntEQ(sigSz, 256); + } ExpectNotNull(wolfSSL_X509_CRL_get_lastUpdate(crl)); ExpectNotNull(wolfSSL_X509_CRL_get_nextUpdate(crl)); @@ -20310,6 +20313,312 @@ static int test_sk_X509_CRL(void) return EXPECT_RESULT(); } +#if (defined(OPENSSL_ALL) || defined(OPENSSL_EXTRA)) && !defined(NO_CERTS) && \ + defined(HAVE_CRL) && !defined(NO_FILESYSTEM) && \ + !defined(NO_STDIO_FILESYSTEM) && defined(WOLFSSL_CERT_GEN) +/* Helper function to create, sign, and write a CRL */ +static int generate_crl_test(const char* keyFile, const char* certFile, + const char* derFile, const char* pemFile, + const char* certToRevokePath) +{ + EXPECT_DECLS; + X509_NAME* issuer = NULL; + X509_NAME* decodedIssuer = NULL; + WOLFSSL_X509* cert = NULL; + WOLFSSL_ASN1_TIME asnTime = {0}; + XFILE fp = XBADFILE; + WOLFSSL_X509_CRL* crl = NULL; + WOLFSSL_X509_CRL* decodedCrl = NULL; + WOLFSSL_EVP_PKEY* pkey = NULL; + int i = 0; + int revokedCount = 0; + byte serialsToRevokeBytes[][1] = { { 0x02 }, { 0x03 }, { 0x04 } }; + static const int numSerials = + (int)(sizeof(serialsToRevokeBytes) / + sizeof(serialsToRevokeBytes[0])); + WOLFSSL_ASN1_INTEGER serialsToRevoke[3] = { + { .data = serialsToRevokeBytes[0], + .length = sizeof(serialsToRevokeBytes[0]) }, + { .data = serialsToRevokeBytes[1], + .length = sizeof(serialsToRevokeBytes[1]) }, + { .data = serialsToRevokeBytes[2], + .length = sizeof(serialsToRevokeBytes[2]) } + }; + WOLFSSL_X509_REVOKED revoked[3] = { + { .serialNumber = &serialsToRevoke[0] }, + { .serialNumber = &serialsToRevoke[1] }, + { .serialNumber = &serialsToRevoke[2] } + }; + WOLFSSL_X509* certToRevoke = NULL; + + /* Load certificate to get issuer name (CRL issuer = cert subject) */ + ExpectTrue((fp = XFOPEN(certFile, "rb")) != XBADFILE); + if (fp != XBADFILE) { + ExpectNotNull(cert = PEM_read_X509(fp, NULL, NULL, NULL)); + XFCLOSE(fp); + fp = XBADFILE; + } + + /* Create a new empty CRL */ + ExpectNotNull(crl = wolfSSL_X509_CRL_new()); + ExpectIntEQ(wolfSSL_X509_CRL_version(crl), 2); + + /* Set issuer name, must match certificate subject for verification */ + ExpectNotNull(issuer = X509_get_subject_name(cert)); + ExpectIntEQ(wolfSSL_X509_CRL_set_issuer_name(crl, issuer), WOLFSSL_SUCCESS); + + /* Set thisUpdate to current time */ + ExpectNotNull(wolfSSL_ASN1_TIME_adj(&asnTime, XTIME(NULL), 0, 0)); + ExpectIntEQ(wolfSSL_X509_CRL_set_lastUpdate(crl, &asnTime), + WOLFSSL_SUCCESS); + + /* Set nextUpdate to 30 days from now */ + ExpectNotNull(wolfSSL_ASN1_TIME_adj(&asnTime, XTIME(NULL), 30, 0)); + ExpectIntEQ(wolfSSL_X509_CRL_set_nextUpdate(crl, &asnTime), + WOLFSSL_SUCCESS); + + /* Add revoked certificates based on serial numbers */ + for (i = 0; i < numSerials; i++) { + ExpectIntEQ(wolfSSL_X509_CRL_add_revoked(crl, &revoked[i]), + WOLFSSL_SUCCESS); + } + + /* Add a revoked certificate entry to CRL by parsing a different + * certificate buffer */ + ExpectTrue((fp = XFOPEN(certToRevokePath, "rb")) != XBADFILE); + if (fp != XBADFILE) { + ExpectNotNull(certToRevoke = PEM_read_X509(fp, NULL, NULL, NULL)); + XFCLOSE(fp); + fp = XBADFILE; + } + { + const byte* certDer = NULL; + int certDerSz = 0; + + ExpectNotNull(certDer = + wolfSSL_X509_get_der(certToRevoke, &certDerSz)); + ExpectIntEQ(wolfSSL_X509_CRL_add_revoked_cert(crl, certDer, certDerSz), + WOLFSSL_SUCCESS); + } + wolfSSL_X509_free(certToRevoke); + certToRevoke = NULL; + + /* Count revoked certificates - should be 'numSerials' + 1 */ + revokedCount = 0; + if (EXPECT_SUCCESS() && crl != NULL && crl->crlList != NULL) { + RevokedCert* rev = crl->crlList->certs; + while (rev != NULL) { + revokedCount++; + rev = rev->next; + } + } + ExpectIntEQ(revokedCount, numSerials + 1); + + /* Load private key for signing */ + ExpectTrue((fp = XFOPEN(keyFile, "rb")) != XBADFILE); + if (fp != XBADFILE) { + ExpectNotNull(pkey = PEM_read_PrivateKey(fp, NULL, NULL, NULL)); + XFCLOSE(fp); + fp = XBADFILE; + } + + /* Sign the CRL */ + ExpectIntEQ(wolfSSL_X509_CRL_sign(crl, pkey, wolfSSL_EVP_sha256()), + WOLFSSL_SUCCESS); + + /* Encode CRL to DER */ + ExpectIntEQ(wolfSSL_write_X509_CRL(crl, derFile, WOLFSSL_FILETYPE_ASN1), + WOLFSSL_SUCCESS); + + /* Encode CRL to PEM */ + ExpectIntEQ(wolfSSL_write_X509_CRL(crl, pemFile, WOLFSSL_FILETYPE_PEM), + WOLFSSL_SUCCESS); + + /* ===== Validate encoded DER CRL by decoding and checking contents ===== */ + ExpectTrue((fp = XFOPEN(derFile, "rb")) != XBADFILE); + if (fp != XBADFILE) { + ExpectNotNull(decodedCrl = d2i_X509_CRL_fp(fp, NULL)); + XFCLOSE(fp); + fp = XBADFILE; + } + + /* Verify CRL version is v2 */ + ExpectIntEQ(wolfSSL_X509_CRL_version(decodedCrl), 2); + + /* Verify issuer name matches */ + ExpectNotNull(decodedIssuer = wolfSSL_X509_CRL_get_issuer_name(decodedCrl)); + ExpectIntEQ(X509_NAME_cmp(issuer, decodedIssuer), 0); + + /* Verify signature type is SHA256 with RSA or ECDSA */ + ExpectIntNE(wolfSSL_X509_CRL_get_signature_type(decodedCrl), 0); + + /* Verify lastUpdate and nextUpdate are set */ + ExpectNotNull(wolfSSL_X509_CRL_get_lastUpdate(decodedCrl)); + ExpectNotNull(wolfSSL_X509_CRL_get_nextUpdate(decodedCrl)); + + /* Count revoked certificates - should be 'numSerials' */ + revokedCount = 0; + if (EXPECT_SUCCESS() && decodedCrl != NULL && decodedCrl->crlList != NULL) { + RevokedCert* rev = decodedCrl->crlList->certs; + while (rev != NULL) { + revokedCount++; + rev = rev->next; + } + } + ExpectIntEQ(revokedCount, numSerials + 1); + + wolfSSL_X509_CRL_free(decodedCrl); + decodedCrl = NULL; + + /* ===== Validate encoded PEM CRL by decoding and checking contents ===== */ + ExpectTrue((fp = XFOPEN(pemFile, "rb")) != XBADFILE); + if (fp != XBADFILE) { + ExpectNotNull(decodedCrl = (X509_CRL*)PEM_read_X509_CRL(fp, NULL, + NULL, NULL)); + XFCLOSE(fp); + fp = XBADFILE; + } + + /* Verify CRL version is v2 */ + ExpectIntEQ(wolfSSL_X509_CRL_version(decodedCrl), 2); + + /* Verify issuer name matches */ + ExpectNotNull(decodedIssuer = wolfSSL_X509_CRL_get_issuer_name(decodedCrl)); + ExpectIntEQ(X509_NAME_cmp(issuer, decodedIssuer), 0); + + /* Count revoked certificates from PEM - should also be 'numSerials' */ + revokedCount = 0; + if (EXPECT_SUCCESS() && decodedCrl != NULL && decodedCrl->crlList != NULL) { + RevokedCert* rev = decodedCrl->crlList->certs; + while (rev != NULL) { + revokedCount++; + rev = rev->next; + } + } + ExpectIntEQ(revokedCount, numSerials + 1); + + wolfSSL_X509_CRL_free(decodedCrl); + + wolfSSL_EVP_PKEY_free(pkey); + wolfSSL_X509_CRL_free(crl); + wolfSSL_X509_free(cert); + + return EXPECT_RESULT(); +} +#endif + +static int test_sk_X509_CRL_encode(void) +{ + EXPECT_DECLS; +#if (defined(OPENSSL_ALL) || defined(OPENSSL_EXTRA)) && !defined(NO_CERTS) && \ + defined(HAVE_CRL) && !defined(NO_FILESYSTEM) && \ + !defined(NO_STDIO_FILESYSTEM) && defined(WOLFSSL_CERT_GEN) +#ifndef NO_RSA + static const char* crlRsaPemFile = "./certs/crl/crlRsaOut.pem"; + static const char* crlRsaDerFile = "./certs/crl/crlRsaOut.der"; + static const char* testRsaKeyFile = "./certs/ca-key.pem"; + /* Use ca-cert.pem to match ca-key.pem for proper CRL verification */ + static const char* testRsaCertFile = "./certs/ca-cert.pem"; + static const char* revokeRsaCertFile = "./certs/server-cert.pem"; +#endif +#ifdef HAVE_ECC + static const char* crlEccPemFile = "./certs/crl/crlEccOut.pem"; + static const char* crlEccDerFile = "./certs/crl/crlEccOut.der"; + static const char* testEccKeyFile = "./certs/ca-ecc-key.pem"; + /* Use ca-ecc-cert.pem to match ca-ecc-key.pem for proper CRL + * verification */ + static const char* testEccCertFile = "./certs/ca-ecc-cert.pem"; + static const char* revokeEccCertFile = "./certs/server-ecc.pem"; +#endif + +#ifndef NO_RSA + /* Generate RSA-signed CRL (PEM and DER) */ + ExpectIntEQ(generate_crl_test(testRsaKeyFile, testRsaCertFile, + crlRsaDerFile, crlRsaPemFile, revokeRsaCertFile), TEST_SUCCESS); +#endif + +#ifdef HAVE_ECC + /* Generate ECC-signed CRL (PEM and DER) */ + ExpectIntEQ(generate_crl_test(testEccKeyFile, testEccCertFile, + crlEccDerFile, crlEccPemFile, revokeEccCertFile), TEST_SUCCESS); +#endif +#endif + return EXPECT_RESULT(); +} + +static int test_wolfSSL_X509_CRL_sign_large(void) +{ + EXPECT_DECLS; +#if (defined(OPENSSL_ALL) || defined(OPENSSL_EXTRA)) && !defined(NO_CERTS) && \ + defined(HAVE_CRL) && !defined(NO_FILESYSTEM) && \ + !defined(NO_STDIO_FILESYSTEM) && defined(WOLFSSL_CERT_GEN) +#ifndef NO_RSA + static const char* testRsaKeyFile = "./certs/ca-key.pem"; + static const char* testRsaCertFile = "./certs/ca-cert.pem"; + X509_NAME* issuer = NULL; + WOLFSSL_X509* cert = NULL; + WOLFSSL_X509_CRL* crl = NULL; + WOLFSSL_EVP_PKEY* pkey = NULL; + WOLFSSL_ASN1_TIME asnTime = {0}; + WOLFSSL_X509_REVOKED revoked; + XFILE fp = XBADFILE; + int i; + byte serial[4]; + + ExpectTrue((fp = XFOPEN(testRsaCertFile, "rb")) != XBADFILE); + if (fp != XBADFILE) { + ExpectNotNull(cert = PEM_read_X509(fp, NULL, NULL, NULL)); + XFCLOSE(fp); + fp = XBADFILE; + } + + ExpectNotNull(crl = wolfSSL_X509_CRL_new()); + ExpectNotNull(issuer = X509_get_subject_name(cert)); + ExpectIntEQ(wolfSSL_X509_CRL_set_issuer_name(crl, issuer), WOLFSSL_SUCCESS); + + ExpectNotNull(wolfSSL_ASN1_TIME_adj(&asnTime, XTIME(NULL), 0, 0)); + ExpectIntEQ(wolfSSL_X509_CRL_set_lastUpdate(crl, &asnTime), + WOLFSSL_SUCCESS); + + ExpectNotNull(wolfSSL_ASN1_TIME_adj(&asnTime, XTIME(NULL), 30, 0)); + ExpectIntEQ(wolfSSL_X509_CRL_set_nextUpdate(crl, &asnTime), + WOLFSSL_SUCCESS); + + revoked.serialNumber = wolfSSL_ASN1_INTEGER_new(); + revoked.serialNumber->data = serial; + revoked.serialNumber->length = (int)sizeof(serial); + + for (i = 0; i < 1024; i++) { + serial[0] = (byte)(i & 0xff); + serial[1] = (byte)((i >> 8) & 0xff); + serial[2] = (byte)((i >> 16) & 0xff); + serial[3] = (byte)((i >> 24) & 0xff); + ExpectIntEQ(wolfSSL_X509_CRL_add_revoked(crl, &revoked), + WOLFSSL_SUCCESS); + } + + ExpectTrue((fp = XFOPEN(testRsaKeyFile, "rb")) != XBADFILE); + if (fp != XBADFILE) { + ExpectNotNull(pkey = PEM_read_PrivateKey(fp, NULL, NULL, NULL)); + XFCLOSE(fp); + fp = XBADFILE; + } + + ExpectIntEQ(wolfSSL_X509_CRL_sign(crl, pkey, wolfSSL_EVP_sha256()), + WOLFSSL_SUCCESS); + + revoked.serialNumber->data = NULL; + wolfSSL_ASN1_INTEGER_free(revoked.serialNumber); + revoked.serialNumber = NULL; + + wolfSSL_EVP_PKEY_free(pkey); + wolfSSL_X509_CRL_free(crl); + wolfSSL_X509_free(cert); +#endif /* !NO_RSA */ +#endif + return EXPECT_RESULT(); +} + static int test_X509_REQ(void) { EXPECT_DECLS; @@ -31597,7 +31906,9 @@ TEST_CASE testCases[] = { /* OpenSSL sk_X509 API test */ TEST_DECL(test_sk_X509), /* OpenSSL sk_X509_CRL API test */ - TEST_DECL(test_sk_X509_CRL), + TEST_DECL(test_sk_X509_CRL_decode), + TEST_DECL(test_sk_X509_CRL_encode), + TEST_DECL(test_wolfSSL_X509_CRL_sign_large), /* OpenSSL X509 REQ API test */ TEST_DECL(test_wolfSSL_d2i_X509_REQ), diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index 4ea0743ce6..666692d5e9 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -631,6 +631,7 @@ static word32 SizeASNLength(word32 length) #define ASNIntMSBSet(asn, data_a, i) \ (((asn)[i].tag == ASN_INTEGER) && \ ((data_a)[i].data.buffer.data != NULL && \ + ((data_a)[i].data.buffer.length > 0) && \ ((data_a)[i].data.buffer.data[0] & 0x80) == 0x80)) @@ -3131,11 +3132,10 @@ const char* GetSigName(int oid) { #if !defined(WOLFSSL_ASN_TEMPLATE) || defined(HAVE_PKCS7) || \ - defined(OPENSSL_EXTRA) + defined(OPENSSL_EXTRA) || defined(WOLFSSL_CERT_GEN) #if !defined(NO_DSA) || defined(HAVE_ECC) || !defined(NO_CERTS) || \ - (!defined(NO_RSA) && \ - (defined(WOLFSSL_CERT_GEN) || \ - ((defined(WOLFSSL_KEY_GEN) || defined(OPENSSL_EXTRA))))) + defined(WOLFSSL_CERT_GEN) || \ + (!defined(NO_RSA) && (defined(WOLFSSL_KEY_GEN) || defined(OPENSSL_EXTRA))) /* Set the DER/BER encoding of the ASN.1 INTEGER header. * * When output is NULL, calculate the header length only. @@ -41496,6 +41496,423 @@ int ParseCRL(RevokedCert* rcert, DecodedCRL* dcrl, const byte* buff, word32 sz, #endif /* WOLFSSL_ASN_TEMPLATE */ } +#ifdef WOLFSSL_CERT_GEN +/* Encode a date as ASN.1 (UTC or GeneralizedTime). + * Returns length written to output (including tag and length bytes). + * If output is NULL, just returns the required size. + */ +static word32 EncodeCrlDate(byte* output, const byte* date, byte format) +{ + word32 idx = 0; + word32 dateLen; + + /* Determine date length based on format */ + if (format == ASN_UTC_TIME) { + dateLen = ASN_UTC_TIME_SIZE - 1; /* exclude null terminator */ + } + else if (format == ASN_GENERALIZED_TIME) { + dateLen = ASN_GENERALIZED_TIME_SIZE - 1; + } + else { + return 0; /* unsupported format */ + } + + if (output != NULL) { + output[idx] = format; + } + idx++; + + if (output != NULL) { + idx += SetLength(dateLen, output + idx); + XMEMCPY(output + idx, date, dateLen); + } + else { + idx += SetLength(dateLen, NULL); + } + idx += dateLen; + + return idx; +} + +/* Encode a serial number as ASN.1 INTEGER. + * Similar to SetSerialNumber but always available. + */ +static int EncodeCrlSerial(const byte* sn, word32 snSz, byte* output, + word32 outputSz) +{ + int i; + int snSzInt = (int)snSz; + const byte* snPtr = sn; + + if (sn == NULL || snSzInt < 0) + return BAD_FUNC_ARG; + + /* remove leading zeros */ + while (snSzInt > 0 && snPtr[0] == 0) { + snSzInt--; + snPtr++; + } + /* Serial numbers must be non-negative; allow zero for tolerance */ + if (snSzInt == 0) { + i = SetASNInt(1, 0x00, output); + if (1 > (int)outputSz - i) { + return BUFFER_E; + } + if (output != NULL) { + output[i] = 0x00; + } + return i + 1; + } + + i = SetASNInt(snSzInt, snPtr[0], NULL); + /* sanity check number of bytes to copy */ + if (snSzInt > (int)outputSz - i || snSzInt <= 0) { + return BUFFER_E; + } + + if (output != NULL) { + /* write out ASN.1 Integer */ + (void)SetASNInt(snSzInt, snPtr[0], output); + XMEMCPY(output + i, snPtr, (size_t)snSzInt); + } + + return i + snSzInt; +} + +/* Encode a single revoked certificate entry. + * Returns length written to output. + */ +static word32 EncodeRevokedCert(byte* output, const RevokedCert* rc) +{ + word32 idx = 0; + word32 snSz, dateSz, seqSz; + byte snBuf[MAX_SN_SZ]; + byte dateBuf[MAX_DATE_SIZE + 2]; /* tag + length + data */ + byte seqBuf[MAX_SEQ_SZ]; + + /* Encode serial number */ + snSz = (word32)EncodeCrlSerial(rc->serialNumber, (word32)rc->serialSz, + snBuf, sizeof(snBuf)); + if ((int)snSz < 0) + return 0; + + /* Encode revocation date */ + dateSz = EncodeCrlDate(dateBuf, rc->revDate, rc->revDateFormat); + if (dateSz == 0) + return 0; + + /* Wrap in SEQUENCE */ + seqSz = SetSequence(snSz + dateSz, seqBuf); + + if (output != NULL) { + XMEMCPY(output + idx, seqBuf, seqSz); + idx += seqSz; + XMEMCPY(output + idx, snBuf, snSz); + idx += snSz; + XMEMCPY(output + idx, dateBuf, dateSz); + idx += dateSz; + } + else { + idx = seqSz + snSz + dateSz; + } + + return idx; +} + +/* Encode the CRL Number extension. + * Returns length written to output. + */ +static word32 EncodeCrlNumberExt(byte* output, const byte* crlNum, + word32 crlNumSz) +{ + word32 idx = 0; + word32 oidSz, intSz, octetSz, seqSz; + byte seqBuf[MAX_SEQ_SZ]; + byte octetBuf[MAX_OCTET_STR_SZ]; + byte intBuf[MAX_SN_SZ]; + + /* CRL Number OID: 2.5.29.20 */ + static const byte crlNumOid[] = { 0x06, 0x03, 0x55, 0x1d, 0x14 }; + oidSz = sizeof(crlNumOid); + + /* Encode the INTEGER for CRL number */ + intSz = (word32)EncodeCrlSerial(crlNum, crlNumSz, intBuf, sizeof(intBuf)); + if ((int)intSz < 0) + return 0; + + /* Wrap INTEGER in OCTET STRING */ + octetSz = SetOctetString(intSz, octetBuf); + + /* Wrap in extension SEQUENCE */ + seqSz = SetSequence(oidSz + octetSz + intSz, seqBuf); + + if (output != NULL) { + XMEMCPY(output + idx, seqBuf, seqSz); + idx += seqSz; + XMEMCPY(output + idx, crlNumOid, oidSz); + idx += oidSz; + XMEMCPY(output + idx, octetBuf, octetSz); + idx += octetSz; + XMEMCPY(output + idx, intBuf, intSz); + idx += intSz; + } + else { + idx = seqSz + oidSz + octetSz + intSz; + } + + return idx; +} + +/* Build CRL TBSCertList from fields. + * issuerDer: DER-encoded issuer Name + * issuerSz: size of issuer DER + * lastDate/lastDateFmt: thisUpdate time and format + * nextDate/nextDateFmt: nextUpdate time and format + * certs: linked list of revoked certificates (may be NULL) + * crlNumber/crlNumberSz: CRL number extension data (may be NULL) + * sigType: signature algorithm type (e.g., CTC_SHA256wRSA) + * version: CRL version (1 or 2; 2 required for extensions) + * output: buffer to write TBS (NULL to calculate size) + * outputSz: size of output buffer + * + * Returns: size of TBS on success, negative error code on failure + */ +int wc_MakeCRL_ex(const byte* issuerDer, word32 issuerSz, + const byte* lastDate, byte lastDateFmt, + const byte* nextDate, byte nextDateFmt, + RevokedCert* certs, int totalCerts, + const byte* crlNumber, word32 crlNumberSz, + int sigType, int version, + byte* output, word32 outputSz) +{ + word32 idx = 0; + word32 tbsContentSz = 0; + word32 versionSz = 0, algoSz = 0, lastDateSz = 0, nextDateSz = 0; + word32 revokedSz = 0, extSz = 0, extSeqSz = 0, extCtxSz = 0; + word32 tbsSeqSz; + byte tbsSeqBuf[MAX_SEQ_SZ]; + byte versionBuf[MAX_VERSION_SZ]; + byte algoBuf[MAX_ALGO_SZ]; + byte lastDateBuf[MAX_DATE_SIZE + 2]; + byte nextDateBuf[MAX_DATE_SIZE + 2]; + byte revokedSeqBuf[MAX_SEQ_SZ]; + byte extSeqBuf[MAX_SEQ_SZ]; + byte extCtxBuf[MAX_SEQ_SZ + 1]; /* context tag + length */ + RevokedCert* rc; + int i; + + (void)totalCerts; + + if (issuerDer == NULL || issuerSz == 0 || lastDate == NULL) + return BAD_FUNC_ARG; + + /* Version: only include if v2 (version = 2 means value 1 in ASN.1) */ + if (version >= 2) { + versionSz = (word32)SetMyVersion(version - 1, versionBuf, FALSE); + } + + /* Signature AlgorithmIdentifier */ + algoSz = SetAlgoID(sigType, algoBuf, oidSigType, 0); + if (algoSz == 0) + return ALGO_ID_E; + + /* thisUpdate */ + lastDateSz = EncodeCrlDate(lastDateBuf, lastDate, lastDateFmt); + if (lastDateSz == 0) + return ASN_DATE_SZ_E; + + /* nextUpdate (optional) */ + if (nextDate != NULL && nextDateFmt != 0) { + nextDateSz = EncodeCrlDate(nextDateBuf, nextDate, nextDateFmt); + } + + /* revokedCertificates (optional) */ + if (certs != NULL) { + word32 contentSz = 0; + /* First pass: calculate size */ + for (rc = certs; rc != NULL; rc = rc->next) { + word32 entrySz = EncodeRevokedCert(NULL, rc); + if (entrySz == 0) + return ASN_PARSE_E; + contentSz += entrySz; + } + revokedSz = SetSequence(contentSz, revokedSeqBuf) + contentSz; + } + + /* crlExtensions (optional) - CRL Number */ + if (crlNumber != NULL && crlNumberSz > 0 && version >= 2) { + word32 crlNumExtSz = EncodeCrlNumberExt(NULL, crlNumber, crlNumberSz); + if (crlNumExtSz > 0) { + extSeqSz = SetSequence(crlNumExtSz, extSeqBuf); + /* Context tag [0] EXPLICIT */ + extCtxBuf[0] = (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 0); + extCtxSz = 1 + SetLength(extSeqSz + crlNumExtSz, extCtxBuf + 1); + extSz = extCtxSz + extSeqSz + crlNumExtSz; + } + } + + /* Calculate total TBS content size */ + tbsContentSz = versionSz + algoSz + issuerSz + lastDateSz + nextDateSz + + revokedSz + extSz; + + /* TBS SEQUENCE header */ + tbsSeqSz = SetSequence(tbsContentSz, tbsSeqBuf); + + /* Check buffer size */ + if (output != NULL && (tbsSeqSz + tbsContentSz > outputSz)) + return BUFFER_E; + + /* If output is NULL, just return required size */ + if (output == NULL) + return (int)(tbsSeqSz + tbsContentSz); + + /* Encode TBS */ + idx = 0; + + /* TBS SEQUENCE header */ + XMEMCPY(output + idx, tbsSeqBuf, tbsSeqSz); + idx += tbsSeqSz; + + /* Version (optional) */ + if (versionSz > 0) { + XMEMCPY(output + idx, versionBuf, versionSz); + idx += versionSz; + } + + /* Signature AlgorithmIdentifier */ + XMEMCPY(output + idx, algoBuf, algoSz); + idx += algoSz; + + /* Issuer Name */ + XMEMCPY(output + idx, issuerDer, issuerSz); + idx += issuerSz; + + /* thisUpdate */ + XMEMCPY(output + idx, lastDateBuf, lastDateSz); + idx += lastDateSz; + + /* nextUpdate (optional) */ + if (nextDateSz > 0) { + XMEMCPY(output + idx, nextDateBuf, nextDateSz); + idx += nextDateSz; + } + + /* revokedCertificates (optional) */ + if (revokedSz > 0) { + word32 contentSz = 0; + for (rc = certs; rc != NULL; rc = rc->next) { + contentSz += EncodeRevokedCert(NULL, rc); + } + idx += SetSequence(contentSz, output + idx); + for (rc = certs, i = 0; rc != NULL; rc = rc->next, i++) { + idx += EncodeRevokedCert(output + idx, rc); + } + } + + /* crlExtensions (optional) */ + if (extSz > 0) { + word32 crlNumExtSz = EncodeCrlNumberExt(NULL, crlNumber, crlNumberSz); + /* Context tag [0] */ + output[idx++] = (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 0); + idx += SetLength(extSeqSz + crlNumExtSz, output + idx); + /* Extensions SEQUENCE */ + idx += SetSequence(crlNumExtSz, output + idx); + /* CRL Number extension */ + idx += EncodeCrlNumberExt(output + idx, crlNumber, crlNumberSz); + } + + return (int)idx; +} + +/* Sign a CRL TBS and produce complete CRL DER. + * tbsBuf: contains the TBS at the beginning + * tbsSz: size of TBS in tbsBuf + * sType: signature type (e.g., CTC_SHA256wRSA) + * buf: output buffer for complete CRL + * bufSz: size of output buffer + * rsaKey/eccKey: signing key (one must be non-NULL) + * rng: random number generator + * + * Returns: size of complete CRL on success, negative error on failure + */ +int wc_SignCRL_ex(const byte* tbsBuf, int tbsSz, int sType, + byte* buf, word32 bufSz, + RsaKey* rsaKey, ecc_key* eccKey, WC_RNG* rng) +{ + int ret; + int sigSz; + CertSignCtx certSignCtx_lcl; + CertSignCtx* certSignCtx = &certSignCtx_lcl; + void* heap = NULL; + + if (tbsBuf == NULL || tbsSz <= 0 || buf == NULL || rng == NULL) + return BAD_FUNC_ARG; + if (rsaKey == NULL && eccKey == NULL) + return BAD_FUNC_ARG; + + XMEMSET(certSignCtx, 0, sizeof(*certSignCtx)); + + if (rsaKey != NULL) { + heap = rsaKey->heap; + } +#ifdef HAVE_ECC + else if (eccKey != NULL) { + heap = eccKey->heap; + } +#endif + + /* Copy TBS to output buffer first */ + if ((word32)tbsSz > bufSz) + return BUFFER_E; + XMEMCPY(buf, tbsBuf, (size_t)tbsSz); + +#ifndef WOLFSSL_NO_MALLOC + certSignCtx->sig = (byte*)XMALLOC(MAX_ENCODED_SIG_SZ, heap, + DYNAMIC_TYPE_TMP_BUFFER); + if (certSignCtx->sig == NULL) + return MEMORY_E; + /* Initialize first byte to avoid static analysis warnings about using + * uninitialized memory if MakeSignature fails before writing sig. */ + certSignCtx->sig[0] = 0; +#endif + + /* Create signature */ + sigSz = MakeSignature(certSignCtx, buf, (word32)tbsSz, certSignCtx->sig, + MAX_ENCODED_SIG_SZ, rsaKey, eccKey, NULL, NULL, NULL, + NULL, NULL, rng, (word32)sType, heap); + if (sigSz < 0) { +#ifndef WOLFSSL_NO_MALLOC + XFREE(certSignCtx->sig, heap, DYNAMIC_TYPE_TMP_BUFFER); +#endif + return sigSz; + } + + /* Ensure output buffer is large enough for signature wrapper */ + ret = AddSignature(NULL, tbsSz, certSignCtx->sig, sigSz, sType); + if (ret < 0) { +#ifndef WOLFSSL_NO_MALLOC + XFREE(certSignCtx->sig, heap, DYNAMIC_TYPE_TMP_BUFFER); +#endif + return ret; + } + if ((word32)ret > bufSz) { +#ifndef WOLFSSL_NO_MALLOC + XFREE(certSignCtx->sig, heap, DYNAMIC_TYPE_TMP_BUFFER); +#endif + return BUFFER_E; + } + + /* Add signature algorithm and signature to buffer */ + ret = AddSignature(buf, tbsSz, certSignCtx->sig, sigSz, sType); + +#ifndef WOLFSSL_NO_MALLOC + XFREE(certSignCtx->sig, heap, DYNAMIC_TYPE_TMP_BUFFER); +#endif + + return ret; +} +#endif /* WOLFSSL_CERT_GEN */ + #endif /* HAVE_CRL */ diff --git a/wolfcrypt/src/evp.c b/wolfcrypt/src/evp.c index 1912b2eb47..d74a2c9a57 100644 --- a/wolfcrypt/src/evp.c +++ b/wolfcrypt/src/evp.c @@ -28,7 +28,8 @@ #elif defined(WOLFCRYPT_ONLY) #else -#if defined(OPENSSL_EXTRA) || defined(HAVE_CURL) +#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) || \ + defined(HAVE_CURL) #if !defined(HAVE_PKCS7) && \ ((defined(HAVE_FIPS) && defined(HAVE_FIPS_VERSION) && \ diff --git a/wolfssl/crl.h b/wolfssl/crl.h index 059edeef84..774f9361c2 100644 --- a/wolfssl/crl.h +++ b/wolfssl/crl.h @@ -39,8 +39,11 @@ WOLFSSL_LOCAL void FreeCRL(WOLFSSL_CRL* crl, int dynamic); WOLFSSL_LOCAL int LoadCRL(WOLFSSL_CRL* crl, const char* path, int type, int monitor); +WOLFSSL_LOCAL int StoreCRL(WOLFSSL_CRL* crl, const char* file, int type); WOLFSSL_LOCAL int BufferLoadCRL(WOLFSSL_CRL* crl, const byte* buff, long sz, int type, int verify); +WOLFSSL_LOCAL int BufferStoreCRL(WOLFSSL_CRL* crl, byte* buff, long* inOutSz, + int type); WOLFSSL_LOCAL int CheckCertCRL(WOLFSSL_CRL* crl, DecodedCert* cert); WOLFSSL_LOCAL int CheckCertCRL_ex(WOLFSSL_CRL* crl, byte* issuerHash, byte* serial, int serialSz, byte* serialHash, const byte* extCrlInfo, diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 59fb268aef..73b5d355d0 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -5464,7 +5464,9 @@ struct WOLFSSL_X509 { #endif /* WOLFSSL_CERT_REQ || WOLFSSL_CERT_GEN */ WOLFSSL_X509_NAME issuer; WOLFSSL_X509_NAME subject; -#if defined(OPENSSL_ALL) || defined(WOLFSSL_HAPROXY) || defined(WOLFSSL_WPAS) +#if defined(OPENSSL_ALL) || defined(OPENSSL_EXTRA) || \ + defined(OPENSSL_EXTRA_X509_SMALL) || defined(WOLFSSL_APACHE_HTTPD) || \ + defined(WOLFSSL_HAPROXY) || defined(WOLFSSL_WPAS) WOLFSSL_X509_ALGOR algor; WOLFSSL_X509_PUBKEY key; #endif diff --git a/wolfssl/openssl/ssl.h b/wolfssl/openssl/ssl.h index 5b8175564e..4a2dd2dd27 100644 --- a/wolfssl/openssl/ssl.h +++ b/wolfssl/openssl/ssl.h @@ -804,10 +804,13 @@ wolfSSL_X509_STORE_set_verify_cb((WOLFSSL_X509_STORE *)(s), (WOLFSSL_X509_STORE_ #define d2i_X509_CRL wolfSSL_d2i_X509_CRL #define d2i_X509_CRL_fp wolfSSL_d2i_X509_CRL_fp +#define i2d_X509_CRL wolfSSL_i2d_X509_CRL #define PEM_read_X509_CRL wolfSSL_PEM_read_X509_CRL +#define X509_CRL_new wolfSSL_X509_CRL_new #define X509_CRL_dup wolfSSL_X509_CRL_dup #define X509_CRL_free wolfSSL_X509_CRL_free +#define X509_CRL_sign wolfSSL_X509_CRL_sign #define X509_CRL_get_lastUpdate wolfSSL_X509_CRL_get_lastUpdate #define X509_CRL_get0_lastUpdate wolfSSL_X509_CRL_get_lastUpdate #define X509_CRL_get_nextUpdate wolfSSL_X509_CRL_get_nextUpdate @@ -817,6 +820,14 @@ wolfSSL_X509_STORE_set_verify_cb((WOLFSSL_X509_STORE *)(s), (WOLFSSL_X509_STORE_ #define X509_CRL_get_issuer wolfSSL_X509_CRL_get_issuer_name #define X509_CRL_get_signature_nid wolfSSL_X509_CRL_get_signature_nid #define X509_CRL_get_version wolfSSL_X509_CRL_version +#define X509_CRL_set_lastUpdate wolfSSL_X509_CRL_set_lastUpdate +#define X509_CRL_set1_lastUpdate wolfSSL_X509_CRL_set_lastUpdate +#define X509_CRL_set_nextUpdate wolfSSL_X509_CRL_set_nextUpdate +#define X509_CRL_set1_nextUpdate wolfSSL_X509_CRL_set_nextUpdate +#define X509_CRL_set_issuer_name wolfSSL_X509_CRL_set_issuer_name +#define X509_CRL_set_version wolfSSL_X509_CRL_set_version +#define X509_CRL_get0_signature wolfSSL_X509_CRL_get_signature +#define X509_CRL_get_signature_nid wolfSSL_X509_CRL_get_signature_nid #define X509_load_crl_file wolfSSL_X509_load_crl_file #define X509_ACERT_new wolfSSL_X509_ACERT_new diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h index 08989907c3..d21542d1d2 100644 --- a/wolfssl/ssl.h +++ b/wolfssl/ssl.h @@ -886,6 +886,9 @@ struct WOLFSSL_X509_VERIFY_PARAM { typedef struct WOLFSSL_X509_REVOKED { WOLFSSL_ASN1_INTEGER* serialNumber; /* stunnel dereference */ + /* TODO: add other fields to match OpenSSL's X509_REVOKED + * (struct x509_revoked_st) such as revocationDate, extensions, etc. + * Then update wolfSSL_X509_CRL_add_revoked to handle those fields. */ } WOLFSSL_X509_REVOKED; typedef enum { @@ -2371,8 +2374,11 @@ WOLFSSL_API void wolfSSL_X509_STORE_CTX_trusted_stack(WOLFSSL_X509_STORE_CTX *ct WOLF_STACK_OF(WOLFSSL_X509) *sk); WOLFSSL_API WOLFSSL_ASN1_TIME* wolfSSL_X509_CRL_get_lastUpdate(WOLFSSL_X509_CRL* crl); +WOLFSSL_API int wolfSSL_X509_CRL_set_lastUpdate(WOLFSSL_X509_CRL* crl, + const WOLFSSL_ASN1_TIME* time); WOLFSSL_API WOLFSSL_ASN1_TIME* wolfSSL_X509_CRL_get_nextUpdate(WOLFSSL_X509_CRL* crl); - +WOLFSSL_API int wolfSSL_X509_CRL_set_nextUpdate(WOLFSSL_X509_CRL* crl, + const WOLFSSL_ASN1_TIME* time); WOLFSSL_API WOLFSSL_EVP_PKEY* wolfSSL_X509_get_pubkey(WOLFSSL_X509* x509); WOLFSSL_API int wolfSSL_X509_CRL_verify(WOLFSSL_X509_CRL* crl, WOLFSSL_EVP_PKEY* pkey); WOLFSSL_API void wolfSSL_X509_OBJECT_free_contents(WOLFSSL_X509_OBJECT* obj); @@ -3467,15 +3473,25 @@ WOLFSSL_API WOLFSSL_X509_CRL *wolfSSL_d2i_X509_CRL_fp(XFILE file, WOLFSSL_X509_C WOLFSSL_API WOLFSSL_X509_CRL *wolfSSL_d2i_X509_CRL_bio(WOLFSSL_BIO *bp, WOLFSSL_X509_CRL **crl); WOLFSSL_API int wolfSSL_X509_CRL_version(WOLFSSL_X509_CRL *crl); +WOLFSSL_API int wolfSSL_X509_CRL_set_version(WOLFSSL_X509_CRL* crl, + long version); WOLFSSL_API int wolfSSL_X509_CRL_get_signature_type(WOLFSSL_X509_CRL* crl); +WOLFSSL_API int wolfSSL_X509_CRL_set_signature_type(WOLFSSL_X509_CRL* crl, + int signatureType); WOLFSSL_API int wolfSSL_X509_CRL_get_signature_nid( const WOLFSSL_X509_CRL* crl); +WOLFSSL_API int wolfSSL_X509_CRL_set_signature_nid(WOLFSSL_X509_CRL* crl, + int nid); WOLFSSL_API int wolfSSL_X509_CRL_get_signature(WOLFSSL_X509_CRL* crl, unsigned char* buf, int* bufSz); +WOLFSSL_API int wolfSSL_X509_CRL_set_signature(WOLFSSL_X509_CRL* crl, + unsigned char* buf, int bufSz); WOLFSSL_API int wolfSSL_X509_CRL_print(WOLFSSL_BIO* bio, WOLFSSL_X509_CRL* crl); WOLFSSL_API WOLFSSL_X509_NAME* wolfSSL_X509_CRL_get_issuer_name( - WOLFSSL_X509_CRL *crl); + const WOLFSSL_X509_CRL *crl); +WOLFSSL_API int wolfSSL_X509_CRL_set_issuer_name(WOLFSSL_X509_CRL* crl, + const WOLFSSL_X509_NAME* name); WOLFSSL_API int wolfSSL_X509_REVOKED_get_serial_number(RevokedCert* rev, byte* in, int* inOutSz); #endif @@ -3483,6 +3499,20 @@ WOLFSSL_API int wolfSSL_X509_REVOKED_get_serial_number(RevokedCert* rev, WOLFSSL_API WOLFSSL_X509_CRL* wolfSSL_X509_CRL_dup(const WOLFSSL_X509_CRL* crl); WOLFSSL_API void wolfSSL_X509_CRL_free(WOLFSSL_X509_CRL *crl); #endif +#if defined(HAVE_CRL) && defined(OPENSSL_EXTRA) +WOLFSSL_API WOLFSSL_X509_CRL* wolfSSL_X509_CRL_new(void); +#ifdef WOLFSSL_CERT_GEN +WOLFSSL_API int wolfSSL_X509_CRL_add_revoked(WOLFSSL_X509_CRL* crl, + WOLFSSL_X509_REVOKED* revoked); +WOLFSSL_API int wolfSSL_X509_CRL_add_revoked_cert(WOLFSSL_X509_CRL* crl, + const unsigned char* certBuf, int certSz); +WOLFSSL_API int wolfSSL_X509_CRL_sign(WOLFSSL_X509_CRL* crl, + WOLFSSL_EVP_PKEY* pkey, + const WOLFSSL_EVP_MD* md); +#endif /* WOLFSSL_CERT_GEN */ +WOLFSSL_API int wolfSSL_i2d_X509_CRL(WOLFSSL_X509_CRL* crl, + unsigned char** out); +#endif /* HAVE_CRL && OPENSSL_EXTRA */ #if defined(WOLFSSL_ACERT) && \ (defined(OPENSSL_EXTRA_X509_SMALL) || defined(OPENSSL_EXTRA)) @@ -5053,8 +5083,10 @@ WOLFSSL_API WOLFSSL_X509_NAME* wolfSSL_X509_NAME_new(void); #ifndef wolfSSL_X509_NAME_new_ex WOLFSSL_API WOLFSSL_X509_NAME* wolfSSL_X509_NAME_new_ex(void *heap); #endif -WOLFSSL_API WOLFSSL_X509_NAME* wolfSSL_X509_NAME_dup(WOLFSSL_X509_NAME* name); -WOLFSSL_API int wolfSSL_X509_NAME_copy(WOLFSSL_X509_NAME* from, WOLFSSL_X509_NAME* to); +WOLFSSL_API WOLFSSL_X509_NAME* wolfSSL_X509_NAME_dup( + const WOLFSSL_X509_NAME* name); +WOLFSSL_API int wolfSSL_X509_NAME_copy(const WOLFSSL_X509_NAME* from, + WOLFSSL_X509_NAME* to); WOLFSSL_API int wolfSSL_check_private_key(const WOLFSSL* ssl); #endif /* !NO_CERTS */ #endif /* OPENSSL_ALL || OPENSSL_EXTRA || OPENSSL_EXTRA_X509_SMALL */ @@ -5229,6 +5261,8 @@ WOLFSSL_API WOLF_STACK_OF(WOLFSSL_X509_INFO)* wolfSSL_PEM_X509_INFO_read_bio( #ifndef NO_FILESYSTEM WOLFSSL_API WOLFSSL_X509_CRL *wolfSSL_PEM_read_X509_CRL(XFILE fp, WOLFSSL_X509_CRL **x, wc_pem_password_cb *cb, void *u); +WOLFSSL_API int wolfSSL_write_X509_CRL(WOLFSSL_X509_CRL* crl, + const char* path, int type); #endif WOLFSSL_API int wolfSSL_PEM_get_EVP_CIPHER_INFO(const char* header, EncryptedInfo* cipher); @@ -5254,7 +5288,8 @@ struct WOLFSSL_CONF_CTX { WOLFSSL* ssl; }; -WOLFSSL_API WOLFSSL_X509_NAME_ENTRY *wolfSSL_X509_NAME_get_entry(WOLFSSL_X509_NAME *name, int loc); +WOLFSSL_API WOLFSSL_X509_NAME_ENTRY *wolfSSL_X509_NAME_get_entry( + const WOLFSSL_X509_NAME *name, int loc); WOLFSSL_API int wolfSSL_X509_NAME_ENTRY_set(const WOLFSSL_X509_NAME_ENTRY *ne); #endif /* OPENSSL_EXTRA || WOLFSSL_WPAS_SMALL */ @@ -5490,7 +5525,8 @@ WOLFSSL_API int wolfSSL_sk_CONF_VALUE_push(WOLF_STACK_OF(WOLFSSL_CONF_VALUE)* sk WOLFSSL_CONF_VALUE* val); #endif /* OPENSSL_ALL || HAVE_STUNNEL || WOLFSSL_NGINX || WOLFSSL_HAPROXY || OPENSSL_EXTRA || HAVE_LIGHTY */ -#if defined(OPENSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL) +#if !defined(NO_ASN) && (defined(OPENSSL_EXTRA) || \ + defined(OPENSSL_EXTRA_X509_SMALL) || defined(WOLFSSL_WPAS_SMALL)) WOLFSSL_API WOLFSSL_ASN1_BIT_STRING* wolfSSL_ASN1_BIT_STRING_new(void); WOLFSSL_API void wolfSSL_ASN1_BIT_STRING_free(WOLFSSL_ASN1_BIT_STRING* str); WOLFSSL_API WOLFSSL_ASN1_BIT_STRING* wolfSSL_X509_get0_pubkey_bitstr( @@ -5503,7 +5539,8 @@ WOLFSSL_API int wolfSSL_i2d_ASN1_BIT_STRING(const WOLFSSL_ASN1_BIT_STRING* bstr, unsigned char** pp); WOLFSSL_API WOLFSSL_ASN1_BIT_STRING* wolfSSL_d2i_ASN1_BIT_STRING( WOLFSSL_ASN1_BIT_STRING** out, const byte** src, long len); -#endif /* OPENSSL_EXTRA || WOLFSSL_WPAS_SMALL */ +#endif /* !NO_ASN && (OPENSSL_EXTRA || OPENSSL_EXTRA_X509_SMALL || + WOLFSSL_WPAS_SMALL) */ WOLFSSL_API int wolfSSL_version(WOLFSSL* ssl); diff --git a/wolfssl/wolfcrypt/asn_public.h b/wolfssl/wolfcrypt/asn_public.h index 685f806996..3c6ab6da9c 100644 --- a/wolfssl/wolfcrypt/asn_public.h +++ b/wolfssl/wolfcrypt/asn_public.h @@ -593,6 +593,21 @@ WOLFSSL_API int wc_SetCustomExtension(Cert *cert, int critical, const char *oid, #endif /* WOLFSSL_CERT_EXT */ +#if defined(WOLFSSL_CERT_GEN) && defined(HAVE_CRL) +/* CRL Generation functions */ +struct RevokedCert; /* forward declaration */ +WOLFSSL_API int wc_MakeCRL_ex(const byte* issuerDer, word32 issuerSz, + const byte* lastDate, byte lastDateFmt, + const byte* nextDate, byte nextDateFmt, + struct RevokedCert* certs, int totalCerts, + const byte* crlNumber, word32 crlNumberSz, + int sigType, int version, + byte* output, word32 outputSz); +WOLFSSL_API int wc_SignCRL_ex(const byte* tbsBuf, int tbsSz, int sType, + byte* buf, word32 bufSz, + RsaKey* rsaKey, ecc_key* eccKey, WC_RNG* rng); +#endif /* WOLFSSL_CERT_GEN && HAVE_CRL */ + WOLFSSL_API int wc_GetDateInfo(const byte* certDate, int certDateSz, const byte** date, byte* format, int* length); #ifndef NO_ASN_TIME