Class: Rpdf2txt::PdfEncrypt

Inherits:
PdfObject show all
Defined in:
lib/rpdf2txt/object.rb

Defined Under Namespace

Classes: DecryptionError

Constant Summary collapse

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"

Instance Attribute Summary

Attributes inherited from PdfObject

#attributes, #decoder, #oid, #src

Instance Method Summary collapse

Methods inherited from PdfObject

#_parse_attributes, #build_tree, #catalogue_object, #decoded_stream, #extract_attribute_stream, #initialize, #parse_attributes, #revision_id

Constructor Details

This class inherits a constructor from Rpdf2txt::PdfObject

Instance Method Details

#arc4(key, input) ⇒ Object



132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/rpdf2txt/object.rb', line 132

def arc4(key, input)
  output = ''
  s, j, k = (0..255).to_a, 0, (key*256)[0,256].unpack('C*')
  (0..255).each { |x|
    j = (j + s[x] + k[x]) % 256
    s[x], s[j] = s[j], s[x]
  }
  i = j = 0
  input.each_byte { |b|
    i = (i + 1) % 256
    j = (j + s[i]) % 256
    s[i], s[j] = s[j], s[i]
    output << (b ^ s[(s[i] + s[j])%256]).chr
  }
  output
end

#big_endian?Boolean

Returns:

  • (Boolean)


182
183
184
185
186
187
188
189
# File 'lib/rpdf2txt/object.rb', line 182

def big_endian?
	#big endian (ppc) little endian x86
	if ([1].pack('I*') ==  "\000\000\000\001") 
		true 
	else 
		false
	end
end

#compute_user_key(encryption_key) ⇒ Object



148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/rpdf2txt/object.rb', line 148

def compute_user_key encryption_key
  if revision < 3
    pdf_escape arc4(encryption_key, PADDING)
  else
    crypt = Digest::MD5.digest PADDING + file_id
    20.times do |xor|
      key = encryption_key.unpack('C*').collect! do |byte|
        byte ^ xor
      end.pack('C*')
      crypt = arc4(key, crypt)
    end
    pdf_escape crypt
  end
end

#decrypt(pdf_object) ⇒ Object



162
163
164
165
166
# File 'lib/rpdf2txt/object.rb', line 162

def decrypt(pdf_object)
	arc4_key = decrypt_key(pdf_object)
	stream = pdf_object.raw_stream
	arc4(arc4_key, stream)
end

#decrypt_key(pdf_object) ⇒ Object



167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/rpdf2txt/object.rb', line 167

def decrypt_key(pdf_object)
	oid = pdf_object.oid
	rev_id = pdf_object.revision_id
	#if it is a ppc we use reverse
	if(self.big_endian?)
		oid_three_bytes = [oid].pack('I*').reverse[0,3]
		rev_id_two_bytes = [rev_id].pack('I*').reverse[0,2]
	else
		oid_three_bytes = [oid].pack('I*')[0,3]
		rev_id_two_bytes = [rev_id].pack('I*')[0,2]
	end
	input = encryption_key << oid_three_bytes << rev_id_two_bytes
	digest = Digest::MD5.digest(input)
	digest[0,[keylength + 5,16].min]
end

#encryption_keyObject



190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
# File 'lib/rpdf2txt/object.rb', line 190

def encryption_key
  input_string = PADDING.dup
  ## we don't support a user-password. if we did, it would have to replace
  #  the first [n..32] bytes of the padding string here.
  input_string << owner_key
  input_string << permission_flag
  input_string << file_id
  ## revision >= 4: add 0xffffffff if document metadata is not encrypted
  digest = Digest::MD5.digest(input_string)
  uk = user_key
  if revision >= 3
    50.times do digest = Digest::MD5.digest(digest[0,keylength]) end
    uk = uk[0,16]
  end
  encryption_key = digest[0,keylength]
  test_key = compute_user_key encryption_key
  ## Comment out the following, since import_gkv (de.oddb.org) stops due to this error
  #  See http://dev.ywesee.com/wiki.php/Masa/20110209-debug-importGkv-rpdf2txt
  #  Also refer to http://trac.ywesee.com/ticket/74#comment:5
  #
  #if(test_key != uk)
  #  raise DecryptionError, "test-key did not match user-key ('#{test_key.inspect}' / '#{uk.inspect}')"
  #end
  encryption_key
end

#file_idObject



218
219
220
# File 'lib/rpdf2txt/object.rb', line 218

def file_id
  [@file_id].pack("H*")
end

#file_id=(file_id) ⇒ Object



215
216
217
# File 'lib/rpdf2txt/object.rb', line 215

def file_id= (file_id)
	@file_id = file_id
end

#keylengthObject



221
222
223
# File 'lib/rpdf2txt/object.rb', line 221

def keylength
  @keylength ||= (@attributes[:length] || 40).to_i / 8
end

#owner_keyObject



224
225
226
# File 'lib/rpdf2txt/object.rb', line 224

def owner_key
	@attributes[:o].to_s
end

#permission_flagObject



227
228
229
230
231
232
233
# File 'lib/rpdf2txt/object.rb', line 227

def permission_flag
	if (self.big_endian?)
		[@attributes[:p].to_i].pack('I*').reverse
	else
		[@attributes[:p].to_i].pack('I*')
	end
end

#revisionObject



234
235
236
# File 'lib/rpdf2txt/object.rb', line 234

def revision
  @attributes[:r].to_i
end

#user_keyObject



237
238
239
# File 'lib/rpdf2txt/object.rb', line 237

def user_key
  @attributes[:u].to_s
end