Skip to content

Encryption and Decryption Algorithms ​

This section explains the specific algorithms that are used when values 1 through 9 are specified for the ENCRYPT_KS and DECRYPT_KS functions. Values 4 through 9 only are supported for ENCRYPTED WITH column constraints in CREATE TABLE statements, using the same algorithms.

Function ParameterAlgorithmOutput Length (Encryption)Output Length (Decryption)
1Initialize AES with a 128-bit key and encrypt (or decrypt) using Output Feedback Mode (OFB).floor((n1 * 8 + 5) / 6)floor((n1 * 6) / 8))
2Initialize AES with a 192-bit key and encrypt (or decrypt) using OFB.floor((n1 * 8 + 5) / 6)floor((n1 * 6) / 8))
3Initialize AES with a 256-bit key and encrypt (or decrypt) using OFB.floor((n1 * 8 + 5) / 6)floor((n1 * 6) / 8))
4Use 128-bit key deterministic encryption (or deterministic decryption).ceil((17 + n1) / 3) * 4(floor(n1 / 4) * 3) - 17
5Use 192-bit key deterministic encryption (or deterministic decryption).ceil((25 + n1) / 3) * 4(floor(n1 / 4) * 3) - 25
6Use 256-bit key deterministic encryption (or deterministic decryption).ceil((33 + n1) / 3) * 4(floor(n1 / 4) * 3) - 33
7Use 128-bit key randomized encryption (or randomized decryption).ceil((17 + n1) / 3) * 4(floor(n1 / 4) * 3) - 17
8Use 192-bit key randomized encryption (or randomized decryption).ceil((25 + n1) / 3) * 4(floor(n1 / 4) * 3) - 25
9Use 256-bit key randomized encryption (or randomized decryption).ceil((33 + n1) / 3) * 4(floor(n1 / 4) * 3) - 33

Randomized Encryption ​

The algorithm for randomized (non-deterministic) encryption follows these steps:

  1. Accept (binary) input string plaintext with length plaintext_length bytes.
  2. Accept (binary) input key with length key_length bytes.
  3. Accept (integer) input aes_keybits (must be 128, 192, or 256).
  4. Reuse/fold key to aes_keybits. The actual size of the key required depends on the algorithm selected. The actual size of the initialization vector for modes 4 to 9 is 128 bits.
  • When the input key or ivec is shorter than expected, reuse the input until the required size is reached.
  • When the input key or ivec is longer than expected, fold the extra input with the required input via XOR.
  1. Generate a cryptographically random salt with aes_keybits (likely generated via Fortuna).
  2. ciphertext = ofb_aes(plaintext, key = (key XOR salt), iv = (salt >> (aes_keybits - 128))
  3. ciphertext_length = plaintext_length
  4. outputtext = version (1 byte) + salt (aes_keybits/8) + ciphertext (ciphertext_length bytes)
  5. outputtext_length = 1 + (aes_keybits/8) + ciphertext_length

Note:

  • This encryption algorithm is specifically designed to be secure against any kind of chosen plain-text attacks (including dictionary-based attacks for low-cardinality data sets).
  • This algorithm is also resistant to length-extension attacks; that is, an attacker with encrypt access cannot pad a chosen (binary) string to an already encrypted value.
  • Use an HMAC on the outputtext separately if you need to protect against random padding.
  • ciphertext_length mirrors plaintext_length, and therefore leaks information about the input. For example, ciphertext for 'm' will be indistinguishable (in length and randomness) from ciphertext for 'f'; however, ciphertext_length for 'male' will be different from ciphertext_length for 'female'. To guard against this leakage, pad all input to the same length.

Randomized Decryption ​

The algorithm for randomized (non-deterministic) decryption follows these steps:

  1. Accept (binary) input string outputtext with length outputtext_length bytes.
  2. Accept (binary) input key with length key_length bytes.
  3. Accept (integer) input aes_keybits (must 128, 192, or 256).
  4. Reuse/fold key to aes_keybits. The actual size of the key required depends on the algorithm selected. The actual size of the initialization vector for modes 4 to 9 is 128 bits.
  • When the input key or ivec is shorter than expected, reuse the input until the required size is reached.
  • When the input key or ivec is longer than expected, fold the extra input with the required input via XOR.
  1. version = outputtext[0:0]
  2. salt = outputtext[1:(aes_keybits/8)]
  3. ciphertext = outputtext[(aes_keybits/8):(outputtext_length-1)]
  4. plaintext = ofb_aes(ciphertext, key = (key XOR salt), iv = (salt >> (aes_keybits - 128))
  5. plaintext_length = outputtext_length - (1 + (aes_keybits/8))

Note:

  • This decryption algorithm is not susceptible to padding-oracle attacks.
  • OFB enables decryption in a stream-cipher mode instead of requiring PKCS-like padding.
  • An attacker who has decrypt access but not encrypt access may be able to encrypt chosen plain texts. This may be a problem if a user erroneously relies on an encryption system for message-authentication; use a proper HMAC if needed to prevent message forgery.

Deterministic Encryption ​

The algorithm is the same as in randomized encryption, except for the choice of salt in step 5:

salt = hmac_sha256(key, plaintext) >> (256 - aes_keybits);

Note:

  • Identical plaintext strings encrypt to identical outputtext strings when you use the same key; this method enables equality comparison.
  • The same plaintext value encrypted with two different keys generates two different outputtext values.

Deterministic Decryption ​

The algorithm for deterministic decryption works exactly the same as the algorithm for randomized decryption.

Note:

  • padding-oracle attacks are not possible.
  • Given that deterministic decryption uses an hmac_sha256 instead of sha256, it is possible to detect and prevent random length extensions. However, for compatibility with randomized encryption, Yellowbrick decryption instead returns an undefined result.

A Warning about "Symmetric" Algorithms ​

Encryption algorithms 1, 2, and 3 are considered "symmetric." Users need to be careful about choosing these algorithms if the default initialization vector (IV) is being used, if the IV is well-known, or if the IV may have been leaked. In these scenarios, roles who have ENCRYPT but not DECRYPT access may be able to decipher strings by reverse engineering the bit-stream that was used to protect those strings.

For example, assume that encryption key k0 and role r0 exist in your system, that r0 is neither a superuser nor the owner of k0, and that r0 has been granted ENCRYPT but not DECRYPT access on k0.

In turn, r0 can run a query where:

cipher-text = encrypt(plain-text, k0, {1 | 2 | 3}, iv)

Although r0 cannot run an equivalent DECRYPT query, the following property holds true for binary data:

plain-text = encrypt(encrypt(plain-text, k0, {1 | 2 | 3}, iv), k0, {1 | 2 | 3}, iv)

Therefore, if r0 happens to know the iv in use, r0 can recover the plain-text from the cipher-text by reverse-engineering the bit-stream used to protect the plain-text string.

Parent topic:ENCRYPT_KS