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