origami 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
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
+