hexapdf 0.17.1 → 0.17.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 +4 -4
- data/CHANGELOG.md +1024 -0
- data/LICENSE +29 -0
- data/README.md +129 -0
- data/Rakefile +109 -0
- data/agpl-3.0.txt +661 -0
- data/examples/001-hello_world.rb +16 -0
- data/examples/002-graphics.rb +275 -0
- data/examples/003-arcs.rb +50 -0
- data/examples/004-optimizing.rb +23 -0
- data/examples/005-merging.rb +27 -0
- data/examples/006-standard_pdf_fonts.rb +73 -0
- data/examples/007-truetype.rb +42 -0
- data/examples/008-show_char_bboxes.rb +55 -0
- data/examples/009-text_layouter_alignment.rb +47 -0
- data/examples/010-text_layouter_inline_boxes.rb +64 -0
- data/examples/011-text_layouter_line_wrapping.rb +57 -0
- data/examples/012-text_layouter_styling.rb +122 -0
- data/examples/013-text_layouter_shapes.rb +176 -0
- data/examples/014-text_in_polygon.rb +60 -0
- data/examples/015-boxes.rb +76 -0
- data/examples/016-frame_automatic_box_placement.rb +90 -0
- data/examples/017-frame_text_flow.rb +60 -0
- data/examples/018-composer.rb +44 -0
- data/examples/019-acro_form.rb +88 -0
- data/examples/emoji-smile.png +0 -0
- data/examples/emoji-wink.png +0 -0
- data/examples/machupicchu.jpg +0 -0
- data/lib/hexapdf/content/graphic_object/endpoint_arc.rb +66 -0
- data/lib/hexapdf/content/graphic_object/geom2d.rb +13 -0
- data/lib/hexapdf/version.rb +1 -1
- data/test/data/aes-test-vectors/CBCGFSbox-128-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCGFSbox-128-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCGFSbox-192-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCGFSbox-192-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCGFSbox-256-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCGFSbox-256-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCKeySbox-128-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCKeySbox-128-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCKeySbox-192-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCKeySbox-192-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCKeySbox-256-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCKeySbox-256-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarKey-128-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarKey-128-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarKey-192-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarKey-192-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarKey-256-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarKey-256-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarTxt-128-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarTxt-128-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarTxt-192-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarTxt-192-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarTxt-256-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarTxt-256-encrypt.data.gz +0 -0
- data/test/data/fonts/Ubuntu-Title.ttf +0 -0
- data/test/data/images/cmyk.jpg +0 -0
- data/test/data/images/fillbytes.jpg +0 -0
- data/test/data/images/gray.jpg +0 -0
- data/test/data/images/greyscale-1bit.png +0 -0
- data/test/data/images/greyscale-2bit.png +0 -0
- data/test/data/images/greyscale-4bit.png +0 -0
- data/test/data/images/greyscale-8bit.png +0 -0
- data/test/data/images/greyscale-alpha-8bit.png +0 -0
- data/test/data/images/greyscale-trns-8bit.png +0 -0
- data/test/data/images/greyscale-with-gamma1.0.png +0 -0
- data/test/data/images/greyscale-with-gamma1.5.png +0 -0
- data/test/data/images/indexed-1bit.png +0 -0
- data/test/data/images/indexed-2bit.png +0 -0
- data/test/data/images/indexed-4bit.png +0 -0
- data/test/data/images/indexed-8bit.png +0 -0
- data/test/data/images/indexed-alpha-4bit.png +0 -0
- data/test/data/images/indexed-alpha-8bit.png +0 -0
- data/test/data/images/rgb.jpg +0 -0
- data/test/data/images/truecolour-8bit.png +0 -0
- data/test/data/images/truecolour-alpha-8bit.png +0 -0
- data/test/data/images/truecolour-gama-chrm-8bit.png +0 -0
- data/test/data/images/truecolour-srgb-8bit.png +0 -0
- data/test/data/images/ycck.jpg +0 -0
- data/test/data/minimal.pdf +44 -0
- data/test/data/standard-security-handler/README +9 -0
- data/test/data/standard-security-handler/bothpwd-aes-128bit-V4.pdf +44 -0
- data/test/data/standard-security-handler/bothpwd-aes-256bit-V5.pdf +0 -0
- data/test/data/standard-security-handler/bothpwd-arc4-128bit-V2.pdf +43 -0
- data/test/data/standard-security-handler/bothpwd-arc4-128bit-V4.pdf +43 -0
- data/test/data/standard-security-handler/bothpwd-arc4-40bit-V1.pdf +0 -0
- data/test/data/standard-security-handler/nopwd-aes-128bit-V4.pdf +43 -0
- data/test/data/standard-security-handler/nopwd-aes-256bit-V5.pdf +0 -0
- data/test/data/standard-security-handler/nopwd-arc4-128bit-V2.pdf +43 -0
- data/test/data/standard-security-handler/nopwd-arc4-128bit-V4.pdf +43 -0
- data/test/data/standard-security-handler/nopwd-arc4-40bit-V1.pdf +43 -0
- data/test/data/standard-security-handler/ownerpwd-aes-128bit-V4.pdf +0 -0
- data/test/data/standard-security-handler/ownerpwd-aes-256bit-V5.pdf +43 -0
- data/test/data/standard-security-handler/ownerpwd-arc4-128bit-V2.pdf +43 -0
- data/test/data/standard-security-handler/ownerpwd-arc4-128bit-V4.pdf +43 -0
- data/test/data/standard-security-handler/ownerpwd-arc4-40bit-V1.pdf +43 -0
- data/test/data/standard-security-handler/userpwd-aes-128bit-V4.pdf +43 -0
- data/test/data/standard-security-handler/userpwd-aes-256bit-V5.pdf +43 -0
- data/test/data/standard-security-handler/userpwd-arc4-128bit-V2.pdf +0 -0
- data/test/data/standard-security-handler/userpwd-arc4-128bit-V4.pdf +0 -0
- data/test/data/standard-security-handler/userpwd-arc4-40bit-V1.pdf +43 -0
- data/test/hexapdf/common_tokenizer_tests.rb +236 -0
- data/test/hexapdf/content/common.rb +39 -0
- data/test/hexapdf/content/graphic_object/test_arc.rb +102 -0
- data/test/hexapdf/content/graphic_object/test_endpoint_arc.rb +90 -0
- data/test/hexapdf/content/graphic_object/test_geom2d.rb +79 -0
- data/test/hexapdf/content/graphic_object/test_solid_arc.rb +86 -0
- data/test/hexapdf/content/test_canvas.rb +1279 -0
- data/test/hexapdf/content/test_color_space.rb +176 -0
- data/test/hexapdf/content/test_graphics_state.rb +151 -0
- data/test/hexapdf/content/test_operator.rb +619 -0
- data/test/hexapdf/content/test_parser.rb +99 -0
- data/test/hexapdf/content/test_processor.rb +163 -0
- data/test/hexapdf/content/test_transformation_matrix.rb +64 -0
- data/test/hexapdf/document/test_files.rb +72 -0
- data/test/hexapdf/document/test_fonts.rb +60 -0
- data/test/hexapdf/document/test_images.rb +72 -0
- data/test/hexapdf/document/test_pages.rb +130 -0
- data/test/hexapdf/encryption/common.rb +87 -0
- data/test/hexapdf/encryption/test_aes.rb +129 -0
- data/test/hexapdf/encryption/test_arc4.rb +39 -0
- data/test/hexapdf/encryption/test_fast_aes.rb +17 -0
- data/test/hexapdf/encryption/test_fast_arc4.rb +12 -0
- data/test/hexapdf/encryption/test_identity.rb +21 -0
- data/test/hexapdf/encryption/test_ruby_aes.rb +23 -0
- data/test/hexapdf/encryption/test_ruby_arc4.rb +20 -0
- data/test/hexapdf/encryption/test_security_handler.rb +380 -0
- data/test/hexapdf/encryption/test_standard_security_handler.rb +322 -0
- data/test/hexapdf/filter/common.rb +53 -0
- data/test/hexapdf/filter/test_ascii85_decode.rb +59 -0
- data/test/hexapdf/filter/test_ascii_hex_decode.rb +38 -0
- data/test/hexapdf/filter/test_crypt.rb +21 -0
- data/test/hexapdf/filter/test_encryption.rb +24 -0
- data/test/hexapdf/filter/test_flate_decode.rb +44 -0
- data/test/hexapdf/filter/test_lzw_decode.rb +52 -0
- data/test/hexapdf/filter/test_predictor.rb +219 -0
- data/test/hexapdf/filter/test_run_length_decode.rb +32 -0
- data/test/hexapdf/font/cmap/test_parser.rb +102 -0
- data/test/hexapdf/font/cmap/test_writer.rb +66 -0
- data/test/hexapdf/font/encoding/test_base.rb +45 -0
- data/test/hexapdf/font/encoding/test_difference_encoding.rb +29 -0
- data/test/hexapdf/font/encoding/test_glyph_list.rb +59 -0
- data/test/hexapdf/font/encoding/test_zapf_dingbats_encoding.rb +16 -0
- data/test/hexapdf/font/test_cmap.rb +104 -0
- data/test/hexapdf/font/test_encoding.rb +27 -0
- data/test/hexapdf/font/test_invalid_glyph.rb +34 -0
- data/test/hexapdf/font/test_true_type_wrapper.rb +186 -0
- data/test/hexapdf/font/test_type1_wrapper.rb +107 -0
- data/test/hexapdf/font/true_type/common.rb +17 -0
- data/test/hexapdf/font/true_type/table/common.rb +27 -0
- data/test/hexapdf/font/true_type/table/test_cmap.rb +47 -0
- data/test/hexapdf/font/true_type/table/test_cmap_subtable.rb +141 -0
- data/test/hexapdf/font/true_type/table/test_directory.rb +30 -0
- data/test/hexapdf/font/true_type/table/test_glyf.rb +58 -0
- data/test/hexapdf/font/true_type/table/test_head.rb +56 -0
- data/test/hexapdf/font/true_type/table/test_hhea.rb +26 -0
- data/test/hexapdf/font/true_type/table/test_hmtx.rb +30 -0
- data/test/hexapdf/font/true_type/table/test_kern.rb +61 -0
- data/test/hexapdf/font/true_type/table/test_loca.rb +33 -0
- data/test/hexapdf/font/true_type/table/test_maxp.rb +50 -0
- data/test/hexapdf/font/true_type/table/test_name.rb +76 -0
- data/test/hexapdf/font/true_type/table/test_os2.rb +55 -0
- data/test/hexapdf/font/true_type/table/test_post.rb +78 -0
- data/test/hexapdf/font/true_type/test_builder.rb +42 -0
- data/test/hexapdf/font/true_type/test_font.rb +116 -0
- data/test/hexapdf/font/true_type/test_optimizer.rb +26 -0
- data/test/hexapdf/font/true_type/test_subsetter.rb +73 -0
- data/test/hexapdf/font/true_type/test_table.rb +48 -0
- data/test/hexapdf/font/type1/common.rb +6 -0
- data/test/hexapdf/font/type1/test_afm_parser.rb +65 -0
- data/test/hexapdf/font/type1/test_font.rb +104 -0
- data/test/hexapdf/font/type1/test_font_metrics.rb +22 -0
- data/test/hexapdf/font/type1/test_pfb_parser.rb +37 -0
- data/test/hexapdf/font_loader/test_from_configuration.rb +43 -0
- data/test/hexapdf/font_loader/test_from_file.rb +36 -0
- data/test/hexapdf/font_loader/test_standard14.rb +33 -0
- data/test/hexapdf/image_loader/test_jpeg.rb +93 -0
- data/test/hexapdf/image_loader/test_pdf.rb +47 -0
- data/test/hexapdf/image_loader/test_png.rb +259 -0
- data/test/hexapdf/layout/test_box.rb +154 -0
- data/test/hexapdf/layout/test_frame.rb +350 -0
- data/test/hexapdf/layout/test_image_box.rb +73 -0
- data/test/hexapdf/layout/test_inline_box.rb +71 -0
- data/test/hexapdf/layout/test_line.rb +206 -0
- data/test/hexapdf/layout/test_style.rb +790 -0
- data/test/hexapdf/layout/test_text_box.rb +140 -0
- data/test/hexapdf/layout/test_text_fragment.rb +375 -0
- data/test/hexapdf/layout/test_text_layouter.rb +758 -0
- data/test/hexapdf/layout/test_text_shaper.rb +62 -0
- data/test/hexapdf/layout/test_width_from_polygon.rb +109 -0
- data/test/hexapdf/task/test_dereference.rb +51 -0
- data/test/hexapdf/task/test_optimize.rb +162 -0
- data/test/hexapdf/test_composer.rb +258 -0
- data/test/hexapdf/test_configuration.rb +93 -0
- data/test/hexapdf/test_data_dir.rb +32 -0
- data/test/hexapdf/test_dictionary.rb +340 -0
- data/test/hexapdf/test_dictionary_fields.rb +269 -0
- data/test/hexapdf/test_document.rb +641 -0
- data/test/hexapdf/test_filter.rb +100 -0
- data/test/hexapdf/test_importer.rb +106 -0
- data/test/hexapdf/test_object.rb +258 -0
- data/test/hexapdf/test_parser.rb +645 -0
- data/test/hexapdf/test_pdf_array.rb +169 -0
- data/test/hexapdf/test_rectangle.rb +73 -0
- data/test/hexapdf/test_reference.rb +50 -0
- data/test/hexapdf/test_revision.rb +188 -0
- data/test/hexapdf/test_revisions.rb +196 -0
- data/test/hexapdf/test_serializer.rb +195 -0
- data/test/hexapdf/test_stream.rb +274 -0
- data/test/hexapdf/test_tokenizer.rb +80 -0
- data/test/hexapdf/test_type.rb +18 -0
- data/test/hexapdf/test_writer.rb +140 -0
- data/test/hexapdf/test_xref_section.rb +61 -0
- data/test/hexapdf/type/acro_form/test_appearance_generator.rb +795 -0
- data/test/hexapdf/type/acro_form/test_button_field.rb +308 -0
- data/test/hexapdf/type/acro_form/test_choice_field.rb +220 -0
- data/test/hexapdf/type/acro_form/test_field.rb +259 -0
- data/test/hexapdf/type/acro_form/test_form.rb +357 -0
- data/test/hexapdf/type/acro_form/test_signature_field.rb +38 -0
- data/test/hexapdf/type/acro_form/test_text_field.rb +201 -0
- data/test/hexapdf/type/acro_form/test_variable_text_field.rb +88 -0
- data/test/hexapdf/type/actions/test_launch.rb +24 -0
- data/test/hexapdf/type/actions/test_uri.rb +23 -0
- data/test/hexapdf/type/annotations/test_markup_annotation.rb +22 -0
- data/test/hexapdf/type/annotations/test_text.rb +34 -0
- data/test/hexapdf/type/annotations/test_widget.rb +225 -0
- data/test/hexapdf/type/test_annotation.rb +97 -0
- data/test/hexapdf/type/test_catalog.rb +48 -0
- data/test/hexapdf/type/test_cid_font.rb +61 -0
- data/test/hexapdf/type/test_file_specification.rb +141 -0
- data/test/hexapdf/type/test_font.rb +67 -0
- data/test/hexapdf/type/test_font_descriptor.rb +61 -0
- data/test/hexapdf/type/test_font_simple.rb +176 -0
- data/test/hexapdf/type/test_font_true_type.rb +31 -0
- data/test/hexapdf/type/test_font_type0.rb +120 -0
- data/test/hexapdf/type/test_font_type1.rb +142 -0
- data/test/hexapdf/type/test_font_type3.rb +26 -0
- data/test/hexapdf/type/test_form.rb +120 -0
- data/test/hexapdf/type/test_image.rb +261 -0
- data/test/hexapdf/type/test_info.rb +9 -0
- data/test/hexapdf/type/test_object_stream.rb +117 -0
- data/test/hexapdf/type/test_page.rb +598 -0
- data/test/hexapdf/type/test_page_tree_node.rb +315 -0
- data/test/hexapdf/type/test_resources.rb +209 -0
- data/test/hexapdf/type/test_trailer.rb +116 -0
- data/test/hexapdf/type/test_xref_stream.rb +143 -0
- data/test/hexapdf/utils/test_bit_field.rb +63 -0
- data/test/hexapdf/utils/test_bit_stream.rb +69 -0
- data/test/hexapdf/utils/test_graphics_helpers.rb +37 -0
- data/test/hexapdf/utils/test_lru_cache.rb +22 -0
- data/test/hexapdf/utils/test_object_hash.rb +120 -0
- data/test/hexapdf/utils/test_pdf_doc_encoding.rb +18 -0
- data/test/hexapdf/utils/test_sorted_tree_node.rb +239 -0
- data/test/test_helper.rb +58 -0
- metadata +263 -3
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
require_relative 'common'
|
|
4
|
+
require 'hexapdf/encryption/aes'
|
|
5
|
+
|
|
6
|
+
describe HexaPDF::Encryption::AES do
|
|
7
|
+
include EncryptionAlgorithmInterfaceTests
|
|
8
|
+
|
|
9
|
+
before do
|
|
10
|
+
@algorithm_class = Class.new do
|
|
11
|
+
prepend HexaPDF::Encryption::AES
|
|
12
|
+
|
|
13
|
+
attr_reader :key, :iv, :mode
|
|
14
|
+
|
|
15
|
+
def initialize(key, iv, mode)
|
|
16
|
+
@key, @iv, @mode = key, iv, mode
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def process(data)
|
|
20
|
+
raise "invalid data" if data.empty? || data.length % 16 != 0
|
|
21
|
+
data
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
@padding_data = (0..15).map do |length|
|
|
26
|
+
{
|
|
27
|
+
plain: '5' * length,
|
|
28
|
+
cipher_padding: '5' * length + (16 - length).chr * (16 - length),
|
|
29
|
+
length: 32,
|
|
30
|
+
}
|
|
31
|
+
end
|
|
32
|
+
@padding_data << {plain: '5' * 16, cipher_padding: '5' * 16 + 16.chr * 16, length: 48}
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
describe "klass.encrypt/.decrypt" do
|
|
36
|
+
it "returns the padded result with IV on klass.encrypt" do
|
|
37
|
+
@padding_data.each do |data|
|
|
38
|
+
result = @algorithm_class.encrypt('some key' * 2, data[:plain])
|
|
39
|
+
assert_equal(data[:length], result.length)
|
|
40
|
+
assert_equal(data[:cipher_padding][-16, 16], result[-16, 16])
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "returns the decrypted result without padding and with IV removed on klass.decrypt" do
|
|
45
|
+
@padding_data.each do |data|
|
|
46
|
+
result = @algorithm_class.decrypt('some key' * 2, 'iv' * 8 + data[:cipher_padding])
|
|
47
|
+
assert_equal(data[:plain], result)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it "fails on decryption if not enough bytes are provided" do
|
|
52
|
+
assert_raises(HexaPDF::EncryptionError) do
|
|
53
|
+
@algorithm_class.decrypt('some' * 4, 'no iv')
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
describe "klass.encryption_fiber/.decryption_fiber" do
|
|
59
|
+
before do
|
|
60
|
+
@fiber = Fiber.new { Fiber.yield('first'); 'second' }
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it "returns the padded result with IV on encryption_fiber" do
|
|
64
|
+
@padding_data.each do |data|
|
|
65
|
+
result = @algorithm_class.encryption_fiber('some key' * 2, Fiber.new { data[:plain] })
|
|
66
|
+
result = TestHelper.collector(result)
|
|
67
|
+
assert_equal(data[:length], result.length)
|
|
68
|
+
assert_equal(data[:cipher_padding][-16, 16], result[-16, 16])
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it "returns the decrypted result without padding and with IV removed on decryption_fiber" do
|
|
73
|
+
@padding_data.each do |data|
|
|
74
|
+
result = @algorithm_class.decryption_fiber('some key' * 2,
|
|
75
|
+
Fiber.new { 'iv' * 8 + data[:cipher_padding] })
|
|
76
|
+
result = TestHelper.collector(result)
|
|
77
|
+
assert_equal(data[:plain], result)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it "encryption works with multiple yielded strings" do
|
|
82
|
+
f = Fiber.new { Fiber.yield('a' * 40); Fiber.yield('test'); "b" * 20 }
|
|
83
|
+
result = TestHelper.collector(@algorithm_class.encryption_fiber('some key' * 2, f))
|
|
84
|
+
assert_equal('a' * 40 << 'test' << 'b' * 20, result[16..-17])
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
it "decryption works with multiple yielded strings" do
|
|
88
|
+
f = Fiber.new do
|
|
89
|
+
Fiber.yield('iv' * 4)
|
|
90
|
+
Fiber.yield('iv' * 4)
|
|
91
|
+
Fiber.yield('a' * 20)
|
|
92
|
+
Fiber.yield('a' * 20)
|
|
93
|
+
8.chr * 8
|
|
94
|
+
end
|
|
95
|
+
result = TestHelper.collector(@algorithm_class.decryption_fiber('some key' * 2, f))
|
|
96
|
+
assert_equal('a' * 40, result)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
it "decryption works if the padding is invalid" do
|
|
100
|
+
f = Fiber.new { 'a' * 32 }
|
|
101
|
+
result = TestHelper.collector(@algorithm_class.decryption_fiber('some' * 4, f))
|
|
102
|
+
assert_equal('a' * 16, result)
|
|
103
|
+
|
|
104
|
+
f = Fiber.new { 'a' * 31 << "\x00" }
|
|
105
|
+
result = TestHelper.collector(@algorithm_class.decryption_fiber('some' * 4, f))
|
|
106
|
+
assert_equal('a' * 15 << "\x00", result)
|
|
107
|
+
|
|
108
|
+
f = Fiber.new { 'a' * 29 << "\x00\x01\x03" }
|
|
109
|
+
result = TestHelper.collector(@algorithm_class.decryption_fiber('some' * 4, f))
|
|
110
|
+
assert_equal('a' * 13 << "\x00\x01\x03", result)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
it "fails on decryption if not enough bytes are provided" do
|
|
114
|
+
[4, 20, 40].each do |length|
|
|
115
|
+
assert_raises(HexaPDF::EncryptionError) do
|
|
116
|
+
TestHelper.collector(@algorithm_class.decryption_fiber('some' * 4,
|
|
117
|
+
Fiber.new { 'a' * length }))
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
it "does basic validation on initialization" do
|
|
124
|
+
assert_raises(HexaPDF::EncryptionError) { @algorithm_class.new('t' * 7, '0' * 16, :encrypt) }
|
|
125
|
+
assert_raises(HexaPDF::EncryptionError) { @algorithm_class.new('t' * 16, '0' * 7, :encrypt) }
|
|
126
|
+
obj = @algorithm_class.new('t' * 16, 'i' * 16, 'encrypt')
|
|
127
|
+
assert_equal(:encrypt, obj.mode)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
require_relative 'common'
|
|
4
|
+
require 'hexapdf/encryption/arc4'
|
|
5
|
+
|
|
6
|
+
describe HexaPDF::Encryption::ARC4 do
|
|
7
|
+
include EncryptionAlgorithmInterfaceTests
|
|
8
|
+
|
|
9
|
+
before do
|
|
10
|
+
@algorithm_class = Class.new do
|
|
11
|
+
prepend HexaPDF::Encryption::ARC4
|
|
12
|
+
|
|
13
|
+
def initialize(key)
|
|
14
|
+
@data = key
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def process(data)
|
|
18
|
+
raise if data.empty?
|
|
19
|
+
result = @data << data
|
|
20
|
+
@data = ''
|
|
21
|
+
result
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "correctly uses klass.encrypt and klass.decrypt" do
|
|
27
|
+
assert_equal('mykeydata', @algorithm_class.encrypt('mykey', 'data'))
|
|
28
|
+
assert_equal('mykeydata', @algorithm_class.decrypt('mykey', 'data'))
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "correctly uses klass.encryption_fiber and klass.decryption_fiber" do
|
|
32
|
+
f = Fiber.new { Fiber.yield('first'); Fiber.yield(''); 'second' }
|
|
33
|
+
assert_equal('mykeyfirstsecond',
|
|
34
|
+
TestHelper.collector(@algorithm_class.encryption_fiber('mykey', f)))
|
|
35
|
+
f = Fiber.new { Fiber.yield('first'); 'second' }
|
|
36
|
+
assert_equal('mykeyfirstsecond',
|
|
37
|
+
TestHelper.collector(@algorithm_class.decryption_fiber('mykey', f)))
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
require_relative 'common'
|
|
4
|
+
require 'hexapdf/encryption/fast_aes'
|
|
5
|
+
|
|
6
|
+
describe HexaPDF::Encryption::FastAES do
|
|
7
|
+
include AESEncryptionTests
|
|
8
|
+
|
|
9
|
+
before do
|
|
10
|
+
@algorithm_class = HexaPDF::Encryption::FastAES
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it "uses a better random bytes generator" do
|
|
14
|
+
assert_equal(@algorithm_class.singleton_class, @algorithm_class.method(:random_bytes).owner)
|
|
15
|
+
assert_equal(16, @algorithm_class.random_bytes(16).length)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
require_relative 'common'
|
|
4
|
+
require 'hexapdf/encryption/identity'
|
|
5
|
+
|
|
6
|
+
describe HexaPDF::Encryption::Identity do
|
|
7
|
+
include EncryptionAlgorithmInterfaceTests
|
|
8
|
+
|
|
9
|
+
before do
|
|
10
|
+
@algorithm_class = HexaPDF::Encryption::Identity
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it "returns the data unmodified for encrypt/decrypt" do
|
|
14
|
+
assert_equal('data', @algorithm_class.encrypt('key', 'data'))
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "returns the source Fiber unmodified for encryption_fiber/decryption_fiber" do
|
|
18
|
+
f = Fiber.new { 'data' }
|
|
19
|
+
assert_equal(f, @algorithm_class.encryption_fiber('key', f))
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
require_relative 'common'
|
|
4
|
+
require 'hexapdf/encryption/ruby_aes'
|
|
5
|
+
require 'hexapdf/encryption/fast_aes'
|
|
6
|
+
|
|
7
|
+
describe HexaPDF::Encryption::RubyAES do
|
|
8
|
+
include AESEncryptionTests
|
|
9
|
+
|
|
10
|
+
before do
|
|
11
|
+
@algorithm_class = HexaPDF::Encryption::RubyAES
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it "is compatible with the OpenSSL based FastAES implementation" do
|
|
15
|
+
sample = Random.new.bytes(1024)
|
|
16
|
+
key = Random.new.bytes(16)
|
|
17
|
+
iv = Random.new.bytes(16)
|
|
18
|
+
assert_equal(sample, HexaPDF::Encryption::FastAES.new(key, iv, :encrypt).
|
|
19
|
+
process(HexaPDF::Encryption::RubyAES.new(key, iv, :decrypt).process(sample)))
|
|
20
|
+
assert_equal(sample, HexaPDF::Encryption::FastAES.new(key, iv, :decrypt).
|
|
21
|
+
process(HexaPDF::Encryption::RubyAES.new(key, iv, :encrypt).process(sample)))
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
require_relative 'common'
|
|
4
|
+
require 'hexapdf/encryption/ruby_arc4'
|
|
5
|
+
require 'hexapdf/encryption/fast_arc4'
|
|
6
|
+
|
|
7
|
+
describe HexaPDF::Encryption::RubyARC4 do
|
|
8
|
+
include ARC4EncryptionTests
|
|
9
|
+
|
|
10
|
+
before do
|
|
11
|
+
@algorithm_class = HexaPDF::Encryption::RubyARC4
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it "is compatible with the OpenSSL based FastARC4 implementation" do
|
|
15
|
+
@keys.each_with_index do |key, i|
|
|
16
|
+
assert_equal(@plain[i], HexaPDF::Encryption::FastARC4.new(key).
|
|
17
|
+
process(HexaPDF::Encryption::RubyARC4.new(key).process(@plain[i])))
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
require 'test_helper'
|
|
4
|
+
require 'hexapdf/encryption/security_handler'
|
|
5
|
+
require 'hexapdf/document'
|
|
6
|
+
require 'hexapdf/stream'
|
|
7
|
+
|
|
8
|
+
describe HexaPDF::Encryption::EncryptionDictionary do
|
|
9
|
+
before do
|
|
10
|
+
@document = HexaPDF::Document.new
|
|
11
|
+
@dict = HexaPDF::Encryption::EncryptionDictionary.new({}, document: @document)
|
|
12
|
+
@dict[:Filter] = :Standard
|
|
13
|
+
@dict[:V] = 1
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "must be an indirect object" do
|
|
17
|
+
assert(@dict.must_be_indirect?)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "validates the /V value" do
|
|
21
|
+
@dict[:V] = 1
|
|
22
|
+
assert(@dict.validate)
|
|
23
|
+
@dict[:V] = 3
|
|
24
|
+
refute(@dict.validate)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "validates the /Length field when /V=2" do
|
|
28
|
+
@dict[:V] = 2
|
|
29
|
+
refute(@dict.validate)
|
|
30
|
+
|
|
31
|
+
@dict[:Length] = 32
|
|
32
|
+
refute(@dict.validate)
|
|
33
|
+
@dict[:Length] = 136
|
|
34
|
+
refute(@dict.validate)
|
|
35
|
+
@dict[:Length] = 55
|
|
36
|
+
refute(@dict.validate)
|
|
37
|
+
|
|
38
|
+
@dict[:Length] = 120
|
|
39
|
+
assert(@dict.validate)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
describe HexaPDF::Encryption::SecurityHandler do
|
|
44
|
+
class TestHandler < HexaPDF::Encryption::SecurityHandler
|
|
45
|
+
|
|
46
|
+
attr_accessor :strf, :myopt
|
|
47
|
+
public :dict
|
|
48
|
+
|
|
49
|
+
def prepare_encryption(**_options)
|
|
50
|
+
dict[:Filter] = :Test
|
|
51
|
+
@key = "a" * key_length
|
|
52
|
+
@strf ||= :aes
|
|
53
|
+
@stmf ||= :arc4
|
|
54
|
+
@eff ||= :identity
|
|
55
|
+
[@key, @strf, @stmf, @eff]
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def prepare_decryption(myopt: nil)
|
|
59
|
+
@myopt = myopt
|
|
60
|
+
@key = "a" * key_length
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
before do
|
|
66
|
+
@document = HexaPDF::Document.new
|
|
67
|
+
@obj = @document.add({})
|
|
68
|
+
@handler = TestHandler.new(@document)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
describe "class methods" do
|
|
72
|
+
before do
|
|
73
|
+
@document.config['encryption.filter_map'][:Test] = TestHandler
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
describe "set_up_encryption" do
|
|
77
|
+
it "fails if the requested security handler cannot be found" do
|
|
78
|
+
assert_raises(HexaPDF::EncryptionError) do
|
|
79
|
+
HexaPDF::Encryption::SecurityHandler.set_up_encryption(@document, :non_standard)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it "updates the trailer's /Encrypt entry to be wrapped by an encryption dictionary" do
|
|
84
|
+
HexaPDF::Encryption::SecurityHandler.set_up_encryption(@document, :Test)
|
|
85
|
+
assert_kind_of(HexaPDF::Encryption::EncryptionDictionary, @document.trailer[:Encrypt])
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
it "returns the frozen security handler" do
|
|
89
|
+
handler = HexaPDF::Encryption::SecurityHandler.set_up_encryption(@document, :Test)
|
|
90
|
+
assert(handler.frozen?)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
describe "set_up_decryption" do
|
|
95
|
+
it "fails if the document has no /Encrypt dictionary" do
|
|
96
|
+
exp = assert_raises(HexaPDF::EncryptionError) do
|
|
97
|
+
HexaPDF::Encryption::SecurityHandler.set_up_decryption(@document)
|
|
98
|
+
end
|
|
99
|
+
assert_match(/No \/Encrypt/i, exp.message)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
it "fails if the requested security handler cannot be found" do
|
|
103
|
+
@document.trailer[:Encrypt] = {Filter: :NonStandard}
|
|
104
|
+
assert_raises(HexaPDF::EncryptionError) do
|
|
105
|
+
HexaPDF::Encryption::SecurityHandler.set_up_decryption(@document)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
it "updates the trailer's /Encrypt entry to be wrapped by an encryption dictionary" do
|
|
110
|
+
@document.trailer[:Encrypt] = {Filter: :Test, V: 1}
|
|
111
|
+
HexaPDF::Encryption::SecurityHandler.set_up_decryption(@document)
|
|
112
|
+
assert_kind_of(HexaPDF::Encryption::EncryptionDictionary, @document.trailer[:Encrypt])
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
it "returns the frozen security handler" do
|
|
116
|
+
@document.trailer[:Encrypt] = {Filter: :Test, V: 1}
|
|
117
|
+
handler = HexaPDF::Encryption::SecurityHandler.set_up_decryption(@document)
|
|
118
|
+
assert(handler.frozen?)
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
it "doesn't have a valid encryption key directly after creation" do
|
|
124
|
+
refute(@handler.encryption_key_valid?)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
describe "set_up_encryption" do
|
|
128
|
+
it "sets the correct /V value for the given key length and algorithm" do
|
|
129
|
+
[[40, :arc4, 1], [128, :arc4, 2], [128, :arc4, 4],
|
|
130
|
+
[128, :aes, 4], [256, :aes, 5]].each do |length, algorithm, version|
|
|
131
|
+
@handler.set_up_encryption(key_length: length, algorithm: algorithm, force_v4: version == 4)
|
|
132
|
+
assert_equal(version, @handler.dict[:V])
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
it "sets the correct /Length value for the given key length" do
|
|
137
|
+
[[40, nil], [48, 48], [128, 128], [256, nil]].each do |key_length, result|
|
|
138
|
+
algorithm = (key_length == 256 ? :aes : :arc4)
|
|
139
|
+
@handler.set_up_encryption(key_length: key_length, algorithm: algorithm)
|
|
140
|
+
assert(result == @handler.dict[:Length])
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
it "calls the prepare_encryption method" do
|
|
145
|
+
@handler.set_up_encryption
|
|
146
|
+
assert_equal(:Test, @handler.dict[:Filter])
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
it "returns the generated encryption dictionary wrapped in an encryption class" do
|
|
150
|
+
dict = @handler.set_up_encryption
|
|
151
|
+
assert_kind_of(HexaPDF::Encryption::EncryptionDictionary, dict)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
it "set's up the handler for encryption" do
|
|
155
|
+
[:arc4, :aes].each do |algorithm|
|
|
156
|
+
@handler.set_up_encryption(key_length: 128, algorithm: algorithm)
|
|
157
|
+
@obj[:X] = @handler.encrypt_string('data', @obj)
|
|
158
|
+
assert_equal('data', @handler.decrypt(@obj)[:X])
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
it "generates a valid encryption key" do
|
|
163
|
+
@document.trailer[:Encrypt] = @handler.set_up_encryption
|
|
164
|
+
assert(@handler.encryption_key_valid?)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
it "provides correct encryption details" do
|
|
168
|
+
@handler.set_up_encryption
|
|
169
|
+
assert_equal({version: 4, string_algorithm: :aes, stream_algorithm: :arc4,
|
|
170
|
+
embedded_file_algorithm: :identity, key_length: 128},
|
|
171
|
+
@handler.encryption_details)
|
|
172
|
+
assert_equal(HexaPDF::Encryption::Identity, @handler.send(:embedded_file_algorithm))
|
|
173
|
+
assert_equal(HexaPDF::Encryption::FastAES, @handler.send(:string_algorithm))
|
|
174
|
+
assert_equal(HexaPDF::Encryption::FastARC4, @handler.send(:stream_algorithm))
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
it "fails for unsupported encryption key lengths" do
|
|
178
|
+
exp = assert_raises(HexaPDF::UnsupportedEncryptionError) do
|
|
179
|
+
@handler.set_up_encryption(key_length: 43)
|
|
180
|
+
end
|
|
181
|
+
assert_match(/Invalid key length/i, exp.message)
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
it "fails for unsupported encryption algorithms" do
|
|
185
|
+
exp = assert_raises(HexaPDF::UnsupportedEncryptionError) do
|
|
186
|
+
@handler.set_up_encryption(algorithm: :test)
|
|
187
|
+
end
|
|
188
|
+
assert_match(/Unsupported encryption algorithm/i, exp.message)
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
it "fails for the aes algorithm with key lengths != 128 or 256" do
|
|
192
|
+
exp = assert_raises(HexaPDF::UnsupportedEncryptionError) do
|
|
193
|
+
@handler.set_up_encryption(algorithm: :aes, key_length: 40)
|
|
194
|
+
end
|
|
195
|
+
assert_match(/AES algorithm.*key length/i, exp.message)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
it "fails for the arc4 algorithm with a key length of 256" do
|
|
199
|
+
exp = assert_raises(HexaPDF::UnsupportedEncryptionError) do
|
|
200
|
+
@handler.set_up_encryption(algorithm: :arc4, key_length: 256)
|
|
201
|
+
end
|
|
202
|
+
assert_match(/ARC4 algorithm.*key length/i, exp.message)
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
describe "set_up_decryption" do
|
|
207
|
+
it "wraps the given hash in an encryption dictionary class, uses it for its dict, returns it" do
|
|
208
|
+
dict = @handler.set_up_decryption({Filter: :test, V: 1})
|
|
209
|
+
assert_equal(dict, @handler.dict)
|
|
210
|
+
assert_kind_of(HexaPDF::Encryption::EncryptionDictionary, @handler.dict)
|
|
211
|
+
assert_equal({Filter: :test, V: 1}, @handler.dict.value)
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
it "doesn't modify the trailer's /Encrypt dictionary" do
|
|
215
|
+
@handler.set_up_decryption({Filter: :test, V: 4, Length: 128})
|
|
216
|
+
assert_nil(@document.trailer[:Encrypt])
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
it "calls prepare_decryption" do
|
|
220
|
+
@handler.set_up_decryption({Filter: :test, V: 4, Length: 128}, myopt: 5)
|
|
221
|
+
assert_equal(5, @handler.myopt)
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
it "selects the correct algorithm based on the /V and /CF values" do
|
|
225
|
+
@enc = @handler.dup
|
|
226
|
+
|
|
227
|
+
[
|
|
228
|
+
[:arc4, 40, {V: 1}],
|
|
229
|
+
[:arc4, 80, {V: 2, Length: 80}],
|
|
230
|
+
[:arc4, 128, {V: 4, StrF: :Mine, CF: {Mine: {CFM: :V2}}}],
|
|
231
|
+
[:aes, 128, {V: 4, StrF: :Mine, CF: {Mine: {CFM: :AESV2}}}],
|
|
232
|
+
[:aes, 256, {V: 5, StrF: :Mine, CF: {Mine: {CFM: :AESV3}}}],
|
|
233
|
+
[:identity, 128, {V: 4, StrF: :Mine, CF: {Mine: {CFM: :None}}}],
|
|
234
|
+
[:identity, 128, {V: 4, CF: {Mine: {CFM: :AESV2}}}],
|
|
235
|
+
].each do |alg, length, dict|
|
|
236
|
+
@enc.strf = alg
|
|
237
|
+
@enc.set_up_encryption(key_length: length, algorithm: (alg == :identity ? :aes : alg))
|
|
238
|
+
@obj[:X] = @enc.encrypt_string('data', @obj)
|
|
239
|
+
@handler.set_up_decryption(dict)
|
|
240
|
+
assert_equal('data', @handler.decrypt(@obj)[:X])
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
it "selects the correct algorithm for string, stream and embedded file decryption" do
|
|
245
|
+
@handler.set_up_decryption({V: 4, StrF: :Mine, StmF: :Mine, EFF: :Mine,
|
|
246
|
+
CF: {Mine: {CFM: :V2}}})
|
|
247
|
+
assert_equal(HexaPDF::Encryption::FastARC4, @handler.send(:embedded_file_algorithm))
|
|
248
|
+
assert_equal(HexaPDF::Encryption::FastARC4, @handler.send(:string_algorithm))
|
|
249
|
+
assert_equal(HexaPDF::Encryption::FastARC4, @handler.send(:stream_algorithm))
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
it "provides correct encryption details" do
|
|
253
|
+
@handler.set_up_decryption({Filter: :test, V: 2, Length: 128}, myopt: 5)
|
|
254
|
+
assert_equal({version: 2, string_algorithm: :arc4, stream_algorithm: :arc4,
|
|
255
|
+
embedded_file_algorithm: :arc4, key_length: 128},
|
|
256
|
+
@handler.encryption_details)
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
it "fails for unsupported /V values in the dict" do
|
|
260
|
+
exp = assert_raises(HexaPDF::UnsupportedEncryptionError) do
|
|
261
|
+
@handler.set_up_decryption({V: 3})
|
|
262
|
+
end
|
|
263
|
+
assert_match(/Unsupported encryption version/i, exp.message)
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
it "fails for unsupported crypt filter encryption methods" do
|
|
267
|
+
exp = assert_raises(HexaPDF::UnsupportedEncryptionError) do
|
|
268
|
+
@handler.set_up_decryption({V: 4, StrF: :Mine, CF: {Mine: {CFM: :Unknown}}})
|
|
269
|
+
end
|
|
270
|
+
assert_match(/Unsupported encryption method/i, exp.message)
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
describe "decrypt" do
|
|
275
|
+
before do
|
|
276
|
+
@handler.set_up_decryption({V: 1})
|
|
277
|
+
@encrypted = @handler.encrypt_string('string', @obj)
|
|
278
|
+
@obj.value = {Key: @encrypted.dup, Array: [@encrypted.dup], Hash: {Another: @encrypted.dup}}
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
it "decrypts all strings in an object" do
|
|
282
|
+
@handler.decrypt(@obj)
|
|
283
|
+
assert_equal('string', @obj[:Key])
|
|
284
|
+
assert_equal('string', @obj[:Array][0])
|
|
285
|
+
assert_equal('string', @obj[:Hash][:Another])
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
it "decrypts the content of a stream object" do
|
|
289
|
+
data = HexaPDF::StreamData.new(proc { @encrypted })
|
|
290
|
+
obj = @document.wrap({}, oid: @obj.oid, stream: data)
|
|
291
|
+
@handler.decrypt(obj)
|
|
292
|
+
assert_equal('string', obj.stream)
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
it "doesn't decrypt a document's Encrypt dictionary" do
|
|
296
|
+
@document.trailer[:Encrypt] = @obj
|
|
297
|
+
assert_equal(@encrypted, @handler.decrypt(@obj)[:Key])
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
it "defers handling encryption to a Crypt filter is specified" do
|
|
301
|
+
data = HexaPDF::StreamData.new(proc { 'mydata' }, filter: :Crypt)
|
|
302
|
+
obj = @document.wrap({}, oid: 1, stream: data)
|
|
303
|
+
@handler.decrypt(obj)
|
|
304
|
+
assert_equal('mydata', obj.stream)
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
it "doesn't decrypt XRef streams" do
|
|
308
|
+
@obj[:Type] = :XRef
|
|
309
|
+
assert_equal(@encrypted, @handler.decrypt(@obj)[:Key])
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
it "doesn't decrypt the /Contents of a signature dictionary" do
|
|
313
|
+
@obj[:Type] = :Sig
|
|
314
|
+
@obj[:Contents] = "test"
|
|
315
|
+
assert_equal("test", @handler.decrypt(@obj)[:Contents])
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
it "fails if V < 5 and the object number changes" do
|
|
319
|
+
@obj.oid = 55
|
|
320
|
+
@handler.decrypt(@obj)
|
|
321
|
+
refute_equal('string', @obj[:Key])
|
|
322
|
+
end
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
describe "encryption" do
|
|
326
|
+
before do
|
|
327
|
+
@handler.set_up_encryption(key_length: 128, algorithm: :arc4)
|
|
328
|
+
@stream = @document.wrap({}, oid: 1, stream: HexaPDF::StreamData.new(proc { "string" }))
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
it "encrypts strings of indirect objects" do
|
|
332
|
+
@obj[:Key] = @handler.encrypt_string('string', @obj)
|
|
333
|
+
assert_equal('string', @handler.decrypt(@obj)[:Key])
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
it "encrypts streams" do
|
|
337
|
+
result = TestHelper.collector(@handler.encrypt_stream(@stream))
|
|
338
|
+
@stream.stream = HexaPDF::StreamData.new(proc { result })
|
|
339
|
+
assert_equal('string', @handler.decrypt(@stream).stream)
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
it "doesn't encrypt strings in a document's Encrypt dictionary" do
|
|
343
|
+
@document.trailer[:Encrypt] = @handler.dict
|
|
344
|
+
@document.trailer[:Encrypt][:Mine] = 'string'
|
|
345
|
+
assert_equal('string', @handler.encrypt_string('string', @document.trailer[:Encrypt]))
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
it "doesn't encrypt XRef streams" do
|
|
349
|
+
@stream[:Type] = :XRef
|
|
350
|
+
assert_equal('string', @handler.encrypt_stream(@stream).resume)
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
it "defers encrypting to a Crypt filter if specified" do
|
|
354
|
+
@stream.set_filter(:Crypt)
|
|
355
|
+
assert_equal('string', @handler.encrypt_stream(@stream).resume)
|
|
356
|
+
|
|
357
|
+
@stream.set_filter([:Crypt])
|
|
358
|
+
assert_equal('string', @handler.encrypt_stream(@stream).resume)
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
it "doesn't encrypt the /Contents key of signature dictionaries" do
|
|
362
|
+
@obj[:Type] = :Sig
|
|
363
|
+
@obj[:Contents] = "test"
|
|
364
|
+
refute_equal('test', @handler.encrypt_string("test", @obj))
|
|
365
|
+
assert_equal('test', @handler.encrypt_string(@obj[:Contents], @obj))
|
|
366
|
+
end
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
it "works correctly with different decryption and encryption handlers" do
|
|
370
|
+
test_file = File.join(TEST_DATA_DIR, 'standard-security-handler', 'nopwd-arc4-40bit-V1.pdf')
|
|
371
|
+
doc = HexaPDF::Document.new(io: StringIO.new(File.read(test_file)))
|
|
372
|
+
doc.encrypt(algorithm: :aes, password: 'test')
|
|
373
|
+
out = StringIO.new(''.b)
|
|
374
|
+
doc.write(out, update_fields: false)
|
|
375
|
+
|
|
376
|
+
assert_raises(HexaPDF::EncryptionError) { HexaPDF::Document.new(io: out) }
|
|
377
|
+
doc = HexaPDF::Document.new(io: out, decryption_opts: {password: 'test'})
|
|
378
|
+
assert_equal('D:20150409164600', doc.trailer[:Info].value[:ModDate])
|
|
379
|
+
end
|
|
380
|
+
end
|