origami 1.0.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 (108) hide show
  1. data/COPYING.LESSER +165 -0
  2. data/README +77 -0
  3. data/VERSION +1 -0
  4. data/bin/config/pdfcop.conf.yml +237 -0
  5. data/bin/gui/about.rb +46 -0
  6. data/bin/gui/config.rb +132 -0
  7. data/bin/gui/file.rb +385 -0
  8. data/bin/gui/hexdump.rb +74 -0
  9. data/bin/gui/hexview.rb +91 -0
  10. data/bin/gui/imgview.rb +72 -0
  11. data/bin/gui/menu.rb +392 -0
  12. data/bin/gui/properties.rb +132 -0
  13. data/bin/gui/signing.rb +635 -0
  14. data/bin/gui/textview.rb +107 -0
  15. data/bin/gui/treeview.rb +409 -0
  16. data/bin/gui/walker.rb +282 -0
  17. data/bin/gui/xrefs.rb +79 -0
  18. data/bin/pdf2graph +121 -0
  19. data/bin/pdf2ruby +353 -0
  20. data/bin/pdfcocoon +104 -0
  21. data/bin/pdfcop +455 -0
  22. data/bin/pdfdecompress +104 -0
  23. data/bin/pdfdecrypt +95 -0
  24. data/bin/pdfencrypt +112 -0
  25. data/bin/pdfextract +221 -0
  26. data/bin/pdfmetadata +123 -0
  27. data/bin/pdfsh +13 -0
  28. data/bin/pdfwalker +7 -0
  29. data/bin/shell/.irbrc +104 -0
  30. data/bin/shell/console.rb +136 -0
  31. data/bin/shell/hexdump.rb +83 -0
  32. data/origami.rb +36 -0
  33. data/origami/3d.rb +239 -0
  34. data/origami/acroform.rb +321 -0
  35. data/origami/actions.rb +299 -0
  36. data/origami/adobe/fdf.rb +259 -0
  37. data/origami/adobe/ppklite.rb +489 -0
  38. data/origami/annotations.rb +775 -0
  39. data/origami/array.rb +187 -0
  40. data/origami/boolean.rb +101 -0
  41. data/origami/catalog.rb +486 -0
  42. data/origami/destinations.rb +213 -0
  43. data/origami/dictionary.rb +188 -0
  44. data/origami/docmdp.rb +96 -0
  45. data/origami/encryption.rb +1293 -0
  46. data/origami/export.rb +283 -0
  47. data/origami/file.rb +222 -0
  48. data/origami/filters.rb +250 -0
  49. data/origami/filters/ascii.rb +189 -0
  50. data/origami/filters/ccitt.rb +515 -0
  51. data/origami/filters/crypt.rb +47 -0
  52. data/origami/filters/dct.rb +61 -0
  53. data/origami/filters/flate.rb +112 -0
  54. data/origami/filters/jbig2.rb +63 -0
  55. data/origami/filters/jpx.rb +53 -0
  56. data/origami/filters/lzw.rb +195 -0
  57. data/origami/filters/predictors.rb +276 -0
  58. data/origami/filters/runlength.rb +117 -0
  59. data/origami/font.rb +209 -0
  60. data/origami/functions.rb +93 -0
  61. data/origami/graphics.rb +33 -0
  62. data/origami/graphics/colors.rb +191 -0
  63. data/origami/graphics/instruction.rb +126 -0
  64. data/origami/graphics/path.rb +154 -0
  65. data/origami/graphics/patterns.rb +180 -0
  66. data/origami/graphics/state.rb +164 -0
  67. data/origami/graphics/text.rb +224 -0
  68. data/origami/graphics/xobject.rb +493 -0
  69. data/origami/header.rb +90 -0
  70. data/origami/linearization.rb +318 -0
  71. data/origami/metadata.rb +114 -0
  72. data/origami/name.rb +170 -0
  73. data/origami/null.rb +75 -0
  74. data/origami/numeric.rb +188 -0
  75. data/origami/obfuscation.rb +233 -0
  76. data/origami/object.rb +527 -0
  77. data/origami/outline.rb +59 -0
  78. data/origami/page.rb +559 -0
  79. data/origami/parser.rb +268 -0
  80. data/origami/parsers/fdf.rb +45 -0
  81. data/origami/parsers/pdf.rb +27 -0
  82. data/origami/parsers/pdf/linear.rb +113 -0
  83. data/origami/parsers/ppklite.rb +86 -0
  84. data/origami/pdf.rb +1144 -0
  85. data/origami/reference.rb +113 -0
  86. data/origami/signature.rb +474 -0
  87. data/origami/stream.rb +575 -0
  88. data/origami/string.rb +416 -0
  89. data/origami/trailer.rb +173 -0
  90. data/origami/webcapture.rb +87 -0
  91. data/origami/xfa.rb +3027 -0
  92. data/origami/xreftable.rb +447 -0
  93. data/templates/patterns.rb +66 -0
  94. data/templates/widgets.rb +173 -0
  95. data/templates/xdp.rb +92 -0
  96. data/tests/dataset/test.dummycrt +28 -0
  97. data/tests/dataset/test.dummykey +27 -0
  98. data/tests/tc_actions.rb +32 -0
  99. data/tests/tc_annotations.rb +85 -0
  100. data/tests/tc_pages.rb +37 -0
  101. data/tests/tc_pdfattach.rb +24 -0
  102. data/tests/tc_pdfencrypt.rb +110 -0
  103. data/tests/tc_pdfnew.rb +32 -0
  104. data/tests/tc_pdfparse.rb +98 -0
  105. data/tests/tc_pdfsig.rb +37 -0
  106. data/tests/tc_streams.rb +129 -0
  107. data/tests/ts_pdf.rb +45 -0
  108. metadata +193 -0
@@ -0,0 +1,1293 @@
1
+ =begin
2
+
3
+ = File
4
+ encryption.rb
5
+
6
+ = Info
7
+ This file is part of Origami, PDF manipulation framework for Ruby
8
+ Copyright (C) 2010 Guillaume DelugrÈ <guillaume@security-labs.org>
9
+ All right reserved.
10
+
11
+ Origami is free software: you can redistribute it and/or modify
12
+ it under the terms of the GNU Lesser General Public License as published by
13
+ the Free Software Foundation, either version 3 of the License, or
14
+ (at your option) any later version.
15
+
16
+ Origami is distributed in the hope that it will be useful,
17
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
18
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
+ GNU Lesser General Public License for more details.
20
+
21
+ You should have received a copy of the GNU Lesser General Public License
22
+ along with Origami. If not, see <http://www.gnu.org/licenses/>.
23
+
24
+ =end
25
+
26
+ require 'digest/md5'
27
+ require 'digest/sha2'
28
+ require 'iconv'
29
+
30
+ module Origami
31
+
32
+ class EncryptionError < Exception #:nodoc:
33
+ end
34
+
35
+ class EncryptionInvalidPasswordError < EncryptionError #:nodoc:
36
+ end
37
+
38
+ class EncryptionNotSupportedError < EncryptionError #:nodoc:
39
+ end
40
+
41
+ class PDF
42
+
43
+ #
44
+ # Returns whether the PDF file is encrypted.
45
+ #
46
+ def is_encrypted?
47
+ has_attr? :Encrypt
48
+ end
49
+
50
+ #
51
+ # Decrypts the current document (only RC4 40..128 bits).
52
+ # TODO: AESv3
53
+ # _passwd_:: The password to decrypt the document.
54
+ #
55
+ def decrypt(passwd = "")
56
+
57
+ unless self.is_encrypted?
58
+ raise EncryptionError, "PDF is not encrypted"
59
+ end
60
+
61
+ encrypt_dict = get_doc_attr(:Encrypt)
62
+ handler = Encryption::Standard::Dictionary.new(encrypt_dict.copy)
63
+
64
+ unless handler.Filter == :Standard
65
+ raise EncryptionNotSupportedError, "Unknown security handler : '#{handler.Filter.to_s}'"
66
+ end
67
+
68
+ case handler.V.to_i
69
+ when 1,2 then str_algo = stm_algo = Encryption::ARC4
70
+ when 4,5
71
+ if handler[:CF].is_a?(Dictionary)
72
+ cfs = handler[:CF]
73
+
74
+ if handler[:StrF].is_a?(Name) and cfs[handler[:StrF]].is_a?(Dictionary)
75
+ cfdict = cfs[handler[:StrF]]
76
+
77
+ str_algo =
78
+ if cfdict[:CFM] == :V2 then Encryption::ARC4
79
+ elsif cfdict[:CFM] == :AESV2 then Encryption::AES
80
+ elsif cfdict[:CFM] == :None then Encryption::Identity
81
+ elsif cfdict[:CFM] == :AESV3 and handler.V.to_i == 5 then Encryption::AES
82
+ else
83
+ raise EncryptionNotSupportedError, "Unsupported encryption version : #{handler.V}"
84
+ end
85
+ else
86
+ str_algo = Encryption::Identity
87
+ end
88
+
89
+ if handler[:StmF].is_a?(Name) and cfs[handler[:StmF]].is_a?(Dictionary)
90
+ cfdict = cfs[handler[:StmF]]
91
+
92
+ stm_algo =
93
+ if cfdict[:CFM] == :V2 then Encryption::ARC4
94
+ elsif cfdict[:CFM] == :AESV2 then Encryption::AES
95
+ elsif cfdict[:CFM] == :None then Encryption::Identity
96
+ elsif cfdict[:CFM] == :AESV3 and handler.V.to_i == 5 then Encryption::AES
97
+ else
98
+ raise EncryptionNotSupportedError, "Unsupported encryption version : #{handler.V}"
99
+ end
100
+ else
101
+ stm_algo = Encryption::Identity
102
+ end
103
+
104
+ else
105
+ str_algo = stm_algo = Encryption::Identity
106
+ end
107
+
108
+ else
109
+ raise EncryptionNotSupportedError, "Unsupported encryption version : #{handler.V}"
110
+ end
111
+
112
+ doc_id = get_doc_attr(:ID)
113
+ unless doc_id.is_a?(Array)
114
+ raise EncryptionError, "Document ID was not found or is invalid" unless handler.V.to_i == 5
115
+ else
116
+ doc_id = doc_id.first
117
+ end
118
+
119
+ if handler.is_owner_password?(passwd, doc_id)
120
+ if handler.V.to_i < 5
121
+ user_passwd = handler.retrieve_user_password(passwd)
122
+ encryption_key = handler.compute_user_encryption_key(user_passwd, doc_id)
123
+ else
124
+ encryption_key = handler.compute_owner_encryption_key(passwd)
125
+ end
126
+
127
+ elsif handler.is_user_password?(passwd, doc_id)
128
+ encryption_key = handler.compute_user_encryption_key(passwd, doc_id)
129
+ else
130
+ raise EncryptionInvalidPasswordError
131
+ end
132
+
133
+
134
+ #self.extend(Encryption::EncryptedDocument)
135
+ #self.encryption_dict = encrypt_dict
136
+ #self.encryption_key = encryption_key
137
+ #self.stm_algo = self.str_algo = algorithm
138
+
139
+ encrypt_metadata = (handler.EncryptMetadata != false)
140
+
141
+ self.extend(Encryption::EncryptedDocument)
142
+ self.encryption_dict = handler
143
+ self.encryption_key = encryption_key
144
+ self.stm_algo,self.str_algo = stm_algo,str_algo
145
+
146
+ #
147
+ # Should be fixed to exclude only the active XRefStream
148
+ #
149
+ metadata = self.Catalog.Metadata
150
+
151
+ self.indirect_objects.each do |indobj|
152
+ encrypted_objects = []
153
+ case indobj
154
+ when String,Stream then encrypted_objects << indobj
155
+ when Dictionary,Array then encrypted_objects |= indobj.strings_cache
156
+ end
157
+
158
+ encrypted_objects.each do |obj|
159
+
160
+ case obj
161
+ when String
162
+ next if obj.equal?(encrypt_dict[:U]) or
163
+ obj.equal?(encrypt_dict[:O]) or
164
+ obj.equal?(encrypt_dict[:UE]) or
165
+ obj.equal?(encrypt_dict[:OE]) or
166
+ obj.equal?(encrypt_dict[:Perms]) or
167
+ (obj.parent.is_a?(Signature::DigitalSignature) and obj.equal?(obj.parent[:Contents]))
168
+
169
+ obj.extend(Encryption::EncryptedString)
170
+ obj.encryption_handler = handler
171
+ obj.encryption_key = encryption_key
172
+ obj.algorithm = stm_algo
173
+ obj.decrypted = false
174
+ obj.decrypt!
175
+
176
+ when Stream
177
+ next if obj.is_a?(XRefStream) or (not encrypt_metadata and obj.equal?(metadata))
178
+ obj.extend(Encryption::EncryptedStream)
179
+ obj.encryption_handler = handler
180
+ obj.encryption_key = encryption_key
181
+ obj.algorithm = stm_algo
182
+ obj.decrypted = false
183
+ end
184
+ end
185
+ end
186
+
187
+ self
188
+ end
189
+
190
+ #
191
+ # Encrypts the current document with the provided passwords.
192
+ # The document will be encrypted at writing-on-disk time.
193
+ # _userpasswd_:: The user password.
194
+ # _ownerpasswd_:: The owner password.
195
+ # _options_:: A set of options to configure encryption.
196
+ #
197
+ def encrypt(options = {})
198
+
199
+ if self.is_encrypted?
200
+ raise EncryptionError, "PDF is already encrypted"
201
+ end
202
+
203
+ #
204
+ # Default encryption options.
205
+ #
206
+ params =
207
+ {
208
+ :user_passwd => '',
209
+ :owner_passwd => '',
210
+ :cipher => 'rc4', # :RC4 or :AES
211
+ :key_size => 128, # Key size in bits
212
+ :encrypt_metadata => true, # Metadata shall be encrypted?
213
+ :permissions => Encryption::Standard::Permissions::ALL # Document permissions
214
+ }.update(options)
215
+
216
+ userpasswd, ownerpasswd = params[:user_passwd], params[:owner_passwd]
217
+
218
+ case params[:cipher].upcase
219
+ when 'RC4'
220
+ algorithm = Encryption::ARC4
221
+ if (40..128) === params[:key_size] and params[:key_size] % 8 == 0
222
+ if params[:key_size] > 40
223
+ version = 2
224
+ revision = 3
225
+ else
226
+ version = 1
227
+ revision = 2
228
+ end
229
+ else
230
+ raise EncryptionError, "Invalid RC4 key length"
231
+ end
232
+ when 'AES'
233
+ algorithm = Encryption::AES
234
+ if params[:key_size] == 128
235
+ version = revision = 4
236
+ elsif params[:key_size] == 256
237
+ version = revision = 5
238
+ else
239
+ raise EncryptionError, "Invalid AES key length (Only 128 and 256 bits keys are supported)"
240
+ end
241
+ else
242
+ raise EncryptionNotSupportedError, "Cipher not supported : #{params[:cipher]}"
243
+ end
244
+
245
+ doc_id = (get_doc_attr(:ID) || gen_id).first
246
+
247
+ handler = Encryption::Standard::Dictionary.new
248
+ handler.Filter = :Standard #:nodoc:
249
+ handler.V = version
250
+ handler.R = revision
251
+ handler.Length = params[:key_size]
252
+ handler.P = -1 # params[:Permissions]
253
+
254
+ if revision >= 4
255
+ handler.EncryptMetadata = params[:encrypt_metadata]
256
+ handler.CF = Dictionary.new
257
+ cryptfilter = Encryption::CryptFilterDictionary.new
258
+ cryptfilter.AuthEvent = :DocOpen
259
+
260
+ if revision == 4
261
+ cryptfilter.CFM = :AESV2
262
+ else
263
+ cryptfilter.CFM = :AESV3
264
+ end
265
+
266
+ cryptfilter.Length = params[:key_size] >> 3
267
+
268
+ handler.CF[:StdCF] = cryptfilter
269
+ handler.StmF = handler.StrF = :StdCF
270
+ end
271
+
272
+ handler.set_passwords(ownerpasswd, userpasswd, doc_id)
273
+ encryption_key = handler.compute_user_encryption_key(userpasswd, doc_id)
274
+
275
+ fileInfo = get_trailer_info
276
+ fileInfo[:Encrypt] = self << handler
277
+
278
+ self.extend(Encryption::EncryptedDocument)
279
+ self.encryption_dict = handler
280
+ self.encryption_key = encryption_key
281
+ self.stm_algo = self.str_algo = algorithm
282
+
283
+ self
284
+ end
285
+
286
+ end
287
+
288
+ #
289
+ # Module to provide support for encrypting and decrypting PDF documents.
290
+ #
291
+ module Encryption
292
+
293
+ module EncryptedDocument
294
+
295
+ attr_writer :encryption_key
296
+ attr_writer :encryption_dict
297
+ attr_writer :stm_algo
298
+ attr_writer :str_algo
299
+
300
+ def physicalize(options = {})
301
+
302
+ def build(obj, revision, options) #:nodoc:
303
+ if obj.is_a?(EncryptedObject) # already built
304
+ if options[:decrypt] == true
305
+ obj.pre_build
306
+ obj.decrypt!
307
+ obj.decrypted = false # makes it believe no encryption pass is required
308
+ obj.post_build
309
+ end
310
+
311
+ return
312
+ end
313
+
314
+ if obj.is_a?(ObjectStream)
315
+ obj.each do |subobj|
316
+ build(subobj, revision, options)
317
+ end
318
+ end
319
+
320
+ obj.pre_build
321
+
322
+ case obj
323
+ when String
324
+ if not obj.equal?(@encryption_dict[:U]) and
325
+ not obj.equal?(@encryption_dict[:O]) and
326
+ not obj.equal?(@encryption_dict[:UE]) and
327
+ not obj.equal?(@encryption_dict[:OE]) and
328
+ not obj.equal?(@encryption_dict[:Perms]) and
329
+ not (obj.parent.is_a?(Signature::DigitalSignature) and obj.equal?(obj.parent[:Contents])) and
330
+ not obj.indirect_parent.parent.is_a?(ObjectStream)
331
+
332
+ obj.extend(EncryptedString)
333
+ obj.decrypted = true
334
+ obj.encryption_handler = @encryption_dict
335
+ obj.encryption_key = @encryption_key
336
+ obj.algorithm = @str_algo
337
+ end
338
+
339
+ when Stream
340
+ return if obj.is_a?(XRefStream)
341
+ return if obj.equal?(self.Catalog.Metadata) and not @encryption_dict.EncryptMetadata
342
+ obj.extend(EncryptedStream)
343
+ obj.decrypted = true
344
+ obj.encryption_handler = @encryption_dict
345
+ obj.encryption_key = @encryption_key
346
+ obj.algorithm = @stm_algo
347
+
348
+ when Dictionary, Array
349
+ obj.map! do |subobj|
350
+ if subobj.is_indirect?
351
+ if get_object(subobj.reference)
352
+ subobj.reference
353
+ else
354
+ ref = add_to_revision(subobj, revision)
355
+ build(subobj, revision, options)
356
+ ref
357
+ end
358
+ else
359
+ subobj
360
+ end
361
+ end
362
+
363
+ obj.each do |subobj|
364
+ build(subobj, revision, options)
365
+ end
366
+ end
367
+
368
+ obj.post_build
369
+ end
370
+
371
+ # stack up every root objects
372
+ indirect_objects_by_rev.each do |obj, revision|
373
+ build(obj, revision, options)
374
+ end
375
+
376
+ # remove encrypt dictionary if requested
377
+ if options[:decrypt]
378
+ delete_object(get_trailer_info[:Encrypt])
379
+ get_trailer_info[:Encrypt] = nil
380
+ end
381
+
382
+ self
383
+ end
384
+
385
+ end
386
+
387
+ #
388
+ # Module for encrypted PDF objects.
389
+ #
390
+ module EncryptedObject #:nodoc
391
+
392
+ attr_writer :encryption_key
393
+ attr_writer :algorithm
394
+ attr_writer :encryption_handler
395
+ attr_accessor :decrypted
396
+
397
+ def self.extended(obj)
398
+ obj.decrypted = false
399
+ end
400
+
401
+ def post_build
402
+ encrypt!
403
+
404
+ super
405
+ end
406
+
407
+ private
408
+
409
+ def compute_object_key
410
+ if @encryption_handler.V < 5
411
+ parent = self.indirect_parent
412
+ no, gen = parent.no, parent.generation
413
+ k = @encryption_key + [no].pack("I")[0..2] + [gen].pack("I")[0..1]
414
+
415
+ key_len = (k.length > 16) ? 16 : k.length
416
+ k << "sAlT" if @algorithm == Encryption::AES
417
+
418
+ Digest::MD5.digest(k)[0, key_len]
419
+ else
420
+ @encryption_key
421
+ end
422
+ end
423
+
424
+ end
425
+
426
+ #
427
+ # Module for encrypted String.
428
+ #
429
+ module EncryptedString
430
+ include EncryptedObject
431
+
432
+ def encrypt!
433
+ if @decrypted
434
+ key = compute_object_key
435
+
436
+ encrypted_data =
437
+ if @algorithm == ARC4 or @algorithm == Identity
438
+ @algorithm.encrypt(key, self.value)
439
+ else
440
+ iv = ::Array.new(AES::BLOCKSIZE) { rand(256) }.pack('C*')
441
+ @algorithm.encrypt(key, iv, self.value)
442
+ end
443
+
444
+ @decrypted = false
445
+
446
+ self.replace(encrypted_data)
447
+ self.freeze
448
+ end
449
+
450
+ self
451
+ end
452
+
453
+ def decrypt!
454
+ unless @decrypted
455
+ key = compute_object_key
456
+ self.replace(@algorithm.decrypt(key, self.to_str))
457
+ @decrypted = true
458
+ end
459
+
460
+ self
461
+ end
462
+ end
463
+
464
+ #
465
+ # Module for encrypted Stream.
466
+ #
467
+ module EncryptedStream
468
+ include EncryptedObject
469
+
470
+ def encrypt!
471
+ if @decrypted
472
+ encode!
473
+
474
+ key = compute_object_key
475
+
476
+ @rawdata =
477
+ if @algorithm == ARC4 or @algorithm == Identity
478
+ @algorithm.encrypt(key, self.rawdata)
479
+ else
480
+ iv = ::Array.new(AES::BLOCKSIZE) { rand(256) }.pack('C*')
481
+ @algorithm.encrypt(key, iv, @rawdata)
482
+ end
483
+
484
+ @decrypted = false
485
+
486
+ @rawdata.freeze
487
+ self.freeze
488
+ end
489
+
490
+ self
491
+ end
492
+
493
+ def decrypt!
494
+ unless @decrypted
495
+ key = compute_object_key
496
+
497
+ self.rawdata = @algorithm.decrypt(key, @rawdata)
498
+ @decrypted = true
499
+ end
500
+
501
+ self
502
+ end
503
+
504
+ end
505
+
506
+ #
507
+ # Identity transformation.
508
+ #
509
+ module Identity
510
+ def Identity.encrypt(key, data)
511
+ data
512
+ end
513
+
514
+ def Identity.decrypt(key, data)
515
+ data
516
+ end
517
+ end
518
+
519
+ #
520
+ # Pure Ruby implementation of the aRC4 symmetric algorithm
521
+ #
522
+ class ARC4
523
+
524
+ #
525
+ # Encrypts data using the given key
526
+ #
527
+ def ARC4.encrypt(key, data)
528
+ ARC4.new(key).encrypt(data)
529
+ end
530
+
531
+ #
532
+ # Decrypts data using the given key
533
+ #
534
+ def ARC4.decrypt(key, data)
535
+ ARC4.new(key).decrypt(data)
536
+ end
537
+
538
+ #
539
+ # Creates and initialises a new aRC4 generator using given key
540
+ #
541
+ def initialize(key)
542
+ if Origami::OPTIONS[:use_openssl]
543
+ @key = key
544
+ else
545
+ @state = init(key)
546
+ end
547
+ end
548
+
549
+ #
550
+ # Encrypt/decrypt data with the aRC4 encryption algorithm
551
+ #
552
+ def cipher(data)
553
+ return "" if data.empty?
554
+
555
+ if Origami::OPTIONS[:use_openssl]
556
+ rc4 = OpenSSL::Cipher::RC4.new.encrypt
557
+ rc4.key_len = @key.length
558
+ rc4.key = @key
559
+
560
+ output = rc4.update(data) << rc4.final
561
+ else
562
+ output = ""
563
+ i, j = 0, 0
564
+ data.each_byte do |byte|
565
+ i = i.succ & 0xFF
566
+ j = (j + @state[i]) & 0xFF
567
+
568
+ @state[i], @state[j] = @state[j], @state[i]
569
+
570
+ output << (@state[@state[i] + @state[j] & 0xFF] ^ byte).chr
571
+ end
572
+ end
573
+
574
+ output
575
+ end
576
+
577
+ alias encrypt cipher
578
+ alias decrypt cipher
579
+
580
+ private
581
+
582
+ def init(key) #:nodoc:
583
+
584
+ state = (0..255).to_a
585
+
586
+ j = 0
587
+ 256.times do |i|
588
+ j = ( j + state[i] + key[i % key.size].ord ) & 0xFF
589
+ state[i], state[j] = state[j], state[i]
590
+ end
591
+
592
+ state
593
+ end
594
+
595
+ end
596
+
597
+ #
598
+ # Pure Ruby implementation of the AES symmetric algorithm.
599
+ # Using mode CBC.
600
+ #
601
+ class AES
602
+
603
+ NROWS = 4
604
+ NCOLS = 4
605
+ BLOCKSIZE = NROWS * NCOLS
606
+
607
+ ROUNDS =
608
+ {
609
+ 16 => 10,
610
+ 24 => 12,
611
+ 32 => 14
612
+ }
613
+
614
+ #
615
+ # Rijndael S-box
616
+ #
617
+ SBOX =
618
+ [
619
+ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
620
+ 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
621
+ 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
622
+ 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
623
+ 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
624
+ 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
625
+ 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
626
+ 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
627
+ 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
628
+ 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
629
+ 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
630
+ 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
631
+ 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
632
+ 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
633
+ 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
634
+ 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
635
+ ]
636
+
637
+ #
638
+ # Inverse of the Rijndael S-box
639
+ #
640
+ RSBOX =
641
+ [
642
+ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
643
+ 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
644
+ 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
645
+ 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
646
+ 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
647
+ 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
648
+ 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
649
+ 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
650
+ 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
651
+ 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
652
+ 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
653
+ 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
654
+ 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
655
+ 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
656
+ 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
657
+ 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
658
+ ]
659
+
660
+ RCON =
661
+ [
662
+ 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,
663
+ 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39,
664
+ 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a,
665
+ 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8,
666
+ 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef,
667
+ 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc,
668
+ 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b,
669
+ 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3,
670
+ 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94,
671
+ 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20,
672
+ 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35,
673
+ 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f,
674
+ 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04,
675
+ 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63,
676
+ 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd,
677
+ 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb
678
+ ]
679
+
680
+ attr_writer :iv
681
+
682
+ def AES.encrypt(key, iv, data)
683
+ AES.new(key, iv).encrypt(data)
684
+ end
685
+
686
+ def AES.decrypt(key, data)
687
+ AES.new(key, nil).decrypt(data)
688
+ end
689
+
690
+ def initialize(key, iv, use_padding = true)
691
+ unless key.size == 16 or key.size == 24 or key.size == 32
692
+ raise EncryptionError, "Key must have a length of 128, 192 or 256 bits."
693
+ end
694
+
695
+ if not iv.nil? and iv.size != BLOCKSIZE
696
+ raise EncryptionError, "Initialization vector must have a length of #{BLOCKSIZE} bytes."
697
+ end
698
+
699
+ @key = key
700
+ @iv = iv
701
+ @use_padding = use_padding
702
+ end
703
+
704
+ def encrypt(data)
705
+
706
+ if @iv.nil?
707
+ raise EncryptionError, "No initialization vector has been set."
708
+ end
709
+
710
+ if @use_padding
711
+ padlen = BLOCKSIZE - (data.size % BLOCKSIZE)
712
+ data << (padlen.chr * padlen)
713
+ end
714
+
715
+ if Origami::OPTIONS[:use_openssl]
716
+ aes = OpenSSL::Cipher::Cipher.new("aes-#{@key.length << 3}-cbc").encrypt
717
+ aes.iv = @iv
718
+ aes.key = @key
719
+ aes.padding = 0
720
+
721
+ @iv + aes.update(data) + aes.final
722
+ else
723
+ cipher = []
724
+ cipherblock = []
725
+ nblocks = data.size / BLOCKSIZE
726
+
727
+ first_round = true
728
+ nblocks.times do |n|
729
+ plainblock = data[n * BLOCKSIZE, BLOCKSIZE].unpack("C*")
730
+
731
+ if first_round
732
+ BLOCKSIZE.times do |i| plainblock[i] ^= @iv[i].ord end
733
+ else
734
+ BLOCKSIZE.times do |i| plainblock[i] ^= cipherblock[i] end
735
+ end
736
+
737
+ first_round = false
738
+ cipherblock = aesEncrypt(plainblock)
739
+ cipher.concat(cipherblock)
740
+ end
741
+
742
+ @iv + cipher.pack("C*")
743
+ end
744
+ end
745
+
746
+ def decrypt(data)
747
+ unless data.size % BLOCKSIZE == 0
748
+ raise EncryptionError,
749
+ "Data must be 16-bytes padded (data size = #{data.size} bytes)"
750
+ end
751
+
752
+ @iv = data.slice!(0, BLOCKSIZE)
753
+
754
+ if Origami::OPTIONS[:use_openssl]
755
+ aes = OpenSSL::Cipher::Cipher.new("aes-#{@key.length << 3}-cbc").decrypt
756
+ aes.iv = @iv
757
+ aes.key = @key
758
+ aes.padding = 0
759
+
760
+ plain = (aes.update(data) + aes.final).unpack("C*")
761
+ else
762
+ plain = []
763
+ plainblock = []
764
+ prev_cipherblock = []
765
+ nblocks = data.size / BLOCKSIZE
766
+
767
+ first_round = true
768
+ nblocks.times do |n|
769
+ cipherblock = data[n * BLOCKSIZE, BLOCKSIZE].unpack("C*")
770
+
771
+ plainblock = aesDecrypt(cipherblock)
772
+
773
+ if first_round
774
+ BLOCKSIZE.times do |i| plainblock[i] ^= @iv[i].ord end
775
+ else
776
+ BLOCKSIZE.times do |i| plainblock[i] ^= prev_cipherblock[i] end
777
+ end
778
+
779
+ first_round = false
780
+ prev_cipherblock = cipherblock
781
+ plain.concat(plainblock)
782
+ end
783
+ end
784
+
785
+ if @use_padding
786
+ padlen = plain[-1]
787
+ unless (1..16) === padlen
788
+ raise EncryptionError, "Incorrect padding length : #{padlen}"
789
+ end
790
+
791
+ padlen.times do
792
+ pad = plain.pop
793
+ raise EncryptionError,
794
+ "Incorrect padding byte : 0x#{pad.to_s 16}" if pad != padlen
795
+ end
796
+ end
797
+
798
+ plain.pack("C*")
799
+ end
800
+
801
+ private
802
+
803
+ def rol(row, n = 1) #:nodoc
804
+ n.times do row.push row.shift end ; row
805
+ end
806
+
807
+ def ror(row, n = 1) #:nodoc:
808
+ n.times do row.unshift row.pop end ; row
809
+ end
810
+
811
+ def galoisMult(a, b) #:nodoc:
812
+ p = 0
813
+
814
+ 8.times do
815
+ p ^= a if b[0] == 1
816
+ highBit = a[7]
817
+ a <<= 1
818
+ a ^= 0x1b if highBit == 1
819
+ b >>= 1
820
+ end
821
+
822
+ p % 256
823
+ end
824
+
825
+ def scheduleCore(word, iter) #:nodoc:
826
+ rol(word)
827
+ word.map! do |byte| SBOX[byte] end
828
+ word[0] ^= RCON[iter]
829
+
830
+ word
831
+ end
832
+
833
+ def transpose(m) #:nodoc:
834
+ [
835
+ m[NROWS * 0, NROWS],
836
+ m[NROWS * 1, NROWS],
837
+ m[NROWS * 2, NROWS],
838
+ m[NROWS * 3, NROWS]
839
+ ].transpose.flatten
840
+ end
841
+
842
+ #
843
+ # AES round methods.
844
+ #
845
+
846
+ def createRoundKey(expandedKey, round = 0) #:nodoc:
847
+ transpose(expandedKey[round * BLOCKSIZE, BLOCKSIZE])
848
+ end
849
+
850
+ def addRoundKey(roundKey) #:nodoc:
851
+ BLOCKSIZE.times do |i| @state[i] ^= roundKey[i] end
852
+ end
853
+
854
+ def subBytes #:nodoc:
855
+ BLOCKSIZE.times do |i| @state[i] = SBOX[ @state[i] ] end
856
+ end
857
+
858
+ def rsubBytes #:nodoc:
859
+ BLOCKSIZE.times do |i| @state[i] = RSBOX[ @state[i] ] end
860
+ end
861
+
862
+ def shiftRows #:nodoc:
863
+ NROWS.times do |i|
864
+ @state[i * NCOLS, NCOLS] = rol(@state[i * NCOLS, NCOLS], i)
865
+ end
866
+ end
867
+
868
+ def rshiftRows #:nodoc:
869
+ NROWS.times do |i|
870
+ @state[i * NCOLS, NCOLS] = ror(@state[i * NCOLS, NCOLS], i)
871
+ end
872
+ end
873
+
874
+ def mixColumnWithField(column, field) #:nodoc:
875
+ p = field
876
+
877
+ column[0], column[1], column[2], column[3] =
878
+ galoisMult(column[0], p[0]) ^ galoisMult(column[3], p[1]) ^ galoisMult(column[2], p[2]) ^ galoisMult(column[1], p[3]),
879
+ galoisMult(column[1], p[0]) ^ galoisMult(column[0], p[1]) ^ galoisMult(column[3], p[2]) ^ galoisMult(column[2], p[3]),
880
+ galoisMult(column[2], p[0]) ^ galoisMult(column[1], p[1]) ^ galoisMult(column[0], p[2]) ^ galoisMult(column[3], p[3]),
881
+ galoisMult(column[3], p[0]) ^ galoisMult(column[2], p[1]) ^ galoisMult(column[1], p[2]) ^ galoisMult(column[0], p[3])
882
+ end
883
+
884
+ def mixColumn(column) #:nodoc:
885
+ mixColumnWithField(column, [ 2, 1, 1, 3 ])
886
+ end
887
+
888
+ def rmixColumn(column) #:nodoc:
889
+ mixColumnWithField(column, [ 14, 9, 13, 11 ])
890
+ end
891
+
892
+ def mixColumns #:nodoc:
893
+ NCOLS.times do |c|
894
+ column = []
895
+ NROWS.times do |r| column << @state[c + r * NCOLS] end
896
+ mixColumn(column)
897
+ NROWS.times do |r| @state[c + r * NCOLS] = column[r] end
898
+ end
899
+ end
900
+
901
+ def rmixColumns #:nodoc:
902
+ NCOLS.times do |c|
903
+ column = []
904
+ NROWS.times do |r| column << @state[c + r * NCOLS] end
905
+ rmixColumn(column)
906
+ NROWS.times do |r| @state[c + r * NCOLS] = column[r] end
907
+ end
908
+ end
909
+
910
+ def expandKey(key) #:nodoc:
911
+
912
+ key = key.unpack("C*")
913
+ size = key.size
914
+ expandedSize = 16 * (ROUNDS[key.size] + 1)
915
+ rconIter = 1
916
+ expandedKey = key[0, size]
917
+
918
+ while expandedKey.size < expandedSize
919
+ temp = expandedKey[-4, 4]
920
+
921
+ if expandedKey.size % size == 0
922
+ scheduleCore(temp, rconIter)
923
+ rconIter = rconIter.succ
924
+ end
925
+
926
+ temp.map! do |b| SBOX[b] end if size == 32 and expandedKey.size % size == 16
927
+
928
+ temp.each do |b| expandedKey << (expandedKey[-size] ^ b) end
929
+ end
930
+
931
+ expandedKey
932
+ end
933
+
934
+ def aesRound(roundKey) #:nodoc:
935
+ subBytes
936
+ #puts "after subBytes: #{@state.inspect}"
937
+ shiftRows
938
+ #puts "after shiftRows: #{@state.inspect}"
939
+ mixColumns
940
+ #puts "after mixColumns: #{@state.inspect}"
941
+ addRoundKey(roundKey)
942
+ #puts "roundKey = #{roundKey.inspect}"
943
+ #puts "after addRoundKey: #{@state.inspect}"
944
+ end
945
+
946
+ def raesRound(roundKey) #:nodoc:
947
+ addRoundKey(roundKey)
948
+ rmixColumns
949
+ rshiftRows
950
+ rsubBytes
951
+ end
952
+
953
+ def aesEncrypt(block) #:nodoc:
954
+ @state = transpose(block)
955
+ expandedKey = expandKey(@key)
956
+ rounds = ROUNDS[@key.size]
957
+
958
+ aesMain(expandedKey, rounds)
959
+ end
960
+
961
+ def aesDecrypt(block) #:nodoc:
962
+ @state = transpose(block)
963
+ expandedKey = expandKey(@key)
964
+ rounds = ROUNDS[@key.size]
965
+
966
+ raesMain(expandedKey, rounds)
967
+ end
968
+
969
+ def aesMain(expandedKey, rounds) #:nodoc:
970
+ #puts "expandedKey: #{expandedKey.inspect}"
971
+ roundKey = createRoundKey(expandedKey)
972
+ addRoundKey(roundKey)
973
+
974
+ for i in 1..rounds-1
975
+ roundKey = createRoundKey(expandedKey, i)
976
+ aesRound(roundKey)
977
+ end
978
+
979
+ roundKey = createRoundKey(expandedKey, rounds)
980
+ subBytes
981
+ shiftRows
982
+ addRoundKey(roundKey)
983
+
984
+ transpose(@state)
985
+ end
986
+
987
+ def raesMain(expandedKey, rounds) #:nodoc:
988
+
989
+ roundKey = createRoundKey(expandedKey, rounds)
990
+ addRoundKey(roundKey)
991
+ rshiftRows
992
+ rsubBytes
993
+
994
+ (rounds - 1).downto(1) do |i|
995
+ roundKey = createRoundKey(expandedKey, i)
996
+ raesRound(roundKey)
997
+ end
998
+
999
+ roundKey = createRoundKey(expandedKey)
1000
+ addRoundKey(roundKey)
1001
+
1002
+ transpose(@state)
1003
+ end
1004
+
1005
+ end
1006
+
1007
+ #
1008
+ # Class representing a crypt filter Dictionary
1009
+ #
1010
+ class CryptFilterDictionary < Dictionary
1011
+ include StandardObject
1012
+
1013
+ field :Type, :Type => Name, :Default => :CryptFilter
1014
+ field :CFM, :Type => Name, :Default => :None
1015
+ field :AuthEvent, :Type => Name, :Default => :DocOpen
1016
+ field :Length, :Type => Integer
1017
+ end
1018
+
1019
+ #
1020
+ # Common class for encryption dictionaries.
1021
+ #
1022
+ class EncryptionDictionary < Dictionary
1023
+ include StandardObject
1024
+
1025
+ field :Filter, :Type => Name, :Default => :Standard, :Required => true
1026
+ field :SubFilter, :Type => Name, :Version => "1.3"
1027
+ field :V, :Type => Integer, :Default => 0
1028
+ field :Length, :Type => Integer, :Default => 40, :Version => "1.4"
1029
+ field :CF, :Type => Dictionary, :Version => "1.5"
1030
+ field :StmF, :Type => Name, :Default => :Identity, :Version => "1.5"
1031
+ field :StrF, :Type => Name, :Default => :Identity, :Version => "1.5"
1032
+ field :EFF, :Type => Name, :Version => "1.6"
1033
+ end
1034
+
1035
+ #
1036
+ # The standard security handler for PDF encryption.
1037
+ #
1038
+ module Standard
1039
+
1040
+ 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" #:nodoc:
1041
+
1042
+ #
1043
+ # Permission constants for encrypted documents.
1044
+ #
1045
+ module Permissions
1046
+ RESERVED = 1 << 6 | 1 << 7 | 0xFFFFF000
1047
+ PRINT = 1 << 2 | RESERVED
1048
+ MODIFY_CONTENTS = 1 << 3 | RESERVED
1049
+ COPY_CONTENTS = 1 << 4 | RESERVED
1050
+ MODIFY_ANNOTATIONS = 1 << 5 | RESERVED
1051
+ FILLIN_FORMS = 1 << 8 | RESERVED
1052
+ EXTRACT_CONTENTS = 1 << 9 | RESERVED
1053
+ ASSEMBLE_DOC = 1 << 10 | RESERVED
1054
+ HIGH_QUALITY_PRINT = 1 << 11 | RESERVED
1055
+
1056
+ ALL = PRINT | MODIFY_CONTENTS | COPY_CONTENTS | MODIFY_ANNOTATIONS | FILLIN_FORMS | EXTRACT_CONTENTS | ASSEMBLE_DOC | HIGH_QUALITY_PRINT
1057
+ end
1058
+
1059
+ #
1060
+ # Class defining a standard encryption dictionary.
1061
+ #
1062
+ class Dictionary < EncryptionDictionary
1063
+
1064
+ field :R, :Type => Number, :Required => true
1065
+ field :O, :Type => String, :Required => true
1066
+ field :U, :Type => String, :Required => true
1067
+ field :OE, :Type => String, :Version => '1.7', :ExtensionLevel => 3
1068
+ field :UE, :Type => String, :Version => '1.7', :ExtensionLevel => 3
1069
+ field :Perms, :Type => String, :Version => '1.7', :ExtensionLevel => 3
1070
+ field :P, :Type => Integer, :Default => 0, :Required => true
1071
+ field :EncryptMetadata, :Type => Boolean, :Default => true, :Version => "1.5"
1072
+
1073
+ #
1074
+ # Computes the key that will be used to encrypt/decrypt the document contents with user password.
1075
+ #
1076
+ def compute_user_encryption_key(userpassword, fileid)
1077
+
1078
+ if self.R < 5
1079
+ padded = pad_password(userpassword)
1080
+
1081
+ padded << self.O
1082
+ padded << [ self.P ].pack("i")
1083
+
1084
+ padded << fileid
1085
+
1086
+ encrypt_metadata = self.EncryptMetadata != false
1087
+ padded << "\xFF\xFF\xFF\xFF" if self.R >= 4 and not encrypt_metadata
1088
+
1089
+ key = Digest::MD5.digest(padded)
1090
+
1091
+ 50.times { key = Digest::MD5.digest(key[0, self.Length / 8]) } if self.R >= 3
1092
+
1093
+ if self.R == 2
1094
+ key[0, 5]
1095
+ elsif self.R >= 3
1096
+ key[0, self.Length / 8]
1097
+ end
1098
+ else
1099
+ passwd = password_to_utf8(userpassword)
1100
+
1101
+ uks = self.U[40, 8]
1102
+ ukey = Digest::SHA256.digest(passwd + uks)
1103
+
1104
+ iv = ::Array.new(AES::BLOCKSIZE, 0).pack("C*")
1105
+ AES.new(ukey, nil, false).decrypt(iv + self.UE.value)
1106
+ end
1107
+ end
1108
+
1109
+ #
1110
+ # Computes the key that will be used to encrypt/decrypt the document contents with owner password.
1111
+ # Revision 5 only.
1112
+ #
1113
+ def compute_owner_encryption_key(ownerpassword)
1114
+ if self.R == 5
1115
+ passwd = password_to_utf8(ownerpassword)
1116
+
1117
+ oks = self.O[40, 8]
1118
+ okey = Digest::SHA256.digest(passwd + oks)
1119
+
1120
+ iv = ::Array.new(AES::BLOCKSIZE, 0).pack("C*")
1121
+ AES.new(okey, nil, false).decrypt(iv + self.OE.value)
1122
+ end
1123
+ end
1124
+
1125
+ #
1126
+ # Set up document passwords.
1127
+ #
1128
+ def set_passwords(ownerpassword, userpassword, salt = nil)
1129
+ if self.R < 5
1130
+ key = compute_owner_key(ownerpassword)
1131
+ upadded = pad_password(userpassword)
1132
+
1133
+ owner_key = ARC4.encrypt(key, upadded)
1134
+ 19.times { |i| owner_key = ARC4.encrypt(xor(key,i+1), owner_key) } if self.R >= 3
1135
+
1136
+ self.O = owner_key
1137
+ self.U = compute_user_password(userpassword, salt)
1138
+
1139
+ elsif self.R == 5
1140
+ upass = password_to_utf8(userpassword)
1141
+ opass = password_to_utf8(ownerpassword)
1142
+
1143
+ uvs, uks, ovs, oks = ::Array.new(4) { ::Array.new(8) { rand(255) }.pack("C*") }
1144
+ file_key = ::Array.new(32) { rand(256) }.pack("C*")
1145
+ iv = ::Array.new(AES::BLOCKSIZE, 0).pack("C*")
1146
+
1147
+ ukey = Digest::SHA256.digest(upass + uks)
1148
+ okey = Digest::SHA256.digest(opass + oks)
1149
+
1150
+ self.UE = AES.new(ukey, iv, false).encrypt(file_key)[iv.size, 32]
1151
+ self.OE = AES.new(okey, iv, false).encrypt(file_key)[iv.size, 32]
1152
+ self.U = Digest::SHA256.digest(upass + uvs) + uvs + uks
1153
+ self.O = Digest::SHA256.digest(opass + ovs + self.U) + ovs + oks
1154
+
1155
+ perms =
1156
+ [ self.P ].pack("V") + # 0-3
1157
+ "\xff" * 4 + # 4-7
1158
+ (self.EncryptMetadata == true ? "T" : "F") + # 8
1159
+ "adb" + # 9-11
1160
+ "\x00" * 4 # 12-15
1161
+
1162
+ self.Perms = AES.new(file_key, iv, false).encrypt(perms)[iv.size, 16]
1163
+
1164
+ file_key
1165
+ end
1166
+ end
1167
+
1168
+ #
1169
+ # Checks user password.
1170
+ # For version 2,3 and 4, _salt_ is the document ID.
1171
+ # For version 5, _salt_ is the User Key Salt.
1172
+ #
1173
+ def is_user_password?(pass, salt)
1174
+
1175
+ if self.R == 2
1176
+ compute_user_password(pass, salt) == self.U
1177
+ elsif self.R == 3 or self.R == 4
1178
+ compute_user_password(pass, salt)[0, 16] == self.U[0, 16]
1179
+ elsif self.R == 5
1180
+ uvs = self.U[32, 8]
1181
+ Digest::SHA256.digest(pass + uvs) == self.U[0, 32]
1182
+ end
1183
+ end
1184
+
1185
+ #
1186
+ # Checks owner password.
1187
+ # For version 2,3 and 4, _salt_ is the document ID.
1188
+ # For version 5, _salt_ is (Owner Key Salt + U)
1189
+ #
1190
+ def is_owner_password?(pass, salt)
1191
+
1192
+ if self.R < 5
1193
+ user_password = retrieve_user_password(pass)
1194
+ is_user_password?(user_password, salt)
1195
+ elsif self.R == 5
1196
+ ovs = self.O[32, 8]
1197
+ Digest::SHA256.digest(pass + ovs + self.U) == self.O[0, 32]
1198
+ end
1199
+ end
1200
+
1201
+ #
1202
+ # Retrieve user password from owner password.
1203
+ # Cannot be used with revision 5.
1204
+ #
1205
+ def retrieve_user_password(ownerpassword)
1206
+ key = compute_owner_key(ownerpassword)
1207
+
1208
+ if self.R == 2
1209
+ ARC4.decrypt(key, self.O)
1210
+ elsif self.R == 3 or self.R == 4
1211
+ user_password = ARC4.decrypt(xor(key, 19), self.O)
1212
+ 19.times { |i| user_password = ARC4.decrypt(xor(key, 18-i), user_password) }
1213
+
1214
+ user_password
1215
+ end
1216
+ end
1217
+
1218
+ private
1219
+
1220
+ #
1221
+ # Used to encrypt/decrypt the O field.
1222
+ # Rev 2,3,4: O = crypt(user_pass, owner_key).
1223
+ # Rev 5: unused.
1224
+ #
1225
+ def compute_owner_key(ownerpassword) #:nodoc:
1226
+
1227
+ opadded = pad_password(ownerpassword)
1228
+
1229
+ hash = Digest::MD5.digest(opadded)
1230
+ 50.times { hash = Digest::MD5.digest(hash) } if self.R >= 3
1231
+
1232
+ if self.R == 2
1233
+ hash[0, 5]
1234
+ elsif self.R >= 3
1235
+ hash[0, self.Length / 8]
1236
+ end
1237
+ end
1238
+
1239
+ #
1240
+ # Compute the value of the U field.
1241
+ # Cannot be used with revision 5.
1242
+ #
1243
+ def compute_user_password(userpassword, salt) #:nodoc:
1244
+
1245
+ if self.R == 2
1246
+ key = compute_user_encryption_key(userpassword, salt)
1247
+ user_key = ARC4.encrypt(key, PADDING)
1248
+ elsif self.R == 3 or self.R == 4
1249
+ key = compute_user_encryption_key(userpassword, salt)
1250
+
1251
+ upadded = PADDING + salt
1252
+ hash = Digest::MD5.digest(upadded)
1253
+
1254
+ user_key = ARC4.encrypt(key, hash)
1255
+
1256
+ 19.times { |i| user_key = ARC4.encrypt(xor(key,i+1), user_key) }
1257
+
1258
+ user_key.ljust(32, "\xFF")
1259
+ end
1260
+ end
1261
+
1262
+ def xor(str, byte) #:nodoc:
1263
+ str.split(//).map!{|c| (c[0].ord ^ byte).chr }.join
1264
+ end
1265
+
1266
+ def pad_password(password) #:nodoc:
1267
+ return PADDING.dup if password.empty? # Fix for Ruby 1.9 bug
1268
+ password[0,32].ljust(32, PADDING)
1269
+ end
1270
+
1271
+ def password_to_utf8(passwd) #:nodoc:
1272
+ Origami::ByteString.new(passwd).to_utf8[0, 127]
1273
+ end
1274
+
1275
+ end
1276
+
1277
+ end
1278
+
1279
+ end
1280
+
1281
+ end
1282
+
1283
+ def hexprint(str)
1284
+ hex = ""
1285
+ str.each_byte do |b|
1286
+ digit = b.to_s(16)
1287
+ digit = "0" + digit if digit.size == 1
1288
+ hex << digit
1289
+ end
1290
+
1291
+ puts hex.upcase
1292
+ end
1293
+