Merge pull request #10687 from rlm2002/zd-NameConstraints

Name Constraints cert chain walk
This commit is contained in:
Daniel Pouzzner
2026-07-01 17:24:52 -05:00
committed by GitHub
18 changed files with 535 additions and 21 deletions
+15
View File
@@ -104,3 +104,18 @@ EXTRA_DIST += \
certs/test/expired/expired-ca.der \
certs/test/expired/expired-cert.pem \
certs/test/expired/expired-cert.der
EXTRA_DIST += \
certs/test/nc-ancestor/gen-nc-ancestor.sh \
certs/test/nc-ancestor/00-root-cert.pem \
certs/test/nc-ancestor/00-root-key.pem \
certs/test/nc-ancestor/00-uri-permit-ca-permissive-cert.pem \
certs/test/nc-ancestor/00-uri-permit-ca-permissive-key.pem \
certs/test/nc-ancestor/01-uri-permit-ca-cert.pem \
certs/test/nc-ancestor/01-uri-permit-ca-key.pem \
certs/test/nc-ancestor/02-benign-sub-ca-cert.pem \
certs/test/nc-ancestor/02-benign-sub-ca-key.pem \
certs/test/nc-ancestor/03-leaf-attacker-key.pem \
certs/test/nc-ancestor/03-leaf-chain.pem \
certs/test/nc-ancestor/03-valid-leaf-cert.pem \
certs/test/nc-ancestor/03-valid-leaf-key.pem
+11
View File
@@ -0,0 +1,11 @@
-----BEGIN CERTIFICATE-----
MIIBnjCCAUWgAwIBAgIBEDAKBggqhkjOPQQDAjA3MQswCQYDVQQGEwJVUzERMA8G
A1UECgwITkMgVGVzdHMxFTATBgNVBAMMDE5DIFRlc3QgUm9vdDAeFw0yNjA2MTcx
NjQ5MTNaFw00NjA2MTIxNjQ5MTNaMDcxCzAJBgNVBAYTAlVTMREwDwYDVQQKDAhO
QyBUZXN0czEVMBMGA1UEAwwMTkMgVGVzdCBSb290MFkwEwYHKoZIzj0CAQYIKoZI
zj0DAQcDQgAEH8XA+HpY8YL8kkIzgHQKUudoe4ZACBd/d0stYnvyQDiko5rOjTEY
Ayha6yaf1oYaZqGdE7LrEXN0+mNplh/fuKNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO
BgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFFgBv14PFBEmIEUkSYhOelfJG8QtMAoG
CCqGSM49BAMCA0cAMEQCIFdTyWY40+eB0OfRni+daV3gSO0+57bwzqtbbIkR+UTS
AiA91cFwnImuRN8cf/sfoow7u91f8YTW/3wzCwNBc4axnA==
-----END CERTIFICATE-----
+5
View File
@@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIIjlWhdRFLGgcr6mtVVFKs38XvU06zb7y0hS2wan70rkoAoGCCqGSM49
AwEHoUQDQgAEH8XA+HpY8YL8kkIzgHQKUudoe4ZACBd/d0stYnvyQDiko5rOjTEY
Ayha6yaf1oYaZqGdE7LrEXN0+mNplh/fuA==
-----END EC PRIVATE KEY-----
@@ -0,0 +1,11 @@
-----BEGIN CERTIFICATE-----
MIIBoDCCAUegAwIBAgIBEjAKBggqhkjOPQQDAjA4MQswCQYDVQQGEwJVUzERMA8G
A1UECgwITkMgVGVzdHMxFjAUBgNVBAMMDVVSSSBQZXJtaXQgQ0EwHhcNMjYwNjE3
MTY0OTEzWhcNNDYwNjEyMTY0OTEzWjA4MQswCQYDVQQGEwJVUzERMA8GA1UECgwI
TkMgVGVzdHMxFjAUBgNVBAMMDVVSSSBQZXJtaXQgQ0EwWTATBgcqhkjOPQIBBggq
hkjOPQMBBwNCAATo9NHaL2G3EUr/H8b80VjTMpaG6wYlwr0O12WJnhc2rbx5OXTj
NCoHZiv1tP9LX4tzDNItcvtTQ4KqucauIVZao0IwQDAPBgNVHRMBAf8EBTADAQH/
MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUbRiM8y/EUI+f3CfZNVab5WsNQSQw
CgYIKoZIzj0EAwIDRwAwRAIgVL6GP7rNVB9/TiHMvb65bMupvmS4D8QmOBTv5wJc
JokCIHLUUM5jO2iequaJ0RW5phjkem74R+2J/KJgKVcUGS+x
-----END CERTIFICATE-----
@@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIDIvBXdwFSH5kRKKGmPAIfkBuiSDZtOzBQIxP8xz2gc4oAoGCCqGSM49
AwEHoUQDQgAE6PTR2i9htxFK/x/G/NFY0zKWhusGJcK9DtdliZ4XNq28eTl04zQq
B2Yr9bT/S1+LcwzSLXL7U0OCqrnGriFWWg==
-----END EC PRIVATE KEY-----
@@ -0,0 +1,13 @@
-----BEGIN CERTIFICATE-----
MIIB4zCCAYmgAwIBAgIBETAKBggqhkjOPQQDAjA3MQswCQYDVQQGEwJVUzERMA8G
A1UECgwITkMgVGVzdHMxFTATBgNVBAMMDE5DIFRlc3QgUm9vdDAeFw0yNjA2MTcx
NjQ5MTNaFw00NjA2MTIxNjQ5MTNaMDgxCzAJBgNVBAYTAlVTMREwDwYDVQQKDAhO
QyBUZXN0czEWMBQGA1UEAwwNVVJJIFBlcm1pdCBDQTBZMBMGByqGSM49AgEGCCqG
SM49AwEHA0IABFrlMrMGfsgCYAIrm6Txj6XX89Xij2nCextBHk3fb3UDhnnYlnY5
0uwhnOGDbuyaoOWdd6xQOi/hLoFUERSArDOjgYQwgYEwDwYDVR0TAQH/BAUwAwEB
/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNe8LgUsg1wiiv2LAqB+mHkWZc10
MB8GA1UdIwQYMBaAFFgBv14PFBEmIEUkSYhOelfJG8QtMB4GA1UdHgEB/wQUMBKg
EDAOhgwuZXhhbXBsZS5jb20wCgYIKoZIzj0EAwIDSAAwRQIgTxJPzS5x3CijTVJn
hZVklkfJdHT/FZsUoq/c7p7Byl0CIQDixmRi8yTjmprhAu+nQCod1m6psWpw1irW
UAdvBlM+EA==
-----END CERTIFICATE-----
@@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIEzfFkozUo56l+WktIla45isYf6tsAZZg5b1Ph7Ou9e3oAoGCCqGSM49
AwEHoUQDQgAEWuUyswZ+yAJgAiubpPGPpdfz1eKPacJ7G0EeTd9vdQOGediWdjnS
7CGc4YNu7Jqg5Z13rFA6L+EugVQRFICsMw==
-----END EC PRIVATE KEY-----
@@ -0,0 +1,12 @@
-----BEGIN CERTIFICATE-----
MIIBwTCCAWigAwIBAgIBIDAKBggqhkjOPQQDAjA4MQswCQYDVQQGEwJVUzERMA8G
A1UECgwITkMgVGVzdHMxFjAUBgNVBAMMDVVSSSBQZXJtaXQgQ0EwHhcNMjYwNjE3
MTY0OTEzWhcNNDYwNjEyMTY0OTEzWjA4MQswCQYDVQQGEwJVUzERMA8GA1UECgwI
TkMgVGVzdHMxFjAUBgNVBAMMDUJlbmlnbiBTdWIgQ0EwWTATBgcqhkjOPQIBBggq
hkjOPQMBBwNCAASZKAPbUSJU9u4tTNkubWqSXWAPOhANvNoZ/prsWQWDLyWVKbbV
Vbo3AdX+O/VZKUPEhPrAegWkGnAp+BIIwEMgo2MwYTAPBgNVHRMBAf8EBTADAQH/
MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUFYNbX112ryF4JdDlgMeqY0Uo6kkw
HwYDVR0jBBgwFoAU17wuBSyDXCKK/YsCoH6YeRZlzXQwCgYIKoZIzj0EAwIDRwAw
RAIgBfGaiofozmQUMuMo4pEW+hGMAONyTkKR6IXDVVIX5RUCIEXrIy0oDP/ETKfi
+4VOsiMiEeOSBUOmdQpAaQfHf0hZ
-----END CERTIFICATE-----
@@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIESDNs2dwdV/XRjqhCbWYYrQqvWq9fDHf7Xzm0xIyj1IoAoGCCqGSM49
AwEHoUQDQgAEmSgD21EiVPbuLUzZLm1qkl1gDzoQDbzaGf6a7FkFgy8llSm21VW6
NwHV/jv1WSlDxIT6wHoFpBpwKfgSCMBDIA==
-----END EC PRIVATE KEY-----
@@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIAZeIz1p8VFPywN6Z6DMurHlIa3aQ1XrnGus1WMEYnk5oAoGCCqGSM49
AwEHoUQDQgAE0DMqgWwITs3KxGRHGYd0jrUcQIdoyEietpnezW8auDJhFe+Z6wCP
Nkh8KmbnySNEp0mlwokUvvi5ol4z8bOyXw==
-----END EC PRIVATE KEY-----
+38
View File
@@ -0,0 +1,38 @@
-----BEGIN CERTIFICATE-----
MIICBzCCAa2gAwIBAgIBMDAKBggqhkjOPQQDAjA4MQswCQYDVQQGEwJVUzERMA8G
A1UECgwITkMgVGVzdHMxFjAUBgNVBAMMDUJlbmlnbiBTdWIgQ0EwHhcNMjYwNjE3
MTY0OTEzWhcNNDYwNjEyMTY0OTEzWjBAMQswCQYDVQQGEwJVUzERMA8GA1UECgwI
TkMgVGVzdHMxHjAcBgNVBAMMFU5DIFRlc3QgQXR0YWNrZXIgTGVhZjBZMBMGByqG
SM49AgEGCCqGSM49AwEHA0IABNAzKoFsCE7NysRkRxmHdI61HECHaMhInraZ3s1v
GrgyYRXvmesAjzZIfCpm58kjRKdJpcKJFL74uaJeM/Gzsl+jgZ8wgZwwDAYDVR0T
AQH/BAIwADAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwHQYD
VR0OBBYEFO6GdfO6MvjzgDGAs71HPqOXaCtBMB8GA1UdIwQYMBaAFBWDW19ddq8h
eCXQ5YDHqmNFKOpJMCcGA1UdEQEB/wQdMBuGGWh0dHBzOi8vYXR0YWNrZXIuY29t
L2xlYWYwCgYIKoZIzj0EAwIDSAAwRQIhAL/dvlCeChSzMyecV6fV7ecfKccFY1RA
NL/g+05/4lyiAiAO4PyaBBz5t/0U/TOInIzjtWqaO1cIL6IzE3xPoqj5KQ==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIBwTCCAWigAwIBAgIBIDAKBggqhkjOPQQDAjA4MQswCQYDVQQGEwJVUzERMA8G
A1UECgwITkMgVGVzdHMxFjAUBgNVBAMMDVVSSSBQZXJtaXQgQ0EwHhcNMjYwNjE3
MTY0OTEzWhcNNDYwNjEyMTY0OTEzWjA4MQswCQYDVQQGEwJVUzERMA8GA1UECgwI
TkMgVGVzdHMxFjAUBgNVBAMMDUJlbmlnbiBTdWIgQ0EwWTATBgcqhkjOPQIBBggq
hkjOPQMBBwNCAASZKAPbUSJU9u4tTNkubWqSXWAPOhANvNoZ/prsWQWDLyWVKbbV
Vbo3AdX+O/VZKUPEhPrAegWkGnAp+BIIwEMgo2MwYTAPBgNVHRMBAf8EBTADAQH/
MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUFYNbX112ryF4JdDlgMeqY0Uo6kkw
HwYDVR0jBBgwFoAU17wuBSyDXCKK/YsCoH6YeRZlzXQwCgYIKoZIzj0EAwIDRwAw
RAIgBfGaiofozmQUMuMo4pEW+hGMAONyTkKR6IXDVVIX5RUCIEXrIy0oDP/ETKfi
+4VOsiMiEeOSBUOmdQpAaQfHf0hZ
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIB4zCCAYmgAwIBAgIBETAKBggqhkjOPQQDAjA3MQswCQYDVQQGEwJVUzERMA8G
A1UECgwITkMgVGVzdHMxFTATBgNVBAMMDE5DIFRlc3QgUm9vdDAeFw0yNjA2MTcx
NjQ5MTNaFw00NjA2MTIxNjQ5MTNaMDgxCzAJBgNVBAYTAlVTMREwDwYDVQQKDAhO
QyBUZXN0czEWMBQGA1UEAwwNVVJJIFBlcm1pdCBDQTBZMBMGByqGSM49AgEGCCqG
SM49AwEHA0IABFrlMrMGfsgCYAIrm6Txj6XX89Xij2nCextBHk3fb3UDhnnYlnY5
0uwhnOGDbuyaoOWdd6xQOi/hLoFUERSArDOjgYQwgYEwDwYDVR0TAQH/BAUwAwEB
/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNe8LgUsg1wiiv2LAqB+mHkWZc10
MB8GA1UdIwQYMBaAFFgBv14PFBEmIEUkSYhOelfJG8QtMB4GA1UdHgEB/wQUMBKg
EDAOhgwuZXhhbXBsZS5jb20wCgYIKoZIzj0EAwIDSAAwRQIgTxJPzS5x3CijTVJn
hZVklkfJdHT/FZsUoq/c7p7Byl0CIQDixmRi8yTjmprhAu+nQCod1m6psWpw1irW
UAdvBlM+EA==
-----END CERTIFICATE-----
@@ -0,0 +1,13 @@
-----BEGIN CERTIFICATE-----
MIICBTCCAaygAwIBAgIBMTAKBggqhkjOPQQDAjA4MQswCQYDVQQGEwJVUzERMA8G
A1UECgwITkMgVGVzdHMxFjAUBgNVBAMMDUJlbmlnbiBTdWIgQ0EwHhcNMjYwNjE3
MTY0OTEzWhcNNDYwNjEyMTY0OTEzWjA9MQswCQYDVQQGEwJVUzERMA8GA1UECgwI
TkMgVGVzdHMxGzAZBgNVBAMMEk5DIFRlc3QgVmFsaWQgTGVhZjBZMBMGByqGSM49
AgEGCCqGSM49AwEHA0IABNcVnczEUQaQMcxlrButtTd8HqQEoMLGBl8XLPdI/eZs
42jj3B4l6txsi/zPG+9klJad5sPkp2Uqf9PiHowizW+jgaEwgZ4wDAYDVR0TAQH/
BAIwADAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwHQYDVR0O
BBYEFLG1JSabNebv3xs8ZTuMVceg4FmrMB8GA1UdIwQYMBaAFBWDW19ddq8heCXQ
5YDHqmNFKOpJMCkGA1UdEQEB/wQfMB2GG2h0dHBzOi8vYmVuaWduLmV4YW1wbGUu
Y29tLzAKBggqhkjOPQQDAgNHADBEAiBWs+jRkdeHkfw7XuJ/v7qEXHFEojWJFu9z
Qhy+9ekdtwIgKRC5gM+FDlcDD2ULdCsHXtU9N/9801gx3gowxldxzA8=
-----END CERTIFICATE-----
@@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIJKBX7gzH3pL3Cjgw5Z623D/6Jo/RrFZXKyu4IobX5DroAoGCCqGSM49
AwEHoUQDQgAE1xWdzMRRBpAxzGWsG621N3wepASgwsYGXxcs90j95mzjaOPcHiXq
3GyL/M8b72SUlp3mw+SnZSp/0+IejCLNbw==
-----END EC PRIVATE KEY-----
+161
View File
@@ -0,0 +1,161 @@
#!/usr/bin/env bash
#
# gen-nc-ancestor.sh
# Re-sign the NameConstraints ancestor-walk test certs from committed
# keys. Cert SKIDs are stable across runs; 01-uri-permit-ca and its
# permissive sibling are pinned to satisfy the AKID-disambiguation test.
set -e
check_result(){
if [ $1 -ne 0 ]; then
echo "$2 Failed, Abort"
exit 1
else
echo "$2 Succeeded!"
fi
}
DIR="$(cd "$(dirname "$0")" && pwd)"
WORK="$(mktemp -d)"
trap 'rm -rf "$WORK"' EXIT
# Issue a child cert from $issuer_cert/$issuer_key.
# $1 child-key $2 subject-CN $3 out-cert $4 ext-file $5 ext-section $6 serial
mkchild(){
local child_key=$1 cn=$2 out=$3 extfile=$4 extsec=$5 serial=$6
openssl req -new -key "$child_key" -out "$WORK/child.csr" \
-subj "/C=US/O=NC Tests/CN=$cn" -config "$extfile"
check_result $? "$(basename "$out"): csr"
openssl x509 -req -in "$WORK/child.csr" \
-CA "$issuer_cert" -CAkey "$issuer_key" \
-set_serial "$serial" -out "$out" -days 7300 -sha256 \
-extfile "$extfile" -extensions "$extsec"
check_result $? "$(basename "$out"): sign"
rm -f "$WORK/child.csr"
}
# ---- ext configs ----
cat > "$WORK/root.cnf" <<'EOF'
[req]
distinguished_name = dn
prompt = no
[dn]
[v3_ca]
basicConstraints = critical, CA:TRUE
keyUsage = critical, digitalSignature, keyCertSign, cRLSign
subjectKeyIdentifier = hash
EOF
cat > "$WORK/uri-permit-ca.cnf" <<'EOF'
[req]
distinguished_name = dn
prompt = no
[dn]
[v3_uri_permit]
basicConstraints = critical, CA:TRUE
keyUsage = critical, digitalSignature, keyCertSign, cRLSign
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid
nameConstraints = critical, permitted;URI:.example.com
EOF
cat > "$WORK/sub-ca-nonc.cnf" <<'EOF'
[req]
distinguished_name = dn
prompt = no
[dn]
[v3_sub_ca]
basicConstraints = critical, CA:TRUE
keyUsage = critical, digitalSignature, keyCertSign, cRLSign
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid
EOF
cat > "$WORK/leaf-attacker.cnf" <<'EOF'
[req]
distinguished_name = dn
prompt = no
[dn]
[v3_leaf_attacker]
basicConstraints = critical, CA:FALSE
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid
subjectAltName = critical, URI:https://attacker.com/leaf
EOF
cat > "$WORK/leaf-valid.cnf" <<'EOF'
[req]
distinguished_name = dn
prompt = no
[dn]
[v3_leaf_valid]
basicConstraints = critical, CA:FALSE
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid
subjectAltName = critical, URI:https://benign.example.com/
EOF
# ---- 00 root (self-signed) ----
openssl req -new -x509 -key "$DIR/00-root-key.pem" \
-out "$DIR/00-root-cert.pem" \
-subj "/C=US/O=NC Tests/CN=NC Test Root" \
-config "$WORK/root.cnf" -extensions v3_ca \
-set_serial 0x10 -days 7300 -sha256
check_result $? "00-root-cert.pem"
# ---- 01 uri-permit-ca (permits URI:.example.com), issued by root ----
issuer_cert="$DIR/00-root-cert.pem"
issuer_key="$DIR/00-root-key.pem"
mkchild "$DIR/01-uri-permit-ca-key.pem" "URI Permit CA" \
"$DIR/01-uri-permit-ca-cert.pem" "$WORK/uri-permit-ca.cnf" \
v3_uri_permit 0x11
# ---- 00 uri-permit-ca-permissive (same-DN distractor, self-signed) ----
openssl req -new -x509 -key "$DIR/00-uri-permit-ca-permissive-key.pem" \
-out "$DIR/00-uri-permit-ca-permissive-cert.pem" \
-subj "/C=US/O=NC Tests/CN=URI Permit CA" \
-config "$WORK/root.cnf" -extensions v3_ca \
-set_serial 0x12 -days 7300 -sha256
check_result $? "00-uri-permit-ca-permissive-cert.pem"
# ---- 02 benign-sub-ca (no NC), issued by uri-permit-ca ----
issuer_cert="$DIR/01-uri-permit-ca-cert.pem"
issuer_key="$DIR/01-uri-permit-ca-key.pem"
mkchild "$DIR/02-benign-sub-ca-key.pem" "Benign Sub CA" \
"$DIR/02-benign-sub-ca-cert.pem" "$WORK/sub-ca-nonc.cnf" \
v3_sub_ca 0x20
# ---- 03 leaf-attacker (URI violates grandparent's permit), issued by sub-ca ----
issuer_cert="$DIR/02-benign-sub-ca-cert.pem"
issuer_key="$DIR/02-benign-sub-ca-key.pem"
mkchild "$DIR/03-leaf-attacker-key.pem" "NC Test Attacker Leaf" \
"$WORK/03-leaf-cert.pem" "$WORK/leaf-attacker.cnf" \
v3_leaf_attacker 0x30
# ---- 03 valid-leaf (URI inside permit), issued by sub-ca ----
mkchild "$DIR/03-valid-leaf-key.pem" "NC Test Valid Leaf" \
"$DIR/03-valid-leaf-cert.pem" "$WORK/leaf-valid.cnf" \
v3_leaf_valid 0x31
# ---- Concatenated bundle: attacker leaf + benign-sub-ca + uri-permit-ca ----
cat "$WORK/03-leaf-cert.pem" \
"$DIR/02-benign-sub-ca-cert.pem" \
"$DIR/01-uri-permit-ca-cert.pem" \
> "$DIR/03-leaf-chain.pem"
check_result $? "03-leaf-chain.pem"
echo "Generated chain in $DIR/"
ls -la "$DIR/"
+90
View File
@@ -3524,3 +3524,93 @@ int test_wolfSSL_X509_V_ERR_strings(void)
#endif
return EXPECT_RESULT();
}
/* Leaf must satisfy a grandparent CA's NCs even when its direct issuer
* carries no constraints. */
int test_wolfSSL_CertManagerNameConstraint_valid_chain(void)
{
EXPECT_DECLS;
#if !defined(NO_FILESYSTEM) && !defined(NO_CERTS) && \
!defined(NO_WOLFSSL_CM_VERIFY) && defined(HAVE_ECC) && \
!defined(IGNORE_NAME_CONSTRAINTS) && !defined(NO_SHA256) && \
defined(WOLFSSL_ALT_NAMES) && \
(defined(WOLFSSL_PEM_TO_DER) || defined(OPENSSL_EXTRA))
WOLFSSL_CERT_MANAGER* cm = NULL;
const char* root_cert =
"./certs/test/nc-ancestor/00-root-cert.pem";
const char* uri_permit_ca_cert =
"./certs/test/nc-ancestor/01-uri-permit-ca-cert.pem";
const char* benign_sub_ca_cert =
"./certs/test/nc-ancestor/02-benign-sub-ca-cert.pem";
const char* valid_leaf_cert =
"./certs/test/nc-ancestor/03-valid-leaf-cert.pem";
const char* attacker_leaf_chain =
"./certs/test/nc-ancestor/03-leaf-chain.pem";
ExpectNotNull(cm = wolfSSL_CertManagerNew());
ExpectIntEQ(wolfSSL_CertManagerLoadCA(cm, root_cert, NULL),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CertManagerLoadCA(cm, uri_permit_ca_cert, NULL),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CertManagerLoadCA(cm, benign_sub_ca_cert, NULL),
WOLFSSL_SUCCESS);
/* Positive: leaf satisfies the grandparent permit. */
ExpectIntEQ(wolfSSL_CertManagerVerify(cm, valid_leaf_cert,
WOLFSSL_FILETYPE_PEM),
WOLFSSL_SUCCESS);
/* Negative: leaf violates the grandparent permit. */
ExpectIntEQ(wolfSSL_CertManagerVerify(cm, attacker_leaf_chain,
WOLFSSL_FILETYPE_PEM),
WC_NO_ERR_TRACE(ASN_NAME_INVALID_E));
wolfSSL_CertManagerFree(cm);
#endif
return EXPECT_RESULT();
}
/* Same-DN sibling without NCs is loaded alongside the strict CA. The
* walk must use AKID->SKID, not a name-only lookup, to find the real
* signer. */
int test_wolfSSL_CertManagerNameConstraint_skid_disambiguates(void)
{
EXPECT_DECLS;
#if !defined(NO_FILESYSTEM) && !defined(NO_CERTS) && \
!defined(NO_WOLFSSL_CM_VERIFY) && defined(HAVE_ECC) && \
!defined(IGNORE_NAME_CONSTRAINTS) && !defined(NO_SHA256) && \
!defined(NO_SKID) && defined(WOLFSSL_ALT_NAMES) && \
(defined(WOLFSSL_PEM_TO_DER) || defined(OPENSSL_EXTRA))
WOLFSSL_CERT_MANAGER* cm = NULL;
const char* root_cert =
"./certs/test/nc-ancestor/00-root-cert.pem";
const char* permissive_cert =
"./certs/test/nc-ancestor/00-uri-permit-ca-permissive-cert.pem";
const char* strict_cert =
"./certs/test/nc-ancestor/01-uri-permit-ca-cert.pem";
const char* benign_sub_ca_cert =
"./certs/test/nc-ancestor/02-benign-sub-ca-cert.pem";
const char* attacker_leaf_chain =
"./certs/test/nc-ancestor/03-leaf-chain.pem";
ExpectNotNull(cm = wolfSSL_CertManagerNew());
ExpectIntEQ(wolfSSL_CertManagerLoadCA(cm, root_cert, NULL),
WOLFSSL_SUCCESS);
/* Load permissive sibling first to favor a name-only lookup. */
ExpectIntEQ(wolfSSL_CertManagerLoadCA(cm, permissive_cert, NULL),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CertManagerLoadCA(cm, strict_cert, NULL),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CertManagerLoadCA(cm, benign_sub_ca_cert, NULL),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CertManagerVerify(cm, attacker_leaf_chain,
WOLFSSL_FILETYPE_PEM),
WC_NO_ERR_TRACE(ASN_NAME_INVALID_E));
wolfSSL_CertManagerFree(cm);
#endif
return EXPECT_RESULT();
}
+7 -1
View File
@@ -52,6 +52,8 @@ int test_wolfSSL_CertManagerCheckOCSPResponse(void);
int test_various_pathlen_chains(void);
int test_wolfSSL_CertManagerRejectMD5Cert(void);
int test_wolfSSL_X509_V_ERR_strings(void);
int test_wolfSSL_CertManagerNameConstraint_valid_chain(void);
int test_wolfSSL_CertManagerNameConstraint_skid_disambiguates(void);
#define TEST_CERTMAN_DECLS \
TEST_DECL_GROUP("certman", test_wolfSSL_CertManagerAPI), \
@@ -83,7 +85,11 @@ int test_wolfSSL_X509_V_ERR_strings(void);
TEST_DECL_GROUP("certman", test_wolfSSL_CertManagerCheckOCSPResponse), \
TEST_DECL_GROUP("certman", test_various_pathlen_chains), \
TEST_DECL_GROUP("certman", test_wolfSSL_CertManagerRejectMD5Cert), \
TEST_DECL_GROUP("certman", test_wolfSSL_X509_V_ERR_strings)
TEST_DECL_GROUP("certman", test_wolfSSL_X509_V_ERR_strings), \
TEST_DECL_GROUP("certman", \
test_wolfSSL_CertManagerNameConstraint_valid_chain), \
TEST_DECL_GROUP("certman", \
test_wolfSSL_CertManagerNameConstraint_skid_disambiguates)
#endif /* WOLFCRYPT_TEST_CERTMAN_H */
+116 -16
View File
@@ -18754,9 +18754,10 @@ static int ConfirmNameConstraints(Signer* signer, DecodedCert* cert)
case ASN_DNS_TYPE:
name = cert->altNames;
/* When no SAN is present, apply DNS name constraints to the
* Subject CN. */
if (cert->subjectCN != NULL && cert->altNames == NULL) {
/* Apply DNS constraints to leaf Subject CN when no SAN
* (legacy hostname-in-CN). Skipped for CAs. */
if (cert->subjectCN != NULL && cert->altNames == NULL &&
!cert->isCA) {
subjectDnsName.next = NULL;
subjectDnsName.type = ASN_DNS_TYPE;
subjectDnsName.len = cert->subjectCNLen;
@@ -23323,6 +23324,71 @@ Signer* findSignerByName(Signer *list, byte *hash)
return NULL;
}
#ifndef IGNORE_NAME_CONSTRAINTS
/* Find a signer for cert in cm and extraCAList. Prefers AKID->SKID
* with name-hash validation. Fall back to name-only when AKID is
* absent. */
static Signer* FindSignerByAkidOrName(void* cm, Signer* extraCAList,
Signer* cert)
{
Signer* signer = NULL;
#ifdef HAVE_CERTIFICATE_STATUS_REQUEST_V2
#ifndef NO_SKID
Signer* exCaSigner;
#endif
#else
(void)extraCAList;
#endif
#ifndef NO_SKID
if (cert->authKeyIdSet) {
signer = GetCA(cm, cert->authKeyIdHash);
if (signer != NULL &&
XMEMCMP(signer->subjectNameHash, cert->issuerNameHash,
SIGNER_DIGEST_SIZE) != 0) {
signer = NULL;
}
/* AKID is authoritative; do not fall back to name when AKID
* is set (could substitute a same-DN sibling). */
}
else {
signer = GetCAByName(cm, cert->issuerNameHash);
}
#else
signer = GetCA(cm, cert->issuerNameHash);
#endif
#ifdef HAVE_CERTIFICATE_STATUS_REQUEST_V2
if (signer == NULL && extraCAList != NULL) {
#ifndef NO_SKID
if (cert->authKeyIdSet) {
for (exCaSigner = extraCAList; exCaSigner != NULL;
exCaSigner = exCaSigner->next) {
if (XMEMCMP(exCaSigner->subjectKeyIdHash,
cert->authKeyIdHash,
SIGNER_DIGEST_SIZE) == 0 &&
XMEMCMP(exCaSigner->subjectNameHash,
cert->issuerNameHash,
SIGNER_DIGEST_SIZE) == 0) {
signer = exCaSigner;
break;
}
}
/* AKID is authoritative; do not fall back to name. */
}
else {
signer = findSignerByName(extraCAList, cert->issuerNameHash);
}
#else
signer = findSignerByName(extraCAList, cert->issuerNameHash);
#endif
}
#endif
return signer;
}
#endif /* !IGNORE_NAME_CONSTRAINTS */
int ParseCertRelative(DecodedCert* cert, int type, int verify, void* cm,
Signer *extraCAList)
{
@@ -23337,6 +23403,12 @@ int ParseCertRelative(DecodedCert* cert, int type, int verify, void* cm,
int idx = 0;
#endif
byte* sce_tsip_encRsaKeyIdx;
#ifndef IGNORE_NAME_CONSTRAINTS
int ncDepth = 0;
Signer* ncSigner = NULL;
Signer* ncParent = NULL;
Signer* ncPrev = NULL;
#endif
(void)extraCAList;
if (cert == NULL) {
@@ -23969,18 +24041,6 @@ int ParseCertRelative(DecodedCert* cert, int type, int verify, void* cm,
}
#endif /* WOLFSSL_DUAL_ALG_CERTS */
}
#ifndef IGNORE_NAME_CONSTRAINTS
if (verify == VERIFY || verify == VERIFY_OCSP ||
verify == VERIFY_NAME || verify == VERIFY_SKIP_DATE) {
/* check that this cert's name is permitted by the signer's
* name constraints */
if (!ConfirmNameConstraints(cert->ca, cert)) {
WOLFSSL_MSG("Confirm name constraint failed");
WOLFSSL_ERROR_VERBOSE(ASN_NAME_INVALID_E);
return ASN_NAME_INVALID_E;
}
}
#endif /* IGNORE_NAME_CONSTRAINTS */
} /* cert->ca */
#ifdef WOLFSSL_CERT_REQ
else if (type == CERTREQ_TYPE) {
@@ -24062,6 +24122,38 @@ int ParseCertRelative(DecodedCert* cert, int type, int verify, void* cm,
}
} /* verify != NO_VERIFY && type != CA_TYPE && type != TRUSTED_PEER_TYPE */
#ifndef IGNORE_NAME_CONSTRAINTS
/* Apply each ancestor CA's name constraints to this cert.
* Signer pointers between lookups are not lock-protected
* (see wolfssl_cm_get_certs_der). */
if ((verify == VERIFY || verify == VERIFY_OCSP ||
verify == VERIFY_NAME || verify == VERIFY_SKIP_DATE) &&
type != TRUSTED_PEER_TYPE && cert->ca != NULL) {
ncSigner = cert->ca;
while (ncSigner != NULL) {
if (!ConfirmNameConstraints(ncSigner, cert)) {
WOLFSSL_MSG("Confirm name constraint failed");
WOLFSSL_ERROR_VERBOSE(ASN_NAME_INVALID_E);
return ASN_NAME_INVALID_E;
}
/* Stop at trust anchor (self-issued). */
if (ncSigner->selfSigned)
break;
ncParent = FindSignerByAkidOrName(cm, extraCAList, ncSigner);
/* Stop on missing parent, self-loop, or A->B->A cycle. */
if (ncParent == NULL || ncParent == ncSigner ||
ncParent == ncPrev)
break;
if (++ncDepth >= WOLFSSL_MAX_CHAIN_DEPTH) {
WOLFSSL_MSG("NC ancestor walk exceeded WOLFSSL_MAX_CHAIN_DEPTH");
WOLFSSL_ERROR_VERBOSE(ASN_PATHLEN_SIZE_E);
return ASN_PATHLEN_SIZE_E;
}
ncPrev = ncSigner;
ncSigner = ncParent;
}
}
#endif /* IGNORE_NAME_CONSTRAINTS */
#if defined(WOLFSSL_NO_TRUSTED_CERTS_VERIFY) && !defined(NO_SKID)
exit_pcr:
#endif
@@ -24140,10 +24232,18 @@ int FillSigner(Signer* signer, DecodedCert* cert, int type, DerBuffer *der)
#ifndef NO_SKID
XMEMCPY(signer->subjectKeyIdHash, cert->extSubjKeyId,
SIGNER_DIGEST_SIZE);
#ifndef IGNORE_NAME_CONSTRAINTS
if (cert->extAuthKeyIdSet) {
XMEMCPY(signer->authKeyIdHash, cert->extAuthKeyId,
SIGNER_DIGEST_SIZE);
signer->authKeyIdSet = 1;
}
#endif
#endif
XMEMCPY(signer->subjectNameHash, cert->subjectHash,
SIGNER_DIGEST_SIZE);
#if defined(HAVE_OCSP) || defined(HAVE_CRL) || defined(WOLFSSL_AKID_NAME)
#if defined(HAVE_OCSP) || defined(HAVE_CRL) || \
defined(WOLFSSL_AKID_NAME) || !defined(IGNORE_NAME_CONSTRAINTS)
XMEMCPY(signer->issuerNameHash, cert->issuerHash,
SIGNER_DIGEST_SIZE);
#endif
+18 -4
View File
@@ -1736,6 +1736,11 @@ typedef struct EncodedName {
#define WOLFSSL_MAX_PATH_LEN 127
#endif
#ifndef WOLFSSL_MAX_CHAIN_DEPTH
/* Max cert chain depth for ancestor walks. */
#define WOLFSSL_MAX_CHAIN_DEPTH 20
#endif
typedef struct DecodedName DecodedName;
typedef struct DecodedCert DecodedCert;
typedef struct Signer Signer;
@@ -2207,6 +2212,10 @@ struct Signer {
*/
WC_BITFIELD extNameConstraintCrit:1;
WC_BITFIELD extNameConstraintHasUnsupported:1;
#ifndef NO_SKID
WC_BITFIELD authKeyIdSet:1; /* true when authKeyIdHash holds the
* AKID extension's keyId */
#endif
#endif
const byte* publicKey;
int nameLen;
@@ -2218,15 +2227,20 @@ struct Signer {
#endif
byte subjectNameHash[SIGNER_DIGEST_SIZE];
/* sha hash of names in certificate */
#if defined(HAVE_OCSP) || defined(HAVE_CRL) || defined(WOLFSSL_AKID_NAME)
#if defined(HAVE_OCSP) || defined(HAVE_CRL) || defined(WOLFSSL_AKID_NAME) || \
!defined(IGNORE_NAME_CONSTRAINTS)
byte issuerNameHash[SIGNER_DIGEST_SIZE];
/* sha hash of issuer names in certificate.
* Used in OCSP to check for authorized
* responders. */
/* sha hash of issuer names; used by
* OCSP and name-constraint walk */
#endif
#ifndef NO_SKID
byte subjectKeyIdHash[SIGNER_DIGEST_SIZE];
/* sha hash of key in certificate */
#ifndef IGNORE_NAME_CONSTRAINTS
byte authKeyIdHash[SIGNER_DIGEST_SIZE];
/* sha hash of AKID; locates signer
* during name-constraint walk */
#endif
#endif
#ifdef HAVE_OCSP
byte subjectKeyHash[KEYID_SIZE];