lib/hexapdf/encryption/standard_security_handler.rb in hexapdf-0.1.0 vs lib/hexapdf/encryption/standard_security_handler.rb in hexapdf-0.2.0

- old
+ new

@@ -65,11 +65,11 @@ end when 6 if !key?(:OE) || !key?(:UE) || !key?(:Perms) yield("Value of /OE, /UE or /Perms is missing for dictionary revision 6", false) end - if value[:U].length != 48 || value[:O].length != 48 || value[:UE].length == 32 || + if value[:U].length != 48 || value[:O].length != 48 || value[:UE].length != 32 || value[:OE].length != 32 || value[:Perms].length != 16 yield("Invalid size for /U, /O, /UE, /OE or /Perms values for revisions 6", false) end else yield("Value of /R is not one of 2, 3, 4 or 6", false) @@ -138,11 +138,11 @@ # Allows everything ALL = PRINT | MODIFY_CONTENT | COPY_CONTENT | MODIFY_ANNOTATION | FILL_IN_FORMS | EXTRACT_CONTENT | ASSEMBLE_DOCUMENT | HIGH_QUALITY_PRINT # Reserved permission bits - RESERVED = 0xFFFFF000 + RESERVED = 0xFFFFF000 | 0b11000000 # Maps permission symbols to their respective value SYMBOL_TO_PERMISSION = { print: PRINT, modify_content: MODIFY_CONTENT, @@ -150,11 +150,11 @@ modify_annotation: MODIFY_ANNOTATION, fill_in_forms: FILL_IN_FORMS, extract_content: EXTRACT_CONTENT, assemble_document: ASSEMBLE_DOCUMENT, high_quality_print: HIGH_QUALITY_PRINT, - } + }.freeze # Maps a permission value to its symbol PERMISSION_TO_SYMBOL = { PRINT => :print, MODIFY_CONTENT => :modify_content, @@ -162,11 +162,11 @@ MODIFY_ANNOTATION => :modify_annotation, FILL_IN_FORMS => :fill_in_forms, EXTRACT_CONTENT => :extract_content, ASSEMBLE_DOCUMENT => :assemble_document, HIGH_QUALITY_PRINT => :high_quality_print, - } + }.freeze end # Defines all possible options that can be passed to a StandardSecurityHandler when setting @@ -196,10 +196,11 @@ # :nodoc: def initialize(data = {}) fallback_pwd = data.delete(:password) { '' } @user_password = data.delete(:user_password) { fallback_pwd } @owner_password = data.delete(:owner_password) { fallback_pwd } + @owner_password = @user_password if @owner_password.to_s.empty? @permissions = process_permissions(data.delete(:permissions) { Permissions::ALL }) @algorithm = data.delete(:algorithm) { :arc4 } @encrypt_metadata = data.delete(:encrypt_metadata) { true } if data.size > 0 raise ArgumentError, "Invalid encryption options: #{data.keys.join(', ')}" @@ -207,26 +208,28 @@ end private # Maps the permissions to an integer for use by the standard security handler. + # + # See: PDF1.7 s7.6.3.2, ADB1.7 3.5.2 (table 3.20 and the paragraphs before) def process_permissions(perms) if perms.kind_of?(Array) perms = perms.inject(0) do |result, perm| result | Permissions::SYMBOL_TO_PERMISSION.fetch(perm, 0) end end - Permissions::RESERVED | perms + ((Permissions::RESERVED | perms) & 0xFFFFFFFC) - 2**32 end end # Additionally checks that the document trailer's ID has not changed. # # See: SecurityHandler#encryption_key_valid? def encryption_key_valid? - super && trailer_id_hash == @trailer_id_hash + super && (document.trailer[:Encrypt][:R] > 4 || trailer_id_hash == @trailer_id_hash) end # Returns the permissions of the managed dictionary as array of symbol values. # # See: Permissions @@ -267,11 +270,11 @@ CFM: cfm, AuthEvent: :DocOpen, Length: key_length, }, } - dict[:StmF] = dict[:StrF] = dict[:EFF] = :StdCF + dict[:StmF] = dict[:StrF] = :StdCF end if dict[:R] <= 4 && !document.trailer[:ID].kind_of?(Array) document.trailer.set_random_id end @@ -565,10 +568,10 @@ i = 0 while i < 64 || e.getbyte(-1) > i - 32 k1 = "#{password}#{k}#{user_key}" * 64 e = aes_algorithm.new(k[0, 16], k[16, 16], :encrypt).process(k1) - k = case e.unpack('C16').inject(&:+) % 3 # 256 % 3 == 1 % 3 --> x*256 % 3 == x % 3 + k = case e.unpack('C16').inject(&:+) % 3 # 256 % 3 == 1 % 3 --> x*256 % 3 == x % 3 when 0 then Digest::SHA256.digest(e) when 1 then Digest::SHA384.digest(e) when 2 then Digest::SHA512.digest(e) end i += 1