ruby-c2pa 0.2.1 → 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 +4 -4
- data/CHANGELOG.md +153 -0
- data/CONTRIBUTING.md +117 -0
- data/README.md +276 -22
- data/Rakefile +29 -1
- data/ext/c2pa_native/Cargo.lock +4215 -0
- data/ext/c2pa_native/Cargo.toml +1 -1
- data/ext/c2pa_native/src/lib.rs +124 -5
- data/lib/c2pa/config.rb +90 -0
- data/lib/c2pa/digital_source_types.rb +72 -0
- data/lib/c2pa/error.rb +3 -0
- data/lib/c2pa/manifest.rb +156 -9
- data/lib/c2pa/version.rb +1 -1
- data/lib/c2pa.rb +105 -4
- data/ruby-c2pa.gemspec +28 -3
- metadata +11 -12
- data/ext/c2pa_native/target/release/build/oid-registry-c350c75504c969dc/out/oid_db.rs +0 -540
- data/ext/c2pa_native/target/release/build/pix-f303ab89d734032b/out/gamma_lut.rs +0 -68
- data/ext/c2pa_native/target/release/build/serde-2e9abb4d9d73cae4/out/private.rs +0 -6
- data/ext/c2pa_native/target/release/build/serde-f471616b462b0caf/out/private.rs +0 -6
- data/ext/c2pa_native/target/release/build/serde_core-2b94e9134dc44065/out/private.rs +0 -5
- data/ext/c2pa_native/target/release/build/serde_core-3dc945fa0ab21dd5/out/private.rs +0 -5
- data/ext/c2pa_native/target/release/build/thiserror-a1f4c63469c326b9/out/private.rs +0 -5
- data/ext/c2pa_native/target/release/build/typenum-4eb7d34b9fb695ef/out/tests.rs +0 -20563
- data/ext/c2pa_native/target/release/build/typenum-b8de96984639a942/out/tests.rs +0 -20563
data/lib/c2pa.rb
CHANGED
|
@@ -2,10 +2,52 @@ require "json"
|
|
|
2
2
|
require_relative "c2pa/version"
|
|
3
3
|
require_relative "c2pa/error"
|
|
4
4
|
require_relative "c2pa/actions"
|
|
5
|
+
require_relative "c2pa/digital_source_types"
|
|
6
|
+
require_relative "c2pa/config"
|
|
5
7
|
require_relative "c2pa/manifest"
|
|
6
8
|
require "c2pa/c2pa_native"
|
|
7
9
|
|
|
8
10
|
module C2PA
|
|
11
|
+
# Validation states that mean a signed asset is good.
|
|
12
|
+
#
|
|
13
|
+
# "Trusted" is "Valid" plus a signing certificate that chains to a root in
|
|
14
|
+
# the active trust list. Accepting only "Valid" would reject exactly the
|
|
15
|
+
# production files that are most correct.
|
|
16
|
+
VALID_STATES = %w[Valid Trusted].freeze
|
|
17
|
+
|
|
18
|
+
# Configure how c2pa-rs validates.
|
|
19
|
+
#
|
|
20
|
+
# Settings are global and take effect for subsequent calls. Signing already
|
|
21
|
+
# in flight on another thread continues against the settings it started
|
|
22
|
+
# with, since the underlying context is replaced rather than mutated.
|
|
23
|
+
#
|
|
24
|
+
# @yieldparam config [C2PA::Config]
|
|
25
|
+
# @return [C2PA::Config] the configuration that was applied
|
|
26
|
+
# @raise [C2PA::InvalidSettingsError] if the settings are not usable
|
|
27
|
+
#
|
|
28
|
+
# @example Trusting a private CA
|
|
29
|
+
# C2PA.configure do |config|
|
|
30
|
+
# config.trust_anchors = "ca/root.pem"
|
|
31
|
+
# end
|
|
32
|
+
#
|
|
33
|
+
# @example An offline environment
|
|
34
|
+
# C2PA.configure do |config|
|
|
35
|
+
# config.remote_manifest_fetch = false
|
|
36
|
+
# config.ocsp_fetch = false
|
|
37
|
+
# end
|
|
38
|
+
def self.configure
|
|
39
|
+
config = Config.new
|
|
40
|
+
yield config if block_given?
|
|
41
|
+
|
|
42
|
+
begin
|
|
43
|
+
Native.configure(config.to_json)
|
|
44
|
+
rescue RuntimeError => e
|
|
45
|
+
raise InvalidSettingsError, e.message
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
config
|
|
49
|
+
end
|
|
50
|
+
|
|
9
51
|
# Sign a file with a C2PA manifest.
|
|
10
52
|
#
|
|
11
53
|
# @param file [String] path to the input file
|
|
@@ -14,7 +56,10 @@ module C2PA
|
|
|
14
56
|
# @param key [String] path to a PEM-encoded private key
|
|
15
57
|
# @param algorithm [String] signing algorithm (default: "es256")
|
|
16
58
|
# @param manifest [C2PA::Manifest] the manifest to embed
|
|
59
|
+
# @param verify [Boolean] read the signed file back and confirm it
|
|
60
|
+
# validates (default: true)
|
|
17
61
|
# @return [String] the output path
|
|
62
|
+
# @raise [C2PA::SigningError] if signing fails, or if the signed file does not validate
|
|
18
63
|
#
|
|
19
64
|
# @example
|
|
20
65
|
# manifest = C2PA::Manifest.new(title: "Sunset over the bay")
|
|
@@ -27,7 +72,7 @@ module C2PA
|
|
|
27
72
|
# key: "key.pem",
|
|
28
73
|
# manifest: manifest
|
|
29
74
|
# )
|
|
30
|
-
def self.sign(file:, output:, certificate:, key:, algorithm: "es256", manifest:)
|
|
75
|
+
def self.sign(file:, output:, certificate:, key:, algorithm: "es256", manifest:, verify: true)
|
|
31
76
|
manifest_json = manifest.to_json
|
|
32
77
|
|
|
33
78
|
raise SigningError, "Source file not found: '#{file}'" unless File.exist?(file)
|
|
@@ -35,9 +80,21 @@ module C2PA
|
|
|
35
80
|
raise SigningError, "Key file not found: '#{key}'" unless File.exist?(key)
|
|
36
81
|
raise SigningError, "Output file already exists: '#{output}'" if File.exist?(output)
|
|
37
82
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
83
|
+
begin
|
|
84
|
+
# to_json is the only thing genuinely required of a manifest, so an
|
|
85
|
+
# object that provides just that still signs — as a creation.
|
|
86
|
+
intent = manifest.respond_to?(:intent) ? manifest.intent&.to_s : nil
|
|
87
|
+
files = manifest.respond_to?(:ingredient_files) ? manifest.ingredient_files : []
|
|
88
|
+
ingredient_files = files.empty? ? nil : JSON.generate(files)
|
|
89
|
+
Native.sign_file(file, output, certificate, key, algorithm, manifest_json,
|
|
90
|
+
intent, ingredient_files)
|
|
91
|
+
rescue RuntimeError => e
|
|
92
|
+
raise SigningError, e.message
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
verify_signed_output!(output) if verify
|
|
96
|
+
|
|
97
|
+
output
|
|
41
98
|
end
|
|
42
99
|
|
|
43
100
|
# Read the C2PA manifest embedded in a signed file.
|
|
@@ -62,4 +119,48 @@ module C2PA
|
|
|
62
119
|
def self.sdk_version
|
|
63
120
|
Native.sdk_version
|
|
64
121
|
end
|
|
122
|
+
|
|
123
|
+
# Read a freshly signed file back and confirm it actually validates.
|
|
124
|
+
#
|
|
125
|
+
# Signing reports success for manifests that verifiers reject. c2pa-rs
|
|
126
|
+
# applies its rules when reading, not when writing, so without this the gem
|
|
127
|
+
# hands back a path to a file that fails everywhere else — which is what
|
|
128
|
+
# released versions did, for years, silently.
|
|
129
|
+
#
|
|
130
|
+
# The rejected file is deleted rather than left behind, so a failed sign
|
|
131
|
+
# cannot leave something shippable on disk.
|
|
132
|
+
#
|
|
133
|
+
# @param output [String] path to the signed file
|
|
134
|
+
# @raise [C2PA::SigningError] if the file does not validate
|
|
135
|
+
def self.verify_signed_output!(output)
|
|
136
|
+
result =
|
|
137
|
+
begin
|
|
138
|
+
read(file: output)
|
|
139
|
+
rescue ReadError => e
|
|
140
|
+
discard(output)
|
|
141
|
+
raise SigningError, "signed file failed verification: could not read it back: #{e.message}"
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
state = result["validation_state"]
|
|
145
|
+
return if VALID_STATES.include?(state)
|
|
146
|
+
|
|
147
|
+
failures = Array(result.dig("validation_results", "activeManifest", "failure"))
|
|
148
|
+
.map { |failure| "#{failure["code"]} (#{failure["explanation"]})" }
|
|
149
|
+
detail = failures.empty? ? "no failure detail reported" : failures.uniq.join(", ")
|
|
150
|
+
|
|
151
|
+
discard(output)
|
|
152
|
+
raise SigningError,
|
|
153
|
+
"signed file failed verification: validation_state=#{state.inspect}, #{detail}. " \
|
|
154
|
+
"The output file has been removed. Pass verify: false to keep it for inspection."
|
|
155
|
+
end
|
|
156
|
+
private_class_method :verify_signed_output!
|
|
157
|
+
|
|
158
|
+
# Remove a file this library created and is about to reject.
|
|
159
|
+
def self.discard(path)
|
|
160
|
+
File.delete(path) if File.exist?(path)
|
|
161
|
+
rescue SystemCallError
|
|
162
|
+
# Leaving the file behind is better than masking the original failure.
|
|
163
|
+
nil
|
|
164
|
+
end
|
|
165
|
+
private_class_method :discard
|
|
65
166
|
end
|
data/ruby-c2pa.gemspec
CHANGED
|
@@ -8,10 +8,35 @@ Gem::Specification.new do |spec|
|
|
|
8
8
|
spec.summary = "Ruby bindings for the c2pa content authenticity library"
|
|
9
9
|
spec.description = "Embed and verify C2PA content provenance and authenticity credentials in images, video, and audio files. Ruby bindings for the official Rust c2pa-rs library."
|
|
10
10
|
spec.license = "MIT"
|
|
11
|
-
spec.homepage = "https://github.com/
|
|
12
|
-
spec.metadata["source_code_uri"] = spec.homepage
|
|
11
|
+
spec.homepage = "https://github.com/eddorre/ruby-c2pa"
|
|
13
12
|
|
|
14
|
-
spec.
|
|
13
|
+
spec.metadata["source_code_uri"] = spec.homepage
|
|
14
|
+
spec.metadata["bug_tracker_uri"] = "#{spec.homepage}/issues"
|
|
15
|
+
spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"
|
|
16
|
+
spec.metadata["documentation_uri"] = "#{spec.homepage}/blob/main/README.md"
|
|
17
|
+
|
|
18
|
+
# Listed explicitly rather than globbed. "ext/**/*.rs" swept in whatever
|
|
19
|
+
# happened to be under ext/c2pa_native/target, so the package contents
|
|
20
|
+
# depended on what had been compiled on the machine that ran `gem build` —
|
|
21
|
+
# 1.3 MB of dependency build-script output shipped in 0.2.1.
|
|
22
|
+
#
|
|
23
|
+
# Cargo.lock is included deliberately. The extension is compiled at install
|
|
24
|
+
# time, so without it every installer resolves the dependency tree afresh.
|
|
25
|
+
# That is how 0.2.1 shipped against a broken atree and aborted the Ruby
|
|
26
|
+
# process on TIFF input.
|
|
27
|
+
spec.files = [
|
|
28
|
+
"LICENSE",
|
|
29
|
+
"README.md",
|
|
30
|
+
"CHANGELOG.md",
|
|
31
|
+
"CONTRIBUTING.md",
|
|
32
|
+
"Rakefile",
|
|
33
|
+
"ruby-c2pa.gemspec",
|
|
34
|
+
"ext/c2pa_native/Cargo.toml",
|
|
35
|
+
"ext/c2pa_native/Cargo.lock",
|
|
36
|
+
"ext/c2pa_native/extconf.rb",
|
|
37
|
+
*Dir["ext/c2pa_native/src/**/*.rs"],
|
|
38
|
+
*Dir["lib/**/*.rb"]
|
|
39
|
+
].sort
|
|
15
40
|
spec.require_paths = ["lib"]
|
|
16
41
|
spec.extensions = ["ext/c2pa_native/extconf.rb"]
|
|
17
42
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ruby-c2pa
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Carlos Rodriguez
|
|
@@ -74,32 +74,31 @@ extensions:
|
|
|
74
74
|
- ext/c2pa_native/extconf.rb
|
|
75
75
|
extra_rdoc_files: []
|
|
76
76
|
files:
|
|
77
|
+
- CHANGELOG.md
|
|
78
|
+
- CONTRIBUTING.md
|
|
77
79
|
- LICENSE
|
|
78
80
|
- README.md
|
|
79
81
|
- Rakefile
|
|
82
|
+
- ext/c2pa_native/Cargo.lock
|
|
80
83
|
- ext/c2pa_native/Cargo.toml
|
|
81
84
|
- ext/c2pa_native/extconf.rb
|
|
82
85
|
- ext/c2pa_native/src/lib.rs
|
|
83
|
-
- ext/c2pa_native/target/release/build/oid-registry-c350c75504c969dc/out/oid_db.rs
|
|
84
|
-
- ext/c2pa_native/target/release/build/pix-f303ab89d734032b/out/gamma_lut.rs
|
|
85
|
-
- ext/c2pa_native/target/release/build/serde-2e9abb4d9d73cae4/out/private.rs
|
|
86
|
-
- ext/c2pa_native/target/release/build/serde-f471616b462b0caf/out/private.rs
|
|
87
|
-
- ext/c2pa_native/target/release/build/serde_core-2b94e9134dc44065/out/private.rs
|
|
88
|
-
- ext/c2pa_native/target/release/build/serde_core-3dc945fa0ab21dd5/out/private.rs
|
|
89
|
-
- ext/c2pa_native/target/release/build/thiserror-a1f4c63469c326b9/out/private.rs
|
|
90
|
-
- ext/c2pa_native/target/release/build/typenum-4eb7d34b9fb695ef/out/tests.rs
|
|
91
|
-
- ext/c2pa_native/target/release/build/typenum-b8de96984639a942/out/tests.rs
|
|
92
86
|
- lib/c2pa.rb
|
|
93
87
|
- lib/c2pa/actions.rb
|
|
88
|
+
- lib/c2pa/config.rb
|
|
89
|
+
- lib/c2pa/digital_source_types.rb
|
|
94
90
|
- lib/c2pa/error.rb
|
|
95
91
|
- lib/c2pa/manifest.rb
|
|
96
92
|
- lib/c2pa/version.rb
|
|
97
93
|
- ruby-c2pa.gemspec
|
|
98
|
-
homepage: https://github.com/
|
|
94
|
+
homepage: https://github.com/eddorre/ruby-c2pa
|
|
99
95
|
licenses:
|
|
100
96
|
- MIT
|
|
101
97
|
metadata:
|
|
102
|
-
source_code_uri: https://github.com/
|
|
98
|
+
source_code_uri: https://github.com/eddorre/ruby-c2pa
|
|
99
|
+
bug_tracker_uri: https://github.com/eddorre/ruby-c2pa/issues
|
|
100
|
+
changelog_uri: https://github.com/eddorre/ruby-c2pa/blob/main/CHANGELOG.md
|
|
101
|
+
documentation_uri: https://github.com/eddorre/ruby-c2pa/blob/main/README.md
|
|
103
102
|
rdoc_options: []
|
|
104
103
|
require_paths:
|
|
105
104
|
- lib
|