Class: AEAD::Cipher

Inherits:
Object
  • Object
show all
Defined in:
lib/aead/cipher.rb

Overview

Wraps AEAD ciphers in a simplified interface.

Defined Under Namespace

Modules: AES_GCM, AES_HMAC Classes: AES_128_GCM, AES_256_CBC_HMAC_SHA_256, AES_256_CTR_HMAC_SHA_256, AES_256_GCM

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.generate_keyString

Returns a securely-generated key of appropriate length for the current Cipher.

Returns:

  • (String)

    a random key



37
38
39
# File 'lib/aead/cipher.rb', line 37

def self.generate_key
  SecureRandom.random_bytes(self.key_len)
end

.generate_nonceString

Returns a unique nonce for the current Cipher.

Returns:

  • (String)

    a random key



46
47
48
# File 'lib/aead/cipher.rb', line 46

def self.generate_nonce
  AEAD::Nonce.generate
end

.new(algorithm, options = {}) ⇒ Class

Returns a particular Cipher implementation.

Parameters:

  • algorithm (String)

    the AEAD implementation to use

Returns:

  • (Class)

    the cipher implementation



22
23
24
25
26
27
28
29
# File 'lib/aead/cipher.rb', line 22

def self.new(algorithm, options = {})
  # run normal Class#new if we're being called from a subclass
  return super unless self == AEAD::Cipher

  # TODO: ciphers should register themselves, as opposed to using a
  # potentiall-unsafe const_get
  self.const_get algorithm.tr('-', '_').upcase
end

.signature_compare(left, right) ⇒ Boolean

Does a constant-time comparison between two strings. Useful to avoid timing attacks when comparing a generated signature against a user-provided signature.

Parameters:

  • left (String)

    any string

  • right (String)

    any string

Returns:

  • (Boolean)

    whether or not the strings are equal



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/aead/cipher.rb', line 59

def self.signature_compare(left, right)
  # short-circuit if the lengths are inequal
  return false if left.to_s.bytesize != right.bytesize

  # Constant-time string comparison algorithm:
  #   1. Break both strings into bytes
  #   2. Subtract the strings from one-another, byte by byte
  #      (any non-equal bytes will subtract to a nonzero value)
  #   3. OR the XOR'd bytes together
  #   4. If the result is nonzero, the strings differed.
  left   = left.bytes.to_a
  right  = right.bytes.to_a
  result = 0

  left.length.times do |i|
    result |= left[i] - right[i]
  end

  result == 0
end

Instance Method Details

#decrypt(nonce, aad, aead) ⇒ String

Decrypts a plaintext using the current Cipher.

Parameters:

  • nonce (String)

    the nonce used when encrypting the AEAD

  • aad (String)

    the additional authentication data used when encrypting the AEAD

  • aead (String)

    the encrypted AEAD

Returns:

  • (String)

    the original plaintext



131
132
133
134
135
136
137
138
139
140
# File 'lib/aead/cipher.rb', line 131

def decrypt(nonce, aad, aead)
  _verify_nonce_bytesize(nonce, self.nonce_len)

  self._decrypt(
    _pad_nonce(nonce),
    aad,
    _extract_ciphertext(aead, self.tag_len),
    _extract_tag(aead, self.tag_len)
  )
end

#encrypt(nonce, aad, plaintext) ⇒ String

Encrypts a plaintext using the current Cipher.

IMPORTANT: Do not ever encrypt data using the same nonce more than once given a particular secret key. Doing so will violate the security guarantees of the AEAD cipher.

Parameters:

  • nonce (String)

    a unique nonce, never before used with the current encryption key

  • aad (String, nil)

    arbitrary additional authentication data that must later be provided to decrypt the aead

  • plaintext (String)

    arbitrary plaintext

Returns:

  • (String)

    the generated AEAD



112
113
114
115
116
117
118
119
120
# File 'lib/aead/cipher.rb', line 112

def encrypt(nonce, aad, plaintext)
  _verify_nonce_bytesize(nonce, self.nonce_len)

  self._encrypt(
     _pad_nonce(nonce),
     aad,
     plaintext
  )
end

#key_lenInteger

The length of keys of encryption keys used by the current Cipher.

Returns:

  • (Integer)

    the length of keys in bytes



85
86
87
# File 'lib/aead/cipher.rb', line 85

def key_len
  self.class.key_len
end

#nonce_lenInteger

The length of nonces used by the current Cipher.

Returns:

  • (Integer)

    the length of nonces in bytes



94
95
96
# File 'lib/aead/cipher.rb', line 94

def nonce_len
  self.class.nonce_len
end