ruby-c2pa 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 98842ac83d8c5e7dacdd28d148cc0e0a0997d2b8ad20205e2d4275238992b813
4
- data.tar.gz: 0dd78073b9b631c901658564b3ce5dc8b80e5e4edaaae6e7a9966df760b0b1c5
3
+ metadata.gz: 66e048e5359881f08def1d3fd82ed11fb8b9b3dfc0d8232d9dbc145026841d39
4
+ data.tar.gz: bd9108538b9e6d47bd3684d974d2145963345cbdbe67ec44250a1892dd277957
5
5
  SHA512:
6
- metadata.gz: 05eb4f8d6c31fb6fc6af2facb25c1c3a335fc505b97b46d00c9a846fbc6b72cd5dd0048805fd985f24a615f6a4dc8dde991a089e7d3423256fff63a3cb5566c3
7
- data.tar.gz: ca81e533198e62ffe4ae92ef7302653191cf3b5dfb14ae1088b878a97c44b5281073b68db2f04d9ab04dc11662e59c7300db7957db99836e677b0774c9f4baad
6
+ metadata.gz: 65b8729aa03c0fb5145f1631fc5c847960be95cfd792553d0352c9c9e7f54451a077b3c7eb599c45ff3b7007698aaa9a91d3f05848c0307f192c5d5de2333c25
7
+ data.tar.gz: 0f46873209bbc9b081a2ef24c276adb0daf079466366a01a712a504d3f0df1eb64b20949fa2f171c469eba9e0da48f429c569b09cba8d5ee48f6e978d3548f72
data/CHANGELOG.md CHANGED
@@ -7,6 +7,53 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.4.0] — 2026-09-11
11
+
12
+ Adds capability on top of 0.3.0. Nothing is removed and no existing call
13
+ changes behaviour, so this is a minor release.
14
+
15
+ ### Added
16
+
17
+ - Provenance chaining through ingredients. `add_ingredient` takes an optional
18
+ `file:`. When the file carries content credentials, its manifest is embedded
19
+ in the signed output and the ingredient points at it, so a verifier can follow
20
+ the chain from your asset back through the original. The description-only
21
+ form is unchanged.
22
+ - `intent: :update` on `C2PA::Manifest`, for non-editorial changes such as
23
+ correcting metadata. The source file is the parent.
24
+ - `C2PA.configure`, for trust and verification settings. Add a private CA's
25
+ root as a trust anchor and its certificates validate as `Trusted`; disable
26
+ `remote_manifest_fetch` and `ocsp_fetch` for environments without network
27
+ access. Only values you set are sent, so defaults are c2pa-rs's own.
28
+ - Reading content credentials from PDFs. `C2PA.read` now parses a PDF and
29
+ returns its manifest if one is present. Signing a PDF remains impossible;
30
+ c2pa-rs has no PDF writer and upstream closed the request to add one.
31
+ - `C2PA::InvalidSettingsError`, raised by `C2PA.configure` for unusable input.
32
+
33
+ ### Changed
34
+
35
+ - c2pa-rs 0.90.15 → 0.90.22. The range includes security fixes: `h2` updated
36
+ for RUSTSEC-2026-0258, `chacha20` moved off a yanked version, hardening of
37
+ BMFF chunk-index handling and CAWG identity bindings. The lockfile ships in
38
+ the gem, so installers get these once they upgrade.
39
+ - The native layer uses c2pa-rs's Context API rather than the deprecated
40
+ `Builder::from_json` and `Reader::from_file`, which read settings from
41
+ thread-local state. One shared Context is reused across calls. Signing from
42
+ several threads concurrently is now tested.
43
+ - CI fails if a deprecated c2pa-rs API reappears.
44
+
45
+ ### Documentation
46
+
47
+ - The certificate section no longer claims certificates must chain to a CA in
48
+ the C2PA trust list, or that self-signed certificates are rejected as such.
49
+ A private CA works; its certificates carry `signingCredential.untrusted`
50
+ until the root is added as an anchor. The certificate profile c2pa-rs
51
+ enforces is listed, including the Authority Key Identifier that
52
+ `openssl x509 -req` omits by default.
53
+ - PDF is listed as read-only, with a note on what the test suite can and
54
+ cannot show: nothing available can produce a C2PA-signed PDF, so reading one
55
+ is untested here, though the code doing it is c2pa-rs's own.
56
+
10
57
  ## [0.3.0] — 2026-08-25
11
58
 
12
59
  Runs on c2pa-rs 0.90, and fixes every defect found while building a test suite
@@ -99,7 +146,8 @@ Tagged retroactively. See the v0.2.1 tag for the defects it shipped with.
99
146
 
100
147
  Tagged retroactively.
101
148
 
102
- [Unreleased]: https://github.com/eddorre/ruby-c2pa/compare/v0.3.0...HEAD
149
+ [Unreleased]: https://github.com/eddorre/ruby-c2pa/compare/v0.4.0...HEAD
150
+ [0.4.0]: https://github.com/eddorre/ruby-c2pa/compare/v0.3.0...v0.4.0
103
151
  [0.3.0]: https://github.com/eddorre/ruby-c2pa/compare/v0.2.1...v0.3.0
104
152
  [0.2.1]: https://github.com/eddorre/ruby-c2pa/compare/v0.2.0...v0.2.1
105
153
  [0.2.0]: https://github.com/eddorre/ruby-c2pa/releases/tag/v0.2.0
data/README.md CHANGED
@@ -64,22 +64,44 @@ The native Rust library is compiled automatically during installation. This take
64
64
 
65
65
  ## Preparing your certificate and key
66
66
 
67
- C2PA signing requires an X.509 certificate chain and private key in PEM format. The certificate must chain to a CA that is trusted by the C2PA ecosystem — **self-signed certificates are rejected by c2pa-rs**.
67
+ C2PA signing requires an X.509 certificate chain and private key in PEM format.
68
+ The certificate file contains the end-entity certificate first, then any
69
+ intermediates, and must **not** include the root.
70
+
71
+ c2pa-rs enforces a certificate profile. An end-entity certificate is rejected
72
+ unless it carries all of:
73
+
74
+ - Basic Constraints `CA:FALSE`, critical
75
+ - Key Usage with `digitalSignature` or `nonRepudiation`, critical
76
+ - an Extended Key Usage that is present and not `any`, critical
77
+ - a Subject Key Identifier
78
+ - an **Authority Key Identifier**
79
+
80
+ The last one is easy to miss — `openssl x509 -req` omits it by default, and the
81
+ certificate is then rejected with nothing more specific than
82
+ `the certificate is invalid`.
83
+
84
+ ### Whose CA?
85
+
86
+ A certificate from a CA in the C2PA trust list validates as `Trusted` with no
87
+ configuration.
88
+
89
+ A certificate from your own CA validates as `Valid` and carries
90
+ `signingCredential.untrusted` — which does **not** make the manifest invalid.
91
+ To have it treated as trusted, add your root as a trust anchor; see
92
+ [Configuring trust](#configuring-trust).
68
93
 
69
94
  ### Development and testing
70
95
 
71
- The c2pa-rs project publishes test certificates that work for local development:
96
+ The test suite generates its own certificates for every supported algorithm:
72
97
 
73
98
  ```bash
74
- curl -sL -o test_cert.pem https://raw.githubusercontent.com/contentauth/c2pa-rs/main/sdk/tests/fixtures/certs/es256.pub
75
- curl -sL -o test_key.pem https://raw.githubusercontent.com/contentauth/c2pa-rs/main/sdk/tests/fixtures/certs/es256.pem
99
+ bundle exec rake fixtures:certs
76
100
  ```
77
101
 
78
- Files signed with these test certificates will include a `signingCredential.untrusted` validation warning since the test CA is not in the public trust list, but are otherwise valid for development purposes.
79
-
80
- ### Production
81
-
82
- Obtain a certificate from a CA trusted by the C2PA ecosystem. The certificate file must contain the full chain (end-entity certificate first, then any intermediates), but must **not** include the root CA.
102
+ They land in `test/fixtures/certs/` and are not committed. See
103
+ [`test/fixtures/generate_certs.rb`](test/fixtures/generate_certs.rb) for a
104
+ worked example of building a chain c2pa-rs accepts.
83
105
 
84
106
  The supported signing algorithms are: `es256`, `es384`, `es512`, `ps256`, `ps384`, `ps512`, `ed25519`.
85
107
 
@@ -212,16 +234,42 @@ manifest.add_assertion(
212
234
 
213
235
  ### Adding ingredients
214
236
 
215
- Ingredients record the source assets a file was derived from:
237
+ Ingredients record the source assets a file was derived from. Supply the file
238
+ so c2pa-rs can read it:
216
239
 
217
240
  ```ruby
218
241
  manifest.add_ingredient(
219
242
  title: "Original photo",
220
243
  format: "image/jpeg",
221
- instance_id: "xmp:iid:original-uuid-here"
244
+ instance_id: "xmp:iid:original-uuid-here",
245
+ relationship: "componentOf",
246
+ file: "original.jpg"
222
247
  )
223
248
  ```
224
249
 
250
+ If the ingredient already carries content credentials, its manifest is embedded
251
+ in the signed output and the ingredient points at it. A verifier can then
252
+ follow the chain from your asset back through the original. For a file with no
253
+ credentials there is nothing to carry forward.
254
+
255
+ Omitting `file:` records the description alone. Nothing binds it to any bytes,
256
+ so a verifier cannot check the claim. That form is kept for compatibility;
257
+ prefer the file.
258
+
259
+ ### Updating an existing asset
260
+
261
+ For a non-editorial change to an asset, such as correcting metadata, use
262
+ `intent: :update`. The source file is the parent, and the change is recorded
263
+ against it without opening a new editing lineage:
264
+
265
+ ```ruby
266
+ manifest = C2PA::Manifest.new(title: "Metadata corrected", intent: :update)
267
+ .add_action(C2PA::Actions::EDITED_METADATA)
268
+ ```
269
+
270
+ c2pa-rs restricts this mode: there is exactly one ingredient, it is the source
271
+ itself, and the hashed content must not change.
272
+
225
273
  ### Signing a file
226
274
 
227
275
  The `output` path must not already exist — `C2PA.sign` will raise a `C2PA::SigningError` if the file is already there.
@@ -352,14 +400,62 @@ rescue C2PA::Error => e
352
400
  end
353
401
  ```
354
402
 
403
+ ## Configuring trust
404
+
405
+ c2pa-rs checks the signing certificate against a trust list, and reports
406
+ `Trusted` when it chains to a root that list contains. That happens by default,
407
+ so a certificate from a CA in the C2PA trust list needs no configuration.
408
+
409
+ For a private or enterprise CA, add its root:
410
+
411
+ ```ruby
412
+ C2PA.configure do |config|
413
+ config.trust_anchors = "ca/root.pem" # a path, or the PEM text itself
414
+ end
415
+ ```
416
+
417
+ Files signed by a certificate chaining to it then validate as `Trusted` rather
418
+ than carrying `signingCredential.untrusted`.
419
+
420
+ ### Offline and air-gapped environments
421
+
422
+ Reading an asset may fetch a remote manifest over the network, and revocation
423
+ checking may contact an OCSP responder. Both can be turned off:
424
+
425
+ ```ruby
426
+ C2PA.configure do |config|
427
+ config.remote_manifest_fetch = false
428
+ config.ocsp_fetch = false
429
+ end
430
+ ```
431
+
432
+ ### Everything configurable
433
+
434
+ | Setting | Default | Purpose |
435
+ |---------|---------|---------|
436
+ | `trust_anchors` | none | additional roots to trust, as PEM |
437
+ | `trust_list` | C2PA list | replaces the trust list rather than adding to it |
438
+ | `allowed_certificates` | none | explicitly allowed certificates, as PEM |
439
+ | `verify_trust` | `true` | whether trust is checked at all |
440
+ | `remote_manifest_fetch` | `true` | whether reading may fetch over the network |
441
+ | `ocsp_fetch` | `false` | whether revocation is checked over OCSP |
442
+
443
+ Settings are global and apply to subsequent calls. Only values you set are
444
+ sent, so anything left alone keeps c2pa-rs's own default. `C2PA.configure` with
445
+ no block resets everything.
446
+
447
+ Turning `verify_trust` off means nothing is ever reported as untrusted, which
448
+ in a library for establishing provenance is rarely what you want. It exists for
449
+ environments that cannot reach a trust list at all.
450
+
355
451
  ## Supported file formats
356
452
 
357
453
  Each format below has a fixture and a signing test in the suite: the file is
358
454
  signed, read back, and asserted to validate.
359
455
 
360
- | Format | MIME type |
361
- |--------|-----------|
362
- | JPEG | `image/jpeg` |
456
+ | Format | MIME type | |
457
+ |--------|-----------|--|
458
+ | JPEG | `image/jpeg` | |
363
459
  | PNG | `image/png` |
364
460
  | WebP | `image/webp` |
365
461
  | TIFF | `image/tiff` |
@@ -369,19 +465,31 @@ signed, read back, and asserted to validate.
369
465
  | MOV | `video/quicktime` |
370
466
  | MP3 | `audio/mpeg` |
371
467
  | WAV | `audio/wav` |
468
+ | PDF | `application/pdf` | read only, see below |
372
469
 
373
470
  The format is detected automatically from the file extension.
374
471
 
375
472
  JPEG XL must be in the ISOBMFF container form. A bare codestream has no boxes
376
473
  to hold a manifest, and c2pa-rs rejects it.
377
474
 
378
- ### Not supported
475
+ ### PDF is read-only
476
+
477
+ `C2PA.read` works on a PDF that carries content credentials, such as one signed
478
+ by Adobe Acrobat. `C2PA.sign` does not: c2pa-rs has no PDF writer at any
479
+ version. `get_writer` returns `None` and `save_cai_store` returns
480
+ `NotImplemented`, and upstream closed the request to expose one in December
481
+ 2025 (contentauth/c2pa-rs#527). Signing a PDF raises `C2PA::SigningError`
482
+ with `type is unsupported`.
483
+
484
+ Earlier releases of this gem listed PDF as signable. That was never correct.
379
485
 
380
- **PDF cannot be signed.** c2pa-rs can read C2PA data out of a PDF but has no
381
- writer for it `get_writer` returns `None` and `save_cai_store` returns
382
- `NotImplemented` so `C2PA.sign` raises `C2PA::SigningError` with
383
- `type is unsupported`. This is true at every c2pa-rs version. Earlier releases
384
- of this gem listed PDF as supported; that was never correct.
486
+ One limit on what the test suite can show. It proves the PDF handler is active,
487
+ by reading a PDF with no credentials and getting `no JUMBF data found` rather
488
+ than `type is unsupported`. It cannot prove that a signed PDF returns its
489
+ manifest, because nothing available can produce one: c2pa-rs cannot write
490
+ them, and no local tool can either. The code doing the reading is c2pa-rs's
491
+ own and is tested upstream; what is untested here is only this gem's
492
+ integration with the success path.
385
493
 
386
494
  ### Test fixtures
387
495