Class: CombinePDF::PDFDecrypt
- Inherits:
-
Object
- Object
- CombinePDF::PDFDecrypt
- Defined in:
- lib/combine_pdf/combine_pdf_decrypt.rb
Instance Method Summary collapse
- #_perform_decrypt_proc_(object, decrypt_proc, encrypted_id = nil, encrypted_generation = nil, encrypted_filter = nil) ⇒ Object
- #decrypt ⇒ Object
- #decrypt_AES(encrypted, encrypted_id, encrypted_generation, encrypted_filter) ⇒ Object
- #decrypt_none(encrypted, encrypted_id, encrypted_generation, encrypted_filter) ⇒ Object
- #decrypt_RC4(encrypted, encrypted_id, encrypted_generation, encrypted_filter) ⇒ Object
-
#initialize(objects = [], root_doctionary = {}) ⇒ PDFDecrypt
constructor
A new instance of PDFDecrypt.
- #raise_encrypted_error(object = nil) ⇒ Object
- #set_general_key(password = "") ⇒ Object
Constructor Details
#initialize(objects = [], root_doctionary = {}) ⇒ PDFDecrypt
Returns a new instance of PDFDecrypt.
11 12 13 14 15 16 17 18 19 20 21 22 23 |
# File 'lib/combine_pdf/combine_pdf_decrypt.rb', line 11 def initialize objects=[], root_doctionary = {} @objects = objects @encryption_dictionary = root_doctionary[:Encrypt] raise "Cannot decrypt an encrypted file without an encryption dictionary!" unless @encryption_dictionary @root_doctionary = root_doctionary @padding_key = [ 0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08, 0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A ] @key_crypt_first_iv_store = nil @encryption_iv = nil PDFOperations.change_references_to_actual_values @objects, @encryption_dictionary end |
Instance Method Details
#_perform_decrypt_proc_(object, decrypt_proc, encrypted_id = nil, encrypted_generation = nil, encrypted_filter = nil) ⇒ Object
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
# File 'lib/combine_pdf/combine_pdf_decrypt.rb', line 113 def _perform_decrypt_proc_ (object, decrypt_proc, encrypted_id = nil, encrypted_generation = nil, encrypted_filter = nil) case when object.is_a?(Array) object.map! { |item| _perform_decrypt_proc_(item, decrypt_proc, encrypted_id, encrypted_generation, encrypted_filter) } when object.is_a?(Hash) encrypted_id ||= object[:indirect_reference_id] encrypted_generation ||= object[:indirect_generation_number] encrypted_filter ||= object[:Filter] if object[:raw_stream_content] stream_length = object[:Length] if stream_length.is_a?(Hash) && stream_length[:is_reference_only] stream_length = PDFOperations.get_refernced_object( @objects, stream_length)[:indirect_without_dictionary] end actual_length = object[:raw_stream_content].length warn "Stream registeded length was #{object[:Length].to_s} and the actual length was #{actual_length}." if actual_length < stream_length length = [ stream_length, actual_length].min object[:raw_stream_content] = decrypt_proc.call( (object[:raw_stream_content][0...length]), encrypted_id, encrypted_generation, encrypted_filter) end object.each {|k, v| object[k] = _perform_decrypt_proc_(v, decrypt_proc, encrypted_id, encrypted_generation, encrypted_filter) if k != :raw_stream_content && (v.is_a?(Hash) || v.is_a?(Array) || v.is_a?(String))} # assumes no decrypting is ever performed on keys when object.is_a?(String) return decrypt_proc.call(object, encrypted_id, encrypted_generation, encrypted_filter) else return object end end |
#decrypt ⇒ Object
67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
# File 'lib/combine_pdf/combine_pdf_decrypt.rb', line 67 def decrypt raise_encrypted_error @encryption_dictionary unless @encryption_dictionary[:Filter] == :Standard @key = set_general_key case @encryption_dictionary[:V] when 1,2 warn "trying to decrypt with RC4." # raise_encrypted_error _perform_decrypt_proc_ @objects, self.method(:decrypt_RC4) else raise_encrypted_error end #rebuild stream lengths? @objects end |
#decrypt_AES(encrypted, encrypted_id, encrypted_generation, encrypted_filter) ⇒ Object
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/combine_pdf/combine_pdf_decrypt.rb', line 95 def decrypt_AES(encrypted, encrypted_id, encrypted_generation, encrypted_filter) ## extract encryption_iv if it wasn't extracted yet unless @encryption_iv @encryption_iv = encrypted[0..15].to_i #raise "Tryed decrypting using AES and couldn't extract iv" if @encryption_iv == 0 @encryption_iv = 0.chr * 16 #encrypted = encrypted[16..-1] end ## start decryption using padding strings object_key = @key.dup (0..2).each { |e| object_key << (encrypted_id >> e*8 & 0xFF ) } (0..1).each { |e| object_key << (encrypted_generation >> e*8 & 0xFF ) } object_key << "sAlT" key_length = object_key.length < 16 ? object_key.length : 16 cipher = OpenSSL::Cipher::Cipher.new("aes-#{object_key.length << 3}-cbc").decrypt cipher.padding = 0 (cipher.update(encrypted) + cipher.final).unpack("C*") end |
#decrypt_none(encrypted, encrypted_id, encrypted_generation, encrypted_filter) ⇒ Object
81 82 83 |
# File 'lib/combine_pdf/combine_pdf_decrypt.rb', line 81 def decrypt_none(encrypted, encrypted_id, encrypted_generation, encrypted_filter) "encrypted" end |
#decrypt_RC4(encrypted, encrypted_id, encrypted_generation, encrypted_filter) ⇒ Object
84 85 86 87 88 89 90 91 92 93 94 |
# File 'lib/combine_pdf/combine_pdf_decrypt.rb', line 84 def decrypt_RC4(encrypted, encrypted_id, encrypted_generation, encrypted_filter) ## start decryption using padding strings object_key = @key.dup object_key << [encrypted_id].pack('i')[0..2] object_key << [encrypted_generation].pack('i')[0..1] # (0..2).each { |e| object_key << (encrypted_id >> e*8 & 0xFF ) } # (0..1).each { |e| object_key << (encrypted_generation >> e*8 & 0xFF ) } key_length = object_key.length < 16 ? object_key.length : 16 rc4 = RC4.new( Digest::MD5.digest(object_key)[(0...key_length)] ) rc4.decrypt(encrypted) end |
#raise_encrypted_error(object = nil) ⇒ Object
140 141 142 143 144 |
# File 'lib/combine_pdf/combine_pdf_decrypt.rb', line 140 def raise_encrypted_error object = nil object ||= @encryption_dictionary.to_s.split(',').join("\n") warn "Data raising exception:\n #{object.to_s.split(',').join("\n")}" raise "File is encrypted - not supported." end |
#set_general_key(password = "") ⇒ Object
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/combine_pdf/combine_pdf_decrypt.rb', line 24 def set_general_key(password = "") # 1) make sure the initial key is 32 byte long (if no password, uses padding). key = (password.bytes[0..32] + @padding_key)[0..31].pack('C*').force_encoding(Encoding::ASCII_8BIT) # 2) add the value of the encryption dictionary’s O entry key << @encryption_dictionary[:O].to_s # 3) Convert the integer value of the P entry to a 32-bit unsigned binary number # and pass these bytes low-order byte first key << [@encryption_dictionary[:P]].pack('i') # 4) Pass the first element of the file’s file identifier array # (the value of the ID entry in the document’s trailer dictionary key << @root_doctionary[:ID][0] # # 4(a) (Security handlers of revision 4 or greater) # # if document metadata is not being encrypted, add 4 bytes with the value 0xFFFFFFFF. if @encryption_dictionary[:R] >= 4 unless @encryption_dictionary[:EncryptMetadata] == false #default is true and nil != false key << "\x00\x00\x00\x00" else key << "\xFF\xFF\xFF\xFF" end end # 5) pass everything as a MD5 hash key = Digest::MD5.digest(key) # 5(a) h) (Security handlers of revision 3 or greater) Do the following 50 times: # Take the output from the previous MD5 hash and # pass the first n bytes of the output as input into a new MD5 hash, # where n is the number of bytes of the encryption key as defined by the value of # the encryption dictionary’s Length entry. if @encryption_dictionary[:R] >= 3 50.times do|i| key = Digest::MD5.digest(key[0...@encryption_dictionary[:Length]]) end end # 6) Set the encryption key to the first n bytes of the output from the final MD5 hash, # where n shall always be 5 for security handlers of revision 2 but, # for security handlers of revision 3 or greater, # shall depend on the value of the encryption dictionary’s Length entry. if @encryption_dictionary[:R] >= 3 @key = key[0..(@encryption_dictionary[:Length]/8)] else @key = key[0..4] end @key end |