S2PA — the Simple Standard for Provenance and Authenticity — extends best-in-class media signing to support the needs of decentralized identity systems. It adds just two things to C2PA: signing with the secp256k1 curve, and a recipe for generating the X.509 certificate C2PA expects directly from a decentralized identifier.
The Coalition for Content Provenance and Authenticity (C2PA) is a media-industry project for embedded provenance metadata. A C2PA manifest is embedded directly in the asset and signed with a private key whose X.509 certificate travels alongside the signature ([[c2pa]]).
C2PA is fundamentally a good idea carrying some dated
assumptions. The embedded JUMBF manifest and the signature
format are solid; the weak link is where trust comes from. A
standard C2PA verifier chains the embedded certificate up to a
configured certificate authority on a bespoke "trust list". That
is the wrong shape for the decentralized web, where we have
"user-generated authority, enabled by self-certifying web
protocols" ([[self-certifying]]). Identities here are
decentralized identifiers ([[did-core]]): the public key behind
one comes either from the identifier itself — a
did:key ([[did-key]]) is a public key — or
from resolving it, such as with
did:web ([[did-web]]). There is no certificate
authority to chain to.
S2PA is a narrow extension that makes C2PA work in that world. It is only two things:
The signing change is small. In COSE terms it is ES256K — ECDSA
over the secp256k1 curve with SHA-256, COSE algorithm
-47 ([[rfc8812]]) — and the signature is the
64-byte r ‖ s P1363 form C2PA already uses for its
other ECDSA algorithms, not ASN.1 DER. Concretely, S2PA adds the
secp256k1 curve to the two places the C2PA specification
enumerates allowed algorithms and key types:
§13.2.1. Signature Algorithms
ECDSA requires elliptic curve keys on the P-256, K-256, P-384, or P-521 elliptic curves.
§14.5.1.1.
If the algorithm field of the certificate'ssubjectPublicKeyInfoisid-ecPublicKey, the parameters field shall be one of the following named curves:prime256v1,secp256k1,secp384r1,secp521r1.
Signing implementations are encouraged to normalize to the low-S form of the signature for maximal compatibility with secp256k1 verifiers.
C2PA's manifest format embeds an X.509 leaf certificate alongside the signature. S2PA's leaf is a self-signed, self-issued X.509 v3 certificate whose only meaningful payload is the public key. Every field is either fixed by this specification, derived from the public key, or chosen freely by the signer; no field needs to be trusted by a verifier.
Use the following steps to
build an S2PA leaf certificate from a 65-byte
uncompressed SEC1 secp256k1 public key pubkey (0x04 || X || Y) and its corresponding private key:
did:key ([[did-key]]) of pubkey — a
DID that is itself an encoding of the public key, so
verification needs no resolution (see § Identity Binding).
subjectKeyIdentifier "method 1" of [[rfc5280]]
§ 4.2.1.2.
2 (X.509 v3).
ecdsa-with-SHA256
(1.2.840.10045.4.3.2).
commonName attribute set to
did. Other DN attributes (e.g.
organizationName) are permitted but
have no S2PA meaning; some C2PA libraries expect at
least one such attribute alongside
commonName in their cert profile check,
and signers targeting those libraries may include
one for compatibility.
id-ecPublicKey
(1.2.840.10045.2.1) with named-curve
parameter secp256k1
(1.3.132.0.10); the
subjectPublicKey bit string carries
pubkey in its 65-byte uncompressed form.
basicConstraints (critical):
cA = FALSE.
keyUsage (critical):
digitalSignature.
extendedKeyUsage:
emailProtection
(1.3.6.1.5.5.7.3.4). This is
one of the EKU OIDs C2PA accepts on a
signing leaf and has no email-related
semantic effect in this context.
subjectKeyIdentifier:
key_id.
authorityKeyIdentifier: the
same keyIdentifier value as
subjectKeyIdentifier
(key_id), since the certificate
is self-issued.
The certificate is self-issued, self-signed, and has no expected parent. Verifiers must not attempt to chain it to any trust anchor.
The certificate's identity-bearing payload is its public key and
the DID in its commonName. A verifier must always
confirm that the embedded public key validates the manifest
signature; depending on the DID method, the verifier may also
resolve the DID and confirm consistency with the public key.