origamindee 3.0.0

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 (139) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +89 -0
  3. data/COPYING.LESSER +165 -0
  4. data/README.md +131 -0
  5. data/bin/config/pdfcop.conf.yml +236 -0
  6. data/bin/pdf2pdfa +87 -0
  7. data/bin/pdf2ruby +333 -0
  8. data/bin/pdfcop +476 -0
  9. data/bin/pdfdecompress +97 -0
  10. data/bin/pdfdecrypt +91 -0
  11. data/bin/pdfencrypt +113 -0
  12. data/bin/pdfexplode +223 -0
  13. data/bin/pdfextract +277 -0
  14. data/bin/pdfmetadata +143 -0
  15. data/bin/pdfsh +12 -0
  16. data/bin/shell/console.rb +128 -0
  17. data/bin/shell/hexdump.rb +59 -0
  18. data/bin/shell/irbrc +69 -0
  19. data/examples/README.md +34 -0
  20. data/examples/attachments/attachment.rb +38 -0
  21. data/examples/attachments/nested_document.rb +51 -0
  22. data/examples/encryption/encryption.rb +28 -0
  23. data/examples/events/events.rb +72 -0
  24. data/examples/flash/flash.rb +37 -0
  25. data/examples/flash/helloworld.swf +0 -0
  26. data/examples/forms/javascript.rb +54 -0
  27. data/examples/forms/xfa.rb +115 -0
  28. data/examples/javascript/hello_world.rb +22 -0
  29. data/examples/javascript/js_emulation.rb +54 -0
  30. data/examples/loop/goto.rb +32 -0
  31. data/examples/loop/named.rb +33 -0
  32. data/examples/signature/signature.rb +65 -0
  33. data/examples/uri/javascript.rb +56 -0
  34. data/examples/uri/open-uri.rb +21 -0
  35. data/examples/uri/submitform.rb +47 -0
  36. data/lib/origami/3d.rb +364 -0
  37. data/lib/origami/acroform.rb +321 -0
  38. data/lib/origami/actions.rb +318 -0
  39. data/lib/origami/annotations.rb +711 -0
  40. data/lib/origami/array.rb +242 -0
  41. data/lib/origami/boolean.rb +90 -0
  42. data/lib/origami/catalog.rb +418 -0
  43. data/lib/origami/collections.rb +144 -0
  44. data/lib/origami/compound.rb +161 -0
  45. data/lib/origami/destinations.rb +252 -0
  46. data/lib/origami/dictionary.rb +192 -0
  47. data/lib/origami/encryption.rb +1084 -0
  48. data/lib/origami/extensions/fdf.rb +347 -0
  49. data/lib/origami/extensions/ppklite.rb +422 -0
  50. data/lib/origami/filespec.rb +197 -0
  51. data/lib/origami/filters/ascii.rb +211 -0
  52. data/lib/origami/filters/ccitt/tables.rb +267 -0
  53. data/lib/origami/filters/ccitt.rb +357 -0
  54. data/lib/origami/filters/crypt.rb +38 -0
  55. data/lib/origami/filters/dct.rb +54 -0
  56. data/lib/origami/filters/flate.rb +69 -0
  57. data/lib/origami/filters/jbig2.rb +57 -0
  58. data/lib/origami/filters/jpx.rb +47 -0
  59. data/lib/origami/filters/lzw.rb +170 -0
  60. data/lib/origami/filters/predictors.rb +292 -0
  61. data/lib/origami/filters/runlength.rb +129 -0
  62. data/lib/origami/filters.rb +364 -0
  63. data/lib/origami/font.rb +196 -0
  64. data/lib/origami/functions.rb +79 -0
  65. data/lib/origami/graphics/colors.rb +230 -0
  66. data/lib/origami/graphics/instruction.rb +98 -0
  67. data/lib/origami/graphics/path.rb +182 -0
  68. data/lib/origami/graphics/patterns.rb +174 -0
  69. data/lib/origami/graphics/render.rb +62 -0
  70. data/lib/origami/graphics/state.rb +149 -0
  71. data/lib/origami/graphics/text.rb +225 -0
  72. data/lib/origami/graphics/xobject.rb +918 -0
  73. data/lib/origami/graphics.rb +38 -0
  74. data/lib/origami/header.rb +75 -0
  75. data/lib/origami/javascript.rb +713 -0
  76. data/lib/origami/linearization.rb +330 -0
  77. data/lib/origami/metadata.rb +172 -0
  78. data/lib/origami/name.rb +135 -0
  79. data/lib/origami/null.rb +65 -0
  80. data/lib/origami/numeric.rb +181 -0
  81. data/lib/origami/obfuscation.rb +245 -0
  82. data/lib/origami/object.rb +760 -0
  83. data/lib/origami/optionalcontent.rb +183 -0
  84. data/lib/origami/outline.rb +54 -0
  85. data/lib/origami/outputintents.rb +85 -0
  86. data/lib/origami/page.rb +722 -0
  87. data/lib/origami/parser.rb +269 -0
  88. data/lib/origami/parsers/fdf.rb +56 -0
  89. data/lib/origami/parsers/pdf/lazy.rb +176 -0
  90. data/lib/origami/parsers/pdf/linear.rb +122 -0
  91. data/lib/origami/parsers/pdf.rb +118 -0
  92. data/lib/origami/parsers/ppklite.rb +57 -0
  93. data/lib/origami/pdf.rb +1108 -0
  94. data/lib/origami/reference.rb +134 -0
  95. data/lib/origami/signature.rb +702 -0
  96. data/lib/origami/stream.rb +705 -0
  97. data/lib/origami/string.rb +444 -0
  98. data/lib/origami/template/patterns.rb +56 -0
  99. data/lib/origami/template/widgets.rb +151 -0
  100. data/lib/origami/trailer.rb +190 -0
  101. data/lib/origami/tree.rb +62 -0
  102. data/lib/origami/version.rb +23 -0
  103. data/lib/origami/webcapture.rb +100 -0
  104. data/lib/origami/xfa/config.rb +453 -0
  105. data/lib/origami/xfa/connectionset.rb +146 -0
  106. data/lib/origami/xfa/datasets.rb +49 -0
  107. data/lib/origami/xfa/localeset.rb +42 -0
  108. data/lib/origami/xfa/package.rb +59 -0
  109. data/lib/origami/xfa/pdf.rb +73 -0
  110. data/lib/origami/xfa/signature.rb +42 -0
  111. data/lib/origami/xfa/sourceset.rb +43 -0
  112. data/lib/origami/xfa/stylesheet.rb +44 -0
  113. data/lib/origami/xfa/template.rb +1691 -0
  114. data/lib/origami/xfa/xdc.rb +42 -0
  115. data/lib/origami/xfa/xfa.rb +146 -0
  116. data/lib/origami/xfa/xfdf.rb +43 -0
  117. data/lib/origami/xfa/xmpmeta.rb +43 -0
  118. data/lib/origami/xfa.rb +62 -0
  119. data/lib/origami/xreftable.rb +557 -0
  120. data/lib/origami.rb +47 -0
  121. data/test/dataset/calc.pdf +85 -0
  122. data/test/dataset/crypto.pdf +36 -0
  123. data/test/dataset/empty.pdf +49 -0
  124. data/test/test_actions.rb +27 -0
  125. data/test/test_annotations.rb +68 -0
  126. data/test/test_forms.rb +30 -0
  127. data/test/test_native_types.rb +83 -0
  128. data/test/test_object_tree.rb +33 -0
  129. data/test/test_pages.rb +60 -0
  130. data/test/test_pdf.rb +20 -0
  131. data/test/test_pdf_attachment.rb +34 -0
  132. data/test/test_pdf_create.rb +24 -0
  133. data/test/test_pdf_encrypt.rb +102 -0
  134. data/test/test_pdf_parse.rb +134 -0
  135. data/test/test_pdf_parse_lazy.rb +69 -0
  136. data/test/test_pdf_sign.rb +97 -0
  137. data/test/test_streams.rb +184 -0
  138. data/test/test_xrefs.rb +67 -0
  139. metadata +280 -0
@@ -0,0 +1,1084 @@
1
+ =begin
2
+
3
+ This file is part of Origami, PDF manipulation framework for Ruby
4
+ Copyright (C) 2016 Guillaume Delugré.
5
+
6
+ Origami is free software: you can redistribute it and/or modify
7
+ it under the terms of the GNU Lesser General Public License as published by
8
+ the Free Software Foundation, either version 3 of the License, or
9
+ (at your option) any later version.
10
+
11
+ Origami is distributed in the hope that it will be useful,
12
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ GNU Lesser General Public License for more details.
15
+
16
+ You should have received a copy of the GNU Lesser General Public License
17
+ along with Origami. If not, see <http://www.gnu.org/licenses/>.
18
+
19
+ =end
20
+
21
+ require 'openssl'
22
+ require 'securerandom'
23
+ require 'digest/md5'
24
+ require 'digest/sha2'
25
+
26
+ module Origami
27
+
28
+ class EncryptionError < Error #:nodoc:
29
+ end
30
+
31
+ class EncryptionInvalidPasswordError < EncryptionError #:nodoc:
32
+ end
33
+
34
+ class EncryptionNotSupportedError < EncryptionError #:nodoc:
35
+ end
36
+
37
+ class PDF
38
+
39
+ #
40
+ # Returns whether the PDF file is encrypted.
41
+ #
42
+ def encrypted?
43
+ trailer_key? :Encrypt
44
+ end
45
+
46
+ #
47
+ # Decrypts the current document.
48
+ # _passwd_:: The password to decrypt the document.
49
+ #
50
+ def decrypt(passwd = "")
51
+ raise EncryptionError, "PDF is not encrypted" unless self.encrypted?
52
+
53
+ # Turn the encryption dictionary into a standard encryption dictionary.
54
+ handler = trailer_key(:Encrypt)
55
+ handler = self.cast_object(handler.reference, Encryption::Standard::Dictionary)
56
+
57
+ unless handler.Filter == :Standard
58
+ raise EncryptionNotSupportedError, "Unknown security handler : '#{handler.Filter}'"
59
+ end
60
+
61
+ doc_id = trailer_key(:ID)
62
+ unless doc_id.is_a?(Array)
63
+ raise EncryptionError, "Document ID was not found or is invalid" unless handler.V.to_i == 5
64
+ else
65
+ doc_id = doc_id.first
66
+ end
67
+
68
+ encryption_key = handler.derive_encryption_key(passwd, doc_id)
69
+
70
+ self.extend(Encryption::EncryptedDocument)
71
+ self.encryption_handler = handler
72
+ self.encryption_key = encryption_key
73
+
74
+ decrypt_objects
75
+
76
+ self
77
+ end
78
+
79
+ #
80
+ # Encrypts the current document with the provided passwords.
81
+ # The document will be encrypted at writing-on-disk time.
82
+ # _userpasswd_:: The user password.
83
+ # _ownerpasswd_:: The owner password.
84
+ # _options_:: A set of options to configure encryption.
85
+ #
86
+ def encrypt(options = {})
87
+ raise EncryptionError, "PDF is already encrypted" if self.encrypted?
88
+
89
+ #
90
+ # Default encryption options.
91
+ #
92
+ params = {
93
+ :user_passwd => '',
94
+ :owner_passwd => '',
95
+ :cipher => 'aes', # :RC4 or :AES
96
+ :key_size => 256, # Key size in bits
97
+ :hardened => false, # Use newer password validation (since Reader X)
98
+ :encrypt_metadata => true, # Metadata shall be encrypted?
99
+ :permissions => Encryption::Standard::Permissions::ALL # Document permissions
100
+ }.update(options)
101
+
102
+ # Get the cryptographic parameters.
103
+ version, revision = crypto_revision_from_options(params)
104
+
105
+ # Create the security handler.
106
+ handler, encryption_key = create_security_handler(version, revision, params)
107
+
108
+ # Turn this document into an EncryptedDocument instance.
109
+ self.extend(Encryption::EncryptedDocument)
110
+ self.encryption_handler = handler
111
+ self.encryption_key = encryption_key
112
+
113
+ self
114
+ end
115
+
116
+ private
117
+
118
+ #
119
+ # Installs the standard security dictionary, marking the document as being encrypted.
120
+ # Returns the handler and the encryption key used for protecting contents.
121
+ #
122
+ def create_security_handler(version, revision, params)
123
+
124
+ # Ensure the document has an ID.
125
+ doc_id = (trailer_key(:ID) || generate_id).first
126
+
127
+ # Create the standard encryption dictionary.
128
+ handler = Encryption::Standard::Dictionary.new
129
+ handler.Filter = :Standard
130
+ handler.V = version
131
+ handler.R = revision
132
+ handler.Length = params[:key_size]
133
+ handler.P = -1 # params[:Permissions]
134
+
135
+ # Build the crypt filter dictionary.
136
+ if revision >= 4
137
+ handler.EncryptMetadata = params[:encrypt_metadata]
138
+ handler.CF = Dictionary.new
139
+ crypt_filter = Encryption::CryptFilterDictionary.new
140
+ crypt_filter.AuthEvent = :DocOpen
141
+
142
+ if revision == 4
143
+ crypt_filter.CFM = :AESV2
144
+ else
145
+ crypt_filter.CFM = :AESV3
146
+ end
147
+
148
+ crypt_filter.Length = params[:key_size] >> 3
149
+
150
+ handler.CF[:StdCF] = crypt_filter
151
+ handler.StmF = handler.StrF = :StdCF
152
+ end
153
+
154
+ user_passwd, owner_passwd = params[:user_passwd], params[:owner_passwd]
155
+
156
+ # Setup keys.
157
+ handler.set_passwords(owner_passwd, user_passwd, doc_id)
158
+ encryption_key = handler.compute_user_encryption_key(user_passwd, doc_id)
159
+
160
+ # Install the encryption dictionary to the document.
161
+ self.trailer.Encrypt = self << handler
162
+
163
+ [ handler, encryption_key ]
164
+ end
165
+
166
+ #
167
+ # Converts the parameters passed to PDF#encrypt.
168
+ # Returns [ version, revision, crypt_filters ]
169
+ #
170
+ def crypto_revision_from_options(params)
171
+ case params[:cipher].upcase
172
+ when 'RC4'
173
+ crypto_revision_from_rc4_key(params[:key_size])
174
+ when 'AES'
175
+ crypto_revision_from_aes_key(params[:key_size], params[:hardened])
176
+ else
177
+ raise EncryptionNotSupportedError, "Cipher not supported : #{params[:cipher]}"
178
+ end
179
+ end
180
+
181
+ #
182
+ # Compute the required standard security handler version based on the RC4 key size.
183
+ # _key_size_:: Key size in bits.
184
+ # Returns [ version, revision ].
185
+ #
186
+ def crypto_revision_from_rc4_key(key_size)
187
+ raise EncryptionError, "Invalid RC4 key length" unless key_size.between?(40, 128) and key_size % 8 == 0
188
+
189
+ if key_size > 40
190
+ version = 2
191
+ revision = 3
192
+ else
193
+ version = 1
194
+ revision = 2
195
+ end
196
+
197
+ [ version, revision ]
198
+ end
199
+
200
+ #
201
+ # Compute the required standard security handler version based on the AES key size.
202
+ # _key_size_:: Key size in bits.
203
+ # _hardened_:: Use the extension level 8 hardened derivation algorithm.
204
+ # Returns [ version, revision ].
205
+ #
206
+ def crypto_revision_from_aes_key(key_size, hardened)
207
+ if key_size == 128
208
+ version = revision = 4
209
+ elsif key_size == 256
210
+ version = 5
211
+ if hardened
212
+ revision = 6
213
+ else
214
+ revision = 5
215
+ end
216
+ else
217
+ raise EncryptionError, "Invalid AES key length (Only 128 and 256 bits keys are supported)"
218
+ end
219
+
220
+ [ version, revision ]
221
+ end
222
+ end
223
+
224
+ #
225
+ # Module to provide support for encrypting and decrypting PDF documents.
226
+ #
227
+ module Encryption
228
+
229
+ #
230
+ # Generates _n_ random bytes from a fast PRNG.
231
+ #
232
+ def self.rand_bytes(n)
233
+ Random.new.bytes(n)
234
+ end
235
+
236
+ #
237
+ # Generates _n_ random bytes from a crypto PRNG.
238
+ #
239
+ def self.strong_rand_bytes(n)
240
+ SecureRandom.random_bytes(n)
241
+ end
242
+
243
+ module EncryptedDocument
244
+ attr_accessor :encryption_key
245
+ attr_accessor :encryption_handler
246
+
247
+ # Get the encryption cipher from the crypt filter name.
248
+ def encryption_cipher(name)
249
+ @encryption_handler.encryption_cipher(name)
250
+ end
251
+
252
+ # Get the default string encryption cipher.
253
+ def string_encryption_cipher
254
+ @encryption_handler.string_encryption_cipher
255
+ end
256
+
257
+ # Get the default stream encryption cipher.
258
+ def stream_encryption_cipher
259
+ @encryption_handler.stream_encryption_cipher
260
+ end
261
+
262
+ private
263
+
264
+ #
265
+ # For each object subject to encryption, convert it to an EncryptedObject and decrypt it if necessary.
266
+ #
267
+ def decrypt_objects
268
+ each_encryptable_object do |object|
269
+ case object
270
+ when String
271
+ object.extend(EncryptedString) unless object.is_a?(EncryptedString)
272
+ object.decrypt!
273
+
274
+ when Stream
275
+ object.extend(EncryptedStream) unless object.is_a?(EncryptedStream)
276
+ end
277
+ end
278
+ end
279
+
280
+ #
281
+ # For each object subject to encryption, convert it to an EncryptedObject and mark it as not encrypted yet.
282
+ #
283
+ def encrypt_objects
284
+ each_encryptable_object do |object|
285
+ case object
286
+ when String
287
+ unless object.is_a?(EncryptedString)
288
+ object.extend(EncryptedString)
289
+ object.decrypted = true
290
+ end
291
+
292
+ when Stream
293
+ unless object.is_a?(EncryptedStream)
294
+ object.extend(EncryptedStream)
295
+ object.decrypted = true
296
+ end
297
+ end
298
+ end
299
+ end
300
+
301
+ #
302
+ # Iterates over each encryptable objects in the document.
303
+ #
304
+ def each_encryptable_object(&b)
305
+
306
+ # Metadata may not be encrypted depending on the security handler configuration.
307
+ encrypt_metadata = (@encryption_handler.EncryptMetadata != false)
308
+ metadata = self.Catalog.Metadata
309
+
310
+ self.each_object(recursive: true)
311
+ .lazy
312
+ .select { |object|
313
+ case object
314
+ when Stream
315
+ not object.is_a?(XRefStream) or (encrypt_metadata and object.equal?(metadata))
316
+ when String
317
+ not object.parent.equal?(@encryption_handler)
318
+ end
319
+ }
320
+ .each(&b)
321
+ end
322
+
323
+ def physicalize(options = {})
324
+ encrypt_objects
325
+
326
+ super
327
+
328
+ # remove encrypt dictionary if requested
329
+ if options[:decrypt]
330
+ delete_object(self.trailer[:Encrypt])
331
+ self.trailer[:Encrypt] = nil
332
+ end
333
+
334
+ self
335
+ end
336
+
337
+ def build_object(object, revision, options)
338
+ if object.is_a?(EncryptedObject) and options[:decrypt]
339
+ object.pre_build
340
+ object.decrypt!
341
+ object.decrypted = false # makes it believe no encryption pass is required
342
+ object.post_build
343
+
344
+ return
345
+ end
346
+
347
+ super
348
+ end
349
+ end
350
+
351
+ #
352
+ # Module for encrypted PDF objects.
353
+ #
354
+ module EncryptedObject #:nodoc
355
+ attr_accessor :decrypted
356
+
357
+ def post_build
358
+ encrypt!
359
+
360
+ super
361
+ end
362
+
363
+ private
364
+
365
+ def compute_object_key(cipher)
366
+ doc = self.document
367
+ raise EncryptionError, "Document is not encrypted" unless doc.is_a?(EncryptedDocument)
368
+
369
+ encryption_key = doc.encryption_key
370
+
371
+ if doc.encryption_handler.V < 5
372
+ parent = self.indirect_parent
373
+ no, gen = parent.no, parent.generation
374
+ k = encryption_key + [no].pack("I")[0..2] + [gen].pack("I")[0..1]
375
+
376
+ key_len = [k.length, 16].min
377
+ k << "sAlT" if cipher == Encryption::AES
378
+
379
+ Digest::MD5.digest(k)[0, key_len]
380
+ else
381
+ encryption_key
382
+ end
383
+ end
384
+ end
385
+
386
+ #
387
+ # Module for encrypted String.
388
+ #
389
+ module EncryptedString
390
+ include EncryptedObject
391
+
392
+ def self.extended(obj)
393
+ obj.decrypted = false
394
+ end
395
+
396
+ def encrypt!
397
+ return self unless @decrypted
398
+
399
+ cipher = self.document.string_encryption_cipher
400
+ raise EncryptionError, "Cannot find string encryption filter" if cipher.nil?
401
+
402
+ key = compute_object_key(cipher)
403
+
404
+ encrypted_data =
405
+ if cipher == RC4 or cipher == Identity
406
+ cipher.encrypt(key, self.value)
407
+ else
408
+ iv = Encryption.rand_bytes(AES::BLOCKSIZE)
409
+ cipher.encrypt(key, iv, self.value)
410
+ end
411
+
412
+ @decrypted = false
413
+
414
+ self.replace(encrypted_data)
415
+ self.freeze
416
+
417
+ self
418
+ end
419
+
420
+ def decrypt!
421
+ return self if @decrypted
422
+
423
+ cipher = self.document.string_encryption_cipher
424
+ raise EncryptionError, "Cannot find string encryption filter" if cipher.nil?
425
+
426
+ key = compute_object_key(cipher)
427
+
428
+ self.replace(cipher.decrypt(key, self.to_str))
429
+ @decrypted = true
430
+
431
+ self
432
+ end
433
+ end
434
+
435
+ #
436
+ # Module for encrypted Stream.
437
+ #
438
+ module EncryptedStream
439
+ include EncryptedObject
440
+
441
+ def self.extended(obj)
442
+ obj.decrypted = false
443
+ end
444
+
445
+ def encrypt!
446
+ return self unless @decrypted
447
+
448
+ encode!
449
+
450
+ cipher = get_encryption_cipher
451
+ key = compute_object_key(cipher)
452
+
453
+ @encoded_data =
454
+ if cipher == RC4 or cipher == Identity
455
+ cipher.encrypt(key, self.encoded_data)
456
+ else
457
+ iv = Encryption.rand_bytes(AES::BLOCKSIZE)
458
+ cipher.encrypt(key, iv, @encoded_data)
459
+ end
460
+
461
+ @decrypted = false
462
+
463
+ @encoded_data.freeze
464
+ self.freeze
465
+
466
+ self
467
+ end
468
+
469
+ def decrypt!
470
+ return self if @decrypted
471
+
472
+ cipher = get_encryption_cipher
473
+ key = compute_object_key(cipher)
474
+
475
+ self.encoded_data = cipher.decrypt(key, @encoded_data)
476
+ @decrypted = true
477
+
478
+ self
479
+ end
480
+
481
+ private
482
+
483
+ #
484
+ # Get the stream encryption cipher.
485
+ # The cipher used may depend on the presence of a Crypt filter.
486
+ #
487
+ def get_encryption_cipher
488
+ if self.filters.first == :Crypt
489
+ params = decode_params.first
490
+
491
+ if params.is_a?(Dictionary) and params.Name.is_a?(Name)
492
+ crypt_filter = params.Name.value
493
+ else
494
+ crypt_filter = :Identity
495
+ end
496
+
497
+ cipher = self.document.encryption_cipher(crypt_filter)
498
+ else
499
+ cipher = self.document.stream_encryption_cipher
500
+ end
501
+
502
+ raise EncryptionError, "Cannot find stream encryption filter" if cipher.nil?
503
+
504
+ cipher
505
+ end
506
+ end
507
+
508
+ #
509
+ # Identity transformation.
510
+ #
511
+ module Identity
512
+ def Identity.encrypt(_key, data)
513
+ data
514
+ end
515
+
516
+ def Identity.decrypt(_key, data)
517
+ data
518
+ end
519
+ end
520
+
521
+ #
522
+ # Class wrapper for the RC4 algorithm.
523
+ #
524
+ class RC4
525
+
526
+ #
527
+ # Encrypts data using the given key
528
+ #
529
+ def RC4.encrypt(key, data)
530
+ RC4.new(key).encrypt(data)
531
+ end
532
+
533
+ #
534
+ # Decrypts data using the given key
535
+ #
536
+ def RC4.decrypt(key, data)
537
+ RC4.new(key).decrypt(data)
538
+ end
539
+
540
+ #
541
+ # Creates and initialises a new RC4 generator using given key
542
+ #
543
+ def initialize(key)
544
+ @key = key
545
+ end
546
+
547
+ #
548
+ # Encrypt/decrypt data with the RC4 encryption algorithm
549
+ #
550
+ def cipher(data)
551
+ return '' if data.empty?
552
+
553
+ rc4 = OpenSSL::Cipher::RC4.new.encrypt
554
+ rc4.key_len = @key.length
555
+ rc4.key = @key
556
+
557
+ rc4.update(data) + rc4.final
558
+ end
559
+
560
+ alias encrypt cipher
561
+ alias decrypt cipher
562
+ end
563
+
564
+ #
565
+ # Class wrapper for AES mode CBC.
566
+ #
567
+ class AES
568
+ BLOCKSIZE = 16
569
+
570
+ attr_writer :iv
571
+
572
+ def AES.encrypt(key, iv, data)
573
+ AES.new(key, iv).encrypt(data)
574
+ end
575
+
576
+ def AES.decrypt(key, data)
577
+ AES.new(key, nil).decrypt(data)
578
+ end
579
+
580
+ def initialize(key, iv, use_padding = true)
581
+ unless [16, 24, 32].include?(key.size)
582
+ raise EncryptionError, "Key must have a length of 128, 192 or 256 bits."
583
+ end
584
+
585
+ if not iv.nil? and iv.size != BLOCKSIZE
586
+ raise EncryptionError, "Initialization vector must have a length of #{BLOCKSIZE} bytes."
587
+ end
588
+
589
+ @key = key
590
+ @iv = iv
591
+ @use_padding = use_padding
592
+ end
593
+
594
+ def encrypt(data)
595
+ if @iv.nil?
596
+ raise EncryptionError, "No initialization vector has been set."
597
+ end
598
+
599
+ if @use_padding
600
+ padlen = BLOCKSIZE - (data.size % BLOCKSIZE)
601
+ data << (padlen.chr * padlen)
602
+ end
603
+
604
+ aes = OpenSSL::Cipher.new("aes-#{@key.length << 3}-cbc").encrypt
605
+ aes.iv = @iv
606
+ aes.key = @key
607
+ aes.padding = 0
608
+
609
+ @iv + aes.update(data) + aes.final
610
+ end
611
+
612
+ def decrypt(data)
613
+ unless data.size % BLOCKSIZE == 0
614
+ raise EncryptionError, "Data must be 16-bytes padded (data size = #{data.size} bytes)"
615
+ end
616
+
617
+ @iv = data.slice!(0, BLOCKSIZE)
618
+
619
+ aes = OpenSSL::Cipher.new("aes-#{@key.length << 3}-cbc").decrypt
620
+ aes.iv = @iv
621
+ aes.key = @key
622
+ aes.padding = 0
623
+
624
+ plain = (aes.update(data) + aes.final).unpack("C*")
625
+
626
+ if @use_padding
627
+ padlen = plain[-1]
628
+ unless padlen.between?(1, 16)
629
+ raise EncryptionError, "Incorrect padding length : #{padlen}"
630
+ end
631
+
632
+ padlen.times do
633
+ pad = plain.pop
634
+ raise EncryptionError, "Incorrect padding byte : 0x#{pad.to_s 16}" if pad != padlen
635
+ end
636
+ end
637
+
638
+ plain.pack("C*")
639
+ end
640
+ end
641
+
642
+ #
643
+ # Class representing a crypt filter Dictionary
644
+ #
645
+ class CryptFilterDictionary < Dictionary
646
+ include StandardObject
647
+
648
+ field :Type, :Type => Name, :Default => :CryptFilter
649
+ field :CFM, :Type => Name, :Default => :None
650
+ field :AuthEvent, :Type => Name, :Default => :DocOpen
651
+ field :Length, :Type => Integer
652
+ end
653
+
654
+ #
655
+ # Common class for encryption dictionaries.
656
+ #
657
+ class EncryptionDictionary < Dictionary
658
+ include StandardObject
659
+
660
+ field :Filter, :Type => Name, :Default => :Standard, :Required => true
661
+ field :SubFilter, :Type => Name, :Version => "1.3"
662
+ field :V, :Type => Integer, :Default => 0
663
+ field :Length, :Type => Integer, :Default => 40, :Version => "1.4"
664
+ field :CF, :Type => Dictionary, :Version => "1.5"
665
+ field :StmF, :Type => Name, :Default => :Identity, :Version => "1.5"
666
+ field :StrF, :Type => Name, :Default => :Identity, :Version => "1.5"
667
+ field :EFF, :Type => Name, :Version => "1.6"
668
+
669
+ #
670
+ # Returns the default string encryption cipher.
671
+ #
672
+ def string_encryption_cipher
673
+ encryption_cipher(self.StrF || :Identity)
674
+ end
675
+
676
+ #
677
+ # Returns the default stream encryption cipher.
678
+ #
679
+ def stream_encryption_cipher
680
+ encryption_cipher(self.StmF || :Identity)
681
+ end
682
+
683
+ #
684
+ # Returns the encryption cipher corresponding to a crypt filter name.
685
+ #
686
+ def encryption_cipher(name)
687
+ case self.V.to_i
688
+ when 1, 2
689
+ Encryption::RC4
690
+ when 4, 5
691
+ return Encryption::Identity if name == :Identity
692
+
693
+ select_cipher_by_name(name)
694
+ else
695
+ raise EncryptionNotSupportedError, "Unsupported encryption version: #{handler.V}"
696
+ end
697
+ end
698
+
699
+ private
700
+
701
+ #
702
+ # Returns the cipher associated with a crypt filter name.
703
+ #
704
+ def select_cipher_by_name(name)
705
+ raise EncryptionError, "Broken CF entry" unless self.CF.is_a?(Dictionary)
706
+
707
+ self.CF.select { |key, dict| key == name and dict.is_a?(Dictionary) }
708
+ .map { |_, dict| cipher_from_crypt_filter_method(dict[:CFM] || :None) }
709
+ .first
710
+ end
711
+
712
+ #
713
+ # Converts a crypt filter method identifier to its cipher class.
714
+ #
715
+ def cipher_from_crypt_filter_method(name)
716
+ case name.to_sym
717
+ when :None then Encryption::Identity
718
+ when :V2 then Encryption::RC4
719
+ when :AESV2 then Encryption::AES
720
+ when :AESV3
721
+ raise EncryptionNotSupportedError, "AESV3 requires a version 5 handler" if self.V.to_i != 5
722
+ Encryption::AES
723
+ else
724
+ raise EncryptionNotSupportedError, "Unsupported crypt filter method: #{name}"
725
+ end
726
+ end
727
+ end
728
+
729
+ #
730
+ # The standard security handler for PDF encryption.
731
+ #
732
+ module Standard
733
+ PADDING = "\x28\xBF\x4E\x5E\x4E\x75\x8A\x41\x64\x00\x4E\x56\xFF\xFA\x01\x08\x2E\x2E\x00\xB6\xD0\x68\x3E\x80\x2F\x0C\xA9\xFE\x64\x53\x69\x7A".b #:nodoc:
734
+
735
+ #
736
+ # Permission constants for encrypted documents.
737
+ #
738
+ module Permissions
739
+ RESERVED = 1 << 6 | 1 << 7 | 0xFFFFF000
740
+ PRINT = 1 << 2 | RESERVED
741
+ MODIFY_CONTENTS = 1 << 3 | RESERVED
742
+ COPY_CONTENTS = 1 << 4 | RESERVED
743
+ MODIFY_ANNOTATIONS = 1 << 5 | RESERVED
744
+ FILLIN_FORMS = 1 << 8 | RESERVED
745
+ EXTRACT_CONTENTS = 1 << 9 | RESERVED
746
+ ASSEMBLE_DOC = 1 << 10 | RESERVED
747
+ HIGH_QUALITY_PRINT = 1 << 11 | RESERVED
748
+
749
+ ALL = PRINT | MODIFY_CONTENTS | COPY_CONTENTS |
750
+ MODIFY_ANNOTATIONS | FILLIN_FORMS | EXTRACT_CONTENTS |
751
+ ASSEMBLE_DOC | HIGH_QUALITY_PRINT
752
+ end
753
+
754
+ #
755
+ # Class defining a standard encryption dictionary.
756
+ #
757
+ class Dictionary < EncryptionDictionary
758
+
759
+ field :R, :Type => Number, :Required => true
760
+ field :O, :Type => String, :Required => true
761
+ field :U, :Type => String, :Required => true
762
+ field :OE, :Type => String, :Version => '1.7', :ExtensionLevel => 3
763
+ field :UE, :Type => String, :Version => '1.7', :ExtensionLevel => 3
764
+ field :Perms, :Type => String, :Version => '1.7', :ExtensionLevel => 3
765
+ field :P, :Type => Integer, :Default => 0, :Required => true
766
+ field :EncryptMetadata, :Type => Boolean, :Default => true, :Version => "1.5"
767
+
768
+ def version_required #:nodoc:
769
+ if self.R > 5
770
+ [ '1.7', 8 ]
771
+ else
772
+ super
773
+ end
774
+ end
775
+
776
+ #
777
+ # Checks the given password and derives the document encryption key.
778
+ # Raises EncryptionInvalidPasswordError on invalid password.
779
+ #
780
+ def derive_encryption_key(passwd, doc_id)
781
+ if is_user_password?(passwd, doc_id)
782
+ compute_user_encryption_key(passwd, doc_id)
783
+ elsif is_owner_password?(passwd, doc_id)
784
+ if self.V.to_i < 5
785
+ user_passwd = retrieve_user_password(passwd)
786
+ compute_user_encryption_key(user_passwd, doc_id)
787
+ else
788
+ compute_owner_encryption_key(passwd)
789
+ end
790
+ else
791
+ raise EncryptionInvalidPasswordError
792
+ end
793
+ end
794
+
795
+ #
796
+ # Computes the key that will be used to encrypt/decrypt the document contents with user password.
797
+ # Called at all revisions.
798
+ #
799
+ def compute_user_encryption_key(user_password, file_id)
800
+ return compute_legacy_user_encryption_key(user_password, file_id) if self.R < 5
801
+
802
+ passwd = password_to_utf8(user_password)
803
+
804
+ uks = self.U[40, 8]
805
+
806
+ if self.R == 5
807
+ ukey = Digest::SHA256.digest(passwd + uks)
808
+ else
809
+ ukey = compute_hardened_hash(passwd, uks)
810
+ end
811
+
812
+ iv = ::Array.new(AES::BLOCKSIZE, 0).pack("C*")
813
+ AES.new(ukey, nil, false).decrypt(iv + self.UE.value)
814
+ end
815
+
816
+ #
817
+ # Computes the key that will be used to encrypt/decrypt the document contents.
818
+ # Only for Revision 4 and less.
819
+ #
820
+ def compute_legacy_user_encryption_key(user_password, file_id)
821
+ padded = pad_password(user_password)
822
+ padded.force_encoding('binary')
823
+
824
+ padded << self.O
825
+ padded << [ self.P ].pack("i")
826
+
827
+ padded << file_id
828
+
829
+ encrypt_metadata = self.EncryptMetadata != false
830
+ padded << [ -1 ].pack("i") if self.R >= 4 and not encrypt_metadata
831
+
832
+ key = Digest::MD5.digest(padded)
833
+
834
+ 50.times { key = Digest::MD5.digest(key[0, self.Length / 8]) } if self.R >= 3
835
+
836
+ truncate_key(key)
837
+ end
838
+
839
+ #
840
+ # Computes the key that will be used to encrypt/decrypt the document contents with owner password.
841
+ # Revision 5 and above.
842
+ #
843
+ def compute_owner_encryption_key(owner_password)
844
+ return if self.R < 5
845
+
846
+ passwd = password_to_utf8(owner_password)
847
+ oks = self.O[40, 8]
848
+
849
+ if self.R == 5
850
+ okey = Digest::SHA256.digest(passwd + oks + self.U)
851
+ else
852
+ okey = compute_hardened_hash(passwd, oks, self.U)
853
+ end
854
+
855
+ iv = ::Array.new(AES::BLOCKSIZE, 0).pack("C*")
856
+ AES.new(okey, nil, false).decrypt(iv + self.OE.value)
857
+ end
858
+
859
+ #
860
+ # Set up document passwords.
861
+ #
862
+ def set_passwords(owner_password, user_password, salt = nil)
863
+ return set_legacy_passwords(owner_password, user_password, salt) if self.R < 5
864
+
865
+ upass = password_to_utf8(user_password)
866
+ opass = password_to_utf8(owner_password)
867
+
868
+ uvs, uks, ovs, oks = ::Array.new(4) { Encryption.rand_bytes(8) }
869
+ file_key = Encryption.strong_rand_bytes(32)
870
+ iv = ::Array.new(AES::BLOCKSIZE, 0).pack("C*")
871
+
872
+ if self.R == 5
873
+ self.U = Digest::SHA256.digest(upass + uvs) + uvs + uks
874
+ self.O = Digest::SHA256.digest(opass + ovs + self.U) + ovs + oks
875
+ ukey = Digest::SHA256.digest(upass + uks)
876
+ okey = Digest::SHA256.digest(opass + oks + self.U)
877
+ else
878
+ self.U = compute_hardened_hash(upass, uvs) + uvs + uks
879
+ self.O = compute_hardened_hash(opass, ovs, self.U) + ovs + oks
880
+ ukey = compute_hardened_hash(upass, uks)
881
+ okey = compute_hardened_hash(opass, oks, self.U)
882
+ end
883
+
884
+ self.UE = AES.new(ukey, iv, false).encrypt(file_key)[iv.size, 32]
885
+ self.OE = AES.new(okey, iv, false).encrypt(file_key)[iv.size, 32]
886
+
887
+ perms =
888
+ [ self.P ].pack("V") + # 0-3
889
+ [ -1 ].pack("V") + # 4-7
890
+ (self.EncryptMetadata == true ? "T" : "F") + # 8
891
+ "adb" + # 9-11
892
+ [ 0 ].pack("V") # 12-15
893
+
894
+ self.Perms = AES.new(file_key, iv, false).encrypt(perms)[iv.size, 16]
895
+
896
+ file_key
897
+ end
898
+
899
+ #
900
+ # Set up document passwords.
901
+ # Only for Revision 4 and less.
902
+ #
903
+ def set_legacy_passwords(owner_password, user_password, salt)
904
+ owner_key = compute_owner_key(owner_password)
905
+ upadded = pad_password(user_password)
906
+
907
+ owner_key_hash = RC4.encrypt(owner_key, upadded)
908
+ 19.times { |i| owner_key_hash = RC4.encrypt(xor(owner_key, i + 1), owner_key_hash) } if self.R >= 3
909
+
910
+ self.O = owner_key_hash
911
+ self.U = compute_user_password_hash(user_password, salt)
912
+ end
913
+
914
+ #
915
+ # Checks user password.
916
+ # For version 2, 3 and 4, _salt_ is the document ID.
917
+ # For version 5 and 6, _salt_ is the User Key Salt.
918
+ #
919
+ def is_user_password?(pass, salt)
920
+
921
+ if self.R == 2
922
+ compute_user_password_hash(pass, salt) == self.U
923
+ elsif self.R == 3 or self.R == 4
924
+ compute_user_password_hash(pass, salt)[0, 16] == self.U[0, 16]
925
+ elsif self.R == 5
926
+ uvs = self.U[32, 8]
927
+ Digest::SHA256.digest(password_to_utf8(pass) + uvs) == self.U[0, 32]
928
+ elsif self.R == 6
929
+ uvs = self.U[32, 8]
930
+ compute_hardened_hash(password_to_utf8(pass), uvs) == self.U[0, 32]
931
+ end
932
+ end
933
+
934
+ #
935
+ # Checks owner password.
936
+ # For version 2,3 and 4, _salt_ is the document ID.
937
+ # For version 5, _salt_ is (Owner Key Salt + U)
938
+ #
939
+ def is_owner_password?(pass, salt)
940
+
941
+ if self.R < 5
942
+ user_password = retrieve_user_password(pass)
943
+ is_user_password?(user_password, salt)
944
+ elsif self.R == 5
945
+ ovs = self.O[32, 8]
946
+ Digest::SHA256.digest(password_to_utf8(pass) + ovs + self.U) == self.O[0, 32]
947
+ elsif self.R == 6
948
+ ovs = self.O[32, 8]
949
+ compute_hardened_hash(password_to_utf8(pass), ovs, self.U[0,48]) == self.O[0, 32]
950
+ end
951
+ end
952
+
953
+ #
954
+ # Retrieve user password from owner password.
955
+ # Cannot be used with revision 5.
956
+ #
957
+ def retrieve_user_password(owner_password)
958
+
959
+ key = compute_owner_key(owner_password)
960
+
961
+ if self.R == 2
962
+ RC4.decrypt(key, self.O)
963
+ elsif self.R == 3 or self.R == 4
964
+ user_password = RC4.decrypt(xor(key, 19), self.O)
965
+ 19.times { |i| user_password = RC4.decrypt(xor(key, 18-i), user_password) }
966
+
967
+ user_password
968
+ end
969
+ end
970
+
971
+ private
972
+
973
+ #
974
+ # Used to encrypt/decrypt the O field.
975
+ # Rev 2,3,4: O = crypt(user_pass, owner_key).
976
+ # Rev 5: unused.
977
+ #
978
+ def compute_owner_key(owner_password) #:nodoc:
979
+
980
+ opadded = pad_password(owner_password)
981
+
982
+ owner_key = Digest::MD5.digest(opadded)
983
+ 50.times { owner_key = Digest::MD5.digest(owner_key) } if self.R >= 3
984
+
985
+ truncate_key(owner_key)
986
+ end
987
+
988
+ #
989
+ # Compute the value of the U field.
990
+ # Cannot be used with revision 5.
991
+ #
992
+ def compute_user_password_hash(user_password, salt) #:nodoc:
993
+
994
+ if self.R == 2
995
+ key = compute_user_encryption_key(user_password, salt)
996
+ user_key = RC4.encrypt(key, PADDING)
997
+ elsif self.R == 3 or self.R == 4
998
+ key = compute_user_encryption_key(user_password, salt)
999
+
1000
+ upadded = PADDING + salt
1001
+ hash = Digest::MD5.digest(upadded)
1002
+
1003
+ user_key = RC4.encrypt(key, hash)
1004
+
1005
+ 19.times { |i| user_key = RC4.encrypt(xor(key,i+1), user_key) }
1006
+
1007
+ user_key.ljust(32, 0xFF.chr)
1008
+ end
1009
+ end
1010
+
1011
+ #
1012
+ # Computes hardened hash used in revision 6 (extension level 8).
1013
+ #
1014
+ def compute_hardened_hash(password, salt, vector = '')
1015
+ block_size = 32
1016
+ input = Digest::SHA256.digest(password + salt + vector) + "\x00" * 32
1017
+ key = input[0, 16]
1018
+ iv = input[16, 16]
1019
+ digest, aes, h, x = nil, nil, nil, nil
1020
+
1021
+ i = 0
1022
+ while i < 64 or i < x[-1].ord + 32
1023
+
1024
+ block = input[0, block_size]
1025
+
1026
+ aes = OpenSSL::Cipher.new("aes-128-cbc").encrypt
1027
+ aes.iv = iv
1028
+ aes.key = key
1029
+ aes.padding = 0
1030
+
1031
+ 64.times do |j|
1032
+ x = ''
1033
+ x += aes.update(password) unless password.empty?
1034
+ x += aes.update(block)
1035
+ x += aes.update(vector) unless vector.empty?
1036
+
1037
+ if j == 0
1038
+ block_size = 32 + (x.unpack("C16").inject(0) {|a,b| a+b} % 3) * 16
1039
+ digest = Digest::SHA2.new(block_size << 3)
1040
+ end
1041
+
1042
+ digest.update(x)
1043
+ end
1044
+
1045
+ h = digest.digest
1046
+ key = h[0, 16]
1047
+ input[0, block_size] = h[0, block_size]
1048
+ iv = h[16, 16]
1049
+
1050
+ i = i + 1
1051
+ end
1052
+
1053
+ h[0, 32]
1054
+ end
1055
+
1056
+ #
1057
+ # Some revision handlers require different key sizes.
1058
+ # Revision 2 uses 40-bit keys.
1059
+ # Revisions 3 and higher rely on the Length field for the key size.
1060
+ #
1061
+ def truncate_key(key)
1062
+ if self.R == 2
1063
+ key[0, 5]
1064
+ elsif self.R >= 3
1065
+ key[0, self.Length / 8]
1066
+ end
1067
+ end
1068
+
1069
+ def xor(str, byte) #:nodoc:
1070
+ str.bytes.map!{|b| b ^ byte }.pack("C*")
1071
+ end
1072
+
1073
+ def pad_password(password) #:nodoc:
1074
+ password[0, 32].ljust(32, PADDING)
1075
+ end
1076
+
1077
+ def password_to_utf8(passwd) #:nodoc:
1078
+ LiteralString.new(passwd).to_utf8[0, 127]
1079
+ end
1080
+ end
1081
+ end
1082
+ end
1083
+
1084
+ end