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 Parameter | Algorithm | Output Length (Encryption) | Output Length (Decryption) |
---|---|---|---|
1 | Initialize AES with a 128-bit key and encrypt (or decrypt) using Output Feedback Mode (OFB). | floor((n1 * 8 + 5) / 6) |
floor((n1 * 6) / 8)) |
2 | Initialize AES with a 192-bit key and encrypt (or decrypt) using OFB. | floor((n1 * 8 + 5) / 6) |
floor((n1 * 6) / 8)) |
3 | Initialize AES with a 256-bit key and encrypt (or decrypt) using OFB. | floor((n1 * 8 + 5) / 6) |
floor((n1 * 6) / 8)) |
4 | Use 128-bit key deterministic encryption (or deterministic decryption). | ceil((17 + n1) / 3) * 4 |
(floor(n1 / 4) * 3) - 17 |
5 | Use 192-bit key deterministic encryption (or deterministic decryption). | ceil((25 + n1) / 3) * 4 |
(floor(n1 / 4) * 3) - 25 |
6 | Use 256-bit key deterministic encryption (or deterministic decryption). | ceil((33 + n1) / 3) * 4 |
(floor(n1 / 4) * 3) - 33 |
7 | Use 128-bit key randomized encryption (or randomized decryption). | ceil((17 + n1) / 3) * 4 |
(floor(n1 / 4) * 3) - 17 |
8 | Use 192-bit key randomized encryption (or randomized decryption). | ceil((25 + n1) / 3) * 4 |
(floor(n1 / 4) * 3) - 25 |
9 | Use 256-bit key randomized encryption (or randomized decryption). | ceil((33 + n1) / 3) * 4 |
(floor(n1 / 4) * 3) - 33 |
Randomized Encryption
- Accept (binary) input string
plaintext
with lengthplaintext_length
bytes. - Accept (binary) input key with length
key_length
bytes. - Accept (integer) input
aes_keybits
(must be 128, 192, or 256). - 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
orivec
is shorter than expected, reuse the input until the required size is reached. - When the input
key
orivec
is longer than expected, fold the extra input with the required input viaXOR
.
- When the input
- Generate a cryptographically random salt with
aes_keybits
(likely generated via Fortuna). ciphertext = ofb_aes(plaintext, key = (key XOR salt), iv = (salt >> (aes_keybits - 128))
ciphertext_length = plaintext_length
outputtext = version (1 byte) + salt (aes_keybits/8) + ciphertext (ciphertext_length bytes)
outputtext_length = 1 + (aes_keybits/8) + ciphertext_length
- 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
mirrorsplaintext_length
, and therefore leaks information about the input. For example,ciphertext
for'm'
will be indistinguishable (in length and randomness) fromciphertext
for'f'
; however,ciphertext_length
for'male'
will be different fromciphertext_length
for'female'
. To guard against this leakage, pad all input to the same length.
Randomized Decryption
- Accept (binary) input string
outputtext
with lengthoutputtext_length
bytes. - Accept (binary) input key with length
key_length
bytes. - Accept (integer) input
aes_keybits
(must 128, 192, or 256). - 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
orivec
is shorter than expected, reuse the input until the required size is reached. - When the input
key
orivec
is longer than expected, fold the extra input with the required input viaXOR
.
- When the input
version = outputtext[0:0]
salt = outputtext[1:(aes_keybits/8)]
ciphertext = outputtext[(aes_keybits/8):(outputtext_length-1)]
plaintext = ofb_aes(ciphertext, key = (key XOR salt), iv = (salt >> (aes_keybits - 128))
plaintext_length = outputtext_length - (1 + (aes_keybits/8))
- 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
salt
in step 5:
salt = hmac_sha256(key, plaintext) >> (256 - aes_keybits);
- Identical
plaintext
strings encrypt to identicaloutputtext
strings when you use the samekey
; this method enables equality comparison. - The same
plaintext
value encrypted with two different keys generates two differentoutputtext
values.
Deterministic Decryption
The algorithm for deterministic decryption works exactly the same as the algorithm for randomized decryption.
- padding-oracle attacks are not possible.
- Given that deterministic decryption uses an
hmac_sha256
instead ofsha256
, 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
.
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.