Class: Rack::Session::EncryptedCookie::Encryptor

Inherits:
Object
  • Object
show all
Defined in:
lib/encrypted_cookie/encryptor.rb

Overview

Encrypts messages with authentication

The use of authentication is essential to avoid Chosen Ciphertext Attacks. By using this in an encrypt then MAC form, we avoid some attacks such as e.g. being used as a CBC padding oracle to decrypt the ciphertext.

Instance Method Summary collapse

Constructor Details

#initialize(secret, cipher = 'aes-256-cbc', hmac = 'SHA256') ⇒ Encryptor

Create the encryptor

Pass in the secret, which should be at least 32-bytes worth of entropy, e.g. a string generated by ‘SecureRandom.hex(32)`. This also allows specification of the algorithm for the cipher and MAC. But don’t change that unless you’re very sure.



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/encrypted_cookie/encryptor.rb', line 19

def initialize(secret, cipher = 'aes-256-cbc', hmac = 'SHA256')
  @cipher = cipher
  @hmac   = hmac

  # use the HMAC to derive two independent keys for the encryption and
  # authentication of ciphertexts It is bad practice to use the same key
  # for encryption and authentication.  This also allows us to use all
  # of the entropy in a long key (e.g. 64 hex bytes) when straight
  # assignement would could result in assigning a key with a much
  # reduced key space.  Also, the personalisation strings further help
  # reduce the possibility of key reuse by ensuring it should be unique
  # to this gem, even with shared secrets.
  @encryption_key     = hmac("EncryptedCookie Encryption",     secret)
  @authentication_key = hmac("EncryptedCookie Authentication", secret)
end

Instance Method Details

#decrypt(ciphertext) ⇒ Object

decrypts base64 encoded ciphertext

First, it checks the message tag and returns nil if that fails to verify. Otherwise, the data is passed on to the AES function for decryption.



51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/encrypted_cookie/encryptor.rb', line 51

def decrypt(ciphertext)
  ciphertext = ciphertext.unpack('m').first
  tag        = ciphertext[0, hmac_length]
  ciphertext = ciphertext[hmac_length..-1]

  # make sure we actually had enough data for the tag too.
  if tag && ciphertext && verify_message(tag, ciphertext)
    decrypt_ciphertext(ciphertext)
  else
    nil
  end
end

#encrypt(message) ⇒ Object

Encrypts message

Returns the base64 encoded ciphertext plus IV. In addtion, the message is prepended with a MAC code to prevent chosen ciphertext attacks.



40
41
42
43
44
45
# File 'lib/encrypted_cookie/encryptor.rb', line 40

def encrypt(message)
  # encrypt the message
  encrypted = encrypt_message(message)

  [authenticate_message(encrypted) +   encrypted].pack('m0')
end