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.

If a problem is encountered, an error message is yielded. If no block is given or if the supplied block returns true, an error is raised.

See: PDF2.0 s7.6.3



119
120
121
122
123
124
125
126
127
128
# File 'lib/hexapdf/encryption/aes.rb', line 119

def decrypt(key, data) # :yields: error_message
  return data if data.empty? # Handle invalid files with empty strings
  if data.length % BLOCK_SIZE != 0 || data.length < BLOCK_SIZE
    msg = "Invalid data for decryption, need 32 + 16*n bytes"
    (!block_given? || yield(msg)) && raise(HexaPDF::EncryptionError, msg)
  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, the initialization vector and an optionally given block are handled like in #decrypt.



135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/hexapdf/encryption/aes.rb', line 135

def decryption_fiber(key, source) # :yields: error_message
  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
      msg = "Invalid data for decryption, need 32 + 16*n bytes"
      (!block_given? || yield(msg)) && raise(HexaPDF::EncryptionError, msg)
    end
    if 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.



168
169
170
# File 'lib/hexapdf/encryption/aes.rb', line 168

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