Add support for C2PA validation modes

This permits chains to be validated when the intended
use is to validate a C2PA time stamp signer or C2PA
manifest signer.

As in the RCS MLS case, further C2PA specific checks
for the purpose intended may be needed on the certificate
after verify is run.

Bug: 400963456
Change-Id: I22bfe6b2d3ad3cfd939a2c5a13debb5f0d3ccbb3
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/77487
Commit-Queue: Bob Beck <bbe@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/gen/sources.bzl b/gen/sources.bzl
index c1562e8..f579fa1 100644
--- a/gen/sources.bzl
+++ b/gen/sources.bzl
@@ -2208,6 +2208,18 @@
     "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-any-and-clientauth/serverauth-strict-leaf.test",
     "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-any-and-clientauth/serverauth-strict.test",
     "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-any-and-clientauth/serverauth.test",
+    "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/any.test",
+    "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/c2pamanifest.test",
+    "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/c2patimestamp.test",
+    "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/chain.pem",
+    "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/clientauth.test",
+    "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/serverauth.test",
+    "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/any.test",
+    "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/c2pamanifest.test",
+    "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/c2patimestamp.test",
+    "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/chain.pem",
+    "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/clientauth.test",
+    "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/serverauth.test",
     "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-clientauth/any.test",
     "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-clientauth/chain.pem",
     "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-clientauth/clientauth-strict-leaf.test",
diff --git a/gen/sources.cmake b/gen/sources.cmake
index ae0edd5..7074d39 100644
--- a/gen/sources.cmake
+++ b/gen/sources.cmake
@@ -2252,6 +2252,18 @@
   pki/testdata/verify_certificate_chain_unittest/intermediate-eku-any-and-clientauth/serverauth-strict-leaf.test
   pki/testdata/verify_certificate_chain_unittest/intermediate-eku-any-and-clientauth/serverauth-strict.test
   pki/testdata/verify_certificate_chain_unittest/intermediate-eku-any-and-clientauth/serverauth.test
+  pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/any.test
+  pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/c2pamanifest.test
+  pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/c2patimestamp.test
+  pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/chain.pem
+  pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/clientauth.test
+  pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/serverauth.test
+  pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/any.test
+  pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/c2pamanifest.test
+  pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/c2patimestamp.test
+  pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/chain.pem
+  pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/clientauth.test
+  pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/serverauth.test
   pki/testdata/verify_certificate_chain_unittest/intermediate-eku-clientauth/any.test
   pki/testdata/verify_certificate_chain_unittest/intermediate-eku-clientauth/chain.pem
   pki/testdata/verify_certificate_chain_unittest/intermediate-eku-clientauth/clientauth-strict-leaf.test
diff --git a/gen/sources.gni b/gen/sources.gni
index d6e6d63..e810fa2 100644
--- a/gen/sources.gni
+++ b/gen/sources.gni
@@ -2208,6 +2208,18 @@
   "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-any-and-clientauth/serverauth-strict-leaf.test",
   "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-any-and-clientauth/serverauth-strict.test",
   "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-any-and-clientauth/serverauth.test",
+  "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/any.test",
+  "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/c2pamanifest.test",
+  "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/c2patimestamp.test",
+  "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/chain.pem",
+  "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/clientauth.test",
+  "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/serverauth.test",
+  "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/any.test",
+  "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/c2pamanifest.test",
+  "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/c2patimestamp.test",
+  "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/chain.pem",
+  "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/clientauth.test",
+  "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/serverauth.test",
   "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-clientauth/any.test",
   "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-clientauth/chain.pem",
   "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-clientauth/clientauth-strict-leaf.test",
diff --git a/gen/sources.json b/gen/sources.json
index d17a56d..7a70566 100644
--- a/gen/sources.json
+++ b/gen/sources.json
@@ -2189,6 +2189,18 @@
       "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-any-and-clientauth/serverauth-strict-leaf.test",
       "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-any-and-clientauth/serverauth-strict.test",
       "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-any-and-clientauth/serverauth.test",
+      "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/any.test",
+      "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/c2pamanifest.test",
+      "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/c2patimestamp.test",
+      "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/chain.pem",
+      "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/clientauth.test",
+      "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/serverauth.test",
+      "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/any.test",
+      "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/c2pamanifest.test",
+      "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/c2patimestamp.test",
+      "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/chain.pem",
+      "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/clientauth.test",
+      "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/serverauth.test",
       "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-clientauth/any.test",
       "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-clientauth/chain.pem",
       "pki/testdata/verify_certificate_chain_unittest/intermediate-eku-clientauth/clientauth-strict-leaf.test",
diff --git a/gen/sources.mk b/gen/sources.mk
index bf16d1d..5bb2422 100644
--- a/gen/sources.mk
+++ b/gen/sources.mk
@@ -2187,6 +2187,18 @@
   pki/testdata/verify_certificate_chain_unittest/intermediate-eku-any-and-clientauth/serverauth-strict-leaf.test \
   pki/testdata/verify_certificate_chain_unittest/intermediate-eku-any-and-clientauth/serverauth-strict.test \
   pki/testdata/verify_certificate_chain_unittest/intermediate-eku-any-and-clientauth/serverauth.test \
+  pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/any.test \
+  pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/c2pamanifest.test \
+  pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/c2patimestamp.test \
+  pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/chain.pem \
+  pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/clientauth.test \
+  pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/serverauth.test \
+  pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/any.test \
+  pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/c2pamanifest.test \
+  pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/c2patimestamp.test \
+  pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/chain.pem \
+  pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/clientauth.test \
+  pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/serverauth.test \
   pki/testdata/verify_certificate_chain_unittest/intermediate-eku-clientauth/any.test \
   pki/testdata/verify_certificate_chain_unittest/intermediate-eku-clientauth/chain.pem \
   pki/testdata/verify_certificate_chain_unittest/intermediate-eku-clientauth/clientauth-strict-leaf.test \
diff --git a/include/openssl/pki/verify.h b/include/openssl/pki/verify.h
index 1396e80..d22f693 100644
--- a/include/openssl/pki/verify.h
+++ b/include/openssl/pki/verify.h
@@ -73,6 +73,8 @@
     SERVER_AUTH_STRICT_LEAF,
     CLIENT_AUTH_STRICT_LEAF,
     RCS_MLS_CLIENT_AUTH,
+    C2PA_TIMESTAMPING,
+    C2PA_MANIFEST
   };
 
   CertificateVerifyOptions();
diff --git a/pki/common_cert_errors.cc b/pki/common_cert_errors.cc
index edf7daf..4bb0707 100644
--- a/pki/common_cert_errors.cc
+++ b/pki/common_cert_errors.cc
@@ -76,6 +76,14 @@
 DEFINE_CERT_ERROR_ID(kEkuIncorrectForRcsMlsClient,
                      "The extended key usage does not contain only the "
                      "rcsMlsClient key purpose.");
+DEFINE_CERT_ERROR_ID(kEkuIncorrectForC2PATimeStamping,
+                     "The extended key usage does not contain the time "
+                     "stamping key purpose, or contains prohibited key usages");
+DEFINE_CERT_ERROR_ID(
+    kEkuIncorrectForC2PAManifest,
+    "The extended key usage must contain at least one of: email protection or "
+    "document signing, and must not contain prohibited key usages");
+
 DEFINE_CERT_ERROR_ID(kEkuNotPresent,
                      "Certificate does not have extended key usage");
 DEFINE_CERT_ERROR_ID(kCertIsNotTrustAnchor,
diff --git a/pki/common_cert_errors.h b/pki/common_cert_errors.h
index f8db8df..5bbf412 100644
--- a/pki/common_cert_errors.h
+++ b/pki/common_cert_errors.h
@@ -146,6 +146,12 @@
 // The certificate's EKU is incorrect for an RcsMlsClient.
 OPENSSL_EXPORT extern const CertErrorId kEkuIncorrectForRcsMlsClient;
 
+// The certificate's EKU is incorrect for C2PA Time Stamping
+OPENSSL_EXPORT extern const CertErrorId kEkuIncorrectForC2PATimeStamping;
+
+// The certificate's EKU is incorrect for C2PA Manifest Signing
+OPENSSL_EXPORT extern const CertErrorId kEkuIncorrectForC2PAManifest;
+
 // The certificate does not have EKU.
 OPENSSL_EXPORT extern const CertErrorId kEkuNotPresent;
 
diff --git a/pki/extended_key_usage.h b/pki/extended_key_usage.h
index c0915fd..fb10c77 100644
--- a/pki/extended_key_usage.h
+++ b/pki/extended_key_usage.h
@@ -78,6 +78,12 @@
 inline constexpr uint8_t kOCSPSigning[] = {0x2b, 0x06, 0x01, 0x05,
                                            0x05, 0x07, 0x03, 0x09};
 
+// From RFC 9336 section 3.1:
+// id-kp-documentSigning  OBJECT IDENTIFIER  ::=  { id-kp 36 }
+// In dotted notation: 1.3.6.1.5.5.7.3.36
+inline constexpr uint8_t kDocumentSigning[] = {0x2b, 0x06, 0x01, 0x05,
+                                               0x05, 0x07, 0x03, 0x24};
+
 // 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)}
diff --git a/pki/test_helpers.cc b/pki/test_helpers.cc
index a66b520..d408ba5 100644
--- a/pki/test_helpers.cc
+++ b/pki/test_helpers.cc
@@ -321,6 +321,10 @@
         test->key_purpose = KeyPurpose::CLIENT_AUTH_STRICT_LEAF;
       } else if (value == "MLS_CLIENT_AUTH") {
         test->key_purpose = KeyPurpose::RCS_MLS_CLIENT_AUTH;
+      } else if (value == "C2PA_TIMESTAMPING") {
+        test->key_purpose = KeyPurpose::C2PA_TIMESTAMPING;
+      } else if (value == "C2PA_MANIFEST") {
+        test->key_purpose = KeyPurpose::C2PA_MANIFEST;
       } else {
         ADD_FAILURE() << "Unrecognized key_purpose: " << value;
         return false;
diff --git a/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/any.test b/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/any.test
new file mode 100644
index 0000000..4d092a9
--- /dev/null
+++ b/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/any.test
@@ -0,0 +1,5 @@
+chain: chain.pem
+last_cert_trust: TRUSTED_ANCHOR
+utc_time: DEFAULT
+key_purpose: ANY_EKU
+expected_errors:
diff --git a/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/c2pamanifest.test b/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/c2pamanifest.test
new file mode 100644
index 0000000..ffae69c
--- /dev/null
+++ b/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/c2pamanifest.test
@@ -0,0 +1,5 @@
+chain: chain.pem
+last_cert_trust: TRUSTED_ANCHOR
+utc_time: DEFAULT
+key_purpose: C2PA_MANIFEST
+expected_errors:
diff --git a/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/c2patimestamp.test b/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/c2patimestamp.test
new file mode 100644
index 0000000..c587a36
--- /dev/null
+++ b/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/c2patimestamp.test
@@ -0,0 +1,8 @@
+chain: chain.pem
+last_cert_trust: TRUSTED_ANCHOR
+utc_time: DEFAULT
+key_purpose: C2PA_TIMESTAMPING
+expected_errors:
+----- Certificate i=0 (CN=C2PA Cert Leaf) -----
+ERROR: The extended key usage does not contain the time stamping key purpose, or contains prohibited key usages
+
diff --git a/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/chain.pem b/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/chain.pem
new file mode 100644
index 0000000..321652c
--- /dev/null
+++ b/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/chain.pem
@@ -0,0 +1,32 @@
+-----BEGIN CERTIFICATE-----
+MIIBlDCCATqgAwIBAgIBAzAKBggqhkjOPQQDAjAhMR8wHQYDVQQDExZDMlBBIENl
+cnQgSW50ZXJtZWRpYXRlMCIYDzAwMDAwMTAxMDAwMDAwWhgPOTk5OTEyMzEyMzU5
+NTlaMBkxFzAVBgNVBAMTDkMyUEEgQ2VydCBMZWFmMFkwEwYHKoZIzj0CAQYIKoZI
+zj0DAQcDQgAEkSrYvFVtkZJmvirfY0JDDYrZQrNJecPLt0ksJux2URL5nAQiQY1S
+ERGnEaiNLpoc0dleTS8wQT/cjw/wPgoeV6NnMGUwDgYDVR0PAQH/BAQDAgeAMB0G
+A1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDBDAMBgNVHRMBAf8EAjAAMA0GA1Ud
+DgQGBARsZWFmMBcGA1UdIwQQMA6ADGludGVybWVkaWF0ZTAKBggqhkjOPQQDAgNI
+ADBFAiAqKEacSfBZeF/TXI04niDtv5S+UJ7/TVFAwnC8xH75uwIhAJSQsLXs+a3O
+NkoRHJKGsnuJ8Py2hMfY7r6h95b4p5A3
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIBijCCATGgAwIBAgIBAjAKBggqhkjOPQQDAjAZMRcwFQYDVQQDEw5DMlBBIENl
+cnQgUm9vdDAiGA8wMDAwMDEwMTAwMDAwMFoYDzk5OTkxMjMxMjM1OTU5WjAhMR8w
+HQYDVQQDExZDMlBBIENlcnQgSW50ZXJtZWRpYXRlMFkwEwYHKoZIzj0CAQYIKoZI
+zj0DAQcDQgAE4jp8qIzeMUuQvICfzxy+XDhLDG7KBGOik/cUUEofJRC+HdVW9+pK
+lIJKannRCJqNDoODsJOlVlVUf5uC5PslMKNeMFwwDgYDVR0PAQH/BAQDAgIEMBEG
+A1UdJQQKMAgGBmeBEgIBAzAPBgNVHRMBAf8EBTADAQH/MBUGA1UdDgQOBAxpbnRl
+cm1lZGlhdGUwDwYDVR0jBAgwBoAEcm9vdDAKBggqhkjOPQQDAgNHADBEAiBIGr9B
+G10qB/vTMMruFZWBwzUFM7AnP9tlpqKyRorXJwIgEQDzXE5xS+q9PiRjtnD5NA2k
+2tBHjho1duXcd1EsKdc=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIBVTCB/aADAgECAgEBMAoGCCqGSM49BAMCMBkxFzAVBgNVBAMTDkMyUEEgQ2Vy
+dCBSb290MCIYDzAwMDAwMTAxMDAwMDAwWhgPOTk5OTEyMzEyMzU5NTlaMBkxFzAV
+BgNVBAMTDkMyUEEgQ2VydCBSb290MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE
+Jnal2JdRr2O8t43E0yujFVyEK+OE6goUWCOiHlfbSAeoyLDmPkKJdW5PMf+wORRj
+p1FhVSxADxD0icD53Y6JbaMyMDAwDgYDVR0PAQH/BAQDAgIEMA8GA1UdEwEB/wQF
+MAMBAf8wDQYDVR0OBAYEBHJvb3QwCgYIKoZIzj0EAwIDRwAwRAIgcB1lb5KE/GOi
+RTy4bcRudwGKQ65BMBPn2AVGPnmrJ9wCIGwrE1m36y5EkgDHujNcnQD+x6o6gqj3
+WOZICZjXCDKx
+-----END CERTIFICATE-----
diff --git a/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/clientauth.test b/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/clientauth.test
new file mode 100644
index 0000000..b46cc5a
--- /dev/null
+++ b/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/clientauth.test
@@ -0,0 +1,11 @@
+chain: chain.pem
+last_cert_trust: TRUSTED_ANCHOR
+utc_time: DEFAULT
+key_purpose: CLIENT_AUTH
+expected_errors:
+----- Certificate i=0 (CN=C2PA Cert Leaf) -----
+ERROR: The extended key usage does not include client auth
+
+----- Certificate i=1 (CN=C2PA Cert Intermediate) -----
+ERROR: The extended key usage does not include client auth
+
diff --git a/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/make-chain.go b/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/make-chain.go
new file mode 100644
index 0000000..e964425
--- /dev/null
+++ b/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/make-chain.go
@@ -0,0 +1,170 @@
+// Copyright 2025 The BoringSSL Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//go:build ignore
+
+package main
+
+import (
+	"crypto/ecdsa"
+	"crypto/rand"
+	"crypto/x509"
+	"crypto/x509/pkix"
+	"encoding/asn1"
+	"encoding/pem"
+	"math/big"
+	"os"
+	"time"
+)
+
+var leafKey, intermediateKey, rootKey *ecdsa.PrivateKey
+
+func init() {
+	leafKey = mustParseECDSAKey(leafKeyPEM)
+	intermediateKey = mustParseECDSAKey(intermediateKeyPEM)
+	rootKey = mustParseECDSAKey(rootKeyPEM)
+}
+
+type templateAndKey struct {
+	template x509.Certificate
+	key      *ecdsa.PrivateKey
+}
+
+func mustStartChain(path string, subject, issuer *templateAndKey) []byte {
+	cert, err := x509.CreateCertificate(rand.Reader, &subject.template, &issuer.template, &subject.key.PublicKey, issuer.key)
+	if err != nil {
+		panic(err)
+	}
+	file, err := os.OpenFile(path, os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0644)
+	if err != nil {
+		panic(err)
+	}
+	defer file.Close()
+	err = pem.Encode(file, &pem.Block{Type: "CERTIFICATE", Bytes: cert})
+	if err != nil {
+		panic(err)
+	}
+	file.Close()
+	return cert
+}
+
+func mustAppendToChain(path string, subject, issuer *templateAndKey) []byte {
+	cert, err := x509.CreateCertificate(rand.Reader, &subject.template, &issuer.template, &subject.key.PublicKey, issuer.key)
+	if err != nil {
+		panic(err)
+	}
+	file, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY, 0644)
+	if err != nil {
+		panic(err)
+	}
+	defer file.Close()
+	err = pem.Encode(file, &pem.Block{Type: "CERTIFICATE", Bytes: cert})
+	if err != nil {
+		panic(err)
+	}
+	file.Close()
+	return cert
+}
+
+func main() {
+	notBefore, err := time.Parse(time.RFC3339, "0000-01-01T00:00:00Z")
+	if err != nil {
+		panic(err)
+	}
+	notAfter, err := time.Parse(time.RFC3339, "9999-12-31T23:59:59Z")
+	if err != nil {
+		panic(err)
+	}
+
+	root := templateAndKey{
+		template: x509.Certificate{
+			SerialNumber:          new(big.Int).SetInt64(1),
+			Subject:               pkix.Name{CommonName: "C2PA Cert Root"},
+			NotBefore:             notBefore,
+			NotAfter:              notAfter,
+			BasicConstraintsValid: true,
+			IsCA:                  true,
+			KeyUsage:              x509.KeyUsageCertSign,
+			SignatureAlgorithm:    x509.ECDSAWithSHA256,
+			SubjectKeyId:          []byte("root"),
+		},
+		key: rootKey,
+	}
+	intermediate := templateAndKey{
+		template: x509.Certificate{
+			SerialNumber:          new(big.Int).SetInt64(2),
+			Subject:               pkix.Name{CommonName: "C2PA Cert Intermediate"},
+			NotBefore:             notBefore,
+			NotAfter:              notAfter,
+			BasicConstraintsValid: true,
+			IsCA:                  true,
+			KeyUsage:              x509.KeyUsageCertSign,
+			SignatureAlgorithm:    x509.ECDSAWithSHA256,
+			SubjectKeyId:          []byte("intermediate"),
+			UnknownExtKeyUsage:    []asn1.ObjectIdentifier{[]int{2, 23, 146, 2, 1, 3}},
+		},
+		key: intermediateKey,
+	}
+
+	leaf := templateAndKey{
+		template: x509.Certificate{
+			SerialNumber:          new(big.Int).SetInt64(3),
+			Subject:               pkix.Name{CommonName: "C2PA Cert Leaf"},
+			NotBefore:             notBefore,
+			NotAfter:              notAfter,
+			BasicConstraintsValid: true,
+			IsCA:                  false,
+			KeyUsage:              x509.KeyUsageDigitalSignature,
+			ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageEmailProtection},
+			SignatureAlgorithm:    x509.ECDSAWithSHA256,
+			SubjectKeyId:          []byte("leaf"),
+		},
+		key: leafKey,
+	}
+
+	// Generate a valid certificate chain from the templates.
+	mustStartChain("chain.pem", &leaf, &intermediate)
+	mustAppendToChain("chain.pem", &intermediate, &root)
+	mustAppendToChain("chain.pem", &root, &root)
+}
+
+const leafKeyPEM = `-----BEGIN PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgoPUXNXuH9mgiS/nk
+024SYxryxMa3CyGJldiHymLxSquhRANCAASRKti8VW2Rkma+Kt9jQkMNitlCs0l5
+w8u3SSwm7HZREvmcBCJBjVIREacRqI0umhzR2V5NLzBBP9yPD/A+Ch5X
+-----END PRIVATE KEY-----`
+
+const intermediateKeyPEM = `-----BEGIN PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgWHKCKgY058ahE3t6
+vpxVQgzlycgCVMogwjK0y3XMNfWhRANCAATiOnyojN4xS5C8gJ/PHL5cOEsMbsoE
+Y6KT9xRQSh8lEL4d1Vb36kqUgkpqedEImo0Og4Owk6VWVVR/m4Lk+yUw
+-----END PRIVATE KEY-----`
+
+const rootKeyPEM = `-----BEGIN PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgBwND/eHytW0I417J
+Hr+qcPlp5N1jM3ACXys57bPujg+hRANCAAQmdqXYl1GvY7y3jcTTK6MVXIQr44Tq
+ChRYI6IeV9tIB6jIsOY+Qol1bk8x/7A5FGOnUWFVLEAPEPSJwPndjolt
+-----END PRIVATE KEY-----`
+
+func mustParseECDSAKey(in string) *ecdsa.PrivateKey {
+	keyBlock, _ := pem.Decode([]byte(in))
+	if keyBlock == nil || keyBlock.Type != "PRIVATE KEY" {
+		panic("could not decode private key")
+	}
+	key, err := x509.ParsePKCS8PrivateKey(keyBlock.Bytes)
+	if err != nil {
+		panic(err)
+	}
+	return key.(*ecdsa.PrivateKey)
+}
diff --git a/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/serverauth.test b/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/serverauth.test
new file mode 100644
index 0000000..ee67119
--- /dev/null
+++ b/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2pamanifest/serverauth.test
@@ -0,0 +1,8 @@
+chain: chain.pem
+last_cert_trust: TRUSTED_ANCHOR
+utc_time: DEFAULT
+key_purpose: SERVER_AUTH
+expected_errors:
+----- Certificate i=1 (CN=C2PA Cert Intermediate) -----
+ERROR: The extended key usage does not include server auth
+
diff --git a/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/any.test b/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/any.test
new file mode 100644
index 0000000..4d092a9
--- /dev/null
+++ b/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/any.test
@@ -0,0 +1,5 @@
+chain: chain.pem
+last_cert_trust: TRUSTED_ANCHOR
+utc_time: DEFAULT
+key_purpose: ANY_EKU
+expected_errors:
diff --git a/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/c2pamanifest.test b/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/c2pamanifest.test
new file mode 100644
index 0000000..e48463c
--- /dev/null
+++ b/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/c2pamanifest.test
@@ -0,0 +1,8 @@
+chain: chain.pem
+last_cert_trust: TRUSTED_ANCHOR
+utc_time: DEFAULT
+key_purpose: C2PA_MANIFEST
+expected_errors:
+----- Certificate i=0 (CN=C2PA Cert Leaf) -----
+ERROR: The extended key usage must contain at least one of: email protection or document signing, and must not contain prohibited key usages
+
diff --git a/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/c2patimestamp.test b/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/c2patimestamp.test
new file mode 100644
index 0000000..b6c6911
--- /dev/null
+++ b/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/c2patimestamp.test
@@ -0,0 +1,5 @@
+chain: chain.pem
+last_cert_trust: TRUSTED_ANCHOR
+utc_time: DEFAULT
+key_purpose: C2PA_TIMESTAMPING
+expected_errors:
diff --git a/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/chain.pem b/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/chain.pem
new file mode 100644
index 0000000..8df49ce
--- /dev/null
+++ b/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/chain.pem
@@ -0,0 +1,32 @@
+-----BEGIN CERTIFICATE-----
+MIIBgzCCASqgAwIBAgIBAzAKBggqhkjOPQQDAjAhMR8wHQYDVQQDExZDMlBBIENl
+cnQgSW50ZXJtZWRpYXRlMCIYDzAwMDAwMTAxMDAwMDAwWhgPOTk5OTEyMzEyMzU5
+NTlaMBkxFzAVBgNVBAMTDkMyUEEgQ2VydCBMZWFmMFkwEwYHKoZIzj0CAQYIKoZI
+zj0DAQcDQgAEkSrYvFVtkZJmvirfY0JDDYrZQrNJecPLt0ksJux2URL5nAQiQY1S
+ERGnEaiNLpoc0dleTS8wQT/cjw/wPgoeV6NXMFUwHQYDVR0lBBYwFAYIKwYBBQUH
+AwEGCCsGAQUFBwMIMAwGA1UdEwEB/wQCMAAwDQYDVR0OBAYEBGxlYWYwFwYDVR0j
+BBAwDoAMaW50ZXJtZWRpYXRlMAoGCCqGSM49BAMCA0cAMEQCIDfZSkZ9qfE9DtRb
+lU5IOLhh8xpEcwoaQWRUS2UIIAD5AiAFbPG4214Y2skTsSVm58ynjbjam/lO36V0
+C5YREd86tw==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIBizCCATGgAwIBAgIBAjAKBggqhkjOPQQDAjAZMRcwFQYDVQQDEw5DMlBBIENl
+cnQgUm9vdDAiGA8wMDAwMDEwMTAwMDAwMFoYDzk5OTkxMjMxMjM1OTU5WjAhMR8w
+HQYDVQQDExZDMlBBIENlcnQgSW50ZXJtZWRpYXRlMFkwEwYHKoZIzj0CAQYIKoZI
+zj0DAQcDQgAE4jp8qIzeMUuQvICfzxy+XDhLDG7KBGOik/cUUEofJRC+HdVW9+pK
+lIJKannRCJqNDoODsJOlVlVUf5uC5PslMKNeMFwwDgYDVR0PAQH/BAQDAgIEMBEG
+A1UdJQQKMAgGBmeBEgIBAzAPBgNVHRMBAf8EBTADAQH/MBUGA1UdDgQOBAxpbnRl
+cm1lZGlhdGUwDwYDVR0jBAgwBoAEcm9vdDAKBggqhkjOPQQDAgNIADBFAiEA+M4D
+YTyTHKD6Ism47OXmG52xO0kNCiFSmZ69/6zNW+QCIEPo5nZgHp8QvYN8wMVMKtbt
+dAKAnjgW98HoANRg8zI5
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIBVjCB/aADAgECAgEBMAoGCCqGSM49BAMCMBkxFzAVBgNVBAMTDkMyUEEgQ2Vy
+dCBSb290MCIYDzAwMDAwMTAxMDAwMDAwWhgPOTk5OTEyMzEyMzU5NTlaMBkxFzAV
+BgNVBAMTDkMyUEEgQ2VydCBSb290MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE
+Jnal2JdRr2O8t43E0yujFVyEK+OE6goUWCOiHlfbSAeoyLDmPkKJdW5PMf+wORRj
+p1FhVSxADxD0icD53Y6JbaMyMDAwDgYDVR0PAQH/BAQDAgIEMA8GA1UdEwEB/wQF
+MAMBAf8wDQYDVR0OBAYEBHJvb3QwCgYIKoZIzj0EAwIDSAAwRQIhAIfA7n5sX+4L
+S7/98FuSque5GpwHoELRrQdDxvqzb97oAiBugKrTQN76A+/hvoHQmylV07xnq0v2
+Jn77ADgWGozs8Q==
+-----END CERTIFICATE-----
diff --git a/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/clientauth.test b/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/clientauth.test
new file mode 100644
index 0000000..b5b9e03
--- /dev/null
+++ b/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/clientauth.test
@@ -0,0 +1,12 @@
+chain: chain.pem
+last_cert_trust: TRUSTED_ANCHOR
+utc_time: DEFAULT
+key_purpose: CLIENT_AUTH
+expected_errors:
+----- Certificate i=0 (CN=C2PA Cert Leaf) -----
+WARNING: The extended key usage includes time stamping which is not permitted for this use
+ERROR: The extended key usage does not include client auth
+
+----- Certificate i=1 (CN=C2PA Cert Intermediate) -----
+ERROR: The extended key usage does not include client auth
+
diff --git a/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/make-chain.go b/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/make-chain.go
new file mode 100644
index 0000000..fd37ad5
--- /dev/null
+++ b/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/make-chain.go
@@ -0,0 +1,169 @@
+// Copyright 2025 The BoringSSL Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//go:build ignore
+
+package main
+
+import (
+	"crypto/ecdsa"
+	"crypto/rand"
+	"crypto/x509"
+	"crypto/x509/pkix"
+	"encoding/asn1"
+	"encoding/pem"
+	"math/big"
+	"os"
+	"time"
+)
+
+var leafKey, intermediateKey, rootKey *ecdsa.PrivateKey
+
+func init() {
+	leafKey = mustParseECDSAKey(leafKeyPEM)
+	intermediateKey = mustParseECDSAKey(intermediateKeyPEM)
+	rootKey = mustParseECDSAKey(rootKeyPEM)
+}
+
+type templateAndKey struct {
+	template x509.Certificate
+	key      *ecdsa.PrivateKey
+}
+
+func mustStartChain(path string, subject, issuer *templateAndKey) []byte {
+	cert, err := x509.CreateCertificate(rand.Reader, &subject.template, &issuer.template, &subject.key.PublicKey, issuer.key)
+	if err != nil {
+		panic(err)
+	}
+	file, err := os.OpenFile(path, os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0644)
+	if err != nil {
+		panic(err)
+	}
+	defer file.Close()
+	err = pem.Encode(file, &pem.Block{Type: "CERTIFICATE", Bytes: cert})
+	if err != nil {
+		panic(err)
+	}
+	file.Close()
+	return cert
+}
+
+func mustAppendToChain(path string, subject, issuer *templateAndKey) []byte {
+	cert, err := x509.CreateCertificate(rand.Reader, &subject.template, &issuer.template, &subject.key.PublicKey, issuer.key)
+	if err != nil {
+		panic(err)
+	}
+	file, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY, 0644)
+	if err != nil {
+		panic(err)
+	}
+	defer file.Close()
+	err = pem.Encode(file, &pem.Block{Type: "CERTIFICATE", Bytes: cert})
+	if err != nil {
+		panic(err)
+	}
+	file.Close()
+	return cert
+}
+
+func main() {
+	notBefore, err := time.Parse(time.RFC3339, "0000-01-01T00:00:00Z")
+	if err != nil {
+		panic(err)
+	}
+	notAfter, err := time.Parse(time.RFC3339, "9999-12-31T23:59:59Z")
+	if err != nil {
+		panic(err)
+	}
+
+	root := templateAndKey{
+		template: x509.Certificate{
+			SerialNumber:          new(big.Int).SetInt64(1),
+			Subject:               pkix.Name{CommonName: "C2PA Cert Root"},
+			NotBefore:             notBefore,
+			NotAfter:              notAfter,
+			BasicConstraintsValid: true,
+			IsCA:                  true,
+			KeyUsage:              x509.KeyUsageCertSign,
+			SignatureAlgorithm:    x509.ECDSAWithSHA256,
+			SubjectKeyId:          []byte("root"),
+		},
+		key: rootKey,
+	}
+	intermediate := templateAndKey{
+		template: x509.Certificate{
+			SerialNumber:          new(big.Int).SetInt64(2),
+			Subject:               pkix.Name{CommonName: "C2PA Cert Intermediate"},
+			NotBefore:             notBefore,
+			NotAfter:              notAfter,
+			BasicConstraintsValid: true,
+			IsCA:                  true,
+			KeyUsage:              x509.KeyUsageCertSign,
+			SignatureAlgorithm:    x509.ECDSAWithSHA256,
+			SubjectKeyId:          []byte("intermediate"),
+			UnknownExtKeyUsage:    []asn1.ObjectIdentifier{[]int{2, 23, 146, 2, 1, 3}},
+		},
+		key: intermediateKey,
+	}
+
+	leaf := templateAndKey{
+		template: x509.Certificate{
+			SerialNumber:          new(big.Int).SetInt64(3),
+			Subject:               pkix.Name{CommonName: "C2PA Cert Leaf"},
+			NotBefore:             notBefore,
+			NotAfter:              notAfter,
+			BasicConstraintsValid: true,
+			IsCA:                  false,
+			ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageTimeStamping},
+			SignatureAlgorithm:    x509.ECDSAWithSHA256,
+			SubjectKeyId:          []byte("leaf"),
+		},
+		key: leafKey,
+	}
+
+	// Generate a valid certificate chain from the templates.
+	mustStartChain("chain.pem", &leaf, &intermediate)
+	mustAppendToChain("chain.pem", &intermediate, &root)
+	mustAppendToChain("chain.pem", &root, &root)
+}
+
+const leafKeyPEM = `-----BEGIN PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgoPUXNXuH9mgiS/nk
+024SYxryxMa3CyGJldiHymLxSquhRANCAASRKti8VW2Rkma+Kt9jQkMNitlCs0l5
+w8u3SSwm7HZREvmcBCJBjVIREacRqI0umhzR2V5NLzBBP9yPD/A+Ch5X
+-----END PRIVATE KEY-----`
+
+const intermediateKeyPEM = `-----BEGIN PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgWHKCKgY058ahE3t6
+vpxVQgzlycgCVMogwjK0y3XMNfWhRANCAATiOnyojN4xS5C8gJ/PHL5cOEsMbsoE
+Y6KT9xRQSh8lEL4d1Vb36kqUgkpqedEImo0Og4Owk6VWVVR/m4Lk+yUw
+-----END PRIVATE KEY-----`
+
+const rootKeyPEM = `-----BEGIN PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgBwND/eHytW0I417J
+Hr+qcPlp5N1jM3ACXys57bPujg+hRANCAAQmdqXYl1GvY7y3jcTTK6MVXIQr44Tq
+ChRYI6IeV9tIB6jIsOY+Qol1bk8x/7A5FGOnUWFVLEAPEPSJwPndjolt
+-----END PRIVATE KEY-----`
+
+func mustParseECDSAKey(in string) *ecdsa.PrivateKey {
+	keyBlock, _ := pem.Decode([]byte(in))
+	if keyBlock == nil || keyBlock.Type != "PRIVATE KEY" {
+		panic("could not decode private key")
+	}
+	key, err := x509.ParsePKCS8PrivateKey(keyBlock.Bytes)
+	if err != nil {
+		panic(err)
+	}
+	return key.(*ecdsa.PrivateKey)
+}
diff --git a/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/serverauth.test b/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/serverauth.test
new file mode 100644
index 0000000..50b77fd
--- /dev/null
+++ b/pki/testdata/verify_certificate_chain_unittest/intermediate-eku-c2patimestamping/serverauth.test
@@ -0,0 +1,11 @@
+chain: chain.pem
+last_cert_trust: TRUSTED_ANCHOR
+utc_time: DEFAULT
+key_purpose: SERVER_AUTH
+expected_errors:
+----- Certificate i=0 (CN=C2PA Cert Leaf) -----
+WARNING: The extended key usage includes time stamping which is not permitted for this use
+
+----- Certificate i=1 (CN=C2PA Cert Intermediate) -----
+ERROR: The extended key usage does not include server auth
+
diff --git a/pki/verify.cc b/pki/verify.cc
index 9acf43a..c0ed8ad 100644
--- a/pki/verify.cc
+++ b/pki/verify.cc
@@ -264,6 +264,12 @@
     case CertificateVerifyOptions::KeyPurpose::RCS_MLS_CLIENT_AUTH:
       key_purpose = KeyPurpose::RCS_MLS_CLIENT_AUTH;
       break;
+    case CertificateVerifyOptions::KeyPurpose::C2PA_TIMESTAMPING:
+      key_purpose = KeyPurpose::C2PA_TIMESTAMPING;
+      break;
+    case CertificateVerifyOptions::KeyPurpose::C2PA_MANIFEST:
+      key_purpose = KeyPurpose::C2PA_MANIFEST;
+      break;
   }
   CertPathBuilder path_builder(leaf_cert, trust_store, &path_builder_delegate,
                                verification_time, key_purpose,
diff --git a/pki/verify_certificate_chain.cc b/pki/verify_certificate_chain.cc
index d94c3cf..9bbad95 100644
--- a/pki/verify_certificate_chain.cc
+++ b/pki/verify_certificate_chain.cc
@@ -226,6 +226,8 @@
   bool has_time_stamping_eku = false;
   bool has_ocsp_signing_eku = false;
   bool has_rcs_mls_client_eku = false;
+  bool has_document_signing_eku = false;
+  bool has_email_protection_eku = false;
   size_t eku_oid_count = 0;
   if (cert.has_extended_key_usage()) {
     for (const auto &key_purpose_oid : cert.extended_key_usage()) {
@@ -251,6 +253,12 @@
       if (key_purpose_oid == der::Input(kRcsMlsClient)) {
         has_rcs_mls_client_eku = true;
       }
+      if (key_purpose_oid == der::Input(kEmailProtection)) {
+        has_email_protection_eku = true;
+      }
+      if (key_purpose_oid == der::Input(kDocumentSigning)) {
+        has_document_signing_eku = true;
+      }
     }
   }
 
@@ -276,6 +284,46 @@
     return;
   }
 
+  if (required_key_purpose == KeyPurpose::C2PA_TIMESTAMPING) {
+    // https://c2pa.org/specifications/specifications/2.1/specs/C2PA_Specification.html#_certificate_profiles
+    // For time stamp signing, C2PA requires that the leaf:
+    // 1) must have EKU
+    // 2) must not have EKU ANY, OCSP signing, document signing, or email protection
+    // 3) must have time stamping
+    // 4) should tolerate other EKU's being present.
+    if (is_target_cert) {
+      if (!cert.has_extended_key_usage()) {
+        errors->AddError(cert_errors::kEkuNotPresent);
+      }
+      if (has_any_eku || has_ocsp_signing_eku || has_document_signing_eku ||
+          has_email_protection_eku || !has_time_stamping_eku) {
+        errors->AddError(cert_errors::kEkuIncorrectForC2PATimeStamping);
+      }
+    }
+    return;
+  }
+
+  if (required_key_purpose == KeyPurpose::C2PA_MANIFEST) {
+    // https://c2pa.org/specifications/specifications/2.1/specs/C2PA_Specification.html#_certificate_profiles
+    // For manifest signing, C2PA requires that the leaf:
+    // 1) must have EKU
+    // 2) must not have EKU ANY, time stamping, or OCSP signing
+    // 3) should have document signing and/or email protection
+    // 4) should tolerate other EKU's being present.
+    if (is_target_cert) {
+      if (!cert.has_extended_key_usage()) {
+        errors->AddError(cert_errors::kEkuNotPresent);
+      }
+      if (!cert.has_key_usage() ||
+          !cert.key_usage().AssertsBit(KEY_USAGE_BIT_DIGITAL_SIGNATURE) ||
+          has_any_eku || has_ocsp_signing_eku || has_time_stamping_eku ||
+          (!has_email_protection_eku && !has_document_signing_eku)) {
+        errors->AddError(cert_errors::kEkuIncorrectForC2PAManifest);
+      }
+    }
+    return;
+  }
+
   // Rules TLS client and server authentication variants.
 
   // Apply strict only to leaf certificates in these cases.
@@ -369,6 +417,8 @@
     case KeyPurpose::CLIENT_AUTH_STRICT_LEAF:
     case KeyPurpose::SERVER_AUTH_STRICT_LEAF:
     case KeyPurpose::RCS_MLS_CLIENT_AUTH:
+    case KeyPurpose::C2PA_TIMESTAMPING:
+    case KeyPurpose::C2PA_MANIFEST:
       assert(0);  // NOTREACHED
       return;
     case KeyPurpose::SERVER_AUTH:
@@ -1274,6 +1324,8 @@
       case KeyPurpose::CLIENT_AUTH_STRICT_LEAF:
       case KeyPurpose::SERVER_AUTH_STRICT_LEAF:
       case KeyPurpose::RCS_MLS_CLIENT_AUTH:
+      case KeyPurpose::C2PA_TIMESTAMPING:
+      case KeyPurpose::C2PA_MANIFEST:
         errors->AddError(cert_errors::kTargetCertShouldNotBeCa);
         break;
     }
diff --git a/pki/verify_certificate_chain.h b/pki/verify_certificate_chain.h
index f37bc5c..16b2aba 100644
--- a/pki/verify_certificate_chain.h
+++ b/pki/verify_certificate_chain.h
@@ -45,6 +45,8 @@
                        // certificate.
   CLIENT_AUTH_STRICT_LEAF,  // Same as above, but only for leaf cert.
   RCS_MLS_CLIENT_AUTH,      // Client auth for RCS-MLS.
+  C2PA_TIMESTAMPING,    // Leaf can sign timestamps for C2PA.
+  C2PA_MANIFEST,        // Leaf can sign manifests for C2PA.
 };
 
 enum class InitialExplicitPolicy {
diff --git a/pki/verify_certificate_chain_typed_unittest.h b/pki/verify_certificate_chain_typed_unittest.h
index f62a4b9..aaf96e6 100644
--- a/pki/verify_certificate_chain_typed_unittest.h
+++ b/pki/verify_certificate_chain_typed_unittest.h
@@ -190,6 +190,18 @@
   this->RunTest("intermediate-eku-mlsclientauth/mlsclientauth.test");
   this->RunTest("intermediate-eku-mlsclientauth-extra/any.test");
   this->RunTest("intermediate-eku-mlsclientauth-extra/mlsclientauth.test");
+  this->RunTest("intermediate-eku-mlsclientauth-extra/any.test");
+  this->RunTest("intermediate-eku-mlsclientauth-extra/mlsclientauth.test");
+  this->RunTest("intermediate-eku-c2patimestamping/any.test");
+  this->RunTest("intermediate-eku-c2patimestamping/serverauth.test");
+  this->RunTest("intermediate-eku-c2patimestamping/clientauth.test");
+  this->RunTest("intermediate-eku-c2patimestamping/c2patimestamp.test");
+  this->RunTest("intermediate-eku-c2patimestamping/c2pamanifest.test");
+  this->RunTest("intermediate-eku-c2pamanifest/any.test");
+  this->RunTest("intermediate-eku-c2pamanifest/serverauth.test");
+  this->RunTest("intermediate-eku-c2pamanifest/clientauth.test");
+  this->RunTest("intermediate-eku-c2pamanifest/c2patimestamp.test");
+  this->RunTest("intermediate-eku-c2pamanifest/c2pamanifest.test");
   this->RunTest("intermediate-eku-any-and-clientauth/any.test");
   this->RunTest("intermediate-eku-any-and-clientauth/serverauth.test");
   this->RunTest("intermediate-eku-any-and-clientauth/serverauth-strict.test");