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.
Files changed (255) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1024 -0
  3. data/LICENSE +29 -0
  4. data/README.md +129 -0
  5. data/Rakefile +109 -0
  6. data/agpl-3.0.txt +661 -0
  7. data/examples/001-hello_world.rb +16 -0
  8. data/examples/002-graphics.rb +275 -0
  9. data/examples/003-arcs.rb +50 -0
  10. data/examples/004-optimizing.rb +23 -0
  11. data/examples/005-merging.rb +27 -0
  12. data/examples/006-standard_pdf_fonts.rb +73 -0
  13. data/examples/007-truetype.rb +42 -0
  14. data/examples/008-show_char_bboxes.rb +55 -0
  15. data/examples/009-text_layouter_alignment.rb +47 -0
  16. data/examples/010-text_layouter_inline_boxes.rb +64 -0
  17. data/examples/011-text_layouter_line_wrapping.rb +57 -0
  18. data/examples/012-text_layouter_styling.rb +122 -0
  19. data/examples/013-text_layouter_shapes.rb +176 -0
  20. data/examples/014-text_in_polygon.rb +60 -0
  21. data/examples/015-boxes.rb +76 -0
  22. data/examples/016-frame_automatic_box_placement.rb +90 -0
  23. data/examples/017-frame_text_flow.rb +60 -0
  24. data/examples/018-composer.rb +44 -0
  25. data/examples/019-acro_form.rb +88 -0
  26. data/examples/emoji-smile.png +0 -0
  27. data/examples/emoji-wink.png +0 -0
  28. data/examples/machupicchu.jpg +0 -0
  29. data/lib/hexapdf/content/graphic_object/endpoint_arc.rb +66 -0
  30. data/lib/hexapdf/content/graphic_object/geom2d.rb +13 -0
  31. data/lib/hexapdf/version.rb +1 -1
  32. data/test/data/aes-test-vectors/CBCGFSbox-128-decrypt.data.gz +0 -0
  33. data/test/data/aes-test-vectors/CBCGFSbox-128-encrypt.data.gz +0 -0
  34. data/test/data/aes-test-vectors/CBCGFSbox-192-decrypt.data.gz +0 -0
  35. data/test/data/aes-test-vectors/CBCGFSbox-192-encrypt.data.gz +0 -0
  36. data/test/data/aes-test-vectors/CBCGFSbox-256-decrypt.data.gz +0 -0
  37. data/test/data/aes-test-vectors/CBCGFSbox-256-encrypt.data.gz +0 -0
  38. data/test/data/aes-test-vectors/CBCKeySbox-128-decrypt.data.gz +0 -0
  39. data/test/data/aes-test-vectors/CBCKeySbox-128-encrypt.data.gz +0 -0
  40. data/test/data/aes-test-vectors/CBCKeySbox-192-decrypt.data.gz +0 -0
  41. data/test/data/aes-test-vectors/CBCKeySbox-192-encrypt.data.gz +0 -0
  42. data/test/data/aes-test-vectors/CBCKeySbox-256-decrypt.data.gz +0 -0
  43. data/test/data/aes-test-vectors/CBCKeySbox-256-encrypt.data.gz +0 -0
  44. data/test/data/aes-test-vectors/CBCVarKey-128-decrypt.data.gz +0 -0
  45. data/test/data/aes-test-vectors/CBCVarKey-128-encrypt.data.gz +0 -0
  46. data/test/data/aes-test-vectors/CBCVarKey-192-decrypt.data.gz +0 -0
  47. data/test/data/aes-test-vectors/CBCVarKey-192-encrypt.data.gz +0 -0
  48. data/test/data/aes-test-vectors/CBCVarKey-256-decrypt.data.gz +0 -0
  49. data/test/data/aes-test-vectors/CBCVarKey-256-encrypt.data.gz +0 -0
  50. data/test/data/aes-test-vectors/CBCVarTxt-128-decrypt.data.gz +0 -0
  51. data/test/data/aes-test-vectors/CBCVarTxt-128-encrypt.data.gz +0 -0
  52. data/test/data/aes-test-vectors/CBCVarTxt-192-decrypt.data.gz +0 -0
  53. data/test/data/aes-test-vectors/CBCVarTxt-192-encrypt.data.gz +0 -0
  54. data/test/data/aes-test-vectors/CBCVarTxt-256-decrypt.data.gz +0 -0
  55. data/test/data/aes-test-vectors/CBCVarTxt-256-encrypt.data.gz +0 -0
  56. data/test/data/fonts/Ubuntu-Title.ttf +0 -0
  57. data/test/data/images/cmyk.jpg +0 -0
  58. data/test/data/images/fillbytes.jpg +0 -0
  59. data/test/data/images/gray.jpg +0 -0
  60. data/test/data/images/greyscale-1bit.png +0 -0
  61. data/test/data/images/greyscale-2bit.png +0 -0
  62. data/test/data/images/greyscale-4bit.png +0 -0
  63. data/test/data/images/greyscale-8bit.png +0 -0
  64. data/test/data/images/greyscale-alpha-8bit.png +0 -0
  65. data/test/data/images/greyscale-trns-8bit.png +0 -0
  66. data/test/data/images/greyscale-with-gamma1.0.png +0 -0
  67. data/test/data/images/greyscale-with-gamma1.5.png +0 -0
  68. data/test/data/images/indexed-1bit.png +0 -0
  69. data/test/data/images/indexed-2bit.png +0 -0
  70. data/test/data/images/indexed-4bit.png +0 -0
  71. data/test/data/images/indexed-8bit.png +0 -0
  72. data/test/data/images/indexed-alpha-4bit.png +0 -0
  73. data/test/data/images/indexed-alpha-8bit.png +0 -0
  74. data/test/data/images/rgb.jpg +0 -0
  75. data/test/data/images/truecolour-8bit.png +0 -0
  76. data/test/data/images/truecolour-alpha-8bit.png +0 -0
  77. data/test/data/images/truecolour-gama-chrm-8bit.png +0 -0
  78. data/test/data/images/truecolour-srgb-8bit.png +0 -0
  79. data/test/data/images/ycck.jpg +0 -0
  80. data/test/data/minimal.pdf +44 -0
  81. data/test/data/standard-security-handler/README +9 -0
  82. data/test/data/standard-security-handler/bothpwd-aes-128bit-V4.pdf +44 -0
  83. data/test/data/standard-security-handler/bothpwd-aes-256bit-V5.pdf +0 -0
  84. data/test/data/standard-security-handler/bothpwd-arc4-128bit-V2.pdf +43 -0
  85. data/test/data/standard-security-handler/bothpwd-arc4-128bit-V4.pdf +43 -0
  86. data/test/data/standard-security-handler/bothpwd-arc4-40bit-V1.pdf +0 -0
  87. data/test/data/standard-security-handler/nopwd-aes-128bit-V4.pdf +43 -0
  88. data/test/data/standard-security-handler/nopwd-aes-256bit-V5.pdf +0 -0
  89. data/test/data/standard-security-handler/nopwd-arc4-128bit-V2.pdf +43 -0
  90. data/test/data/standard-security-handler/nopwd-arc4-128bit-V4.pdf +43 -0
  91. data/test/data/standard-security-handler/nopwd-arc4-40bit-V1.pdf +43 -0
  92. data/test/data/standard-security-handler/ownerpwd-aes-128bit-V4.pdf +0 -0
  93. data/test/data/standard-security-handler/ownerpwd-aes-256bit-V5.pdf +43 -0
  94. data/test/data/standard-security-handler/ownerpwd-arc4-128bit-V2.pdf +43 -0
  95. data/test/data/standard-security-handler/ownerpwd-arc4-128bit-V4.pdf +43 -0
  96. data/test/data/standard-security-handler/ownerpwd-arc4-40bit-V1.pdf +43 -0
  97. data/test/data/standard-security-handler/userpwd-aes-128bit-V4.pdf +43 -0
  98. data/test/data/standard-security-handler/userpwd-aes-256bit-V5.pdf +43 -0
  99. data/test/data/standard-security-handler/userpwd-arc4-128bit-V2.pdf +0 -0
  100. data/test/data/standard-security-handler/userpwd-arc4-128bit-V4.pdf +0 -0
  101. data/test/data/standard-security-handler/userpwd-arc4-40bit-V1.pdf +43 -0
  102. data/test/hexapdf/common_tokenizer_tests.rb +236 -0
  103. data/test/hexapdf/content/common.rb +39 -0
  104. data/test/hexapdf/content/graphic_object/test_arc.rb +102 -0
  105. data/test/hexapdf/content/graphic_object/test_endpoint_arc.rb +90 -0
  106. data/test/hexapdf/content/graphic_object/test_geom2d.rb +79 -0
  107. data/test/hexapdf/content/graphic_object/test_solid_arc.rb +86 -0
  108. data/test/hexapdf/content/test_canvas.rb +1279 -0
  109. data/test/hexapdf/content/test_color_space.rb +176 -0
  110. data/test/hexapdf/content/test_graphics_state.rb +151 -0
  111. data/test/hexapdf/content/test_operator.rb +619 -0
  112. data/test/hexapdf/content/test_parser.rb +99 -0
  113. data/test/hexapdf/content/test_processor.rb +163 -0
  114. data/test/hexapdf/content/test_transformation_matrix.rb +64 -0
  115. data/test/hexapdf/document/test_files.rb +72 -0
  116. data/test/hexapdf/document/test_fonts.rb +60 -0
  117. data/test/hexapdf/document/test_images.rb +72 -0
  118. data/test/hexapdf/document/test_pages.rb +130 -0
  119. data/test/hexapdf/encryption/common.rb +87 -0
  120. data/test/hexapdf/encryption/test_aes.rb +129 -0
  121. data/test/hexapdf/encryption/test_arc4.rb +39 -0
  122. data/test/hexapdf/encryption/test_fast_aes.rb +17 -0
  123. data/test/hexapdf/encryption/test_fast_arc4.rb +12 -0
  124. data/test/hexapdf/encryption/test_identity.rb +21 -0
  125. data/test/hexapdf/encryption/test_ruby_aes.rb +23 -0
  126. data/test/hexapdf/encryption/test_ruby_arc4.rb +20 -0
  127. data/test/hexapdf/encryption/test_security_handler.rb +380 -0
  128. data/test/hexapdf/encryption/test_standard_security_handler.rb +322 -0
  129. data/test/hexapdf/filter/common.rb +53 -0
  130. data/test/hexapdf/filter/test_ascii85_decode.rb +59 -0
  131. data/test/hexapdf/filter/test_ascii_hex_decode.rb +38 -0
  132. data/test/hexapdf/filter/test_crypt.rb +21 -0
  133. data/test/hexapdf/filter/test_encryption.rb +24 -0
  134. data/test/hexapdf/filter/test_flate_decode.rb +44 -0
  135. data/test/hexapdf/filter/test_lzw_decode.rb +52 -0
  136. data/test/hexapdf/filter/test_predictor.rb +219 -0
  137. data/test/hexapdf/filter/test_run_length_decode.rb +32 -0
  138. data/test/hexapdf/font/cmap/test_parser.rb +102 -0
  139. data/test/hexapdf/font/cmap/test_writer.rb +66 -0
  140. data/test/hexapdf/font/encoding/test_base.rb +45 -0
  141. data/test/hexapdf/font/encoding/test_difference_encoding.rb +29 -0
  142. data/test/hexapdf/font/encoding/test_glyph_list.rb +59 -0
  143. data/test/hexapdf/font/encoding/test_zapf_dingbats_encoding.rb +16 -0
  144. data/test/hexapdf/font/test_cmap.rb +104 -0
  145. data/test/hexapdf/font/test_encoding.rb +27 -0
  146. data/test/hexapdf/font/test_invalid_glyph.rb +34 -0
  147. data/test/hexapdf/font/test_true_type_wrapper.rb +186 -0
  148. data/test/hexapdf/font/test_type1_wrapper.rb +107 -0
  149. data/test/hexapdf/font/true_type/common.rb +17 -0
  150. data/test/hexapdf/font/true_type/table/common.rb +27 -0
  151. data/test/hexapdf/font/true_type/table/test_cmap.rb +47 -0
  152. data/test/hexapdf/font/true_type/table/test_cmap_subtable.rb +141 -0
  153. data/test/hexapdf/font/true_type/table/test_directory.rb +30 -0
  154. data/test/hexapdf/font/true_type/table/test_glyf.rb +58 -0
  155. data/test/hexapdf/font/true_type/table/test_head.rb +56 -0
  156. data/test/hexapdf/font/true_type/table/test_hhea.rb +26 -0
  157. data/test/hexapdf/font/true_type/table/test_hmtx.rb +30 -0
  158. data/test/hexapdf/font/true_type/table/test_kern.rb +61 -0
  159. data/test/hexapdf/font/true_type/table/test_loca.rb +33 -0
  160. data/test/hexapdf/font/true_type/table/test_maxp.rb +50 -0
  161. data/test/hexapdf/font/true_type/table/test_name.rb +76 -0
  162. data/test/hexapdf/font/true_type/table/test_os2.rb +55 -0
  163. data/test/hexapdf/font/true_type/table/test_post.rb +78 -0
  164. data/test/hexapdf/font/true_type/test_builder.rb +42 -0
  165. data/test/hexapdf/font/true_type/test_font.rb +116 -0
  166. data/test/hexapdf/font/true_type/test_optimizer.rb +26 -0
  167. data/test/hexapdf/font/true_type/test_subsetter.rb +73 -0
  168. data/test/hexapdf/font/true_type/test_table.rb +48 -0
  169. data/test/hexapdf/font/type1/common.rb +6 -0
  170. data/test/hexapdf/font/type1/test_afm_parser.rb +65 -0
  171. data/test/hexapdf/font/type1/test_font.rb +104 -0
  172. data/test/hexapdf/font/type1/test_font_metrics.rb +22 -0
  173. data/test/hexapdf/font/type1/test_pfb_parser.rb +37 -0
  174. data/test/hexapdf/font_loader/test_from_configuration.rb +43 -0
  175. data/test/hexapdf/font_loader/test_from_file.rb +36 -0
  176. data/test/hexapdf/font_loader/test_standard14.rb +33 -0
  177. data/test/hexapdf/image_loader/test_jpeg.rb +93 -0
  178. data/test/hexapdf/image_loader/test_pdf.rb +47 -0
  179. data/test/hexapdf/image_loader/test_png.rb +259 -0
  180. data/test/hexapdf/layout/test_box.rb +154 -0
  181. data/test/hexapdf/layout/test_frame.rb +350 -0
  182. data/test/hexapdf/layout/test_image_box.rb +73 -0
  183. data/test/hexapdf/layout/test_inline_box.rb +71 -0
  184. data/test/hexapdf/layout/test_line.rb +206 -0
  185. data/test/hexapdf/layout/test_style.rb +790 -0
  186. data/test/hexapdf/layout/test_text_box.rb +140 -0
  187. data/test/hexapdf/layout/test_text_fragment.rb +375 -0
  188. data/test/hexapdf/layout/test_text_layouter.rb +758 -0
  189. data/test/hexapdf/layout/test_text_shaper.rb +62 -0
  190. data/test/hexapdf/layout/test_width_from_polygon.rb +109 -0
  191. data/test/hexapdf/task/test_dereference.rb +51 -0
  192. data/test/hexapdf/task/test_optimize.rb +162 -0
  193. data/test/hexapdf/test_composer.rb +258 -0
  194. data/test/hexapdf/test_configuration.rb +93 -0
  195. data/test/hexapdf/test_data_dir.rb +32 -0
  196. data/test/hexapdf/test_dictionary.rb +340 -0
  197. data/test/hexapdf/test_dictionary_fields.rb +269 -0
  198. data/test/hexapdf/test_document.rb +641 -0
  199. data/test/hexapdf/test_filter.rb +100 -0
  200. data/test/hexapdf/test_importer.rb +106 -0
  201. data/test/hexapdf/test_object.rb +258 -0
  202. data/test/hexapdf/test_parser.rb +645 -0
  203. data/test/hexapdf/test_pdf_array.rb +169 -0
  204. data/test/hexapdf/test_rectangle.rb +73 -0
  205. data/test/hexapdf/test_reference.rb +50 -0
  206. data/test/hexapdf/test_revision.rb +188 -0
  207. data/test/hexapdf/test_revisions.rb +196 -0
  208. data/test/hexapdf/test_serializer.rb +195 -0
  209. data/test/hexapdf/test_stream.rb +274 -0
  210. data/test/hexapdf/test_tokenizer.rb +80 -0
  211. data/test/hexapdf/test_type.rb +18 -0
  212. data/test/hexapdf/test_writer.rb +140 -0
  213. data/test/hexapdf/test_xref_section.rb +61 -0
  214. data/test/hexapdf/type/acro_form/test_appearance_generator.rb +795 -0
  215. data/test/hexapdf/type/acro_form/test_button_field.rb +308 -0
  216. data/test/hexapdf/type/acro_form/test_choice_field.rb +220 -0
  217. data/test/hexapdf/type/acro_form/test_field.rb +259 -0
  218. data/test/hexapdf/type/acro_form/test_form.rb +357 -0
  219. data/test/hexapdf/type/acro_form/test_signature_field.rb +38 -0
  220. data/test/hexapdf/type/acro_form/test_text_field.rb +201 -0
  221. data/test/hexapdf/type/acro_form/test_variable_text_field.rb +88 -0
  222. data/test/hexapdf/type/actions/test_launch.rb +24 -0
  223. data/test/hexapdf/type/actions/test_uri.rb +23 -0
  224. data/test/hexapdf/type/annotations/test_markup_annotation.rb +22 -0
  225. data/test/hexapdf/type/annotations/test_text.rb +34 -0
  226. data/test/hexapdf/type/annotations/test_widget.rb +225 -0
  227. data/test/hexapdf/type/test_annotation.rb +97 -0
  228. data/test/hexapdf/type/test_catalog.rb +48 -0
  229. data/test/hexapdf/type/test_cid_font.rb +61 -0
  230. data/test/hexapdf/type/test_file_specification.rb +141 -0
  231. data/test/hexapdf/type/test_font.rb +67 -0
  232. data/test/hexapdf/type/test_font_descriptor.rb +61 -0
  233. data/test/hexapdf/type/test_font_simple.rb +176 -0
  234. data/test/hexapdf/type/test_font_true_type.rb +31 -0
  235. data/test/hexapdf/type/test_font_type0.rb +120 -0
  236. data/test/hexapdf/type/test_font_type1.rb +142 -0
  237. data/test/hexapdf/type/test_font_type3.rb +26 -0
  238. data/test/hexapdf/type/test_form.rb +120 -0
  239. data/test/hexapdf/type/test_image.rb +261 -0
  240. data/test/hexapdf/type/test_info.rb +9 -0
  241. data/test/hexapdf/type/test_object_stream.rb +117 -0
  242. data/test/hexapdf/type/test_page.rb +598 -0
  243. data/test/hexapdf/type/test_page_tree_node.rb +315 -0
  244. data/test/hexapdf/type/test_resources.rb +209 -0
  245. data/test/hexapdf/type/test_trailer.rb +116 -0
  246. data/test/hexapdf/type/test_xref_stream.rb +143 -0
  247. data/test/hexapdf/utils/test_bit_field.rb +63 -0
  248. data/test/hexapdf/utils/test_bit_stream.rb +69 -0
  249. data/test/hexapdf/utils/test_graphics_helpers.rb +37 -0
  250. data/test/hexapdf/utils/test_lru_cache.rb +22 -0
  251. data/test/hexapdf/utils/test_object_hash.rb +120 -0
  252. data/test/hexapdf/utils/test_pdf_doc_encoding.rb +18 -0
  253. data/test/hexapdf/utils/test_sorted_tree_node.rb +239 -0
  254. data/test/test_helper.rb +58 -0
  255. 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,12 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require_relative 'common'
4
+ require 'hexapdf/encryption/fast_arc4'
5
+
6
+ describe HexaPDF::Encryption::FastARC4 do
7
+ include ARC4EncryptionTests
8
+
9
+ before do
10
+ @algorithm_class = HexaPDF::Encryption::FastARC4
11
+ end
12
+ 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