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 +4 -4
- data/CHANGELOG.md +12 -0
- data/lib/hexapdf/task/dereference.rb +12 -4
- data/lib/hexapdf/type/signature/adbe_pkcs7_detached.rb +1 -5
- data/lib/hexapdf/type/signature/adbe_x509_rsa_sha1.rb +1 -5
- data/lib/hexapdf/type/signature/handler.rb +39 -11
- data/lib/hexapdf/version.rb +1 -1
- data/test/hexapdf/test_writer.rb +2 -2
- data/test/hexapdf/type/signature/test_handler.rb +33 -7
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bc35303fc0e58d54568facf02c328c5b29ca0b63fc4e3104cf705d99d8619aa4
|
4
|
+
data.tar.gz: 6803a1286003cd4029fd71971ac854ef4022ff9b7b4f6dcf9fc91e416edae9f0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
71
|
+
dereference_all(@object)
|
72
72
|
else
|
73
|
-
|
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
|
-
|
105
|
+
val = @doc.object(val)
|
106
|
+
@dereference_later.push(val)
|
107
|
+
val
|
101
108
|
when HexaPDF::Object
|
102
|
-
|
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 =
|
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 =
|
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
|
data/lib/hexapdf/version.rb
CHANGED
data/test/hexapdf/test_writer.rb
CHANGED
@@ -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.
|
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.
|
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
|
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.
|
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:
|
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.
|
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
|