Tolerate RCS-MLS custom critical extensions
As per A.3.8.9 and A.3.8.10 of the RCC MLS spec
We don't do anything with these, but allow them
to be present when doing RCS MLS auth.
Bug: 394580816,394509666,394506732
Change-Id: I5b3ae81269cdca5020a0fbe33575481d7be17839
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/77407
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: Bob Beck <bbe@google.com>
diff --git a/pki/parse_certificate.h b/pki/parse_certificate.h
index 99b822e..d2bf9db 100644
--- a/pki/parse_certificate.h
+++ b/pki/parse_certificate.h
@@ -440,6 +440,32 @@
inline constexpr uint8_t kMSApplicationPoliciesOid[] = {
0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x15, 0x0a};
+// From GSMA RCC.16 v1.0 End-to-End Encryption Specification.
+// id-gsmaRCSE2EE OBJECT IDENTIFIER ::= { joint-iso-itu-t(2)
+// international-organizations(23) gsma(146) rcs(2) rcsE2EE (1)}
+// (Note this spec incorrectly says id-appleDraftRCSE2EE in place of
+// id-gmsaRCSE2EE in several places)
+//
+// From GSMA RCC.16 v1.0 End-to-End Encryption Specification section A.2.8.8,
+// and A.3.8.9.
+// id-participantInformation OBJECT IDENTIFIER ::= { id-gmsaRCS2EE 4 }
+// In dotted notation: 2.23.146.2.1.4
+inline constexpr uint8_t kRcsMlsParticipantInformation[] = {0x67, 0x81, 0x12,
+ 0x02, 0x01, 0x04};
+
+// From GSMA RCC.16 v1.0 End-to-End Encryption Specification.
+// id-gsmaRCSE2EE OBJECT IDENTIFIER ::= { joint-iso-itu-t(2)
+// international-organizations(23) gsma(146) rcs(2) rcsE2EE (1)}
+// (Note this spec incorrectly says id-appleDraftRCSE2EE in place of
+// id-gmsaRCSE2EE in several places)
+//
+// From GSMA RCC.16 v1.0 End-to-End Encryption Specification section A.2.8.8,
+// and A.3.8.10.
+// id-acsParticipantInformation OBJECT IDENTIFIER ::= { id-gmsaRCS2EE 5 }
+// In dotted notation: 2.23.146.2.1.5
+inline constexpr uint8_t kRcsMlsAcsParticipantInformation[] = {
+ 0x67, 0x81, 0x12, 0x02, 0x01, 0x05};
+
// Parses the Extensions sequence as defined by RFC 5280. Extensions are added
// to the map |extensions| keyed by the OID. Parsing guarantees that each OID
// is unique. Note that certificate verification must consume each extension
diff --git a/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-mlsclientauth-extra/any.test b/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-mlsclientauth-extra/any.test
index 4d092a9..67f8d83 100644
--- a/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-mlsclientauth-extra/any.test
+++ b/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-mlsclientauth-extra/any.test
@@ -3,3 +3,11 @@
utc_time: DEFAULT
key_purpose: ANY_EKU
expected_errors:
+----- Certificate i=0 (CN=MLS Cert Leaf) -----
+ERROR: Unconsumed critical extension
+ oid: 678112020104
+ value: 30080606678112020104
+ERROR: Unconsumed critical extension
+ oid: 678112020105
+ value: 30080606678112020105
+
diff --git a/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-mlsclientauth-extra/chain.pem b/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-mlsclientauth-extra/chain.pem
index fcbc08e..d068d24 100644
--- a/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-mlsclientauth-extra/chain.pem
+++ b/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-mlsclientauth-extra/chain.pem
@@ -1,13 +1,14 @@
-----BEGIN CERTIFICATE-----
-MIIBjDCCATOgAwIBAgIBAzAKBggqhkjOPQQDAjAgMR4wHAYDVQQDExVNTFMgQ2Vy
+MIIBwTCCAWegAwIBAgIBAzAKBggqhkjOPQQDAjAgMR4wHAYDVQQDExVNTFMgQ2Vy
dCBJbnRlcm1lZGlhdGUwIhgPMDAwMDAxMDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1
OVowGDEWMBQGA1UEAxMNTUxTIENlcnQgTGVhZjBZMBMGByqGSM49AgEGCCqGSM49
AwEHA0IABJEq2LxVbZGSZr4q32NCQw2K2UKzSXnDy7dJLCbsdlES+ZwEIkGNUhER
-pxGojS6aHNHZXk0vMEE/3I8P8D4KHlejYjBgMA4GA1UdDwEB/wQEAwIFoDAYBgNV
-HSUEETAPBgZngRICAQMGBWeBBQgBMAwGA1UdEwEB/wQCMAAwDQYDVR0OBAYEBGxl
-YWYwFwYDVR0jBBAwDoAMaW50ZXJtZWRpYXRlMAoGCCqGSM49BAMCA0cAMEQCIBPZ
-e8YKCfB9njvqxscvNyXD9x+9oZvwGZ+yiudo1WFWAiB395X3vB8qc9sxVmuWMM8d
-kHjKKyiamynH40DPyKgvjw==
+pxGojS6aHNHZXk0vMEE/3I8P8D4KHlejgZUwgZIwDgYDVR0PAQH/BAQDAgWgMBgG
+A1UdJQQRMA8GBmeBEgIBAwYFZ4EFCAEwDAYDVR0TAQH/BAIwADANBgNVHQ4EBgQE
+bGVhZjAXBgNVHSMEEDAOgAxpbnRlcm1lZGlhdGUwFwYGZ4ESAgEEAQH/BAowCAYG
+Z4ESAgEEMBcGBmeBEgIBBQEB/wQKMAgGBmeBEgIBBTAKBggqhkjOPQQDAgNIADBF
+AiEAoHx0HkcLugc+lfbjUe/V6lEQk26diFlw76bqeLLmrt0CIBOXOwd+gC66weUM
+5np3wcTVet2fxPvyLfu1NwmFQkc/
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIBkzCCATmgAwIBAgIBAjAKBggqhkjOPQQDAjAYMRYwFAYDVQQDEw1NTFMgQ2Vy
diff --git a/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-mlsclientauth/any.test b/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-mlsclientauth/any.test
index 4d092a9..67f8d83 100644
--- a/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-mlsclientauth/any.test
+++ b/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-mlsclientauth/any.test
@@ -3,3 +3,11 @@
utc_time: DEFAULT
key_purpose: ANY_EKU
expected_errors:
+----- Certificate i=0 (CN=MLS Cert Leaf) -----
+ERROR: Unconsumed critical extension
+ oid: 678112020104
+ value: 30080606678112020104
+ERROR: Unconsumed critical extension
+ oid: 678112020105
+ value: 30080606678112020105
+
diff --git a/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-mlsclientauth/chain.pem b/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-mlsclientauth/chain.pem
index b6ee783..c310b97 100644
--- a/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-mlsclientauth/chain.pem
+++ b/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-mlsclientauth/chain.pem
@@ -1,13 +1,14 @@
-----BEGIN CERTIFICATE-----
-MIIBhzCCASygAwIBAgIBAzAKBggqhkjOPQQDAjAgMR4wHAYDVQQDExVNTFMgQ2Vy
+MIIBujCCAWCgAwIBAgIBAzAKBggqhkjOPQQDAjAgMR4wHAYDVQQDExVNTFMgQ2Vy
dCBJbnRlcm1lZGlhdGUwIhgPMDAwMDAxMDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1
OVowGDEWMBQGA1UEAxMNTUxTIENlcnQgTGVhZjBZMBMGByqGSM49AgEGCCqGSM49
AwEHA0IABJEq2LxVbZGSZr4q32NCQw2K2UKzSXnDy7dJLCbsdlES+ZwEIkGNUhER
-pxGojS6aHNHZXk0vMEE/3I8P8D4KHlejWzBZMA4GA1UdDwEB/wQEAwIHgDAMBgNV
-HRMBAf8EAjAAMA0GA1UdDgQGBARsZWFmMBcGA1UdIwQQMA6ADGludGVybWVkaWF0
-ZTARBgNVHSUECjAIBgZngRICAQMwCgYIKoZIzj0EAwIDSQAwRgIhAIwK8bEHhn7W
-iWgVLTGsuSdILRxhNC/aGQDU1CwwsS3GAiEAivWAQPt30IzsWrwdFPyAl/68xbnP
-SNzmnZtG2HC4WTs=
+pxGojS6aHNHZXk0vMEE/3I8P8D4KHlejgY4wgYswDgYDVR0PAQH/BAQDAgeAMBEG
+A1UdJQQKMAgGBmeBEgIBAzAMBgNVHRMBAf8EAjAAMA0GA1UdDgQGBARsZWFmMBcG
+A1UdIwQQMA6ADGludGVybWVkaWF0ZTAXBgZngRICAQQBAf8ECjAIBgZngRICAQQw
+FwYGZ4ESAgEFAQH/BAowCAYGZ4ESAgEFMAoGCCqGSM49BAMCA0gAMEUCIEFVao6y
+OepuSGWgIK9Hdvc4DIRLZV+kYbHc9ub5LEO3AiEAiLXBSJffzY24NTMMqxsN+MCn
+w5ADuHTqNXjz6LFTkV0=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIBiTCCAS+gAwIBAgIBAjAKBggqhkjOPQQDAjAYMRYwFAYDVQQDEw1NTFMgQ2Vy
diff --git a/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-mlsclientauth/clientauth.test b/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-mlsclientauth/clientauth.test
index cbe7baa..f8985ae 100644
--- a/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-mlsclientauth/clientauth.test
+++ b/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-mlsclientauth/clientauth.test
@@ -5,6 +5,12 @@
expected_errors:
----- Certificate i=0 (CN=MLS Cert Leaf) -----
ERROR: The extended key usage does not include client auth
+ERROR: Unconsumed critical extension
+ oid: 678112020104
+ value: 30080606678112020104
+ERROR: Unconsumed critical extension
+ oid: 678112020105
+ value: 30080606678112020105
----- Certificate i=1 (CN=MLS Cert Intermediate) -----
ERROR: The extended key usage does not include client auth
diff --git a/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-mlsclientauth/make-mls-extensions.go b/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-mlsclientauth/make-mls-extensions.go
index 2b73624..b1d64cc 100644
--- a/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-mlsclientauth/make-mls-extensions.go
+++ b/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-mlsclientauth/make-mls-extensions.go
@@ -42,6 +42,26 @@
key *ecdsa.PrivateKey
}
+func rcsMlsParticipantExtension() (ext pkix.Extension, err error) {
+ var oidParticipantInfo = asn1.ObjectIdentifier{2, 23, 146, 2, 1, 4}
+ ext = pkix.Extension{}
+ ext.Id = oidParticipantInfo
+ ext.Critical = true
+ // Not really a valid value, but doesn't matter to us.
+ ext.Value, err = asn1.Marshal([]asn1.ObjectIdentifier{oidParticipantInfo})
+ return ext, err
+}
+
+func rcsAcsMlsParticipantExtension() (ext pkix.Extension, err error) {
+ var oidParticipantInfo = asn1.ObjectIdentifier{2, 23, 146, 2, 1, 5}
+ ext = pkix.Extension{}
+ ext.Id = oidParticipantInfo
+ ext.Critical = true
+ // Not really a valid value, but doesn't matter to us.
+ ext.Value, err = asn1.Marshal([]asn1.ObjectIdentifier{oidParticipantInfo})
+ return ext, err
+}
+
func mustGenerateCertificate(path string, subject, issuer *templateAndKey) []byte {
cert, err := x509.CreateCertificate(rand.Reader, &subject.template, &issuer.template, &subject.key.PublicKey, issuer.key)
if err != nil {
@@ -98,6 +118,17 @@
},
key: intermediateKey,
}
+
+ ParticipantExt, err := rcsMlsParticipantExtension()
+ if err != nil {
+ panic(err)
+ }
+
+ AcsParticipantExt, err := rcsAcsMlsParticipantExtension()
+ if err != nil {
+ panic(err)
+ }
+
leaf := templateAndKey{
template: x509.Certificate{
SerialNumber: new(big.Int).SetInt64(3),
@@ -110,6 +141,7 @@
SignatureAlgorithm: x509.ECDSAWithSHA256,
SubjectKeyId: []byte("leaf"),
UnknownExtKeyUsage: []asn1.ObjectIdentifier{[]int{2, 23, 146, 2, 1, 3}},
+ ExtraExtensions: append([]pkix.Extension{}, ParticipantExt, AcsParticipantExt),
},
key: leafKey,
}
diff --git a/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-mlsclientauth/serverauth.test b/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-mlsclientauth/serverauth.test
index b1c0909..6830ba0 100644
--- a/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-mlsclientauth/serverauth.test
+++ b/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-mlsclientauth/serverauth.test
@@ -5,6 +5,12 @@
expected_errors:
----- Certificate i=0 (CN=MLS Cert Leaf) -----
ERROR: The extended key usage does not include server auth
+ERROR: Unconsumed critical extension
+ oid: 678112020104
+ value: 30080606678112020104
+ERROR: Unconsumed critical extension
+ oid: 678112020105
+ value: 30080606678112020105
----- Certificate i=1 (CN=MLS Cert Intermediate) -----
ERROR: The extended key usage does not include server auth
diff --git a/pki/verify_certificate_chain.cc b/pki/verify_certificate_chain.cc
index 0e45373..d94c3cf 100644
--- a/pki/verify_certificate_chain.cc
+++ b/pki/verify_certificate_chain.cc
@@ -33,8 +33,7 @@
namespace {
-bool IsHandledCriticalExtension(const ParsedExtension &extension,
- const ParsedCertificate &cert) {
+bool IsHandledCriticalExtension(const ParsedExtension &extension) {
if (extension.oid == der::Input(kBasicConstraintsOid)) {
return true;
}
@@ -80,13 +79,6 @@
if (extension.oid == der::Input(kInhibitAnyPolicyOid)) {
return true;
}
- if (extension.oid == der::Input(kMSApplicationPoliciesOid)) {
- // Per https://crbug.com/1439638 and
- // https://learn.microsoft.com/en-us/windows/win32/seccertenroll/supported-extensions#msapplicationpolicies
- // The MSApplicationPolicies extension may be ignored if the
- // extendedKeyUsage extension is also present.
- return cert.has_extended_key_usage();
- }
return false;
}
@@ -95,16 +87,33 @@
// extensions.
void VerifyNoUnconsumedCriticalExtensions(const ParsedCertificate &cert,
CertErrors *errors,
- bool allow_precertificate) {
+ bool allow_precertificate,
+ KeyPurpose key_purpose) {
for (const auto &it : cert.extensions()) {
const ParsedExtension &extension = it.second;
- if (allow_precertificate && extension.oid == der::Input(kCtPoisonOid)) {
- continue;
- }
- if (extension.critical && !IsHandledCriticalExtension(extension, cert)) {
- errors->AddError(cert_errors::kUnconsumedCriticalExtension,
- CreateCertErrorParams2Der("oid", extension.oid, "value",
- extension.value));
+ if (extension.critical) {
+ if (key_purpose == KeyPurpose::RCS_MLS_CLIENT_AUTH) {
+ if (extension.oid == der::Input(kRcsMlsParticipantInformation) ||
+ extension.oid == der::Input(kRcsMlsAcsParticipantInformation)) {
+ continue;
+ }
+ }
+ if (allow_precertificate && extension.oid == der::Input(kCtPoisonOid)) {
+ continue;
+ }
+ if (extension.oid == der::Input(kMSApplicationPoliciesOid) &&
+ cert.has_extended_key_usage()) {
+ // Per https://crbug.com/1439638 and
+ // https://learn.microsoft.com/en-us/windows/win32/seccertenroll/supported-extensions#msapplicationpolicies
+ // The MSApplicationPolicies extension may be ignored if the
+ // extendedKeyUsage extension is also present.
+ continue;
+ }
+ if (!IsHandledCriticalExtension(extension)) {
+ errors->AddError(cert_errors::kUnconsumedCriticalExtension,
+ CreateCertErrorParams2Der("oid", extension.oid,
+ "value", extension.value));
+ }
}
}
}
@@ -717,7 +726,7 @@
// This function corresponds to RFC 5280 section 6.1.4's "Preparation for
// Certificate i+1" procedure. |cert| is expected to be an intermediate.
void PrepareForNextCertificate(const ParsedCertificate &cert,
- CertErrors *errors);
+ KeyPurpose key_purpose, CertErrors *errors);
// This function corresponds with RFC 5280 section 6.1.5's "Wrap-Up
// Procedure". It does processing for the final certificate (the target cert).
@@ -1135,6 +1144,7 @@
}
void PathVerifier::PrepareForNextCertificate(const ParsedCertificate &cert,
+ KeyPurpose key_purpose,
CertErrors *errors) {
// RFC 5280 section 6.1.4 step a-b
VerifyPolicyMappings(cert, errors);
@@ -1238,8 +1248,8 @@
// the certificate. Process any other recognized non-critical
// extension present in the certificate that is relevant to path
// processing.
- VerifyNoUnconsumedCriticalExtensions(cert, errors,
- delegate_->AcceptPreCertificates());
+ VerifyNoUnconsumedCriticalExtensions(
+ cert, errors, delegate_->AcceptPreCertificates(), key_purpose);
}
// Checks if the target certificate has the CA bit set. If it does, add
@@ -1302,7 +1312,8 @@
//
// Note that this is duplicated by PrepareForNextCertificate() so as to
// directly match the procedures in RFC 5280's section 6.1.
- VerifyNoUnconsumedCriticalExtensions(cert, errors, allow_precertificate);
+ VerifyNoUnconsumedCriticalExtensions(cert, errors, allow_precertificate,
+ required_key_purpose);
// This calculates the intersection from RFC 5280 section 6.1.5 step g, as
// well as applying the deferred recursive node that were skipped earlier in
@@ -1404,7 +1415,8 @@
// constraints are enforced, clients MUST reject certification paths
// containing a trust anchor with unrecognized critical extensions.
VerifyNoUnconsumedCriticalExtensions(cert, errors,
- /*allow_precertificate=*/false);
+ /*allow_precertificate=*/false,
+ required_key_purpose);
}
void PathVerifier::ProcessRootCertificate(const ParsedCertificate &cert,
@@ -1507,7 +1519,8 @@
// Checking for unknown critical extensions matches Windows, but is stricter
// than the Mac verifier.
VerifyNoUnconsumedCriticalExtensions(cert, errors,
- /*allow_precertificate=*/false);
+ /*allow_precertificate=*/false,
+ required_key_purpose);
}
bssl::UniquePtr<EVP_PKEY> PathVerifier::ParseAndCheckPublicKey(
@@ -1650,7 +1663,7 @@
return;
}
if (!is_target_cert) {
- PrepareForNextCertificate(cert, cert_errors);
+ PrepareForNextCertificate(cert, required_key_purpose, cert_errors);
} else {
WrapUp(cert, required_key_purpose, user_initial_policy_set,
delegate->AcceptPreCertificates(), cert_errors);