origami-docspring 2.3.1 → 2.3.2

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: f96c9c3796b9fcb1d5c078aea87af6756f11770b596fa10eb04d50a993ceecee
4
- data.tar.gz: 1966d4921273de9c9bbc7d4818ca82fd06ad945ffc3d38058c6e6b99d89e4fcf
3
+ metadata.gz: e2404e2f61e089ef30323cb132ac9af123f99124c28505a25b148d0d59d0d49a
4
+ data.tar.gz: 89f0eb165da4b1d762ca102fc52d74681346026e6093067705725346e9df18f4
5
5
  SHA512:
6
- metadata.gz: 413cc0bf7fcdd3801ef34336eac5a4bdbbcb67e7320efe8fdde08ba730e4705d8f9c5a78b54e5a5cc059fc38d4bcbdccf2c3ff013236649e66eca57366433fa7
7
- data.tar.gz: d4722708378130d9e62dc58e013454c6e5697e701d369d94d4ece4981ce937cf95ba98ad3bf6f3bb2d89d3c641d4edeb4d8268b2306fa28b535858d3e874d549
6
+ metadata.gz: 8352a4ee288b72d720734c817ed2fe7c31a99cbaf383c50e3545bf4585d02d15f8c4d5b47a69cc8017c73a7f6a20696ad8d5ea9a59373bcdffb89bbdd76ade7b
7
+ data.tar.gz: 8c17549cef4f4a533541a2b266b6df73e8a142b1fb50e2f10fe2850a955ddf840d787bdb643dfa5f720bf70e61e43f2c34dd2574b153195af5b9079ca6fae79e
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ 2.3.2 (2025-03-25)
2
+ -----
3
+ * Added support for verifying multiple signatures in PDFs
4
+ * Only validate ByteRange for the last signature in multi-signature files
5
+ * Added new `signatures` method to get all signatures in a PDF
6
+
7
+ 2.3.1 (2025-03-25)
8
+ -----
9
+ * Loosened runtime dependencies, removed specific version constraints
10
+
1
11
  2.3.0 (2025-03-25)
2
12
  -----
3
13
  * Updated to support Ruby 3.2, 3.3, and 3.4
data/README.md CHANGED
@@ -1,8 +1,11 @@
1
1
  Origami
2
2
  =====
3
- [![Gem Version](https://badge.fury.io/rb/origami.svg)](https://rubygems.org/gems/origami)
4
- [![Downloads](https://img.shields.io/gem/dt/origami.svg)](https://rubygems.org/gems/origami)
5
- [![Build Status](https://secure.travis-ci.org/gdelugre/origami.svg?branch=master)](https://travis-ci.org/gdelugre/origami)
3
+
4
+ > **Note**: This is a maintained fork of the original Origami library, released as the gem [origami-docspring](https://rubygems.org/gems/origami-docspring) on RubyGems. Despite the name change, you still `require 'origami'` and use the `Origami` module in your code.
5
+
6
+ [![Gem Version](https://badge.fury.io/rb/origami-docspring.svg)](https://rubygems.org/gems/origami-docspring)
7
+ [![Build Status](https://github.com/DocSpring/origami-docspring/actions/workflows/ci.yml/badge.svg)](https://github.com/DocSpring/origami-docspring/actions/workflows/ci.yml)
8
+ [![Downloads](https://img.shields.io/gem/dt/origami-docspring.svg)](https://rubygems.org/gems/origami-docspring)
6
9
  [![License: LGPL v3](https://img.shields.io/badge/License-LGPL%20v3-blue.svg)](https://www.gnu.org/licenses/lgpl-3.0)
7
10
 
8
11
  Overview
@@ -27,7 +30,7 @@ Origami is able to parse PDF, FDF and PPKLite (Adobe certificate store) files.
27
30
  Requirements
28
31
  ------------
29
32
 
30
- As of version 2, the minimal version required to run Origami is Ruby 2.1.
33
+ As of version 2.3.0, the minimal version required to run origami-docspring is Ruby 3.2.
31
34
 
32
35
  Some optional features require additional gems:
33
36
 
@@ -36,9 +39,9 @@ Some optional features require additional gems:
36
39
  Quick start
37
40
  -----------
38
41
 
39
- First install Origami using the latest gem available:
42
+ First install the gem:
40
43
 
41
- $ gem install origami
44
+ $ gem install origami-docspring
42
45
 
43
46
  Then import Origami with:
44
47
 
@@ -109,6 +112,7 @@ License
109
112
  Origami is distributed under the [LGPL](COPYING.LESSER) license.
110
113
 
111
114
  Copyright © 2019 Guillaume Delugré.
115
+ Copyright © 2025 DocSpring, Inc.
112
116
 
113
117
  [the-ruby-racer]: https://rubygems.org/gems/therubyracer
114
118
  [pdfwalker-gem]: https://rubygems.org/gems/pdfwalker
@@ -38,31 +38,34 @@ module Origami
38
38
  use_system_store: false,
39
39
  allow_self_signed: false,
40
40
  &verify_cb)
41
- digsig = signature
42
- digsig = digsig.cast_to(Signature::DigitalSignature) unless digsig.is_a?(Signature::DigitalSignature)
41
+ signatures.each_with_index do |digsig, index|
42
+ digsig = digsig.cast_to(Signature::DigitalSignature) unless digsig.is_a?(Signature::DigitalSignature)
43
43
 
44
- signature = digsig.signature_data
45
- chain = digsig.certificate_chain
46
- subfilter = digsig.SubFilter.value
44
+ signature = digsig.signature_data
45
+ chain = digsig.certificate_chain
46
+ subfilter = digsig.SubFilter.value
47
47
 
48
- store = OpenSSL::X509::Store.new
49
- store.set_default_paths if use_system_store
50
- trusted_certs.each { |ca| store.add_cert(ca) }
48
+ store = OpenSSL::X509::Store.new
49
+ store.set_default_paths if use_system_store
50
+ trusted_certs.each { |ca| store.add_cert(ca) }
51
51
 
52
- store.verify_callback = ->(success, ctx) {
53
- return true if success
52
+ store.verify_callback = ->(success, ctx) {
53
+ return true if success
54
54
 
55
- error = ctx.error
56
- is_self_signed = error == OpenSSL::X509::V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT ||
57
- error == OpenSSL::X509::V_ERR_SELF_SIGNED_CERT_IN_CHAIN
55
+ error = ctx.error
56
+ is_self_signed = error == OpenSSL::X509::V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT ||
57
+ error == OpenSSL::X509::V_ERR_SELF_SIGNED_CERT_IN_CHAIN
58
58
 
59
- return true if is_self_signed && allow_self_signed && verify_cb.nil?
59
+ return true if is_self_signed && allow_self_signed && verify_cb.nil?
60
60
 
61
- verify_cb.call(ctx) unless verify_cb.nil?
62
- }
61
+ verify_cb.call(ctx) unless verify_cb.nil?
62
+ }
63
63
 
64
- data = extract_signed_data(digsig)
65
- Signature.verify(subfilter.to_s, data, signature, store, chain)
64
+ data = (index == signatures.length - 1) ? extract_signed_data(digsig, last_sig: true) : extract_signed_data(digsig)
65
+ return false unless Signature.verify(subfilter.to_s, data, signature, store, chain)
66
+ end
67
+
68
+ true
66
69
  end
67
70
 
68
71
  #
@@ -300,28 +303,44 @@ module Origami
300
303
  raise SignatureError, "Cannot find digital signature"
301
304
  end
302
305
 
306
+ def signatures
307
+ raise SignatureError, "Not a signed document" unless signed?
308
+
309
+ dig_sigs = []
310
+ each_field do |field|
311
+ dig_sigs << field.V if field.FT == :Sig && field.V.is_a?(Dictionary)
312
+ end
313
+
314
+ return dig_sigs if dig_sigs.count > 0
315
+ raise SignatureError, "Cannot find digital signature"
316
+ end
317
+
303
318
  private
304
319
 
305
320
  #
306
321
  # Verifies the ByteRange field of a digital signature and returned the signed data.
322
+ # Only check for valid ByteRange when it is the last signature
307
323
  #
308
- def extract_signed_data(digsig)
324
+ def extract_signed_data(digsig, last_sig: false)
309
325
  # Computes the boundaries of the Contents field.
310
- start_sig = digsig[:Contents].file_offset
326
+ r1, r2 = digsig.ranges
311
327
 
312
- stream = StringScanner.new(original_data)
313
- stream.pos = digsig[:Contents].file_offset
314
- Object.typeof(stream).parse(stream)
315
- end_sig = stream.pos
316
- stream.terminate
328
+ if last_sig
329
+ start_sig = digsig[:Contents].file_offset
317
330
 
318
- r1, r2 = digsig.ranges
319
- if (r1.begin != 0) ||
320
- (r2.end != original_data.size) ||
321
- (r1.end != start_sig) ||
322
- (r2.begin != end_sig)
331
+ stream = StringScanner.new(original_data)
332
+ stream.pos = digsig[:Contents].file_offset
333
+ Object.typeof(stream).parse(stream)
334
+ end_sig = stream.pos
335
+ stream.terminate
336
+
337
+ if (r1.begin != 0) ||
338
+ (r2.end != original_data.size) ||
339
+ (r1.end != start_sig) ||
340
+ (r2.begin != end_sig)
323
341
 
324
- raise SignatureError, "Invalid signature byte range"
342
+ raise SignatureError, "Invalid signature byte range"
343
+ end
325
344
  end
326
345
 
327
346
  original_data[r1] + original_data[r2]
@@ -19,5 +19,5 @@
19
19
  #
20
20
 
21
21
  module Origami
22
- VERSION = "2.3.1"
22
+ VERSION = "2.3.2"
23
23
  end
@@ -3,8 +3,11 @@
3
3
  require 'minitest/autorun'
4
4
  require 'stringio'
5
5
  require 'openssl'
6
+ require 'origami'
6
7
 
7
8
  class TestSign < Minitest::Test
9
+ include Origami
10
+
8
11
  def create_self_signed_ca_certificate(key_size, expires)
9
12
  key = OpenSSL::PKey::RSA.new key_size
10
13
 
@@ -55,13 +58,7 @@ class TestSign < Minitest::Test
55
58
  def sign_document_with_method(method)
56
59
  document, annotation = setup_document_with_annotation
57
60
 
58
- document.sign(@cert, @key,
59
- method: method,
60
- annotation: annotation,
61
- issuer: "Guillaume Delugré",
62
- location: "France",
63
- contact: "origami@localhost",
64
- reason: "Example")
61
+ sign_document(annotation, document, method)
65
62
 
66
63
  assert document.frozen?
67
64
  assert document.signed?
@@ -83,6 +80,27 @@ class TestSign < Minitest::Test
83
80
  assert result
84
81
  end
85
82
 
83
+ def sign_document_twice_with_method(method)
84
+ document, annotation = setup_document_with_annotation
85
+
86
+ 2.times do
87
+ sign_document(annotation, document, method)
88
+ end
89
+
90
+ assert document.frozen?
91
+ assert document.signed?
92
+
93
+ output = StringIO.new
94
+ document.save(output)
95
+
96
+ document = PDF.read(output.reopen(output.string, 'r'), verbosity: Parser::VERBOSE_QUIET)
97
+
98
+ refute document.verify
99
+ assert document.verify(allow_self_signed: true)
100
+ assert document.verify(trusted_certs: [@cert])
101
+ refute document.verify(trusted_certs: [@other_cert])
102
+ end
103
+
86
104
  def test_sign_pkcs7_sha1
87
105
  sign_document_with_method(Signature::PKCS7_SHA1)
88
106
  end
@@ -94,4 +112,28 @@ class TestSign < Minitest::Test
94
112
  def test_sign_x509_sha1
95
113
  sign_document_with_method(Signature::PKCS1_RSA_SHA1)
96
114
  end
115
+
116
+ def test_sign_pkcs7_sha1_twice
117
+ sign_document_twice_with_method(Signature::PKCS7_SHA1)
118
+ end
119
+
120
+ def test_sign_pkcs7_detached_twice
121
+ sign_document_twice_with_method(Signature::PKCS7_DETACHED)
122
+ end
123
+
124
+ def test_sign_x509_sha1_twice
125
+ sign_document_twice_with_method(Signature::PKCS1_RSA_SHA1)
126
+ end
127
+
128
+ private
129
+
130
+ def sign_document(annotation, document, method)
131
+ document.sign(@cert, @key,
132
+ method: method,
133
+ annotation: annotation,
134
+ issuer: "Guillaume Delugré",
135
+ location: "France",
136
+ contact: "origami@localhost",
137
+ reason: "Example")
138
+ end
97
139
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: origami-docspring
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.1
4
+ version: 2.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Guillaume Delugré