hexapdf 0.20.0 → 0.20.1

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: afdc2d646f24ddd3dacfb52ddc7b0f0d1de9dfdaff138e615a2126e16c86d1f4
4
- data.tar.gz: 482f839f70fe4f68d9006f3e8db7427a27d9fbc7a71da219da63653ddfeedb3a
3
+ metadata.gz: bc35303fc0e58d54568facf02c328c5b29ca0b63fc4e3104cf705d99d8619aa4
4
+ data.tar.gz: 6803a1286003cd4029fd71971ac854ef4022ff9b7b4f6dcf9fc91e416edae9f0
5
5
  SHA512:
6
- metadata.gz: 40c32b6df0f349e3f071e54b37fd546f1c87aef0fd645b9990b5e3b6cfa1c5c0bedb734bbc91b3fb09d968f95fdd8e6b78be004383920583cf91f920aa1623ea
7
- data.tar.gz: e9f3f0a0fa0850e2adb308999d2edd7561ecf1871cbd7b2e244ff6f9144b97466a24afa7ebdadb9f27fc8fcb7964ddd913dc3a41c0b46aed01fd10bc4e26c92a
6
+ metadata.gz: e218312b96bc4b7c46a03f8bf47c808d1db07b7161db100a92164fdff90c88603e3494a5e2b0db648ae446b33dfbbecbb82c4d51afa982f8bdda10e3cfe9c4e0
7
+ data.tar.gz: f9c469bc564f675a52cedf7d373da7ba2fc650653459f0cd779ad041488924af3c0575aa7fcd24160b61c4899d92fc3df1e83f11fc2d4a1a32a3e6e9685c6fe8
data/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## 0.20.1 - 2022-01-05
2
+
3
+ ### Changed
4
+
5
+ * Refactored signature handlers, making `#store_verification_callback` a
6
+ protected method
7
+
8
+ ### Fixed
9
+
10
+ * [HexaPDF::Task::Dereference] to work for even very deeply nested structures
11
+
12
+
1
13
  ## 0.20.0 - 2021-12-30
2
14
 
3
15
  ### Added
@@ -68,9 +68,9 @@ module HexaPDF
68
68
 
69
69
  def execute #:nodoc:
70
70
  if @object
71
- dereference(@object)
71
+ dereference_all(@object)
72
72
  else
73
- dereference(@doc.trailer)
73
+ dereference_all(@doc.trailer)
74
74
  @result = []
75
75
  @doc.each(only_current: false) do |obj|
76
76
  if !@seen.key?(obj.data) && obj.type != :ObjStm && obj.type != :XRef
@@ -83,6 +83,11 @@ module HexaPDF
83
83
  end
84
84
  end
85
85
 
86
+ def dereference_all(object) # :nodoc:
87
+ @dereference_later = [object]
88
+ dereference(@dereference_later.pop) until @dereference_later.empty?
89
+ end
90
+
86
91
  def dereference(object) #:nodoc:
87
92
  return object if object.nil? || @seen.key?(object.data)
88
93
  @seen[object.data] = true
@@ -97,9 +102,12 @@ module HexaPDF
97
102
  when Array
98
103
  val.map! {|v| recurse(v) }
99
104
  when HexaPDF::Reference
100
- dereference(@doc.object(val))
105
+ val = @doc.object(val)
106
+ @dereference_later.push(val)
107
+ val
101
108
  when HexaPDF::Object
102
- dereference(val)
109
+ @dereference_later.push(val)
110
+ val
103
111
  else
104
112
  val
105
113
  end
@@ -78,7 +78,7 @@ module HexaPDF
78
78
 
79
79
  # Verifies the signature using the provided OpenSSL::X509::Store object.
80
80
  def verify(store, allow_self_signed: false)
81
- result = VerificationResult.new
81
+ result = super
82
82
 
83
83
  signer_info = self.signer_info
84
84
  signer_certificate = self.signer_certificate
@@ -104,10 +104,6 @@ module HexaPDF
104
104
  result.log(:error, "Certificate key usage is missing 'Digital Signature'")
105
105
  end
106
106
 
107
- verify_signing_time(result)
108
-
109
- store.verify_callback = store_verification_callback(result,
110
- allow_self_signed: allow_self_signed)
111
107
  if @pkcs7.verify(certificate_chain, store, signature_dict.signed_data,
112
108
  OpenSSL::PKCS7::DETACHED | OpenSSL::PKCS7::BINARY)
113
109
  result.log(:info, "Signature valid")
@@ -60,7 +60,7 @@ module HexaPDF
60
60
 
61
61
  # Verifies the signature using the provided OpenSSL::X509::Store object.
62
62
  def verify(store, allow_self_signed: false)
63
- result = VerificationResult.new
63
+ result = super
64
64
 
65
65
  signer_certificate = self.signer_certificate
66
66
  certificate_chain = self.certificate_chain
@@ -76,10 +76,6 @@ module HexaPDF
76
76
  return result
77
77
  end
78
78
 
79
- verify_signing_time(result)
80
-
81
- store.verify_callback = store_verification_callback(result,
82
- allow_self_signed: allow_self_signed)
83
79
  store.verify(signer_certificate, certificate_chain)
84
80
 
85
81
  if signer_certificate.public_key.verify(OpenSSL::Digest.new('SHA1'),
@@ -78,6 +78,45 @@ module HexaPDF
78
78
  raise "Needs to be implemented by specific handlers"
79
79
  end
80
80
 
81
+ # Verifies general signature properties and prepares the provided OpenSSL::X509::Store
82
+ # object for use by concrete implementations.
83
+ #
84
+ # Needs to be called by specific handlers.
85
+ def verify(store, allow_self_signed: false)
86
+ result = VerificationResult.new
87
+ check_certified_signature(result)
88
+ verify_signing_time(result)
89
+ store.verify_callback =
90
+ store_verification_callback(result, allow_self_signed: allow_self_signed)
91
+ result
92
+ end
93
+
94
+ protected
95
+
96
+ # Verifies that the signing time was within the validity period of the signer certificate.
97
+ def verify_signing_time(result)
98
+ time = signing_time
99
+ cert = signer_certificate
100
+ if time && cert && (time < cert.not_before || time > cert.not_after)
101
+ result.log(:error, "Signer certificate not valid at signing time")
102
+ end
103
+ end
104
+
105
+ DOCMDP_PERMS_MESSAGE_MAP = { # :nodoc:
106
+ 1 => "No changes allowed",
107
+ 2 => "Form filling and signing allowed",
108
+ 3 => "Form filling, signing and annotation manipulation allowed",
109
+ }
110
+
111
+ # Sets an informational message on +result+ whether the signature is a certified signature.
112
+ def check_certified_signature(result)
113
+ sigref = signature_dict[:Reference]&.find {|ref| ref[:TransformMethod] == :DocMDP }
114
+ if sigref && signature_dict.document.catalog[:Perms]&.[](:DocMDP) == signature_dict
115
+ perms = sigref[:TransformParams]&.[](:P) || 2
116
+ result.log(:info, "Certified signature (#{DOCMDP_PERMS_MESSAGE_MAP[perms]})")
117
+ end
118
+ end
119
+
81
120
  # Returns the block that should be used as the OpenSSL::X509::Store verification callback.
82
121
  #
83
122
  # +result+:: The VerificationResult object that should be updated if problems are found.
@@ -94,17 +133,6 @@ module HexaPDF
94
133
  end
95
134
  end
96
135
 
97
- protected
98
-
99
- # Verifies that the signing time was within the validity period of the signer certificate.
100
- def verify_signing_time(result)
101
- time = signing_time
102
- cert = signer_certificate
103
- if time && (time < cert.not_before || time > cert.not_after)
104
- result.log(:error, "Signer certificate not valid at signing time")
105
- end
106
- end
107
-
108
136
  end
109
137
 
110
138
  end
@@ -37,6 +37,6 @@
37
37
  module HexaPDF
38
38
 
39
39
  # The version of HexaPDF.
40
- VERSION = '0.20.0'
40
+ VERSION = '0.20.1'
41
41
 
42
42
  end
@@ -40,7 +40,7 @@ describe HexaPDF::Writer do
40
40
  219
41
41
  %%EOF
42
42
  3 0 obj
43
- <</Producer(HexaPDF version 0.20.0)>>
43
+ <</Producer(HexaPDF version 0.20.1)>>
44
44
  endobj
45
45
  xref
46
46
  3 1
@@ -72,7 +72,7 @@ describe HexaPDF::Writer do
72
72
  141
73
73
  %%EOF
74
74
  6 0 obj
75
- <</Producer(HexaPDF version 0.20.0)>>
75
+ <</Producer(HexaPDF version 0.20.1)>>
76
76
  endobj
77
77
  2 0 obj
78
78
  <</Length 10>>stream
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'test_helper'
4
4
  require 'hexapdf/type/signature'
5
+ require 'hexapdf/document'
5
6
  require 'time'
6
7
  require 'ostruct'
7
8
 
@@ -10,6 +11,7 @@ describe HexaPDF::Type::Signature::Handler do
10
11
  @time = Time.parse("2021-11-14 7:00")
11
12
  @dict = {Name: "handler", M: @time}
12
13
  @handler = HexaPDF::Type::Signature::Handler.new(@dict)
14
+ @result = HexaPDF::Type::Signature::VerificationResult.new
13
15
  end
14
16
 
15
17
  it "returns the signer name" do
@@ -30,7 +32,6 @@ describe HexaPDF::Type::Signature::Handler do
30
32
 
31
33
  describe "store_verification_callback" do
32
34
  before do
33
- @result = HexaPDF::Type::Signature::VerificationResult.new
34
35
  @context = OpenStruct.new
35
36
  end
36
37
 
@@ -40,7 +41,7 @@ describe HexaPDF::Type::Signature::Handler do
40
41
  [true, false].each do |allow_self_signed|
41
42
  @result.messages.clear
42
43
  @context.error = error
43
- @handler.store_verification_callback(@result, allow_self_signed: allow_self_signed).
44
+ @handler.send(:store_verification_callback, @result, allow_self_signed: allow_self_signed).
44
45
  call(false, @context)
45
46
  assert_equal(1, @result.messages.size)
46
47
  assert_match(/self-signed certificate/i, @result.messages[0].content)
@@ -51,26 +52,51 @@ describe HexaPDF::Type::Signature::Handler do
51
52
  end
52
53
 
53
54
  it "verifies the signing time" do
54
- result = HexaPDF::Type::Signature::VerificationResult.new
55
55
  [
56
56
  [true, '6:00', '8:00'],
57
57
  [false, '7:30', '8:00'],
58
58
  [false, '5:00', '6:00'],
59
59
  ].each do |success, not_before, not_after|
60
- result.messages.clear
60
+ @result.messages.clear
61
61
  @handler.define_singleton_method(:signer_certificate) do
62
62
  OpenStruct.new.tap do |struct|
63
63
  struct.not_before = Time.parse("2021-11-14 #{not_before}")
64
64
  struct.not_after = Time.parse("2021-11-14 #{not_after}")
65
65
  end
66
66
  end
67
- @handler.send(:verify_signing_time, result)
67
+ @handler.send(:verify_signing_time, @result)
68
68
  if success
69
- assert(result.messages.empty?)
69
+ assert(@result.messages.empty?)
70
70
  else
71
- assert_equal(1, result.messages.size)
71
+ assert_equal(1, @result.messages.size)
72
72
  end
73
73
  @handler.singleton_class.remove_method(:signer_certificate)
74
74
  end
75
75
  end
76
+
77
+ describe "check_certified_signature" do
78
+ before do
79
+ @dict = HexaPDF::Document.new.wrap({Type: :Sig})
80
+ @handler.instance_variable_set(:@signature_dict, @dict)
81
+ end
82
+
83
+ it "logs nothing if there is no signature reference dictionary" do
84
+ @handler.send(:check_certified_signature, @result)
85
+ assert(@result.messages.empty?)
86
+ end
87
+
88
+ it "logs nothing if the global DocMDP permissions entry doesn't point to the signature" do
89
+ @dict[:Reference] = [{TransformMethod: :DocMDP}]
90
+ @handler.send(:check_certified_signature, @result)
91
+ assert(@result.messages.empty?)
92
+ end
93
+
94
+ it "logs a message if the signature is a certified one" do
95
+ @dict[:Reference] = [{TransformMethod: :DocMDP}]
96
+ @dict.document.catalog[:Perms] = {DocMDP: @dict}
97
+ @handler.send(:check_certified_signature, @result)
98
+ assert_equal(1, @result.messages.size)
99
+ assert_match(/certified signature/i, @result.messages[0].content)
100
+ end
101
+ end
76
102
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hexapdf
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.20.0
4
+ version: 0.20.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thomas Leitner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-12-30 00:00:00.000000000 Z
11
+ date: 2022-01-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cmdparse
@@ -671,7 +671,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
671
671
  - !ruby/object:Gem::Version
672
672
  version: '0'
673
673
  requirements: []
674
- rubygems_version: 3.2.32
674
+ rubygems_version: 3.3.3
675
675
  signing_key:
676
676
  specification_version: 4
677
677
  summary: HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby