Add some tests for policy mapping.

Change-Id: I739f0b9d45ab9411bd0a6f465e159d6dea82277a
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/55754
Reviewed-by: Bob Beck <bbe@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
diff --git a/crypto/x509/test/make_policy_certs.go b/crypto/x509/test/make_policy_certs.go
index 89b1517..b22dc37 100644
--- a/crypto/x509/test/make_policy_certs.go
+++ b/crypto/x509/test/make_policy_certs.go
@@ -34,11 +34,17 @@
 	// https://davidben.net/oid
 	testOID1 = asn1.ObjectIdentifier([]int{1, 2, 840, 113554, 4, 1, 72585, 2, 1})
 	testOID2 = asn1.ObjectIdentifier([]int{1, 2, 840, 113554, 4, 1, 72585, 2, 2})
+	testOID3 = asn1.ObjectIdentifier([]int{1, 2, 840, 113554, 4, 1, 72585, 2, 3})
+	testOID4 = asn1.ObjectIdentifier([]int{1, 2, 840, 113554, 4, 1, 72585, 2, 4})
+	testOID5 = asn1.ObjectIdentifier([]int{1, 2, 840, 113554, 4, 1, 72585, 2, 5})
 
 	// https://www.rfc-editor.org/rfc/rfc5280.html#section-4.2.1.4
 	certificatePoliciesOID = asn1.ObjectIdentifier([]int{2, 5, 29, 32})
 	anyPolicyOID           = asn1.ObjectIdentifier([]int{2, 5, 29, 32, 0})
 
+	// https://www.rfc-editor.org/rfc/rfc5280.html#section-4.2.1.5
+	policyMappingsOID = asn1.ObjectIdentifier([]int{2, 5, 29, 33})
+
 	// https://www.rfc-editor.org/rfc/rfc5280.html#section-4.2.1.11
 	policyConstraintsOID = asn1.ObjectIdentifier([]int{2, 5, 29, 36})
 )
@@ -181,6 +187,59 @@
 	leafAny.template.PolicyIdentifiers = []asn1.ObjectIdentifier{anyPolicyOID}
 	mustGenerateCertificate("policy_leaf_any.pem", &leafAny, &intermediate)
 
+	// An intermediate which maps OID1 to (OID2, OID3), and which asserts the
+	// input OIDs either all at once, or as anyPolicy.
+	b = cryptobyte.NewBuilder(nil)
+	b.AddASN1(cbasn1.SEQUENCE, func(seq *cryptobyte.Builder) {
+		// Map OID3 to (OID1, OID2).
+		seq.AddASN1(cbasn1.SEQUENCE, func(mapping *cryptobyte.Builder) {
+			mapping.AddASN1ObjectIdentifier(testOID3)
+			mapping.AddASN1ObjectIdentifier(testOID1)
+		})
+		seq.AddASN1(cbasn1.SEQUENCE, func(mapping *cryptobyte.Builder) {
+			mapping.AddASN1ObjectIdentifier(testOID3)
+			mapping.AddASN1ObjectIdentifier(testOID2)
+		})
+
+		// Map all pairs of OID4 and OID5 to each other.
+		seq.AddASN1(cbasn1.SEQUENCE, func(mapping *cryptobyte.Builder) {
+			mapping.AddASN1ObjectIdentifier(testOID4)
+			mapping.AddASN1ObjectIdentifier(testOID4)
+		})
+		seq.AddASN1(cbasn1.SEQUENCE, func(mapping *cryptobyte.Builder) {
+			mapping.AddASN1ObjectIdentifier(testOID4)
+			mapping.AddASN1ObjectIdentifier(testOID5)
+		})
+		seq.AddASN1(cbasn1.SEQUENCE, func(mapping *cryptobyte.Builder) {
+			mapping.AddASN1ObjectIdentifier(testOID5)
+			mapping.AddASN1ObjectIdentifier(testOID4)
+		})
+		seq.AddASN1(cbasn1.SEQUENCE, func(mapping *cryptobyte.Builder) {
+			mapping.AddASN1ObjectIdentifier(testOID5)
+			mapping.AddASN1ObjectIdentifier(testOID5)
+		})
+	})
+	intermediateMapped := intermediate
+	intermediateMapped.template.PolicyIdentifiers = []asn1.ObjectIdentifier{testOID1, testOID2, testOID3, testOID4, testOID5}
+	intermediateMapped.template.ExtraExtensions = []pkix.Extension{{Id: policyMappingsOID, Value: b.BytesOrPanic()}}
+	mustGenerateCertificate("policy_intermediate_mapped.pem", &intermediateMapped, &root)
+
+	intermediateMapped.template.PolicyIdentifiers = []asn1.ObjectIdentifier{anyPolicyOID}
+	mustGenerateCertificate("policy_intermediate_mapped_any.pem", &intermediateMapped, &root)
+
+	// Leaves which assert more specific OIDs, to test intermediate_mapped.
+	leafSingle := leaf
+	leafSingle.template.PolicyIdentifiers = []asn1.ObjectIdentifier{testOID1}
+	mustGenerateCertificate("policy_leaf_oid1.pem", &leafSingle, &intermediate)
+	leafSingle.template.PolicyIdentifiers = []asn1.ObjectIdentifier{testOID2}
+	mustGenerateCertificate("policy_leaf_oid2.pem", &leafSingle, &intermediate)
+	leafSingle.template.PolicyIdentifiers = []asn1.ObjectIdentifier{testOID3}
+	mustGenerateCertificate("policy_leaf_oid3.pem", &leafSingle, &intermediate)
+	leafSingle.template.PolicyIdentifiers = []asn1.ObjectIdentifier{testOID4}
+	mustGenerateCertificate("policy_leaf_oid4.pem", &leafSingle, &intermediate)
+	leafSingle.template.PolicyIdentifiers = []asn1.ObjectIdentifier{testOID5}
+	mustGenerateCertificate("policy_leaf_oid5.pem", &leafSingle, &intermediate)
+
 	// TODO(davidben): Generate more certificates to test policy validation more
 	// extensively, including an intermediate with constraints. For now this
 	// just tests the basic case.
diff --git a/crypto/x509/test/policy_intermediate.pem b/crypto/x509/test/policy_intermediate.pem
index b36868c..759deb4 100644
--- a/crypto/x509/test/policy_intermediate.pem
+++ b/crypto/x509/test/policy_intermediate.pem
@@ -1,11 +1,11 @@
 -----BEGIN CERTIFICATE-----
-MIIBrDCCAVGgAwIBAgIBAjAKBggqhkjOPQQDAjAWMRQwEgYDVQQDEwtQb2xpY3kg
+MIIBqjCCAVGgAwIBAgIBAjAKBggqhkjOPQQDAjAWMRQwEgYDVQQDEwtQb2xpY3kg
 Um9vdDAgFw0wMDAxMDEwMDAwMDBaGA8yMTAwMDEwMTAwMDAwMFowHjEcMBoGA1UE
 AxMTUG9saWN5IEludGVybWVkaWF0ZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IA
 BOI6fKiM3jFLkLyAn88cvlw4SwxuygRjopP3FFBKHyUQvh3VVvfqSpSCSmp50Qia
 jQ6Dg7CTpVZVVH+bguT7JTCjgYUwgYIwDgYDVR0PAQH/BAQDAgIEMBMGA1UdJQQM
 MAoGCCsGAQUFBwMBMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFJDS9/4O7qhr
 CIRhwsXrPVBagG2uMCsGA1UdIAQkMCIwDwYNKoZIhvcSBAGEtwkCATAPBg0qhkiG
-9xIEAYS3CQICMAoGCCqGSM49BAMCA0kAMEYCIQCcgAbQr/HNdHwPEcWotOqtXXGH
-di6cAJtWaSynP8+UoQIhAPEMK79OO+tJHzmD0N01OdZefAwKlYZvDCQvAfAQVf7j
+9xIEAYS3CQICMAoGCCqGSM49BAMCA0cAMEQCIFN2ZtknXQ9vz23qD1ecprC9iIo7
+j/SI42Ub64qZQaraAiA+CRCWJz/l+NQ1+TPWYDDWY6Wh2L9Wbddh1Nj5KJEkhQ==
 -----END CERTIFICATE-----
diff --git a/crypto/x509/test/policy_intermediate_any.pem b/crypto/x509/test/policy_intermediate_any.pem
index 5a6b61a..0931964 100644
--- a/crypto/x509/test/policy_intermediate_any.pem
+++ b/crypto/x509/test/policy_intermediate_any.pem
@@ -6,6 +6,6 @@
 jQ6Dg7CTpVZVVH+bguT7JTCjajBoMA4GA1UdDwEB/wQEAwICBDATBgNVHSUEDDAK
 BggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSQ0vf+Du6oawiE
 YcLF6z1QWoBtrjARBgNVHSAECjAIMAYGBFUdIAAwCgYIKoZIzj0EAwIDSQAwRgIh
-AOoHsgnQ3KA7e4e2BCyDoSaPo44NYXB4uQ0OFrC79O8VAiEAtIHnFBkYqdh84P44
-OYpH5jVq17Hk1wrzn62KwkICvfI=
+AJbyXshUwjsFCiqrJkg91GzJdhZZ+3WXOekCJgi8uEESAiEAhv4sEE0wRRqgHDjl
+vIt26IELfFE2Z/FBF3ihGmi6NoI=
 -----END CERTIFICATE-----
diff --git a/crypto/x509/test/policy_intermediate_duplicate.pem b/crypto/x509/test/policy_intermediate_duplicate.pem
index af0fe89..0eafe8d 100644
--- a/crypto/x509/test/policy_intermediate_duplicate.pem
+++ b/crypto/x509/test/policy_intermediate_duplicate.pem
@@ -1,12 +1,12 @@
 -----BEGIN CERTIFICATE-----
-MIIBuzCCAWKgAwIBAgIBAjAKBggqhkjOPQQDAjAWMRQwEgYDVQQDEwtQb2xpY3kg
+MIIBvDCCAWKgAwIBAgIBAjAKBggqhkjOPQQDAjAWMRQwEgYDVQQDEwtQb2xpY3kg
 Um9vdDAgFw0wMDAxMDEwMDAwMDBaGA8yMTAwMDEwMTAwMDAwMFowHjEcMBoGA1UE
 AxMTUG9saWN5IEludGVybWVkaWF0ZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IA
 BOI6fKiM3jFLkLyAn88cvlw4SwxuygRjopP3FFBKHyUQvh3VVvfqSpSCSmp50Qia
 jQ6Dg7CTpVZVVH+bguT7JTCjgZYwgZMwDgYDVR0PAQH/BAQDAgIEMBMGA1UdJQQM
 MAoGCCsGAQUFBwMBMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFJDS9/4O7qhr
 CIRhwsXrPVBagG2uMDwGA1UdIAQ1MDMwDwYNKoZIhvcSBAGEtwkCATAPBg0qhkiG
-9xIEAYS3CQICMA8GDSqGSIb3EgQBhLcJAgIwCgYIKoZIzj0EAwIDRwAwRAIgCnvy
-K47AK/Ve/rzcFSm1fcjFg9UwZoTvOAhZU/xpfLgCIFV4vHl6jsGq9rPs4KblSsIY
-VBjAjG2AYkH0Lq+O4LjO
+9xIEAYS3CQICMA8GDSqGSIb3EgQBhLcJAgIwCgYIKoZIzj0EAwIDSAAwRQIgUpG6
+FUeWrC62BtTPHiSlWBdnLWUYH0llS6uYUkpJFJECIQCWfhoZYXvHdMhgBDSI/vzY
+Sw4uNdcMxrC2kP6lIioUSw==
 -----END CERTIFICATE-----
diff --git a/crypto/x509/test/policy_intermediate_invalid.pem b/crypto/x509/test/policy_intermediate_invalid.pem
index 7467317..11c95af 100644
--- a/crypto/x509/test/policy_intermediate_invalid.pem
+++ b/crypto/x509/test/policy_intermediate_invalid.pem
@@ -5,7 +5,7 @@
 BOI6fKiM3jFLkLyAn88cvlw4SwxuygRjopP3FFBKHyUQvh3VVvfqSpSCSmp50Qia
 jQ6Dg7CTpVZVVH+bguT7JTCjZzBlMA4GA1UdDwEB/wQEAwICBDATBgNVHSUEDDAK
 BggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSQ0vf+Du6oawiE
-YcLF6z1QWoBtrjAOBgNVHSAEB0lOVkFMSUQwCgYIKoZIzj0EAwIDSAAwRQIgf9Jt
-wpHxfA3j6Z8+h88MSh2MHkDGhWcnRY9VboMR/RoCIQDiSiaPGISK/31JBhNVvNnK
-IBo822QHPPMWDR/K/nyWiA==
+YcLF6z1QWoBtrjAOBgNVHSAEB0lOVkFMSUQwCgYIKoZIzj0EAwIDSAAwRQIgS2uK
+cYlZ1bxeqgMy3X0Sfi0arAnqpePsAqAeEf+HJHQCIQDwfCnXrWyHET9lM/gJSkfN
+j/JRJvJELDrAMVewCxZWKA==
 -----END CERTIFICATE-----
diff --git a/crypto/x509/test/policy_intermediate_mapped.pem b/crypto/x509/test/policy_intermediate_mapped.pem
new file mode 100644
index 0000000..fa45e60
--- /dev/null
+++ b/crypto/x509/test/policy_intermediate_mapped.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICrjCCAlSgAwIBAgIBAjAKBggqhkjOPQQDAjAWMRQwEgYDVQQDEwtQb2xpY3kg
+Um9vdDAgFw0wMDAxMDEwMDAwMDBaGA8yMTAwMDEwMTAwMDAwMFowHjEcMBoGA1UE
+AxMTUG9saWN5IEludGVybWVkaWF0ZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IA
+BOI6fKiM3jFLkLyAn88cvlw4SwxuygRjopP3FFBKHyUQvh3VVvfqSpSCSmp50Qia
+jQ6Dg7CTpVZVVH+bguT7JTCjggGHMIIBgzAOBgNVHQ8BAf8EBAMCAgQwEwYDVR0l
+BAwwCgYIKwYBBQUHAwEwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUkNL3/g7u
+qGsIhGHCxes9UFqAba4wXgYDVR0gBFcwVTAPBg0qhkiG9xIEAYS3CQIBMA8GDSqG
+SIb3EgQBhLcJAgIwDwYNKoZIhvcSBAGEtwkCAzAPBg0qhkiG9xIEAYS3CQIEMA8G
+DSqGSIb3EgQBhLcJAgUwgcsGA1UdIQSBwzCBwDAeBg0qhkiG9xIEAYS3CQIDBg0q
+hkiG9xIEAYS3CQIBMB4GDSqGSIb3EgQBhLcJAgMGDSqGSIb3EgQBhLcJAgIwHgYN
+KoZIhvcSBAGEtwkCBAYNKoZIhvcSBAGEtwkCBDAeBg0qhkiG9xIEAYS3CQIEBg0q
+hkiG9xIEAYS3CQIFMB4GDSqGSIb3EgQBhLcJAgUGDSqGSIb3EgQBhLcJAgQwHgYN
+KoZIhvcSBAGEtwkCBQYNKoZIhvcSBAGEtwkCBTAKBggqhkjOPQQDAgNIADBFAiAe
+Ah2vJMZsW/RV35mM7b7/NjsjScjPEIxfDJu49inNXQIhANmGBqyWUogh/gXyVB0/
+IfDro27pANW3R02A+zH34q5k
+-----END CERTIFICATE-----
diff --git a/crypto/x509/test/policy_intermediate_mapped_any.pem b/crypto/x509/test/policy_intermediate_mapped_any.pem
new file mode 100644
index 0000000..ae47bf4
--- /dev/null
+++ b/crypto/x509/test/policy_intermediate_mapped_any.pem
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICYjCCAgegAwIBAgIBAjAKBggqhkjOPQQDAjAWMRQwEgYDVQQDEwtQb2xpY3kg
+Um9vdDAgFw0wMDAxMDEwMDAwMDBaGA8yMTAwMDEwMTAwMDAwMFowHjEcMBoGA1UE
+AxMTUG9saWN5IEludGVybWVkaWF0ZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IA
+BOI6fKiM3jFLkLyAn88cvlw4SwxuygRjopP3FFBKHyUQvh3VVvfqSpSCSmp50Qia
+jQ6Dg7CTpVZVVH+bguT7JTCjggE6MIIBNjAOBgNVHQ8BAf8EBAMCAgQwEwYDVR0l
+BAwwCgYIKwYBBQUHAwEwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUkNL3/g7u
+qGsIhGHCxes9UFqAba4wEQYDVR0gBAowCDAGBgRVHSAAMIHLBgNVHSEEgcMwgcAw
+HgYNKoZIhvcSBAGEtwkCAwYNKoZIhvcSBAGEtwkCATAeBg0qhkiG9xIEAYS3CQID
+Bg0qhkiG9xIEAYS3CQICMB4GDSqGSIb3EgQBhLcJAgQGDSqGSIb3EgQBhLcJAgQw
+HgYNKoZIhvcSBAGEtwkCBAYNKoZIhvcSBAGEtwkCBTAeBg0qhkiG9xIEAYS3CQIF
+Bg0qhkiG9xIEAYS3CQIEMB4GDSqGSIb3EgQBhLcJAgUGDSqGSIb3EgQBhLcJAgUw
+CgYIKoZIzj0EAwIDSQAwRgIhAIOx3GL5xlldQGdTLIvTTAvczm8wiYHzZDAif2yj
+wAjEAiEAg4K02kTYX9x7PC/u1PYdwvo+LVbnGbO6AN6U3K2d7gs=
+-----END CERTIFICATE-----
diff --git a/crypto/x509/test/policy_intermediate_require.pem b/crypto/x509/test/policy_intermediate_require.pem
index c141bfc..5cf5d5b 100644
--- a/crypto/x509/test/policy_intermediate_require.pem
+++ b/crypto/x509/test/policy_intermediate_require.pem
@@ -1,12 +1,12 @@
 -----BEGIN CERTIFICATE-----
-MIIBuTCCAV+gAwIBAgIBAjAKBggqhkjOPQQDAjAWMRQwEgYDVQQDEwtQb2xpY3kg
+MIIBuDCCAV+gAwIBAgIBAjAKBggqhkjOPQQDAjAWMRQwEgYDVQQDEwtQb2xpY3kg
 Um9vdDAgFw0wMDAxMDEwMDAwMDBaGA8yMTAwMDEwMTAwMDAwMFowHjEcMBoGA1UE
 AxMTUG9saWN5IEludGVybWVkaWF0ZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IA
 BOI6fKiM3jFLkLyAn88cvlw4SwxuygRjopP3FFBKHyUQvh3VVvfqSpSCSmp50Qia
 jQ6Dg7CTpVZVVH+bguT7JTCjgZMwgZAwDgYDVR0PAQH/BAQDAgIEMBMGA1UdJQQM
 MAoGCCsGAQUFBwMBMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFJDS9/4O7qhr
 CIRhwsXrPVBagG2uMCsGA1UdIAQkMCIwDwYNKoZIhvcSBAGEtwkCATAPBg0qhkiG
-9xIEAYS3CQICMAwGA1UdJAQFMAOAAQAwCgYIKoZIzj0EAwIDSAAwRQIhALuEN9FY
-tVyp8yVAjc/XDZMY86/p/9nA0t1x4fYJz6T4AiACPS/Lx3PD4FfypUZGlY3uv6TJ
-Dv3tfaa/0GLD98VNIw==
+9xIEAYS3CQICMAwGA1UdJAQFMAOAAQAwCgYIKoZIzj0EAwIDRwAwRAIgbPUZ9ezH
+SgTqom7VLPOvrQQXwy3b/ijSobs7+SOouKMCIDaqcb9143BG005etqeTvlgUyOGF
+GQDWhiW8bizH+KEl
 -----END CERTIFICATE-----
diff --git a/crypto/x509/test/policy_intermediate_require_duplicate.pem b/crypto/x509/test/policy_intermediate_require_duplicate.pem
index b52a8d1..733087a 100644
--- a/crypto/x509/test/policy_intermediate_require_duplicate.pem
+++ b/crypto/x509/test/policy_intermediate_require_duplicate.pem
@@ -7,6 +7,6 @@
 MAoGCCsGAQUFBwMBMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFJDS9/4O7qhr
 CIRhwsXrPVBagG2uMDwGA1UdIAQ1MDMwDwYNKoZIhvcSBAGEtwkCATAPBg0qhkiG
 9xIEAYS3CQICMA8GDSqGSIb3EgQBhLcJAgIwDAYDVR0kBAUwA4ABADAKBggqhkjO
-PQQDAgNIADBFAiEAlowPT0PE6vY2KKCdgh/7ji7x6NBD73OeFe07lk9aBUACIH6R
-ann4GsMCEqkptXfBZ00SIAHJIioAEW+E/8wXU8TX
+PQQDAgNIADBFAiA2GxzMRYYo7NNq8u/ZvffXkCj/phqXQ8I64tEDd0X8pgIhAOJJ
+e+dzzf4vbWfMlYkOQ4kf6ei5Zf+J2PL6VrqVrHQa
 -----END CERTIFICATE-----
diff --git a/crypto/x509/test/policy_intermediate_require_no_policies.pem b/crypto/x509/test/policy_intermediate_require_no_policies.pem
index a78878e..1e81e0c 100644
--- a/crypto/x509/test/policy_intermediate_require_no_policies.pem
+++ b/crypto/x509/test/policy_intermediate_require_no_policies.pem
@@ -1,11 +1,11 @@
 -----BEGIN CERTIFICATE-----
-MIIBijCCATCgAwIBAgIBAjAKBggqhkjOPQQDAjAWMRQwEgYDVQQDEwtQb2xpY3kg
+MIIBizCCATCgAwIBAgIBAjAKBggqhkjOPQQDAjAWMRQwEgYDVQQDEwtQb2xpY3kg
 Um9vdDAgFw0wMDAxMDEwMDAwMDBaGA8yMTAwMDEwMTAwMDAwMFowHjEcMBoGA1UE
 AxMTUG9saWN5IEludGVybWVkaWF0ZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IA
 BOI6fKiM3jFLkLyAn88cvlw4SwxuygRjopP3FFBKHyUQvh3VVvfqSpSCSmp50Qia
 jQ6Dg7CTpVZVVH+bguT7JTCjZTBjMA4GA1UdDwEB/wQEAwICBDATBgNVHSUEDDAK
 BggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSQ0vf+Du6oawiE
-YcLF6z1QWoBtrjAMBgNVHSQEBTADgAEAMAoGCCqGSM49BAMCA0gAMEUCIF2jnBVg
-7hNV09+9ZS7bGaf5GcRifHOkAUC6M596ECXCAiEApuyn9lyP15qD/mHtMz8eYrKn
-8HygEypeq3ClqqKx2c4=
+YcLF6z1QWoBtrjAMBgNVHSQEBTADgAEAMAoGCCqGSM49BAMCA0kAMEYCIQDJYPgf
+50fFDVho5TFeqkNVONx0ArVNgULPB27yPDHLrwIhAN+eua6oM4Q/O0jUESQ4VAKt
+ts7ZCquTZbvgRgyqtjuT
 -----END CERTIFICATE-----
diff --git a/crypto/x509/test/policy_leaf.pem b/crypto/x509/test/policy_leaf.pem
index b140704..fb70306 100644
--- a/crypto/x509/test/policy_leaf.pem
+++ b/crypto/x509/test/policy_leaf.pem
@@ -1,11 +1,11 @@
 -----BEGIN CERTIFICATE-----
-MIIBpjCCAU2gAwIBAgIBAzAKBggqhkjOPQQDAjAeMRwwGgYDVQQDExNQb2xpY3kg
+MIIBpzCCAU2gAwIBAgIBAzAKBggqhkjOPQQDAjAeMRwwGgYDVQQDExNQb2xpY3kg
 SW50ZXJtZWRpYXRlMCAXDTAwMDEwMTAwMDAwMFoYDzIxMDAwMTAxMDAwMDAwWjAa
 MRgwFgYDVQQDEw93d3cuZXhhbXBsZS5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMB
 BwNCAASRKti8VW2Rkma+Kt9jQkMNitlCs0l5w8u3SSwm7HZREvmcBCJBjVIREacR
 qI0umhzR2V5NLzBBP9yPD/A+Ch5Xo34wfDAOBgNVHQ8BAf8EBAMCAgQwEwYDVR0l
 BAwwCgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADAaBgNVHREEEzARgg93d3cuZXhh
 bXBsZS5jb20wKwYDVR0gBCQwIjAPBg0qhkiG9xIEAYS3CQIBMA8GDSqGSIb3EgQB
-hLcJAgIwCgYIKoZIzj0EAwIDRwAwRAIgPTm7NO8gR+z8BqA6gV9FVwrSmOAJVzyu
-5loq9ZTtIS0CIEjBbvBcY4+Y3xWL4SUFQKQk3pNZ37xJoz2v+/yvEE5/
+hLcJAgIwCgYIKoZIzj0EAwIDSAAwRQIgBEOriD1N3/cqoAofxEtf73M7Wi4UfjFK
+jiU9nQhwnnoCIQD1v/XDp2BkWNHxNq7TaPnil3xXTvMX97yUbkUg8IRo0w==
 -----END CERTIFICATE-----
diff --git a/crypto/x509/test/policy_leaf_any.pem b/crypto/x509/test/policy_leaf_any.pem
index 21e8481..d2c1b9e 100644
--- a/crypto/x509/test/policy_leaf_any.pem
+++ b/crypto/x509/test/policy_leaf_any.pem
@@ -1,11 +1,11 @@
 -----BEGIN CERTIFICATE-----
-MIIBjDCCATOgAwIBAgIBAzAKBggqhkjOPQQDAjAeMRwwGgYDVQQDExNQb2xpY3kg
+MIIBjTCCATOgAwIBAgIBAzAKBggqhkjOPQQDAjAeMRwwGgYDVQQDExNQb2xpY3kg
 SW50ZXJtZWRpYXRlMCAXDTAwMDEwMTAwMDAwMFoYDzIxMDAwMTAxMDAwMDAwWjAa
 MRgwFgYDVQQDEw93d3cuZXhhbXBsZS5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMB
 BwNCAASRKti8VW2Rkma+Kt9jQkMNitlCs0l5w8u3SSwm7HZREvmcBCJBjVIREacR
 qI0umhzR2V5NLzBBP9yPD/A+Ch5Xo2QwYjAOBgNVHQ8BAf8EBAMCAgQwEwYDVR0l
 BAwwCgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADAaBgNVHREEEzARgg93d3cuZXhh
-bXBsZS5jb20wEQYDVR0gBAowCDAGBgRVHSAAMAoGCCqGSM49BAMCA0cAMEQCIBs6
-kQg7CXahhSrji0B4KzW2z2QM0CLl3IiCsF7go7rrAiAya2gOki6RAPPtAavZKLFj
-fejsANmLWcQ5ctVeHoq8Gg==
+bXBsZS5jb20wEQYDVR0gBAowCDAGBgRVHSAAMAoGCCqGSM49BAMCA0gAMEUCIQC4
+UwAf1R4HefSzyO8lyQ3fmMjkptVEhFBee0a7N12IvwIgJMYZgQ52VTbqXyXqraJ8
+V+y+o7eHds7NewqnyuLbc78=
 -----END CERTIFICATE-----
diff --git a/crypto/x509/test/policy_leaf_duplicate.pem b/crypto/x509/test/policy_leaf_duplicate.pem
index 80fce22..bdeb13c 100644
--- a/crypto/x509/test/policy_leaf_duplicate.pem
+++ b/crypto/x509/test/policy_leaf_duplicate.pem
@@ -1,12 +1,12 @@
 -----BEGIN CERTIFICATE-----
-MIIBsjCCAVigAwIBAgIBAzAKBggqhkjOPQQDAjAWMRQwEgYDVQQDEwtQb2xpY3kg
+MIIBsTCCAVigAwIBAgIBAzAKBggqhkjOPQQDAjAWMRQwEgYDVQQDEwtQb2xpY3kg
 Um9vdDAgFw0wMDAxMDEwMDAwMDBaGA8yMTAwMDEwMTAwMDAwMFowGjEYMBYGA1UE
 AxMPd3d3LmV4YW1wbGUuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEkSrY
 vFVtkZJmvirfY0JDDYrZQrNJecPLt0ksJux2URL5nAQiQY1SERGnEaiNLpoc0dle
 TS8wQT/cjw/wPgoeV6OBkDCBjTAOBgNVHQ8BAf8EBAMCAgQwEwYDVR0lBAwwCgYI
 KwYBBQUHAwEwDAYDVR0TAQH/BAIwADAaBgNVHREEEzARgg93d3cuZXhhbXBsZS5j
 b20wPAYDVR0gBDUwMzAPBg0qhkiG9xIEAYS3CQIBMA8GDSqGSIb3EgQBhLcJAgIw
-DwYNKoZIhvcSBAGEtwkCAjAKBggqhkjOPQQDAgNIADBFAiEA3MEtsp3pypprhmPB
-UbMC7FwvK+YZI5qo5dDRGUu0H6QCIEbUDagJc0qNdvZ4H//E/cvqb8dH6UmmIXVX
-/WMkIJt2
+DwYNKoZIhvcSBAGEtwkCAjAKBggqhkjOPQQDAgNHADBEAiBjYDwsWcs35hU/wPqa
+5gf0QUMvV/8z5LPX14fB2y4RGQIgMw0ekrt9K5UcgkvFupV/XXIjLRFQvc8URA3C
+/+w+2/4=
 -----END CERTIFICATE-----
diff --git a/crypto/x509/test/policy_leaf_invalid.pem b/crypto/x509/test/policy_leaf_invalid.pem
index 6c0f56f..de7a5e9 100644
--- a/crypto/x509/test/policy_leaf_invalid.pem
+++ b/crypto/x509/test/policy_leaf_invalid.pem
@@ -5,7 +5,7 @@
 vFVtkZJmvirfY0JDDYrZQrNJecPLt0ksJux2URL5nAQiQY1SERGnEaiNLpoc0dle
 TS8wQT/cjw/wPgoeV6NhMF8wDgYDVR0PAQH/BAQDAgIEMBMGA1UdJQQMMAoGCCsG
 AQUFBwMBMAwGA1UdEwEB/wQCMAAwGgYDVR0RBBMwEYIPd3d3LmV4YW1wbGUuY29t
-MA4GA1UdIAQHSU5WQUxJRDAKBggqhkjOPQQDAgNIADBFAiBhnGGMJBM2gTBo9r4C
-NDR89ECTU7dwdvFyOGOIOOZEFgIhAIRIhGdQ9eRRi2qMhN1F19P5VsIUuc4VL1bW
-sXO8fwZM
+MA4GA1UdIAQHSU5WQUxJRDAKBggqhkjOPQQDAgNIADBFAiAgfcDIeqmV+u5YtUe4
+aBnj13tZAJAQh6ttum1xZ+xHEgIhAJqvGX5c0/d1qYelBlm/jE3UuivijdEjVsLX
+GVH+X1VA
 -----END CERTIFICATE-----
diff --git a/crypto/x509/test/policy_leaf_oid1.pem b/crypto/x509/test/policy_leaf_oid1.pem
new file mode 100644
index 0000000..94cd1a7
--- /dev/null
+++ b/crypto/x509/test/policy_leaf_oid1.pem
@@ -0,0 +1,11 @@
+-----BEGIN CERTIFICATE-----
+MIIBlTCCATygAwIBAgIBAzAKBggqhkjOPQQDAjAeMRwwGgYDVQQDExNQb2xpY3kg
+SW50ZXJtZWRpYXRlMCAXDTAwMDEwMTAwMDAwMFoYDzIxMDAwMTAxMDAwMDAwWjAa
+MRgwFgYDVQQDEw93d3cuZXhhbXBsZS5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMB
+BwNCAASRKti8VW2Rkma+Kt9jQkMNitlCs0l5w8u3SSwm7HZREvmcBCJBjVIREacR
+qI0umhzR2V5NLzBBP9yPD/A+Ch5Xo20wazAOBgNVHQ8BAf8EBAMCAgQwEwYDVR0l
+BAwwCgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADAaBgNVHREEEzARgg93d3cuZXhh
+bXBsZS5jb20wGgYDVR0gBBMwETAPBg0qhkiG9xIEAYS3CQIBMAoGCCqGSM49BAMC
+A0cAMEQCIHh4Bo8l/HVJhLMWcYusPOE0arqoDrJ5E0M6nEi3nRhgAiAArK8bBohG
+fZ3DmVMq/2BJtQZwRRj+50VKWuf9mBSflQ==
+-----END CERTIFICATE-----
diff --git a/crypto/x509/test/policy_leaf_oid2.pem b/crypto/x509/test/policy_leaf_oid2.pem
new file mode 100644
index 0000000..10adf86
--- /dev/null
+++ b/crypto/x509/test/policy_leaf_oid2.pem
@@ -0,0 +1,11 @@
+-----BEGIN CERTIFICATE-----
+MIIBlzCCATygAwIBAgIBAzAKBggqhkjOPQQDAjAeMRwwGgYDVQQDExNQb2xpY3kg
+SW50ZXJtZWRpYXRlMCAXDTAwMDEwMTAwMDAwMFoYDzIxMDAwMTAxMDAwMDAwWjAa
+MRgwFgYDVQQDEw93d3cuZXhhbXBsZS5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMB
+BwNCAASRKti8VW2Rkma+Kt9jQkMNitlCs0l5w8u3SSwm7HZREvmcBCJBjVIREacR
+qI0umhzR2V5NLzBBP9yPD/A+Ch5Xo20wazAOBgNVHQ8BAf8EBAMCAgQwEwYDVR0l
+BAwwCgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADAaBgNVHREEEzARgg93d3cuZXhh
+bXBsZS5jb20wGgYDVR0gBBMwETAPBg0qhkiG9xIEAYS3CQICMAoGCCqGSM49BAMC
+A0kAMEYCIQDvW7rdL6MSW/0BPNET4hEeECO6LWmZZHKCHIu6o33dsAIhAPwgm6lD
+KV2hMOxkE6rBDQzlCr+zAkQrxSzQZqJp5p+W
+-----END CERTIFICATE-----
diff --git a/crypto/x509/test/policy_leaf_oid3.pem b/crypto/x509/test/policy_leaf_oid3.pem
new file mode 100644
index 0000000..e5c1031
--- /dev/null
+++ b/crypto/x509/test/policy_leaf_oid3.pem
@@ -0,0 +1,11 @@
+-----BEGIN CERTIFICATE-----
+MIIBlzCCATygAwIBAgIBAzAKBggqhkjOPQQDAjAeMRwwGgYDVQQDExNQb2xpY3kg
+SW50ZXJtZWRpYXRlMCAXDTAwMDEwMTAwMDAwMFoYDzIxMDAwMTAxMDAwMDAwWjAa
+MRgwFgYDVQQDEw93d3cuZXhhbXBsZS5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMB
+BwNCAASRKti8VW2Rkma+Kt9jQkMNitlCs0l5w8u3SSwm7HZREvmcBCJBjVIREacR
+qI0umhzR2V5NLzBBP9yPD/A+Ch5Xo20wazAOBgNVHQ8BAf8EBAMCAgQwEwYDVR0l
+BAwwCgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADAaBgNVHREEEzARgg93d3cuZXhh
+bXBsZS5jb20wGgYDVR0gBBMwETAPBg0qhkiG9xIEAYS3CQIDMAoGCCqGSM49BAMC
+A0kAMEYCIQDBPnPpRsOH20ncg8TKUdlONfbO62WafQj9SKgyi/nGBQIhAMhT8J7f
+fTEou6jlAilaIQwlAgZzVKRqgghIHezFY86T
+-----END CERTIFICATE-----
diff --git a/crypto/x509/test/policy_leaf_oid4.pem b/crypto/x509/test/policy_leaf_oid4.pem
new file mode 100644
index 0000000..7dd7a54
--- /dev/null
+++ b/crypto/x509/test/policy_leaf_oid4.pem
@@ -0,0 +1,11 @@
+-----BEGIN CERTIFICATE-----
+MIIBlzCCATygAwIBAgIBAzAKBggqhkjOPQQDAjAeMRwwGgYDVQQDExNQb2xpY3kg
+SW50ZXJtZWRpYXRlMCAXDTAwMDEwMTAwMDAwMFoYDzIxMDAwMTAxMDAwMDAwWjAa
+MRgwFgYDVQQDEw93d3cuZXhhbXBsZS5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMB
+BwNCAASRKti8VW2Rkma+Kt9jQkMNitlCs0l5w8u3SSwm7HZREvmcBCJBjVIREacR
+qI0umhzR2V5NLzBBP9yPD/A+Ch5Xo20wazAOBgNVHQ8BAf8EBAMCAgQwEwYDVR0l
+BAwwCgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADAaBgNVHREEEzARgg93d3cuZXhh
+bXBsZS5jb20wGgYDVR0gBBMwETAPBg0qhkiG9xIEAYS3CQIEMAoGCCqGSM49BAMC
+A0kAMEYCIQD2gnpCTMxUalCtEV52eXzqeJgsKMYvEpJTuU/VqH5KwQIhAPEavAkt
+cSJsgMgJcJnbBzAdSrbOgHXF2etDHmFbg0hz
+-----END CERTIFICATE-----
diff --git a/crypto/x509/test/policy_leaf_oid5.pem b/crypto/x509/test/policy_leaf_oid5.pem
new file mode 100644
index 0000000..2a9aee7
--- /dev/null
+++ b/crypto/x509/test/policy_leaf_oid5.pem
@@ -0,0 +1,11 @@
+-----BEGIN CERTIFICATE-----
+MIIBlzCCATygAwIBAgIBAzAKBggqhkjOPQQDAjAeMRwwGgYDVQQDExNQb2xpY3kg
+SW50ZXJtZWRpYXRlMCAXDTAwMDEwMTAwMDAwMFoYDzIxMDAwMTAxMDAwMDAwWjAa
+MRgwFgYDVQQDEw93d3cuZXhhbXBsZS5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMB
+BwNCAASRKti8VW2Rkma+Kt9jQkMNitlCs0l5w8u3SSwm7HZREvmcBCJBjVIREacR
+qI0umhzR2V5NLzBBP9yPD/A+Ch5Xo20wazAOBgNVHQ8BAf8EBAMCAgQwEwYDVR0l
+BAwwCgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADAaBgNVHREEEzARgg93d3cuZXhh
+bXBsZS5jb20wGgYDVR0gBBMwETAPBg0qhkiG9xIEAYS3CQIFMAoGCCqGSM49BAMC
+A0kAMEYCIQDDFVjhlQ1Wu0KITcRX8kELpVDeYSKSlvEbZc3rn1QjkQIhAMPthqBi
+I0acz8DPQcdFmHXV0xR2xyC1yuen0gES5WLR
+-----END CERTIFICATE-----
diff --git a/crypto/x509/test/policy_root.pem b/crypto/x509/test/policy_root.pem
index 7bb209a..595f8a1 100644
--- a/crypto/x509/test/policy_root.pem
+++ b/crypto/x509/test/policy_root.pem
@@ -1,10 +1,10 @@
 -----BEGIN CERTIFICATE-----
-MIIBdDCCARqgAwIBAgIBATAKBggqhkjOPQQDAjAWMRQwEgYDVQQDEwtQb2xpY3kg
+MIIBdTCCARqgAwIBAgIBATAKBggqhkjOPQQDAjAWMRQwEgYDVQQDEwtQb2xpY3kg
 Um9vdDAgFw0wMDAxMDEwMDAwMDBaGA8yMTAwMDEwMTAwMDAwMFowFjEUMBIGA1UE
 AxMLUG9saWN5IFJvb3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQmdqXYl1Gv
 Y7y3jcTTK6MVXIQr44TqChRYI6IeV9tIB6jIsOY+Qol1bk8x/7A5FGOnUWFVLEAP
 EPSJwPndjolto1cwVTAOBgNVHQ8BAf8EBAMCAgQwEwYDVR0lBAwwCgYIKwYBBQUH
 AwEwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU0GnnoB+yeN63WMthnh6Uh1HH
-dRIwCgYIKoZIzj0EAwIDSAAwRQIgctaVgroxlAkLhPEaTXvsE3ePYM2X+KGOJZXc
-usyO3YkCIQDN1RLJq9vHGjZzDCEehKjxHsV+XSAkdfU7nB7KjVHTKA==
+dRIwCgYIKoZIzj0EAwIDSQAwRgIhAKVxVAaJnmvt+q4SqegGS23QSzKPM9Yakw9e
+bOUU9+52AiEAjXPRBdd90YDey4VFu4f/78yVe0cxMK30lll7lLl7TTA=
 -----END CERTIFICATE-----
diff --git a/crypto/x509/x509_test.cc b/crypto/x509/x509_test.cc
index 2261dc3..cc3cd00 100644
--- a/crypto/x509/x509_test.cc
+++ b/crypto/x509/x509_test.cc
@@ -5105,6 +5105,12 @@
   bssl::UniquePtr<ASN1_OBJECT> oid3(
       OBJ_txt2obj("1.2.840.113554.4.1.72585.2.3", /*dont_search_names=*/1));
   ASSERT_TRUE(oid3);
+  bssl::UniquePtr<ASN1_OBJECT> oid4(
+      OBJ_txt2obj("1.2.840.113554.4.1.72585.2.4", /*dont_search_names=*/1));
+  ASSERT_TRUE(oid4);
+  bssl::UniquePtr<ASN1_OBJECT> oid5(
+      OBJ_txt2obj("1.2.840.113554.4.1.72585.2.5", /*dont_search_names=*/1));
+  ASSERT_TRUE(oid5);
 
   bssl::UniquePtr<X509> root(
       CertFromPEM(GetTestData("crypto/x509/test/policy_root.pem").c_str()));
@@ -5115,13 +5121,21 @@
   bssl::UniquePtr<X509> intermediate_any(CertFromPEM(
       GetTestData("crypto/x509/test/policy_intermediate_any.pem").c_str()));
   ASSERT_TRUE(intermediate_any);
-  bssl::UniquePtr<X509> intermediate_invalid(CertFromPEM(
-      GetTestData("crypto/x509/test/policy_intermediate_invalid.pem").c_str()));
-  ASSERT_TRUE(intermediate_invalid);
   bssl::UniquePtr<X509> intermediate_duplicate(CertFromPEM(
       GetTestData("crypto/x509/test/policy_intermediate_duplicate.pem")
           .c_str()));
   ASSERT_TRUE(intermediate_duplicate);
+  bssl::UniquePtr<X509> intermediate_invalid(CertFromPEM(
+      GetTestData("crypto/x509/test/policy_intermediate_invalid.pem").c_str()));
+  ASSERT_TRUE(intermediate_invalid);
+  bssl::UniquePtr<X509> intermediate_mapped(CertFromPEM(
+      GetTestData("crypto/x509/test/policy_intermediate_mapped.pem")
+          .c_str()));
+  ASSERT_TRUE(intermediate_mapped);
+  bssl::UniquePtr<X509> intermediate_mapped_any(CertFromPEM(
+      GetTestData("crypto/x509/test/policy_intermediate_mapped_any.pem")
+          .c_str()));
+  ASSERT_TRUE(intermediate_mapped_any);
   bssl::UniquePtr<X509> intermediate_require(CertFromPEM(
       GetTestData("crypto/x509/test/policy_intermediate_require.pem").c_str()));
   ASSERT_TRUE(intermediate_require);
@@ -5140,12 +5154,27 @@
   bssl::UniquePtr<X509> leaf_any(
       CertFromPEM(GetTestData("crypto/x509/test/policy_leaf_any.pem").c_str()));
   ASSERT_TRUE(leaf_any);
-  bssl::UniquePtr<X509> leaf_invalid(CertFromPEM(
-      GetTestData("crypto/x509/test/policy_leaf_invalid.pem").c_str()));
-  ASSERT_TRUE(leaf_invalid);
   bssl::UniquePtr<X509> leaf_duplicate(CertFromPEM(
       GetTestData("crypto/x509/test/policy_leaf_duplicate.pem").c_str()));
   ASSERT_TRUE(leaf_duplicate);
+  bssl::UniquePtr<X509> leaf_invalid(CertFromPEM(
+      GetTestData("crypto/x509/test/policy_leaf_invalid.pem").c_str()));
+  ASSERT_TRUE(leaf_invalid);
+  bssl::UniquePtr<X509> leaf_oid1(CertFromPEM(
+      GetTestData("crypto/x509/test/policy_leaf_oid1.pem").c_str()));
+  ASSERT_TRUE(leaf_oid1);
+  bssl::UniquePtr<X509> leaf_oid2(CertFromPEM(
+      GetTestData("crypto/x509/test/policy_leaf_oid2.pem").c_str()));
+  ASSERT_TRUE(leaf_oid2);
+  bssl::UniquePtr<X509> leaf_oid3(CertFromPEM(
+      GetTestData("crypto/x509/test/policy_leaf_oid3.pem").c_str()));
+  ASSERT_TRUE(leaf_oid3);
+  bssl::UniquePtr<X509> leaf_oid4(CertFromPEM(
+      GetTestData("crypto/x509/test/policy_leaf_oid4.pem").c_str()));
+  ASSERT_TRUE(leaf_oid4);
+  bssl::UniquePtr<X509> leaf_oid5(CertFromPEM(
+      GetTestData("crypto/x509/test/policy_leaf_oid5.pem").c_str()));
+  ASSERT_TRUE(leaf_oid5);
 
   // By default, OpenSSL does not check policies, so even syntax errors in the
   // certificatePolicies extension go unnoticed. (This is probably not
@@ -5312,4 +5341,85 @@
                    [&](X509_VERIFY_PARAM *param) {
                      set_policies(param, {oid3.get()});
                    }));
+
+  for (bool use_any : {false, true}) {
+    SCOPED_TRACE(use_any);
+    X509 *cert =
+        use_any ? intermediate_mapped_any.get() : intermediate_mapped.get();
+    // OID3 is mapped to {OID1, OID2}, which means OID1 and OID2 (or both) are
+    // acceptable for OID3.
+    EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {cert},
+                                /*crls=*/{}, X509_V_FLAG_EXPLICIT_POLICY,
+                                [&](X509_VERIFY_PARAM *param) {
+                                  set_policies(param, {oid3.get()});
+                                }));
+    EXPECT_EQ(X509_V_OK, Verify(leaf_oid1.get(), {root.get()}, {cert},
+                                /*crls=*/{}, X509_V_FLAG_EXPLICIT_POLICY,
+                                [&](X509_VERIFY_PARAM *param) {
+                                  set_policies(param, {oid3.get()});
+                                }));
+    EXPECT_EQ(X509_V_OK, Verify(leaf_oid2.get(), {root.get()}, {cert},
+                                /*crls=*/{}, X509_V_FLAG_EXPLICIT_POLICY,
+                                [&](X509_VERIFY_PARAM *param) {
+                                  set_policies(param, {oid3.get()});
+                                }));
+
+    // If the intermediate's policies were anyPolicy, OID3 at the leaf, despite
+    // being mapped, is still acceptable as OID3 at the root. Despite the OID3
+    // having expected_policy_set = {OID1, OID2}, it can match the anyPolicy
+    // node instead.
+    //
+    // If the intermediate's policies listed OIDs explicitly, OID3 at the leaf
+    // is not acceptable as OID3 at the root. OID3 has expected_polciy_set =
+    // {OID1, OID2} and no other node allows OID3.
+    EXPECT_EQ(use_any ? X509_V_OK : X509_V_ERR_NO_EXPLICIT_POLICY,
+              Verify(leaf_oid3.get(), {root.get()}, {cert},
+                     /*crls=*/{}, X509_V_FLAG_EXPLICIT_POLICY,
+                     [&](X509_VERIFY_PARAM *param) {
+                       set_policies(param, {oid3.get()});
+                     }));
+
+    // If the intermediate's policies were anyPolicy, OID1 at the leaf is no
+    // longer acceptable as OID1 at the root because policies only match
+    // anyPolicy when they match no other policy.
+    //
+    // If the intermediate's policies listed OIDs explicitly, OID1 at the leaf
+    // is acceptable as OID1 at the root because it will match both OID1 and
+    // OID3 (mapped) policies.
+    EXPECT_EQ(use_any ? X509_V_ERR_NO_EXPLICIT_POLICY : X509_V_OK,
+              Verify(leaf_oid1.get(), {root.get()}, {cert},
+                     /*crls=*/{}, X509_V_FLAG_EXPLICIT_POLICY,
+                     [&](X509_VERIFY_PARAM *param) {
+                       set_policies(param, {oid1.get()});
+                     }));
+
+    // All pairs of OID4 and OID5 are mapped together, so either can stand for
+    // the other.
+    EXPECT_EQ(X509_V_OK, Verify(leaf_oid4.get(), {root.get()}, {cert},
+                                /*crls=*/{}, X509_V_FLAG_EXPLICIT_POLICY,
+                                [&](X509_VERIFY_PARAM *param) {
+                                  set_policies(param, {oid4.get()});
+                                }));
+    EXPECT_EQ(X509_V_OK, Verify(leaf_oid4.get(), {root.get()}, {cert},
+                                /*crls=*/{}, X509_V_FLAG_EXPLICIT_POLICY,
+                                [&](X509_VERIFY_PARAM *param) {
+                                  set_policies(param, {oid5.get()});
+                                }));
+    EXPECT_EQ(X509_V_OK, Verify(leaf_oid5.get(), {root.get()}, {cert},
+                                /*crls=*/{}, X509_V_FLAG_EXPLICIT_POLICY,
+                                [&](X509_VERIFY_PARAM *param) {
+                                  set_policies(param, {oid4.get()});
+                                }));
+    EXPECT_EQ(X509_V_OK, Verify(leaf_oid5.get(), {root.get()}, {cert},
+                                /*crls=*/{}, X509_V_FLAG_EXPLICIT_POLICY,
+                                [&](X509_VERIFY_PARAM *param) {
+                                  set_policies(param, {oid5.get()});
+                                }));
+
+    EXPECT_EQ(X509_V_OK, Verify(leaf_oid4.get(), {root.get()}, {cert},
+                                /*crls=*/{}, X509_V_FLAG_EXPLICIT_POLICY,
+                                [&](X509_VERIFY_PARAM *param) {
+                                  set_policies(param, {oid4.get(), oid5.get()});
+                                }));
+  }
 }
diff --git a/sources.cmake b/sources.cmake
index e2e9695..d6ed84c 100644
--- a/sources.cmake
+++ b/sources.cmake
@@ -115,6 +115,8 @@
   crypto/x509/test/policy_intermediate_any.pem
   crypto/x509/test/policy_intermediate_duplicate.pem
   crypto/x509/test/policy_intermediate_invalid.pem
+  crypto/x509/test/policy_intermediate_mapped.pem
+  crypto/x509/test/policy_intermediate_mapped_any.pem
   crypto/x509/test/policy_intermediate_require.pem
   crypto/x509/test/policy_intermediate_require_duplicate.pem
   crypto/x509/test/policy_intermediate_require_no_policies.pem
@@ -122,6 +124,11 @@
   crypto/x509/test/policy_leaf_any.pem
   crypto/x509/test/policy_leaf_duplicate.pem
   crypto/x509/test/policy_leaf_invalid.pem
+  crypto/x509/test/policy_leaf_oid1.pem
+  crypto/x509/test/policy_leaf_oid2.pem
+  crypto/x509/test/policy_leaf_oid3.pem
+  crypto/x509/test/policy_leaf_oid4.pem
+  crypto/x509/test/policy_leaf_oid5.pem
   crypto/x509/test/policy_leaf.pem
   crypto/x509/test/pss_sha1_explicit.pem
   crypto/x509/test/pss_sha1_mgf1_syntax_error.pem