lib/origami/encryption.rb in origami-2.0.4 vs lib/origami/encryption.rb in origami-2.1.0
- old
+ new
@@ -56,77 +56,22 @@
unless handler.Filter == :Standard
raise EncryptionNotSupportedError, "Unknown security handler : '#{handler.Filter}'"
end
- crypt_filters = {
- Identity: Encryption::Identity
- }
-
- case handler.V.to_i
- when 1,2
- crypt_filters = Hash.new(Encryption::RC4)
- string_filter = stream_filter = nil
- when 4,5
- crypt_filters = {
- Identity: Encryption::Identity
- }
-
- if handler[:CF].is_a?(Dictionary)
- handler[:CF].each_pair do |name, cf|
- next unless cf.is_a?(Dictionary)
-
- crypt_filters[name.value] =
- if cf[:CFM] == :V2 then Encryption::RC4
- elsif cf[:CFM] == :AESV2 then Encryption::AES
- elsif cf[:CFM] == :None then Encryption::Identity
- elsif cf[:CFM] == :AESV3 and handler.V.to_i == 5 then Encryption::AES
- else
- raise EncryptionNotSupportedError, "Unsupported encryption version : #{handler.V}"
- end
- end
- end
-
- string_filter = handler.StrF.is_a?(Name) ? handler.StrF.value : :Identity
- stream_filter = handler.StmF.is_a?(Name) ? handler.StmF.value : :Identity
-
- unless crypt_filters.key?(string_filter)
- raise EncryptionError, "Invalid StrF value in encryption dictionary"
- end
-
- unless crypt_filters.key?(stream_filter)
- raise EncryptionError, "Invalid StmF value in encryption dictionary"
- end
- else
- raise EncryptionNotSupportedError, "Unsupported encryption version : #{handler.V}"
- end
-
doc_id = trailer_key(:ID)
unless doc_id.is_a?(Array)
raise EncryptionError, "Document ID was not found or is invalid" unless handler.V.to_i == 5
else
doc_id = doc_id.first
end
- if handler.is_user_password?(passwd, doc_id)
- encryption_key = handler.compute_user_encryption_key(passwd, doc_id)
- elsif handler.is_owner_password?(passwd, doc_id)
- if handler.V.to_i < 5
- user_passwd = handler.retrieve_user_password(passwd)
- encryption_key = handler.compute_user_encryption_key(user_passwd, doc_id)
- else
- encryption_key = handler.compute_owner_encryption_key(passwd)
- end
- else
- raise EncryptionInvalidPasswordError
- end
+ encryption_key = handler.derive_encryption_key(passwd, doc_id)
self.extend(Encryption::EncryptedDocument)
self.encryption_handler = handler
- self.crypt_filters = crypt_filters
self.encryption_key = encryption_key
- self.stm_filter, self.str_filter = stream_filter, string_filter
decrypt_objects
self
end
@@ -154,21 +99,19 @@
:encrypt_metadata => true, # Metadata shall be encrypted?
:permissions => Encryption::Standard::Permissions::ALL # Document permissions
}.update(options)
# Get the cryptographic parameters.
- version, revision, crypt_filters = crypto_revision_from_options(params)
+ version, revision = crypto_revision_from_options(params)
# Create the security handler.
handler, encryption_key = create_security_handler(version, revision, params)
# Turn this document into an EncryptedDocument instance.
self.extend(Encryption::EncryptedDocument)
self.encryption_handler = handler
self.encryption_key = encryption_key
- self.crypt_filters = crypt_filters
- self.stm_filter = self.str_filter = :StdCF
self
end
private
@@ -226,27 +169,16 @@
# Returns [ version, revision, crypt_filters ]
#
def crypto_revision_from_options(params)
case params[:cipher].upcase
when 'RC4'
- algorithm = Encryption::RC4
- version, revision = crypto_revision_from_rc4_key(params[:key_size])
- crypt_filters = Hash.new(algorithm)
-
+ crypto_revision_from_rc4_key(params[:key_size])
when 'AES'
- algorithm = Encryption::AES
- version, revision = crypto_revision_from_aes_key(params[:key_size], params[:hardened])
-
- crypt_filters = {
- Identity: Encryption::Identity,
- StdCF: algorithm
- }
+ crypto_revision_from_aes_key(params[:key_size], params[:hardened])
else
raise EncryptionNotSupportedError, "Cipher not supported : #{params[:cipher]}"
end
-
- [ version, revision, crypt_filters ]
end
#
# Compute the required standard security handler version based on the RC4 key size.
# _key_size_:: Key size in bits.
@@ -310,26 +242,24 @@
end
module EncryptedDocument
attr_accessor :encryption_key
attr_accessor :encryption_handler
- attr_accessor :str_filter, :stm_filter
- attr_accessor :crypt_filters
# Get the encryption cipher from the crypt filter name.
def encryption_cipher(name)
- @crypt_filters[name]
+ @encryption_handler.encryption_cipher(name)
end
# Get the default string encryption cipher.
def string_encryption_cipher
- encryption_cipher @str_filter
+ @encryption_handler.string_encryption_cipher
end
# Get the default stream encryption cipher.
def stream_encryption_cipher
- encryption_cipher @stm_filter
+ @encryption_handler.stream_encryption_cipher
end
private
#
@@ -734,10 +664,61 @@
field :Length, :Type => Integer, :Default => 40, :Version => "1.4"
field :CF, :Type => Dictionary, :Version => "1.5"
field :StmF, :Type => Name, :Default => :Identity, :Version => "1.5"
field :StrF, :Type => Name, :Default => :Identity, :Version => "1.5"
field :EFF, :Type => Name, :Version => "1.6"
+
+ #
+ # Returns the default string encryption cipher.
+ #
+ def string_encryption_cipher
+ encryption_cipher(self.StrF || :Identity)
+ end
+
+ #
+ # Returns the default stream encryption cipher.
+ #
+ def stream_encryption_cipher
+ encryption_cipher(self.StmF || :Identity)
+ end
+
+ #
+ # Returns the encryption cipher corresponding to a crypt filter name.
+ #
+ def encryption_cipher(name)
+ case self.V.to_i
+ when 1, 2
+ Encryption::RC4
+ when 4, 5
+ return Encryption::Identity if name == :Identity
+ raise EncryptionError, "Broken CF entry" unless self.CF.is_a?(Dictionary)
+
+ self.CF.select { |key, dict| key == name and dict.is_a?(Dictionary) }
+ .map { |_, dict| cipher_from_crypt_filter_method(dict[:CFM] || :None) }
+ .first
+ else
+ raise EncryptionNotSupportedError, "Unsupported encryption version: #{handler.V}"
+ end
+ end
+
+ private
+
+ #
+ # Converts a crypt filter method identifier to its cipher class.
+ #
+ def cipher_from_crypt_filter_method(name)
+ case name.to_sym
+ when :None then Encryption::Identity
+ when :V2 then Encryption::RC4
+ when :AESV2 then Encryption::AES
+ when :AESV3
+ raise EncryptionNotSupportedError, "AESV3 requires a version 5 handler" if self.V.to_i != 5
+ Encryption::AES
+ else
+ raise EncryptionNotSupportedError, "Unsupported crypt filter method: #{name}"
+ end
+ end
end
#
# The standard security handler for PDF encryption.
#
@@ -780,9 +761,28 @@
def version_required #:nodoc:
if self.R > 5
[ 1.7, 8 ]
else
super
+ end
+ end
+
+ #
+ # Checks the given password and derives the document encryption key.
+ # Raises EncryptionInvalidPasswordError on invalid password.
+ #
+ def derive_encryption_key(passwd, doc_id)
+ if is_user_password?(passwd, doc_id)
+ compute_user_encryption_key(passwd, doc_id)
+ elsif is_owner_password?(passwd, doc_id)
+ if self.V.to_i < 5
+ user_passwd = retrieve_user_password(passwd)
+ compute_user_encryption_key(user_passwd, doc_id)
+ else
+ compute_owner_encryption_key(passwd)
+ end
+ else
+ raise EncryptionInvalidPasswordError
end
end
#
# Computes the key that will be used to encrypt/decrypt the document contents with user password.