Module: HexaPDF::Encryption::AES::ClassMethods

Defined in:
lib/hexapdf/encryption/aes.rb

Overview

Convenience methods for decryption and encryption that operate according to the PDF specification.

These methods will be available on the class object that prepends the AES module.

Instance Method Summary collapse

Instance Method Details

#decrypt(key, data) ⇒ Object

Decrypts the given data using the key.

It is assumed that the initialization vector is included in the first BLOCK_SIZE bytes of the data. After the decryption the PKCS#5 padding is removed.

See: PDF2.0 s7.6.3



116
117
118
119
120
121
122
123
124
# File 'lib/hexapdf/encryption/aes.rb', line 116

def decrypt(key, data)
  return data if data.empty? # Handle invalid files with empty strings
  if data.length % BLOCK_SIZE != 0 || data.length < BLOCK_SIZE
    raise HexaPDF::EncryptionError, "Invalid data for decryption, need 32 + 16*n bytes"
  end
  iv = data.slice!(0, BLOCK_SIZE)
  # Handle invalid files with missing padding
  data.empty? ? data : unpad(new(key, iv, :decrypt).process(data))
end

#decryption_fiber(key, source) ⇒ Object

Returns a Fiber object that decrypts the data from the given source fiber with the key.

Padding and the initialization vector are handled like in #decrypt.



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/hexapdf/encryption/aes.rb', line 130

def decryption_fiber(key, source)
  Fiber.new do
    data = ''.b
    while data.length < BLOCK_SIZE && source.alive? && (new_data = source.resume)
      data << new_data
    end
    next data if data.empty? # Handle invalid files with empty stream

    algorithm = new(key, data.slice!(0, BLOCK_SIZE), :decrypt)

    while source.alive? && (new_data = source.resume)
      data << new_data
      next if data.length < 2 * BLOCK_SIZE
      new_data = data.slice!(0, data.length - BLOCK_SIZE - data.length % BLOCK_SIZE)
      Fiber.yield(algorithm.process(new_data))
    end

    if data.length % BLOCK_SIZE != 0
      raise HexaPDF::EncryptionError, "Invalid data for decryption, need 32 + 16*n bytes"
    elsif data.empty?
      data # Handle invalid files with missing padding
    else
      unpad(algorithm.process(data))
    end
  end
end

#encrypt(key, data) ⇒ Object

Encrypts the given data using the key and a randomly generated initialization vector.

The data is padded using the PKCS#5 padding scheme and the initialization vector is prepended to the encrypted data,

See: PDF2.0 s7.6.3



83
84
85
86
# File 'lib/hexapdf/encryption/aes.rb', line 83

def encrypt(key, data)
  iv = random_bytes(BLOCK_SIZE)
  iv << new(key, iv, :encrypt).process(pad(data))
end

#encryption_fiber(key, source) ⇒ Object

Returns a Fiber object that encrypts the data from the given source fiber with the key.

Padding and the initialization vector are handled like in #encrypt.



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/hexapdf/encryption/aes.rb', line 92

def encryption_fiber(key, source)
  Fiber.new do
    data = random_bytes(BLOCK_SIZE)
    algorithm = new(key, data, :encrypt)
    Fiber.yield(data)

    data = ''.b
    while source.alive? && (new_data = source.resume)
      data << new_data
      next if data.length < BLOCK_SIZE
      new_data = data.slice!(0, data.length - data.length % BLOCK_SIZE)
      Fiber.yield(algorithm.process(new_data))
    end

    algorithm.process(pad(data))
  end
end

#random_bytes(n) ⇒ Object

Returns a string of n random bytes.

The specific AES algorithm class can override this class method to provide another method for generating random bytes.



161
162
163
# File 'lib/hexapdf/encryption/aes.rb', line 161

def random_bytes(n)
  SecureRandom.random_bytes(n)
end