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