origami 2.0.0 → 2.0.1

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 (80) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -0
  3. data/bin/gui/config.rb +2 -1
  4. data/bin/gui/file.rb +118 -240
  5. data/bin/gui/gtkhex.rb +5 -5
  6. data/bin/gui/hexview.rb +20 -16
  7. data/bin/gui/imgview.rb +1 -1
  8. data/bin/gui/menu.rb +138 -158
  9. data/bin/gui/properties.rb +46 -48
  10. data/bin/gui/signing.rb +183 -214
  11. data/bin/gui/textview.rb +1 -1
  12. data/bin/gui/treeview.rb +13 -7
  13. data/bin/gui/walker.rb +102 -71
  14. data/bin/gui/xrefs.rb +1 -1
  15. data/bin/pdf2ruby +3 -3
  16. data/bin/pdfcop +18 -11
  17. data/bin/pdfextract +14 -5
  18. data/bin/pdfmetadata +3 -3
  19. data/bin/shell/console.rb +8 -8
  20. data/bin/shell/hexdump.rb +4 -4
  21. data/examples/attachments/nested_document.rb +1 -1
  22. data/examples/javascript/hello_world.rb +3 -3
  23. data/lib/origami.rb +0 -1
  24. data/lib/origami/acroform.rb +3 -3
  25. data/lib/origami/array.rb +1 -3
  26. data/lib/origami/boolean.rb +1 -3
  27. data/lib/origami/catalog.rb +3 -9
  28. data/lib/origami/destinations.rb +2 -2
  29. data/lib/origami/dictionary.rb +15 -29
  30. data/lib/origami/encryption.rb +334 -692
  31. data/lib/origami/extensions/fdf.rb +3 -2
  32. data/lib/origami/extensions/ppklite.rb +5 -9
  33. data/lib/origami/filespec.rb +2 -2
  34. data/lib/origami/filters.rb +54 -36
  35. data/lib/origami/filters/ascii.rb +67 -49
  36. data/lib/origami/filters/ccitt.rb +4 -236
  37. data/lib/origami/filters/ccitt/tables.rb +267 -0
  38. data/lib/origami/filters/crypt.rb +1 -1
  39. data/lib/origami/filters/dct.rb +0 -1
  40. data/lib/origami/filters/flate.rb +3 -43
  41. data/lib/origami/filters/lzw.rb +62 -99
  42. data/lib/origami/filters/predictors.rb +135 -105
  43. data/lib/origami/filters/runlength.rb +34 -22
  44. data/lib/origami/graphics.rb +2 -2
  45. data/lib/origami/graphics/colors.rb +89 -63
  46. data/lib/origami/graphics/path.rb +14 -14
  47. data/lib/origami/graphics/patterns.rb +31 -33
  48. data/lib/origami/graphics/render.rb +0 -1
  49. data/lib/origami/graphics/state.rb +9 -9
  50. data/lib/origami/graphics/text.rb +17 -17
  51. data/lib/origami/graphics/xobject.rb +102 -92
  52. data/lib/origami/javascript.rb +91 -68
  53. data/lib/origami/linearization.rb +22 -20
  54. data/lib/origami/metadata.rb +1 -1
  55. data/lib/origami/name.rb +1 -3
  56. data/lib/origami/null.rb +1 -3
  57. data/lib/origami/numeric.rb +3 -13
  58. data/lib/origami/object.rb +100 -72
  59. data/lib/origami/page.rb +24 -28
  60. data/lib/origami/parser.rb +34 -51
  61. data/lib/origami/parsers/fdf.rb +2 -2
  62. data/lib/origami/parsers/pdf.rb +41 -18
  63. data/lib/origami/parsers/pdf/lazy.rb +83 -46
  64. data/lib/origami/parsers/pdf/linear.rb +19 -10
  65. data/lib/origami/parsers/ppklite.rb +1 -1
  66. data/lib/origami/pdf.rb +150 -206
  67. data/lib/origami/reference.rb +4 -6
  68. data/lib/origami/signature.rb +76 -48
  69. data/lib/origami/stream.rb +69 -63
  70. data/lib/origami/string.rb +2 -19
  71. data/lib/origami/trailer.rb +25 -22
  72. data/lib/origami/version.rb +1 -1
  73. data/lib/origami/xfa.rb +6 -4
  74. data/lib/origami/xreftable.rb +29 -29
  75. data/test/test_annotations.rb +16 -38
  76. data/test/test_pdf_attachment.rb +1 -1
  77. data/test/test_pdf_parse.rb +1 -1
  78. data/test/test_xrefs.rb +2 -2
  79. metadata +4 -4
  80. data/lib/origami/export.rb +0 -247
@@ -47,15 +47,15 @@ USAGE
47
47
  OptionParser.new do |opts|
48
48
  opts.banner = BANNER
49
49
 
50
- opts.on("-i", "Extracts document info metadata") do |i|
50
+ opts.on("-i", "--info", "Extracts document info metadata") do
51
51
  options[:doc_info] = true
52
52
  end
53
53
 
54
- opts.on("-x", "Extracts XMP document metadata stream") do |i|
54
+ opts.on("-x", "--xmp", "Extracts XMP document metadata stream") do
55
55
  options[:doc_stream] = true
56
56
  end
57
57
 
58
- opts.on("-n", "Turn off colorized output.") do
58
+ opts.on("-n", "--no-color", "Turn off colorized output.") do
59
59
  options[:disable_colors] = true
60
60
  end
61
61
 
@@ -77,20 +77,20 @@ module Origami
77
77
  class Revision
78
78
  def to_s
79
79
  puts "---------- Body ----------".white.bold
80
- @body.each_value { |obj|
80
+ @body.each_value do |obj|
81
81
  print "#{obj.reference.to_s.rjust(8,' ')}".ljust(10).magenta
82
82
  puts "#{obj.type}".yellow
83
- }
83
+ end
84
84
 
85
85
  puts "---------- Trailer ---------".white.bold
86
86
  if not @trailer.dictionary
87
87
  puts " [x] No trailer found.".blue
88
88
  else
89
- @trailer.dictionary.each_pair { |entry, value|
89
+ @trailer.dictionary.each_pair do |entry, value|
90
90
  print " [*] ".magenta
91
- print "#{entry.to_s}: ".yellow
92
- puts "#{value.to_s}".red
93
- }
91
+ print "#{entry}: ".yellow
92
+ puts "#{value}".red
93
+ end
94
94
 
95
95
  print " [+] ".magenta
96
96
  print "startxref: ".yellow
@@ -111,9 +111,9 @@ module Origami
111
111
  print "Version: ".yellow
112
112
  puts "#{@header.major_version}.#{@header.minor_version}".red
113
113
 
114
- @revisions.each { |revision|
114
+ @revisions.each do |revision|
115
115
  revision.to_s
116
- }
116
+ end
117
117
  puts
118
118
  end
119
119
 
@@ -26,14 +26,14 @@ class String #:nodoc:
26
26
  dump = ""
27
27
  counter = 0
28
28
 
29
- while counter < length
29
+ while counter < self.length
30
30
  offset = sprintf("%010X", counter + delta)
31
31
 
32
- linelen = (counter < length - bytesperline) ? bytesperline : (length - counter)
32
+ linelen = [ self.length - counter, bytesperline ].min
33
33
  bytes = ""
34
34
  linelen.times do |i|
35
- byte = self[counter + i].ord.to_s(16)
36
- byte.insert(0, "0") if byte.size < 2
35
+ byte = self[counter + i].ord.to_s(16).rjust(2, '0')
36
+
37
37
  bytes << byte
38
38
  bytes << " " unless i == bytesperline - 1
39
39
  end
@@ -18,7 +18,7 @@ EMBEDDED_NAME = "#{('a'..'z').to_a.sample(8).join}.pdf"
18
18
  # A simple document displaying a message box.
19
19
  #
20
20
  output_str = StringIO.new
21
- nested_doc = PDF.write(output_str) do |pdf|
21
+ PDF.write(output_str) do |pdf|
22
22
  pdf.onDocumentOpen Action::JavaScript "app.alert('Hello world!');"
23
23
  end
24
24
 
@@ -15,8 +15,8 @@ include Origami
15
15
  OUTPUT_FILE = "#{File.basename(__FILE__, ".rb")}.pdf"
16
16
 
17
17
  # Creating a new file
18
- pdf = PDF.new
19
- .onDocumentOpen(Action::JavaScript 'app.alert("Hello world");')
20
- .save(OUTPUT_FILE)
18
+ PDF.new
19
+ .onDocumentOpen(Action::JavaScript 'app.alert("Hello world");')
20
+ .save(OUTPUT_FILE)
21
21
 
22
22
  puts "PDF file saved as #{OUTPUT_FILE}."
@@ -28,7 +28,6 @@ module Origami
28
28
  enable_type_checking: true, # set to false to disable type consistency checks during compilation.
29
29
  enable_type_guessing: true, # set to false to prevent the parser to guess the type of special dictionary and streams (not recommended).
30
30
  enable_type_propagation: true, # set to false to prevent the parser to propagate type from parents to children.
31
- use_openssl: true, # set to false to use Origami crypto backend.
32
31
  ignore_bad_references: false, # set to interpret invalid references as Null objects, instead of raising an exception.
33
32
  ignore_zlib_errors: false, # set to true to ignore exceptions on invalid Flate streams.
34
33
  ignore_png_errors: false, # set to true to ignore exceptions on invalid PNG predictors.
@@ -26,7 +26,7 @@ module Origami
26
26
  # Returns true if the document contains an acrobat form.
27
27
  #
28
28
  def form?
29
- (not self.Catalog.nil?) and self.Catalog.has_key? :AcroForm
29
+ self.Catalog.key? :AcroForm
30
30
  end
31
31
 
32
32
  #
@@ -101,8 +101,8 @@ module Origami
101
101
  # Flags relative to signature fields.
102
102
  #
103
103
  module SigFlags
104
- SIGNATURESEXIST = 1 << 0
105
- APPENDONLY = 1 << 1
104
+ SIGNATURES_EXIST = 1 << 0
105
+ APPEND_ONLY = 1 << 1
106
106
  end
107
107
 
108
108
  field :Fields, :Type => Array, :Required => true, :Default => []
@@ -170,7 +170,7 @@ module Origami
170
170
  def cast_to(type, parser = nil)
171
171
  super(type)
172
172
 
173
- cast = type.new(self, parser)
173
+ cast = type.new(self.copy, parser)
174
174
  cast.parent = self.parent
175
175
  cast.no, cast.generation = self.no, self.generation
176
176
  if self.indirect?
@@ -182,8 +182,6 @@ module Origami
182
182
  cast
183
183
  end
184
184
 
185
- def self.native_type ; Origami::Array end
186
-
187
185
  #
188
186
  # Parameterized Array class with additional typing information.
189
187
  # Example: Array.of(Integer)
@@ -51,7 +51,7 @@ module Origami
51
51
  super(@value.to_s)
52
52
  end
53
53
 
54
- def self.parse(stream, parser = nil) #:nodoc:
54
+ def self.parse(stream, _parser = nil) #:nodoc:
55
55
  offset = stream.pos
56
56
 
57
57
  if stream.scan(@@regexp).nil?
@@ -73,8 +73,6 @@ module Origami
73
73
  @value
74
74
  end
75
75
 
76
- def self.native_type ; Boolean end
77
-
78
76
  def false?
79
77
  @value == false
80
78
  end
@@ -39,22 +39,16 @@ module Origami
39
39
  #
40
40
  def Catalog
41
41
  cat = trailer_key(:Root)
42
+ raise InvalidPDFError, "Broken catalog" unless cat.is_a?(Catalog)
42
43
 
43
- case cat
44
- when Catalog then
45
- cat
46
- when Dictionary then
47
- cat.cast_to(Catalog)
48
- else
49
- raise InvalidPDFError, "Broken catalog"
50
- end
44
+ cat
51
45
  end
52
46
 
53
47
  #
54
48
  # Sets the current Catalog Dictionary.
55
49
  #
56
50
  def Catalog=(cat)
57
- cat = cat.cast_to(Catalog) unless cat.is_a? Catalog
51
+ raise TypeError, "Must be a Catalog object" unless cat.is_a?(Catalog)
58
52
 
59
53
  delete_object(@revisions.last.trailer[:Root]) if @revisions.last.trailer[:Root]
60
54
 
@@ -157,7 +157,7 @@ module Origami
157
157
  # _left_, _bottom_, _right_, _top_:: The rectangle to fit in.
158
158
  #
159
159
  def self.[](page, left: 0, bottom: 0, right: 0, top: 0)
160
- self.new([pageref, :FitR, left, bottom, right, top])
160
+ self.new([page, :FitR, left, bottom, right, top])
161
161
  end
162
162
  end
163
163
 
@@ -231,7 +231,7 @@ module Origami
231
231
  # _left_:: The horizontal coord.
232
232
  #
233
233
  def self.[](page, left: 0)
234
- self.new([pageref, :FitBV, left])
234
+ self.new([page, :FitBV, left])
235
235
  end
236
236
  end
237
237
 
@@ -29,14 +29,15 @@ module Origami
29
29
  #
30
30
  class Dictionary < Hash
31
31
  include Origami::Object
32
+ include FieldAccessor
32
33
  using TypeConversion
33
34
 
34
35
  TOKENS = %w{ << >> } #:nodoc:
35
36
  @@regexp_open = Regexp.new(WHITESPACES + TOKENS.first + WHITESPACES)
36
37
  @@regexp_close = Regexp.new(WHITESPACES + TOKENS.last + WHITESPACES)
37
38
 
38
- @@cast_fingerprints = {}
39
- @@cast_keys = []
39
+ @@type_signatures = {}
40
+ @@type_keys = []
40
41
 
41
42
  attr_reader :strings_cache, :names_cache, :xref_cache
42
43
 
@@ -92,13 +93,13 @@ module Origami
92
93
  key = Name.parse(stream, parser)
93
94
 
94
95
  type = Object.typeof(stream)
95
- raise InvalidDictionaryObjectError, "Invalid object for field #{key.to_s}" if type.nil?
96
+ raise InvalidDictionaryObjectError, "Invalid object for field #{key}" if type.nil?
96
97
 
97
98
  value = type.parse(stream, parser)
98
99
  hash[key] = value
99
100
  end
100
101
 
101
- if Origami::OPTIONS[:enable_type_guessing] and not (@@cast_keys & hash.keys).empty?
102
+ if Origami::OPTIONS[:enable_type_guessing] and not (@@type_keys & hash.keys).empty?
102
103
  dict_type = self.guess_type(hash)
103
104
  else
104
105
  dict_type = self
@@ -124,7 +125,7 @@ module Origami
124
125
  else
125
126
  content = TOKENS.first.dup
126
127
  self.each_pair do |key,value|
127
- content << "#{key.to_s} #{value.is_a?(Dictionary) ? value.to_s(indent: 0) : value.to_s}"
128
+ content << "#{key} #{value.is_a?(Dictionary) ? value.to_s(indent: 0) : value.to_s}"
128
129
  end
129
130
  content << TOKENS.last
130
131
  end
@@ -179,7 +180,7 @@ module Origami
179
180
  def cast_to(type, parser = nil)
180
181
  super(type)
181
182
 
182
- cast = type.new(self, parser)
183
+ cast = type.new(self.copy, parser)
183
184
  cast.parent = self.parent
184
185
  cast.no, cast.generation = self.no, self.generation
185
186
  if self.indirect?
@@ -198,17 +199,6 @@ module Origami
198
199
  end
199
200
  alias value to_h
200
201
 
201
- def method_missing(field, *args) #:nodoc:
202
- raise NoMethodError, "No method `#{field}' for #{self.class}" unless field.to_s[0,1] =~ /[A-Z]/
203
-
204
- if field.to_s[-1,1] == '='
205
- self[field.to_s[0..-2].to_sym] = args.first
206
- else
207
- obj = self[field];
208
- obj.is_a?(Reference) ? obj.solve : obj
209
- end
210
- end
211
-
212
202
  def copy
213
203
  copy = self.class.new
214
204
  self.each_pair do |k,v|
@@ -223,28 +213,24 @@ module Origami
223
213
  copy
224
214
  end
225
215
 
226
- def self.native_type; Dictionary end
227
-
228
- def self.add_type_info(klass, key, value) #:nodoc:
229
- raise TypeError, "Invalid class #{klass}" unless klass.is_a?(Class) and klass < Dictionary
230
-
216
+ def self.add_type_signature(key, value) #:nodoc:
231
217
  key, value = key.to_o, value.to_o
232
218
 
233
219
  # Inherit the superclass type information.
234
- if not @@cast_fingerprints.key?(klass) and @@cast_fingerprints.key?(klass.superclass)
235
- @@cast_fingerprints[klass] = @@cast_fingerprints[klass.superclass].dup
220
+ if not @@type_signatures.key?(self) and @@type_signatures.key?(self.superclass)
221
+ @@type_signatures[self] = @@type_signatures[self.superclass].dup
236
222
  end
237
223
 
238
- @@cast_fingerprints[klass] ||= {}
239
- @@cast_fingerprints[klass][key] = value
224
+ @@type_signatures[self] ||= {}
225
+ @@type_signatures[self][key] = value
240
226
 
241
- @@cast_keys.push(key) unless @@cast_keys.include?(key)
227
+ @@type_keys.push(key) unless @@type_keys.include?(key)
242
228
  end
243
229
 
244
230
  def self.guess_type(hash) #:nodoc:
245
231
  best_type = self
246
232
 
247
- @@cast_fingerprints.each_pair do |klass, keys|
233
+ @@type_signatures.each_pair do |klass, keys|
248
234
  next unless klass < best_type
249
235
 
250
236
  best_type = klass if keys.all? { |k,v| hash[k] == v }
@@ -253,7 +239,7 @@ module Origami
253
239
  best_type
254
240
  end
255
241
 
256
- def self.hint_type(name); nil end #:nodoc:
242
+ def self.hint_type(_name); nil end #:nodoc:
257
243
 
258
244
  private
259
245
 
@@ -18,12 +18,7 @@
18
18
 
19
19
  =end
20
20
 
21
- begin
22
- require 'openssl' if Origami::OPTIONS[:use_openssl]
23
- rescue LoadError
24
- Origami::OPTIONS[:use_openssl] = false
25
- end
26
-
21
+ require 'openssl'
27
22
  require 'securerandom'
28
23
  require 'digest/md5'
29
24
  require 'digest/sha2'
@@ -55,11 +50,12 @@ module Origami
55
50
  def decrypt(passwd = "")
56
51
  raise EncryptionError, "PDF is not encrypted" unless self.encrypted?
57
52
 
58
- encrypt_dict = trailer_key(:Encrypt)
59
- handler = Encryption::Standard::Dictionary.new(encrypt_dict.dup)
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)
60
56
 
61
57
  unless handler.Filter == :Standard
62
- raise EncryptionNotSupportedError, "Unknown security handler : '#{handler.Filter.to_s}'"
58
+ raise EncryptionNotSupportedError, "Unknown security handler : '#{handler.Filter}'"
63
59
  end
64
60
 
65
61
  crypt_filters = {
@@ -124,47 +120,13 @@ module Origami
124
120
  raise EncryptionInvalidPasswordError
125
121
  end
126
122
 
127
- encrypt_metadata = (handler.EncryptMetadata != false)
128
-
129
123
  self.extend(Encryption::EncryptedDocument)
130
124
  self.encryption_handler = handler
131
125
  self.crypt_filters = crypt_filters
132
126
  self.encryption_key = encryption_key
133
127
  self.stm_filter, self.str_filter = stream_filter, string_filter
134
128
 
135
- #
136
- # Should be fixed to exclude only the active XRefStream
137
- #
138
- metadata = self.Catalog.Metadata
139
-
140
- self.indirect_objects.each do |indobj|
141
- encrypted_objects = []
142
- case indobj
143
- when String,Stream then encrypted_objects << indobj
144
- when Dictionary,Array then encrypted_objects |= indobj.strings_cache
145
- end
146
-
147
- encrypted_objects.each do |obj|
148
- case obj
149
- when String
150
- next if obj.equal?(encrypt_dict[:U]) or
151
- obj.equal?(encrypt_dict[:O]) or
152
- obj.equal?(encrypt_dict[:UE]) or
153
- obj.equal?(encrypt_dict[:OE]) or
154
- obj.equal?(encrypt_dict[:Perms]) or
155
- (obj.parent.is_a?(Signature::DigitalSignature) and
156
- obj.equal?(obj.parent[:Contents]))
157
-
158
- obj.extend(Encryption::EncryptedString) unless obj.is_a?(Encryption::EncryptedString)
159
- obj.decrypt!
160
-
161
- when Stream
162
- next if obj.is_a?(XRefStream) or (not encrypt_metadata and obj.equal?(metadata))
163
-
164
- obj.extend(Encryption::EncryptedStream) unless obj.is_a?(Encryption::EncryptedStream)
165
- end
166
- end
167
- end
129
+ decrypt_objects
168
130
 
169
131
  self
170
132
  end
@@ -186,98 +148,145 @@ module Origami
186
148
  {
187
149
  :user_passwd => '',
188
150
  :owner_passwd => '',
189
- :cipher => 'rc4', # :RC4 or :AES
151
+ :cipher => 'aes', # :RC4 or :AES
190
152
  :key_size => 128, # Key size in bits
191
153
  :hardened => false, # Use newer password validation (since Reader X)
192
154
  :encrypt_metadata => true, # Metadata shall be encrypted?
193
155
  :permissions => Encryption::Standard::Permissions::ALL # Document permissions
194
156
  }.update(options)
195
157
 
196
- userpasswd, ownerpasswd = params[:user_passwd], params[:owner_passwd]
158
+ # Get the cryptographic parameters.
159
+ version, revision, crypt_filters = crypto_revision_from_options(params)
197
160
 
198
- case params[:cipher].upcase
199
- when 'RC4'
200
- algorithm = Encryption::RC4
201
- if (40..128) === params[:key_size] and params[:key_size] % 8 == 0
202
- if params[:key_size] > 40
203
- version = 2
204
- revision = 3
205
- else
206
- version = 1
207
- revision = 2
208
- end
209
- else
210
- raise EncryptionError, "Invalid RC4 key length"
211
- end
161
+ # Create the security handler.
162
+ handler, encryption_key = create_security_handler(version, revision, params)
212
163
 
213
- crypt_filters = Hash.new(algorithm)
214
- string_filter = stream_filter = nil
164
+ # Turn this document into an EncryptedDocument instance.
165
+ self.extend(Encryption::EncryptedDocument)
166
+ self.encryption_handler = handler
167
+ self.encryption_key = encryption_key
168
+ self.crypt_filters = crypt_filters
169
+ self.stm_filter = self.str_filter = :StdCF
215
170
 
216
- when 'AES'
217
- algorithm = Encryption::AES
218
- if params[:key_size] == 128
219
- version = revision = 4
220
- elsif params[:key_size] == 256
221
- version = 5
222
- if params[:hardened]
223
- revision = 6
224
- else
225
- revision = 5
226
- end
227
- else
228
- raise EncryptionError, "Invalid AES key length (Only 128 and 256 bits keys are supported)"
229
- end
171
+ self
172
+ end
230
173
 
231
- crypt_filters = {
232
- Identity: Encryption::Identity,
233
- StdCF: algorithm
234
- }
235
- string_filter = stream_filter = :StdCF
174
+ private
236
175
 
237
- else
238
- raise EncryptionNotSupportedError, "Cipher not supported : #{params[:cipher]}"
239
- end
176
+ #
177
+ # Installs the standard security dictionary, marking the document as being encrypted.
178
+ # Returns the handler and the encryption key used for protecting contents.
179
+ #
180
+ def create_security_handler(version, revision, params)
240
181
 
182
+ # Ensure the document has an ID.
241
183
  doc_id = (trailer_key(:ID) || generate_id).first
242
184
 
185
+ # Create the standard encryption dictionary.
243
186
  handler = Encryption::Standard::Dictionary.new
244
- handler.Filter = :Standard #:nodoc:
187
+ handler.Filter = :Standard
245
188
  handler.V = version
246
189
  handler.R = revision
247
190
  handler.Length = params[:key_size]
248
191
  handler.P = -1 # params[:Permissions]
249
192
 
193
+ # Build the crypt filter dictionary.
250
194
  if revision >= 4
251
195
  handler.EncryptMetadata = params[:encrypt_metadata]
252
196
  handler.CF = Dictionary.new
253
- cryptfilter = Encryption::CryptFilterDictionary.new
254
- cryptfilter.AuthEvent = :DocOpen
197
+ crypt_filter = Encryption::CryptFilterDictionary.new
198
+ crypt_filter.AuthEvent = :DocOpen
255
199
 
256
200
  if revision == 4
257
- cryptfilter.CFM = :AESV2
201
+ crypt_filter.CFM = :AESV2
258
202
  else
259
- cryptfilter.CFM = :AESV3
203
+ crypt_filter.CFM = :AESV3
260
204
  end
261
205
 
262
- cryptfilter.Length = params[:key_size] >> 3
206
+ crypt_filter.Length = params[:key_size] >> 3
263
207
 
264
- handler.CF[:StdCF] = cryptfilter
208
+ handler.CF[:StdCF] = crypt_filter
265
209
  handler.StmF = handler.StrF = :StdCF
266
210
  end
267
211
 
268
- handler.set_passwords(ownerpasswd, userpasswd, doc_id)
269
- encryption_key = handler.compute_user_encryption_key(userpasswd, doc_id)
212
+ user_passwd, owner_passwd = params[:user_passwd], params[:owner_passwd]
270
213
 
271
- file_info = get_trailer_info
272
- file_info[:Encrypt] = self << handler
214
+ # Setup keys.
215
+ handler.set_passwords(owner_passwd, user_passwd, doc_id)
216
+ encryption_key = handler.compute_user_encryption_key(user_passwd, doc_id)
273
217
 
274
- self.extend(Encryption::EncryptedDocument)
275
- self.encryption_handler = handler
276
- self.encryption_key = encryption_key
277
- self.crypt_filters = crypt_filters
278
- self.stm_filter = self.str_filter = :StdCF
218
+ # Install the encryption dictionary to the document.
219
+ self.trailer.Encrypt = self << handler
279
220
 
280
- self
221
+ [ handler, encryption_key ]
222
+ end
223
+
224
+ #
225
+ # Converts the parameters passed to PDF#encrypt.
226
+ # Returns [ version, revision, crypt_filters ]
227
+ #
228
+ def crypto_revision_from_options(params)
229
+ case params[:cipher].upcase
230
+ when 'RC4'
231
+ algorithm = Encryption::RC4
232
+ version, revision = crypto_revision_from_rc4_key(params[:key_size])
233
+ crypt_filters = Hash.new(algorithm)
234
+
235
+ when 'AES'
236
+ algorithm = Encryption::AES
237
+ version, revision = crypto_revision_from_aes_key(params[:key_size], params[:hardened])
238
+
239
+ crypt_filters = {
240
+ Identity: Encryption::Identity,
241
+ StdCF: algorithm
242
+ }
243
+ else
244
+ raise EncryptionNotSupportedError, "Cipher not supported : #{params[:cipher]}"
245
+ end
246
+
247
+ [ version, revision, crypt_filters ]
248
+ end
249
+
250
+ #
251
+ # Compute the required standard security handler version based on the RC4 key size.
252
+ # _key_size_:: Key size in bits.
253
+ # Returns [ version, revision ].
254
+ #
255
+ def crypto_revision_from_rc4_key(key_size)
256
+ raise EncryptionError, "Invalid RC4 key length" unless (40..128) === key_size and key_size % 8 == 0
257
+
258
+ if key_size > 40
259
+ version = 2
260
+ revision = 3
261
+ else
262
+ version = 1
263
+ revision = 2
264
+ end
265
+
266
+ [ version, revision ]
267
+ end
268
+
269
+ #
270
+ # Compute the required standard security handler version based on the AES key size.
271
+ # _key_size_:: Key size in bits.
272
+ # _hardened_:: Use the extension level 8 hardened derivation algorithm.
273
+ # Returns [ version, revision ].
274
+ #
275
+ def crypto_revision_from_aes_key(key_size, hardened)
276
+ if key_size == 128
277
+ version = revision = 4
278
+ elsif key_size == 256
279
+ version = 5
280
+ if hardened
281
+ revision = 6
282
+ else
283
+ revision = 5
284
+ end
285
+ else
286
+ raise EncryptionError, "Invalid AES key length (Only 128 and 256 bits keys are supported)"
287
+ end
288
+
289
+ [ version, revision ]
281
290
  end
282
291
  end
283
292
 
@@ -297,11 +306,7 @@ module Origami
297
306
  # Generates _n_ random bytes from a crypto PRNG.
298
307
  #
299
308
  def self.strong_rand_bytes(n)
300
- if Origami::OPTIONS[:use_openssl]
301
- OpenSSL::Random.random_bytes(n)
302
- else
303
- SecureRandom.random_bytes(n)
304
- end
309
+ SecureRandom.random_bytes(n)
305
310
  end
306
311
 
307
312
  module EncryptedDocument
@@ -327,90 +332,91 @@ module Origami
327
332
 
328
333
  private
329
334
 
330
- def physicalize(options = {})
331
-
332
- build = -> (obj, revision) do
333
- if obj.is_a?(EncryptedObject)
334
- if options[:decrypt] == true
335
- obj.pre_build
336
- obj.decrypt!
337
- obj.decrypted = false # makes it believe no encryption pass is required
338
- obj.post_build
339
-
340
- return
341
- end
342
- end
335
+ #
336
+ # For each object subject to encryption, convert it to an EncryptedObject and decrypt it if necessary.
337
+ #
338
+ def decrypt_objects
339
+ each_encryptable_object do |object|
340
+ case object
341
+ when String
342
+ object.extend(EncryptedString) unless object.is_a?(EncryptedString)
343
+ object.decrypt!
343
344
 
344
- if obj.is_a?(ObjectStream)
345
- obj.each do |subobj|
346
- build.call(subobj, revision)
347
- end
345
+ when Stream
346
+ object.extend(EncryptedStream) unless object.is_a?(EncryptedStream)
348
347
  end
348
+ end
349
+ end
349
350
 
350
- obj.pre_build
351
-
352
- case obj
351
+ #
352
+ # For each object subject to encryption, convert it to an EncryptedObject and mark it as not encrypted yet.
353
+ #
354
+ def encrypt_objects
355
+ each_encryptable_object do |object|
356
+ case object
353
357
  when String
354
- if not obj.equal?(@encryption_handler[:U]) and
355
- not obj.equal?(@encryption_handler[:O]) and
356
- not obj.equal?(@encryption_handler[:UE]) and
357
- not obj.equal?(@encryption_handler[:OE]) and
358
- not obj.equal?(@encryption_handler[:Perms]) and
359
- not (obj.parent.is_a?(Signature::DigitalSignature) and
360
- obj.equal?(obj.parent[:Contents])) and
361
- not obj.indirect_parent.parent.is_a?(ObjectStream)
362
-
363
- unless obj.is_a?(EncryptedString)
364
- obj.extend(EncryptedString)
365
- obj.decrypted = true
366
- end
358
+ unless object.is_a?(EncryptedString)
359
+ object.extend(EncryptedString)
360
+ object.decrypted = true
367
361
  end
368
362
 
369
363
  when Stream
370
- return if obj.is_a?(XRefStream)
371
- return if obj.equal?(self.Catalog.Metadata) and not @encryption_handler.EncryptMetadata
372
-
373
- unless obj.is_a?(EncryptedStream)
374
- obj.extend(EncryptedStream)
375
- obj.decrypted = true
376
- end
377
-
378
- when Dictionary, Array
379
- obj.map! do |subobj|
380
- if subobj.indirect?
381
- if get_object(subobj.reference)
382
- subobj.reference
383
- else
384
- ref = add_to_revision(subobj, revision)
385
- build.call(subobj, revision)
386
- ref
387
- end
388
- else
389
- subobj
390
- end
364
+ unless object.is_a?(EncryptedStream)
365
+ object.extend(EncryptedStream)
366
+ object.decrypted = true
391
367
  end
368
+ end
369
+ end
370
+ end
392
371
 
393
- obj.each do |subobj|
394
- build.call(subobj, revision)
372
+ #
373
+ # Iterates over each encryptable objects in the document.
374
+ #
375
+ def each_encryptable_object(&b)
376
+
377
+ # Metadata may not be encrypted depending on the security handler configuration.
378
+ encrypt_metadata = (@encryption_handler.EncryptMetadata != false)
379
+ metadata = self.Catalog.Metadata
380
+
381
+ self.each_object(recursive: true)
382
+ .lazy
383
+ .select { |object|
384
+ case object
385
+ when Stream
386
+ not object.is_a?(XRefStream) or (encrypt_metadata and object.equal?(metadata))
387
+ when String
388
+ not object.parent.equal?(@encryption_handler)
395
389
  end
396
- end
390
+ }
391
+ .each(&b)
392
+ end
397
393
 
398
- obj.post_build
399
- end
394
+ def physicalize(options = {})
395
+ encrypt_objects
400
396
 
401
- # stack up every root objects
402
- indirect_objects_by_rev.each do |obj, revision|
403
- build.call(obj, revision)
404
- end
397
+ super
405
398
 
406
399
  # remove encrypt dictionary if requested
407
400
  if options[:decrypt]
408
- delete_object(get_trailer_info[:Encrypt])
409
- get_trailer_info[:Encrypt] = nil
401
+ delete_object(self.trailer[:Encrypt])
402
+ self.trailer[:Encrypt] = nil
410
403
  end
411
404
 
412
405
  self
413
406
  end
407
+
408
+ def build_object(object, revision, options)
409
+ if object.is_a?(EncryptedObject) and options[:decrypt]
410
+ object.pre_build
411
+ object.decrypt!
412
+ object.decrypted = false # makes it believe no encryption pass is required
413
+ object.post_build
414
+
415
+ return
416
+ end
417
+
418
+ super
419
+ end
414
420
  end
415
421
 
416
422
  #
@@ -438,7 +444,7 @@ module Origami
438
444
  no, gen = parent.no, parent.generation
439
445
  k = encryption_key + [no].pack("I")[0..2] + [gen].pack("I")[0..1]
440
446
 
441
- key_len = (k.length > 16) ? 16 : k.length
447
+ key_len = [k.length, 16].min
442
448
  k << "sAlT" if cipher == Encryption::AES
443
449
 
444
450
  Digest::MD5.digest(k)[0, key_len]
@@ -512,21 +518,7 @@ module Origami
512
518
 
513
519
  encode!
514
520
 
515
- if self.filters.first == :Crypt
516
- params = decode_params.first
517
-
518
- if params.is_a?(Dictionary) and params.Name.is_a?(Name)
519
- crypt_filter = params.Name.value
520
- else
521
- crypt_filter = :Identity
522
- end
523
-
524
- cipher = self.document.encryption_cipher(crypt_filter)
525
- else
526
- cipher = self.document.stream_encryption_cipher
527
- end
528
- raise EncryptionError, "Cannot find stream encryption filter" if cipher.nil?
529
-
521
+ cipher = get_encryption_cipher
530
522
  key = compute_object_key(cipher)
531
523
 
532
524
  @encoded_data =
@@ -548,6 +540,22 @@ module Origami
548
540
  def decrypt!
549
541
  return self if @decrypted
550
542
 
543
+ cipher = get_encryption_cipher
544
+ key = compute_object_key(cipher)
545
+
546
+ self.encoded_data = cipher.decrypt(key, @encoded_data)
547
+ @decrypted = true
548
+
549
+ self
550
+ end
551
+
552
+ private
553
+
554
+ #
555
+ # Get the stream encryption cipher.
556
+ # The cipher used may depend on the presence of a Crypt filter.
557
+ #
558
+ def get_encryption_cipher
551
559
  if self.filters.first == :Crypt
552
560
  params = decode_params.first
553
561
 
@@ -561,14 +569,10 @@ module Origami
561
569
  else
562
570
  cipher = self.document.stream_encryption_cipher
563
571
  end
564
- raise EncryptionError, "Cannot find stream encryption filter" if cipher.nil?
565
-
566
- key = compute_object_key(cipher)
567
572
 
568
- self.encoded_data = cipher.decrypt(key, @encoded_data)
569
- @decrypted = true
573
+ raise EncryptionError, "Cannot find stream encryption filter" if cipher.nil?
570
574
 
571
- self
575
+ cipher
572
576
  end
573
577
  end
574
578
 
@@ -576,17 +580,17 @@ module Origami
576
580
  # Identity transformation.
577
581
  #
578
582
  module Identity
579
- def Identity.encrypt(key, data)
583
+ def Identity.encrypt(_key, data)
580
584
  data
581
585
  end
582
586
 
583
- def Identity.decrypt(key, data)
587
+ def Identity.decrypt(_key, data)
584
588
  data
585
589
  end
586
590
  end
587
591
 
588
592
  #
589
- # Pure Ruby implementation of the RC4 symmetric algorithm
593
+ # Class wrapper for the RC4 algorithm.
590
594
  #
591
595
  class RC4
592
596
 
@@ -608,140 +612,31 @@ module Origami
608
612
  # Creates and initialises a new RC4 generator using given key
609
613
  #
610
614
  def initialize(key)
611
- if Origami::OPTIONS[:use_openssl]
612
- @key = key
613
- else
614
- @state = init(key)
615
- end
615
+ @key = key
616
616
  end
617
617
 
618
618
  #
619
619
  # Encrypt/decrypt data with the RC4 encryption algorithm
620
620
  #
621
621
  def cipher(data)
622
- return "" if data.empty?
623
-
624
- if Origami::OPTIONS[:use_openssl]
625
- rc4 = OpenSSL::Cipher::RC4.new.encrypt
626
- rc4.key_len = @key.length
627
- rc4.key = @key
628
-
629
- output = rc4.update(data) << rc4.final
630
- else
631
- output = ""
632
- i, j = 0, 0
633
- data.each_byte do |byte|
634
- i = i.succ & 0xFF
635
- j = (j + @state[i]) & 0xFF
622
+ return '' if data.empty?
636
623
 
637
- @state[i], @state[j] = @state[j], @state[i]
624
+ rc4 = OpenSSL::Cipher::RC4.new.encrypt
625
+ rc4.key_len = @key.length
626
+ rc4.key = @key
638
627
 
639
- output << (@state[@state[i] + @state[j] & 0xFF] ^ byte).chr
640
- end
641
- end
642
-
643
- output
628
+ rc4.update(data) + rc4.final
644
629
  end
645
630
 
646
631
  alias encrypt cipher
647
632
  alias decrypt cipher
648
-
649
- private
650
-
651
- def init(key) #:nodoc:
652
- state = (0..255).to_a
653
-
654
- j = 0
655
- 256.times do |i|
656
- j = ( j + state[i] + key[i % key.size].ord ) & 0xFF
657
- state[i], state[j] = state[j], state[i]
658
- end
659
-
660
- state
661
- end
662
633
  end
663
634
 
664
635
  #
665
- # Pure Ruby implementation of the AES symmetric algorithm.
666
- # Using mode CBC.
636
+ # Class wrapper for AES mode CBC.
667
637
  #
668
638
  class AES
669
- NROWS = 4
670
- NCOLS = 4
671
- BLOCKSIZE = NROWS * NCOLS
672
-
673
- ROUNDS =
674
- {
675
- 16 => 10,
676
- 24 => 12,
677
- 32 => 14
678
- }
679
-
680
- #
681
- # Rijndael S-box
682
- #
683
- SBOX =
684
- [
685
- 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
686
- 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
687
- 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
688
- 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
689
- 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
690
- 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
691
- 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
692
- 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
693
- 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
694
- 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
695
- 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
696
- 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
697
- 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
698
- 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
699
- 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
700
- 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
701
- ]
702
-
703
- #
704
- # Inverse of the Rijndael S-box
705
- #
706
- RSBOX =
707
- [
708
- 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
709
- 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
710
- 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
711
- 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
712
- 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
713
- 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
714
- 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
715
- 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
716
- 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
717
- 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
718
- 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
719
- 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
720
- 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
721
- 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
722
- 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
723
- 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
724
- ]
725
-
726
- RCON =
727
- [
728
- 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,
729
- 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39,
730
- 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a,
731
- 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8,
732
- 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef,
733
- 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc,
734
- 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b,
735
- 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3,
736
- 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94,
737
- 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20,
738
- 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35,
739
- 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f,
740
- 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04,
741
- 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63,
742
- 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd,
743
- 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb
744
- ]
639
+ BLOCKSIZE = 16
745
640
 
746
641
  attr_writer :iv
747
642
 
@@ -754,7 +649,7 @@ module Origami
754
649
  end
755
650
 
756
651
  def initialize(key, iv, use_padding = true)
757
- unless key.size == 16 or key.size == 24 or key.size == 32
652
+ unless [16, 24, 32].include?(key.size)
758
653
  raise EncryptionError, "Key must have a length of 128, 192 or 256 bits."
759
654
  end
760
655
 
@@ -777,35 +672,12 @@ module Origami
777
672
  data << (padlen.chr * padlen)
778
673
  end
779
674
 
780
- if Origami::OPTIONS[:use_openssl]
781
- aes = OpenSSL::Cipher::Cipher.new("aes-#{@key.length << 3}-cbc").encrypt
782
- aes.iv = @iv
783
- aes.key = @key
784
- aes.padding = 0
675
+ aes = OpenSSL::Cipher.new("aes-#{@key.length << 3}-cbc").encrypt
676
+ aes.iv = @iv
677
+ aes.key = @key
678
+ aes.padding = 0
785
679
 
786
- @iv + aes.update(data) + aes.final
787
- else
788
- cipher = []
789
- cipherblock = []
790
- nblocks = data.size / BLOCKSIZE
791
-
792
- first_round = true
793
- nblocks.times do |n|
794
- plainblock = data[n * BLOCKSIZE, BLOCKSIZE].unpack("C*")
795
-
796
- if first_round
797
- BLOCKSIZE.times do |i| plainblock[i] ^= @iv[i].ord end
798
- else
799
- BLOCKSIZE.times do |i| plainblock[i] ^= cipherblock[i] end
800
- end
801
-
802
- first_round = false
803
- cipherblock = aes_encrypt(plainblock)
804
- cipher.concat(cipherblock)
805
- end
806
-
807
- @iv + cipher.pack("C*")
808
- end
680
+ @iv + aes.update(data) + aes.final
809
681
  end
810
682
 
811
683
  def decrypt(data)
@@ -815,36 +687,12 @@ module Origami
815
687
 
816
688
  @iv = data.slice!(0, BLOCKSIZE)
817
689
 
818
- if Origami::OPTIONS[:use_openssl]
819
- aes = OpenSSL::Cipher::Cipher.new("aes-#{@key.length << 3}-cbc").decrypt
820
- aes.iv = @iv
821
- aes.key = @key
822
- aes.padding = 0
823
-
824
- plain = (aes.update(data) + aes.final).unpack("C*")
825
- else
826
- plain = []
827
- plainblock = []
828
- prev_cipherblock = []
829
- nblocks = data.size / BLOCKSIZE
830
-
831
- first_round = true
832
- nblocks.times do |n|
833
- cipherblock = data[n * BLOCKSIZE, BLOCKSIZE].unpack("C*")
834
-
835
- plainblock = aes_decrypt(cipherblock)
690
+ aes = OpenSSL::Cipher.new("aes-#{@key.length << 3}-cbc").decrypt
691
+ aes.iv = @iv
692
+ aes.key = @key
693
+ aes.padding = 0
836
694
 
837
- if first_round
838
- BLOCKSIZE.times do |i| plainblock[i] ^= @iv[i].ord end
839
- else
840
- BLOCKSIZE.times do |i| plainblock[i] ^= prev_cipherblock[i] end
841
- end
842
-
843
- first_round = false
844
- prev_cipherblock = cipherblock
845
- plain.concat(plainblock)
846
- end
847
- end
695
+ plain = (aes.update(data) + aes.final).unpack("C*")
848
696
 
849
697
  if @use_padding
850
698
  padlen = plain[-1]
@@ -860,223 +708,6 @@ module Origami
860
708
 
861
709
  plain.pack("C*")
862
710
  end
863
-
864
- private
865
-
866
- def rol(row, n = 1) #:nodoc
867
- n.times do row.push row.shift end ; row
868
- end
869
-
870
- def ror(row, n = 1) #:nodoc:
871
- n.times do row.unshift row.pop end ; row
872
- end
873
-
874
- def galois_mult(a, b) #:nodoc:
875
- p = 0
876
-
877
- 8.times do
878
- p ^= a if b[0] == 1
879
- highBit = a[7]
880
- a <<= 1
881
- a ^= 0x1b if highBit == 1
882
- b >>= 1
883
- end
884
-
885
- p % 256
886
- end
887
-
888
- def schedule_core(word, iter) #:nodoc:
889
- rol(word)
890
- word.map! do |byte| SBOX[byte] end
891
- word[0] ^= RCON[iter]
892
-
893
- word
894
- end
895
-
896
- def transpose(m) #:nodoc:
897
- [
898
- m[NROWS * 0, NROWS],
899
- m[NROWS * 1, NROWS],
900
- m[NROWS * 2, NROWS],
901
- m[NROWS * 3, NROWS]
902
- ].transpose.flatten
903
- end
904
-
905
- #
906
- # AES round methods.
907
- #
908
-
909
- def create_round_key(expanded_key, round = 0) #:nodoc:
910
- transpose(expanded_key[round * BLOCKSIZE, BLOCKSIZE])
911
- end
912
-
913
- def add_round_key(roundKey) #:nodoc:
914
- BLOCKSIZE.times do |i| @state[i] ^= roundKey[i] end
915
- end
916
-
917
- def sub_bytes #:nodoc:
918
- BLOCKSIZE.times do |i| @state[i] = SBOX[ @state[i] ] end
919
- end
920
-
921
- def r_sub_bytes #:nodoc:
922
- BLOCKSIZE.times do |i| @state[i] = RSBOX[ @state[i] ] end
923
- end
924
-
925
- def shift_rows #:nodoc:
926
- NROWS.times do |i|
927
- @state[i * NCOLS, NCOLS] = rol(@state[i * NCOLS, NCOLS], i)
928
- end
929
- end
930
-
931
- def r_shift_rows #:nodoc:
932
- NROWS.times do |i|
933
- @state[i * NCOLS, NCOLS] = ror(@state[i * NCOLS, NCOLS], i)
934
- end
935
- end
936
-
937
- def mix_column_with_field(column, field) #:nodoc:
938
- p = field
939
-
940
- column[0], column[1], column[2], column[3] =
941
- galois_mult(column[0], p[0]) ^
942
- galois_mult(column[3], p[1]) ^
943
- galois_mult(column[2], p[2]) ^
944
- galois_mult(column[1], p[3]),
945
-
946
- galois_mult(column[1], p[0]) ^
947
- galois_mult(column[0], p[1]) ^
948
- galois_mult(column[3], p[2]) ^
949
- galois_mult(column[2], p[3]),
950
-
951
- galois_mult(column[2], p[0]) ^
952
- galois_mult(column[1], p[1]) ^
953
- galois_mult(column[0], p[2]) ^
954
- galois_mult(column[3], p[3]),
955
-
956
- galois_mult(column[3], p[0]) ^
957
- galois_mult(column[2], p[1]) ^
958
- galois_mult(column[1], p[2]) ^
959
- galois_mult(column[0], p[3])
960
- end
961
-
962
- def mix_column(column) #:nodoc:
963
- mix_column_with_field(column, [ 2, 1, 1, 3 ])
964
- end
965
-
966
- def r_mix_column_(column) #:nodoc:
967
- mix_column_with_field(column, [ 14, 9, 13, 11 ])
968
- end
969
-
970
- def mix_columns #:nodoc:
971
- NCOLS.times do |c|
972
- column = []
973
- NROWS.times do |r| column << @state[c + r * NCOLS] end
974
- mix_column(column)
975
- NROWS.times do |r| @state[c + r * NCOLS] = column[r] end
976
- end
977
- end
978
-
979
- def r_mix_columns #:nodoc:
980
- NCOLS.times do |c|
981
- column = []
982
- NROWS.times do |r| column << @state[c + r * NCOLS] end
983
- r_mix_column_(column)
984
- NROWS.times do |r| @state[c + r * NCOLS] = column[r] end
985
- end
986
- end
987
-
988
- def expand_key(key) #:nodoc:
989
- key = key.unpack("C*")
990
- size = key.size
991
- expanded_size = 16 * (ROUNDS[key.size] + 1)
992
- rcon_iter = 1
993
- expanded_key = key[0, size]
994
-
995
- while expanded_key.size < expanded_size
996
- temp = expanded_key[-4, 4]
997
-
998
- if expanded_key.size % size == 0
999
- schedule_core(temp, rcon_iter)
1000
- rcon_iter = rcon_iter.succ
1001
- end
1002
-
1003
- temp.map! do |b| SBOX[b] end if size == 32 and expanded_key.size % size == 16
1004
-
1005
- temp.each do |b| expanded_key << (expanded_key[-size] ^ b) end
1006
- end
1007
-
1008
- expanded_key
1009
- end
1010
-
1011
- def aes_round(round_key) #:nodoc:
1012
- sub_bytes
1013
- #puts "after sub_bytes: #{@state.inspect}"
1014
- shift_rows
1015
- #puts "after shift_rows: #{@state.inspect}"
1016
- mix_columns
1017
- #puts "after mix_columns: #{@state.inspect}"
1018
- add_round_key(round_key)
1019
- #puts "roundKey = #{roundKey.inspect}"
1020
- #puts "after add_round_key: #{@state.inspect}"
1021
- end
1022
-
1023
- def r_aes_round(round_key) #:nodoc:
1024
- add_round_key(round_key)
1025
- r_mix_columns
1026
- r_shift_rows
1027
- r_sub_bytes
1028
- end
1029
-
1030
- def aes_encrypt(block) #:nodoc:
1031
- @state = transpose(block)
1032
- expanded_key = expand_key(@key)
1033
- rounds = ROUNDS[@key.size]
1034
-
1035
- aes_main(expanded_key, rounds)
1036
- end
1037
-
1038
- def aes_decrypt(block) #:nodoc:
1039
- @state = transpose(block)
1040
- expanded_key = expand_key(@key)
1041
- rounds = ROUNDS[@key.size]
1042
-
1043
- r_aes_main(expanded_key, rounds)
1044
- end
1045
-
1046
- def aes_main(expanded_key, rounds) #:nodoc:
1047
- #puts "expandedKey: #{expandedKey.inspect}"
1048
- round_key = create_round_key(expanded_key)
1049
- add_round_key(round_key)
1050
-
1051
- for i in 1..rounds-1
1052
- round_key = create_round_key(expanded_key, i)
1053
- aes_round(round_key)
1054
- end
1055
-
1056
- round_key = create_round_key(expanded_key, rounds)
1057
- sub_bytes
1058
- shift_rows
1059
- add_round_key(round_key)
1060
-
1061
- transpose(@state)
1062
- end
1063
-
1064
- def r_aes_main(expanded_key, rounds) #:nodoc:
1065
- round_key = create_round_key(expanded_key, rounds)
1066
- add_round_key(round_key)
1067
- r_shift_rows
1068
- r_sub_bytes
1069
-
1070
- (rounds - 1).downto(1) do |i|
1071
- round_key = create_round_key(expanded_key, i)
1072
- r_aes_round(round_key)
1073
- end
1074
-
1075
- round_key = create_round_key(expanded_key)
1076
- add_round_key(round_key)
1077
-
1078
- transpose(@state)
1079
- end
1080
711
  end
1081
712
 
1082
713
  #
@@ -1156,127 +787,134 @@ module Origami
1156
787
 
1157
788
  #
1158
789
  # Computes the key that will be used to encrypt/decrypt the document contents with user password.
790
+ # Called at all revisions.
1159
791
  #
1160
- def compute_user_encryption_key(userpassword, fileid)
1161
- if self.R < 5
1162
- padded = pad_password(userpassword)
1163
- padded.force_encoding('binary')
792
+ def compute_user_encryption_key(user_password, file_id)
793
+ return compute_legacy_user_encryption_key(user_password, file_id) if self.R < 5
1164
794
 
1165
- padded << self.O
1166
- padded << [ self.P ].pack("i")
795
+ passwd = password_to_utf8(user_password)
1167
796
 
1168
- padded << fileid
797
+ uks = self.U[40, 8]
1169
798
 
1170
- encrypt_metadata = self.EncryptMetadata != false
1171
- padded << [ -1 ].pack("i") if self.R >= 4 and not encrypt_metadata
799
+ if self.R == 5
800
+ ukey = Digest::SHA256.digest(passwd + uks)
801
+ else
802
+ ukey = compute_hardened_hash(passwd, uks)
803
+ end
1172
804
 
1173
- key = Digest::MD5.digest(padded)
805
+ iv = ::Array.new(AES::BLOCKSIZE, 0).pack("C*")
806
+ AES.new(ukey, nil, false).decrypt(iv + self.UE.value)
807
+ end
1174
808
 
1175
- 50.times { key = Digest::MD5.digest(key[0, self.Length / 8]) } if self.R >= 3
809
+ #
810
+ # Computes the key that will be used to encrypt/decrypt the document contents.
811
+ # Only for Revision 4 and less.
812
+ #
813
+ def compute_legacy_user_encryption_key(user_password, file_id)
814
+ padded = pad_password(user_password)
815
+ padded.force_encoding('binary')
1176
816
 
1177
- if self.R == 2
1178
- key[0, 5]
1179
- elsif self.R >= 3
1180
- key[0, self.Length / 8]
1181
- end
1182
- else
1183
- passwd = password_to_utf8(userpassword)
817
+ padded << self.O
818
+ padded << [ self.P ].pack("i")
1184
819
 
1185
- uks = self.U[40, 8]
820
+ padded << file_id
1186
821
 
1187
- if self.R == 5
1188
- ukey = Digest::SHA256.digest(passwd + uks)
1189
- else
1190
- ukey = compute_hardened_hash(passwd, uks)
1191
- end
822
+ encrypt_metadata = self.EncryptMetadata != false
823
+ padded << [ -1 ].pack("i") if self.R >= 4 and not encrypt_metadata
1192
824
 
1193
- iv = ::Array.new(AES::BLOCKSIZE, 0).pack("C*")
1194
- AES.new(ukey, nil, false).decrypt(iv + self.UE.value)
1195
- end
825
+ key = Digest::MD5.digest(padded)
826
+
827
+ 50.times { key = Digest::MD5.digest(key[0, self.Length / 8]) } if self.R >= 3
828
+
829
+ truncate_key(key)
1196
830
  end
1197
831
 
1198
832
  #
1199
833
  # Computes the key that will be used to encrypt/decrypt the document contents with owner password.
1200
834
  # Revision 5 and above.
1201
835
  #
1202
- def compute_owner_encryption_key(ownerpassword)
1203
- if self.R >= 5
1204
- passwd = password_to_utf8(ownerpassword)
836
+ def compute_owner_encryption_key(owner_password)
837
+ return if self.R < 5
1205
838
 
1206
- oks = self.O[40, 8]
839
+ passwd = password_to_utf8(owner_password)
840
+ oks = self.O[40, 8]
1207
841
 
1208
- if self.R == 5
1209
- okey = Digest::SHA256.digest(passwd + oks + self.U)
1210
- else
1211
- okey = compute_hardened_hash(passwd, oks, self.U)
1212
- end
1213
-
1214
- iv = ::Array.new(AES::BLOCKSIZE, 0).pack("C*")
1215
- AES.new(okey, nil, false).decrypt(iv + self.OE.value)
842
+ if self.R == 5
843
+ okey = Digest::SHA256.digest(passwd + oks + self.U)
844
+ else
845
+ okey = compute_hardened_hash(passwd, oks, self.U)
1216
846
  end
847
+
848
+ iv = ::Array.new(AES::BLOCKSIZE, 0).pack("C*")
849
+ AES.new(okey, nil, false).decrypt(iv + self.OE.value)
1217
850
  end
1218
851
 
1219
852
  #
1220
853
  # Set up document passwords.
1221
854
  #
1222
- def set_passwords(ownerpassword, userpassword, salt = nil)
1223
- if self.R < 5
1224
- key = compute_owner_key(ownerpassword)
1225
- upadded = pad_password(userpassword)
855
+ def set_passwords(owner_password, user_password, salt = nil)
856
+ return set_legacy_passwords(owner_password, user_password, salt) if self.R < 5
1226
857
 
1227
- owner_key = RC4.encrypt(key, upadded)
1228
- 19.times { |i| owner_key = RC4.encrypt(xor(key,i+1), owner_key) } if self.R >= 3
858
+ upass = password_to_utf8(user_password)
859
+ opass = password_to_utf8(owner_password)
1229
860
 
1230
- self.O = owner_key
1231
- self.U = compute_user_password(userpassword, salt)
861
+ uvs, uks, ovs, oks = ::Array.new(4) { Encryption.rand_bytes(8) }
862
+ file_key = Encryption.strong_rand_bytes(32)
863
+ iv = ::Array.new(AES::BLOCKSIZE, 0).pack("C*")
1232
864
 
865
+ if self.R == 5
866
+ self.U = Digest::SHA256.digest(upass + uvs) + uvs + uks
867
+ self.O = Digest::SHA256.digest(opass + ovs + self.U) + ovs + oks
868
+ ukey = Digest::SHA256.digest(upass + uks)
869
+ okey = Digest::SHA256.digest(opass + oks + self.U)
1233
870
  else
1234
- upass = password_to_utf8(userpassword)
1235
- opass = password_to_utf8(ownerpassword)
1236
-
1237
- uvs, uks, ovs, oks = ::Array.new(4) { Encryption.rand_bytes(8) }
1238
- file_key = Encryption.strong_rand_bytes(32)
1239
- iv = ::Array.new(AES::BLOCKSIZE, 0).pack("C*")
1240
-
1241
- if self.R == 5
1242
- self.U = Digest::SHA256.digest(upass + uvs) + uvs + uks
1243
- self.O = Digest::SHA256.digest(opass + ovs + self.U) + ovs + oks
1244
- ukey = Digest::SHA256.digest(upass + uks)
1245
- okey = Digest::SHA256.digest(opass + oks + self.U)
1246
- else
1247
- self.U = compute_hardened_hash(upass, uvs) + uvs + uks
1248
- self.O = compute_hardened_hash(opass, ovs, self.U) + ovs + oks
1249
- ukey = compute_hardened_hash(upass, uks)
1250
- okey = compute_hardened_hash(opass, oks, self.U)
1251
- end
871
+ self.U = compute_hardened_hash(upass, uvs) + uvs + uks
872
+ self.O = compute_hardened_hash(opass, ovs, self.U) + ovs + oks
873
+ ukey = compute_hardened_hash(upass, uks)
874
+ okey = compute_hardened_hash(opass, oks, self.U)
875
+ end
1252
876
 
1253
- self.UE = AES.new(ukey, iv, false).encrypt(file_key)[iv.size, 32]
1254
- self.OE = AES.new(okey, iv, false).encrypt(file_key)[iv.size, 32]
877
+ self.UE = AES.new(ukey, iv, false).encrypt(file_key)[iv.size, 32]
878
+ self.OE = AES.new(okey, iv, false).encrypt(file_key)[iv.size, 32]
1255
879
 
1256
- perms =
1257
- [ self.P ].pack("V") + # 0-3
1258
- [ -1 ].pack("V") + # 4-7
1259
- (self.EncryptMetadata == true ? "T" : "F") + # 8
1260
- "adb" + # 9-11
1261
- [ 0 ].pack("V") # 12-15
880
+ perms =
881
+ [ self.P ].pack("V") + # 0-3
882
+ [ -1 ].pack("V") + # 4-7
883
+ (self.EncryptMetadata == true ? "T" : "F") + # 8
884
+ "adb" + # 9-11
885
+ [ 0 ].pack("V") # 12-15
1262
886
 
1263
- self.Perms = AES.new(file_key, iv, false).encrypt(perms)[iv.size, 16]
887
+ self.Perms = AES.new(file_key, iv, false).encrypt(perms)[iv.size, 16]
1264
888
 
1265
- file_key
1266
- end
889
+ file_key
890
+ end
891
+
892
+ #
893
+ # Set up document passwords.
894
+ # Only for Revision 4 and less.
895
+ #
896
+ def set_legacy_passwords(owner_password, user_password, salt)
897
+ owner_key = compute_owner_key(owner_password)
898
+ upadded = pad_password(user_password)
899
+
900
+ owner_key_hash = RC4.encrypt(owner_key, upadded)
901
+ 19.times { |i| owner_key_hash = RC4.encrypt(xor(owner_key, i + 1), owner_key_hash) } if self.R >= 3
902
+
903
+ self.O = owner_key_hash
904
+ self.U = compute_user_password_hash(user_password, salt)
1267
905
  end
1268
906
 
1269
907
  #
1270
908
  # Checks user password.
1271
- # For version 2,3 and 4, _salt_ is the document ID.
909
+ # For version 2, 3 and 4, _salt_ is the document ID.
1272
910
  # For version 5 and 6, _salt_ is the User Key Salt.
1273
911
  #
1274
912
  def is_user_password?(pass, salt)
1275
913
 
1276
914
  if self.R == 2
1277
- compute_user_password(pass, salt) == self.U
915
+ compute_user_password_hash(pass, salt) == self.U
1278
916
  elsif self.R == 3 or self.R == 4
1279
- compute_user_password(pass, salt)[0, 16] == self.U[0, 16]
917
+ compute_user_password_hash(pass, salt)[0, 16] == self.U[0, 16]
1280
918
  elsif self.R == 5
1281
919
  uvs = self.U[32, 8]
1282
920
  Digest::SHA256.digest(password_to_utf8(pass) + uvs) == self.U[0, 32]
@@ -1309,9 +947,9 @@ module Origami
1309
947
  # Retrieve user password from owner password.
1310
948
  # Cannot be used with revision 5.
1311
949
  #
1312
- def retrieve_user_password(ownerpassword)
950
+ def retrieve_user_password(owner_password)
1313
951
 
1314
- key = compute_owner_key(ownerpassword)
952
+ key = compute_owner_key(owner_password)
1315
953
 
1316
954
  if self.R == 2
1317
955
  RC4.decrypt(key, self.O)
@@ -1330,31 +968,27 @@ module Origami
1330
968
  # Rev 2,3,4: O = crypt(user_pass, owner_key).
1331
969
  # Rev 5: unused.
1332
970
  #
1333
- def compute_owner_key(ownerpassword) #:nodoc:
971
+ def compute_owner_key(owner_password) #:nodoc:
1334
972
 
1335
- opadded = pad_password(ownerpassword)
973
+ opadded = pad_password(owner_password)
1336
974
 
1337
- hash = Digest::MD5.digest(opadded)
1338
- 50.times { hash = Digest::MD5.digest(hash) } if self.R >= 3
975
+ owner_key = Digest::MD5.digest(opadded)
976
+ 50.times { owner_key = Digest::MD5.digest(owner_key) } if self.R >= 3
1339
977
 
1340
- if self.R == 2
1341
- hash[0, 5]
1342
- elsif self.R >= 3
1343
- hash[0, self.Length / 8]
1344
- end
978
+ truncate_key(owner_key)
1345
979
  end
1346
980
 
1347
981
  #
1348
982
  # Compute the value of the U field.
1349
983
  # Cannot be used with revision 5.
1350
984
  #
1351
- def compute_user_password(userpassword, salt) #:nodoc:
985
+ def compute_user_password_hash(user_password, salt) #:nodoc:
1352
986
 
1353
987
  if self.R == 2
1354
- key = compute_user_encryption_key(userpassword, salt)
988
+ key = compute_user_encryption_key(user_password, salt)
1355
989
  user_key = RC4.encrypt(key, PADDING)
1356
990
  elsif self.R == 3 or self.R == 4
1357
- key = compute_user_encryption_key(userpassword, salt)
991
+ key = compute_user_encryption_key(user_password, salt)
1358
992
 
1359
993
  upadded = PADDING + salt
1360
994
  hash = Digest::MD5.digest(upadded)
@@ -1382,14 +1016,10 @@ module Origami
1382
1016
 
1383
1017
  block = input[0, block_size]
1384
1018
 
1385
- if Origami::OPTIONS[:use_openssl]
1386
- aes = OpenSSL::Cipher::Cipher.new("aes-128-cbc").encrypt
1387
- aes.iv = iv
1388
- aes.key = key
1389
- aes.padding = 0
1390
- else
1391
- fail "You need OpenSSL support to encrypt/decrypt documents with this method"
1392
- end
1019
+ aes = OpenSSL::Cipher.new("aes-128-cbc").encrypt
1020
+ aes.iv = iv
1021
+ aes.key = key
1022
+ aes.padding = 0
1393
1023
 
1394
1024
  64.times do |j|
1395
1025
  x = ''
@@ -1416,13 +1046,25 @@ module Origami
1416
1046
  h[0, 32]
1417
1047
  end
1418
1048
 
1049
+ #
1050
+ # Some revision handlers require different key sizes.
1051
+ # Revision 2 uses 40-bit keys.
1052
+ # Revisions 3 and higher rely on the Length field for the key size.
1053
+ #
1054
+ def truncate_key(key)
1055
+ if self.R == 2
1056
+ key[0, 5]
1057
+ elsif self.R >= 3
1058
+ key[0, self.Length / 8]
1059
+ end
1060
+ end
1061
+
1419
1062
  def xor(str, byte) #:nodoc:
1420
- str.split(//).map!{|c| (c[0].ord ^ byte).chr }.join
1063
+ str.bytes.map!{|b| b ^ byte }.pack("C*")
1421
1064
  end
1422
1065
 
1423
1066
  def pad_password(password) #:nodoc:
1424
- return PADDING.dup if password.empty? # Fix for Ruby 1.9 bug
1425
- password[0,32].ljust(32, PADDING)
1067
+ password[0, 32].ljust(32, PADDING)
1426
1068
  end
1427
1069
 
1428
1070
  def password_to_utf8(passwd) #:nodoc: