ruby-c2pa 0.2.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.
@@ -0,0 +1,29 @@
1
+ module C2PA
2
+ module Actions
3
+ CREATED = "c2pa.created"
4
+ OPENED = "c2pa.opened"
5
+ EDITED = "c2pa.edited"
6
+ EDITED_METADATA = "c2pa.edited.metadata"
7
+ ADJUSTED_COLOR = "c2pa.adjustedColor"
8
+ CHANGED_SPEED = "c2pa.changedSpeed"
9
+ CONVERTED = "c2pa.converted"
10
+ CROPPED = "c2pa.cropped"
11
+ DELETED = "c2pa.deleted"
12
+ DRAWING = "c2pa.drawing"
13
+ DUBBED = "c2pa.dubbed"
14
+ ENHANCED = "c2pa.enhanced"
15
+ FILTERED = "c2pa.filtered"
16
+ ORIENTATION = "c2pa.orientation"
17
+ PLACED = "c2pa.placed"
18
+ PUBLISHED = "c2pa.published"
19
+ REDACTED = "c2pa.redacted"
20
+ REMOVED = "c2pa.removed"
21
+ REPACKAGED = "c2pa.repackaged"
22
+ RESIZED = "c2pa.resized"
23
+ TRANSLATED = "c2pa.translated"
24
+ TRANSCODED = "c2pa.transcoded"
25
+ TRIMMED = "c2pa.trimmed"
26
+ UNKNOWN = "c2pa.unknown"
27
+ WATERMARKED = "c2pa.watermarked"
28
+ end
29
+ end
data/lib/c2pa/error.rb ADDED
@@ -0,0 +1,6 @@
1
+ module C2PA
2
+ class Error < StandardError; end
3
+ class SigningError < Error; end
4
+ class ReadError < Error; end
5
+ class InvalidManifestError < Error; end
6
+ end
@@ -0,0 +1,85 @@
1
+ require "json"
2
+
3
+ module C2PA
4
+ class Manifest
5
+ # @param title [String] human-readable title for this asset
6
+ def initialize(title:)
7
+ @title = title
8
+ @actions = []
9
+ @assertions = []
10
+ @ingredients = []
11
+ end
12
+
13
+ # Add a C2PA action to this manifest.
14
+ #
15
+ # @param action [String] one of the C2PA::Actions constants
16
+ # @param when_time [String, nil] ISO 8601 timestamp of when the action occurred
17
+ # @param software_agent [String, nil] name/version of the software that performed the action;
18
+ # defaults to "ruby-c2pa/<version>"
19
+ # @param digital_source_type [String, nil] URI from the C2PA digitalSourceType vocabulary
20
+ # @param changed [Array<String>, nil] list of regions or ingredients that changed
21
+ # @param parameters [Hash, nil] action-specific additional parameters
22
+ # @return [self]
23
+ def add_action(action,
24
+ when_time: nil,
25
+ software_agent: nil,
26
+ digital_source_type: nil,
27
+ changed: nil,
28
+ parameters: nil)
29
+ entry = { "action" => action }
30
+ entry["when"] = when_time if when_time
31
+ entry["softwareAgent"] = software_agent || "ruby-c2pa/#{VERSION}"
32
+ entry["digitalSourceType"] = digital_source_type if digital_source_type
33
+ entry["changed"] = changed if changed
34
+ entry["parameters"] = parameters if parameters
35
+ @actions << entry
36
+ self
37
+ end
38
+
39
+ # Add an arbitrary assertion to this manifest.
40
+ #
41
+ # @param label [String] the assertion label, e.g. "stds.schema-org.CreativeWork"
42
+ # @param data [Hash] the assertion data
43
+ # @return [self]
44
+ def add_assertion(label:, data:)
45
+ @assertions << { "label" => label, "data" => data }
46
+ self
47
+ end
48
+
49
+ # Add an ingredient (source asset) to this manifest.
50
+ #
51
+ # @param title [String] human-readable title of the ingredient
52
+ # @param format [String] MIME type of the ingredient, e.g. "image/jpeg"
53
+ # @param instance_id [String] unique identifier for the ingredient instance
54
+ # @param relationship [String] relationship to this asset; defaults to "parentOf"
55
+ # @return [self]
56
+ def add_ingredient(title:, format:, instance_id:, relationship: "parentOf")
57
+ @ingredients << {
58
+ "title" => title,
59
+ "format" => format,
60
+ "instance_id" => instance_id,
61
+ "relationship" => relationship
62
+ }
63
+ self
64
+ end
65
+
66
+ # Serialize to the JSON structure expected by c2pa-rs.
67
+ #
68
+ # @return [String]
69
+ # @raise [C2PA::InvalidManifestError] if no actions have been added
70
+ def to_json
71
+ raise InvalidManifestError, "at least one action is required" if @actions.empty?
72
+
73
+ manifest = {
74
+ "title" => @title,
75
+ "assertions" => [
76
+ { "label" => "c2pa.actions.v2", "data" => { "actions" => @actions } },
77
+ *@assertions
78
+ ]
79
+ }
80
+ manifest["ingredients"] = @ingredients unless @ingredients.empty?
81
+
82
+ JSON.generate(manifest)
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,3 @@
1
+ module C2PA
2
+ VERSION = "0.2.0"
3
+ end
data/lib/c2pa.rb ADDED
@@ -0,0 +1,58 @@
1
+ require "json"
2
+ require_relative "c2pa/version"
3
+ require_relative "c2pa/error"
4
+ require_relative "c2pa/actions"
5
+ require_relative "c2pa/manifest"
6
+ require "c2pa/c2pa_native"
7
+
8
+ module C2PA
9
+ # Sign a file with a C2PA manifest.
10
+ #
11
+ # @param file [String] path to the input file
12
+ # @param output [String] path for the signed output file (must not already exist)
13
+ # @param certificate [String] path to a PEM-encoded X.509 certificate (chain)
14
+ # @param key [String] path to a PEM-encoded private key
15
+ # @param algorithm [String] signing algorithm (default: "es256")
16
+ # @param manifest [C2PA::Manifest] the manifest to embed
17
+ # @return [String] the output path
18
+ #
19
+ # @example
20
+ # manifest = C2PA::Manifest.new(title: "Sunset over the bay")
21
+ # manifest.add_action(C2PA::Actions::CREATED)
22
+ #
23
+ # C2PA.sign(
24
+ # file: "photo.jpg",
25
+ # output: "photo_signed.jpg",
26
+ # certificate: "cert.pem",
27
+ # key: "key.pem",
28
+ # manifest: manifest
29
+ # )
30
+ def self.sign(file:, output:, certificate:, key:, algorithm: "es256", manifest:)
31
+ Native.sign_file(file, output, certificate, key, algorithm, manifest.to_json)
32
+ rescue RuntimeError => e
33
+ raise SigningError, e.message
34
+ end
35
+
36
+ # Read the C2PA manifest embedded in a signed file.
37
+ #
38
+ # @param file [String] path to the signed file
39
+ # @return [Hash] parsed manifest JSON
40
+ # @raise [C2PA::ReadError] if the file has no valid manifest
41
+ #
42
+ # @example
43
+ # manifest = C2PA.read(file: "photo_signed.jpg")
44
+ # active = manifest["manifests"][manifest["active_manifest"]]
45
+ # puts active["title"]
46
+ def self.read(file:)
47
+ JSON.parse(Native.read_file(file))
48
+ rescue RuntimeError => e
49
+ raise ReadError, e.message
50
+ end
51
+
52
+ # Return the version of the underlying c2pa-rs SDK.
53
+ #
54
+ # @return [String]
55
+ def self.sdk_version
56
+ Native.sdk_version
57
+ end
58
+ end
data/ruby-c2pa.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ require_relative "lib/c2pa/version"
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "ruby-c2pa"
5
+ spec.version = C2PA::VERSION
6
+ spec.authors = [`git config user.name`.strip]
7
+ spec.email = [`git config user.email`.strip]
8
+ spec.summary = "Ruby bindings for the c2pa content authenticity library"
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
+ spec.license = "MIT"
11
+ spec.homepage = "https://github.com/carlosrodriguez/ruby-c2pa"
12
+ spec.metadata["source_code_uri"] = spec.homepage
13
+
14
+ spec.files = Dir["lib/**/*.rb", "ext/**/*.{rs,toml,rb}", "Rakefile", "*.gemspec", "LICENSE", "README.md"]
15
+ spec.require_paths = ["lib"]
16
+ spec.extensions = ["ext/c2pa_native/extconf.rb"]
17
+
18
+ spec.required_ruby_version = ">= 3.0"
19
+
20
+ spec.add_dependency "rb_sys", "~> 0.9"
21
+
22
+ spec.add_development_dependency "rake-compiler", "~> 1.2"
23
+ spec.add_development_dependency "minitest", "~> 5.0"
24
+ spec.add_development_dependency "rake", "~> 13.0"
25
+ end
metadata ADDED
@@ -0,0 +1,120 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ruby-c2pa
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Carlos Rodriguez
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: rb_sys
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '0.9'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '0.9'
26
+ - !ruby/object:Gem::Dependency
27
+ name: rake-compiler
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '1.2'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1.2'
40
+ - !ruby/object:Gem::Dependency
41
+ name: minitest
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '5.0'
47
+ type: :development
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '5.0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: rake
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '13.0'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '13.0'
68
+ description: Embed and verify C2PA content provenance and authenticity credentials
69
+ in images, video, and audio files. Ruby bindings for the official Rust c2pa-rs library.
70
+ email:
71
+ - carlos@eddorre.com
72
+ executables: []
73
+ extensions:
74
+ - ext/c2pa_native/extconf.rb
75
+ extra_rdoc_files: []
76
+ files:
77
+ - LICENSE
78
+ - README.md
79
+ - Rakefile
80
+ - ext/c2pa_native/Cargo.toml
81
+ - ext/c2pa_native/extconf.rb
82
+ - 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
+ - lib/c2pa.rb
93
+ - lib/c2pa/actions.rb
94
+ - lib/c2pa/error.rb
95
+ - lib/c2pa/manifest.rb
96
+ - lib/c2pa/version.rb
97
+ - ruby-c2pa.gemspec
98
+ homepage: https://github.com/carlosrodriguez/ruby-c2pa
99
+ licenses:
100
+ - MIT
101
+ metadata:
102
+ source_code_uri: https://github.com/carlosrodriguez/ruby-c2pa
103
+ rdoc_options: []
104
+ require_paths:
105
+ - lib
106
+ required_ruby_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '3.0'
111
+ required_rubygems_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ requirements: []
117
+ rubygems_version: 4.0.3
118
+ specification_version: 4
119
+ summary: Ruby bindings for the c2pa content authenticity library
120
+ test_files: []