Encryption functions

These functions implement encryption and decryption of data with AES (Advanced Encryption Standard) algorithm.

Key length depends on encryption mode. It is 16, 24, and 32 bytes long for -128-, -196-, and -256- modes respectively.

Initialization vector length is always 16 bytes (bytes in excess of 16 are ignored).

Note that these functions work slowly until ClickHouse 21.1.

encrypt

This function encrypts data using these modes:

  • aes-128-ecb, aes-192-ecb, aes-256-ecb
  • aes-128-cbc, aes-192-cbc, aes-256-cbc
  • aes-128-cfb1, aes-192-cfb1, aes-256-cfb1
  • aes-128-cfb8, aes-192-cfb8, aes-256-cfb8
  • aes-128-cfb128, aes-192-cfb128, aes-256-cfb128
  • aes-128-ofb, aes-192-ofb, aes-256-ofb
  • aes-128-gcm, aes-192-gcm, aes-256-gcm

Syntax

  1. encrypt('mode', 'plaintext', 'key' [, iv, aad])

Arguments

  • mode — Encryption mode. String.
  • plaintext — Text thats need to be encrypted. String.
  • key — Encryption key. String.
  • iv — Initialization vector. Required for -gcm modes, optinal for others. String.
  • aad — Additional authenticated data. It isn’t encrypted, but it affects decryption. Works only in -gcm modes, for others would throw an exception. String.

Returned value

  • Ciphertext binary string. String.

Examples

Create this table:

Query:

  1. CREATE TABLE encryption_test
  2. (
  3. `comment` String,
  4. `secret` String
  5. )
  6. ENGINE = Memory;

Insert some data (please avoid storing the keys/ivs in the database as this undermines the whole concept of encryption), also storing ‘hints’ is unsafe too and used only for illustrative purposes:

Query:

  1. INSERT INTO encryption_test VALUES('aes-256-cfb128 no IV', encrypt('aes-256-cfb128', 'Secret', '12345678910121314151617181920212')),\
  2. ('aes-256-cfb128 no IV, different key', encrypt('aes-256-cfb128', 'Secret', 'keykeykeykeykeykeykeykeykeykeyke')),\
  3. ('aes-256-cfb128 with IV', encrypt('aes-256-cfb128', 'Secret', '12345678910121314151617181920212', 'iviviviviviviviv')),\
  4. ('aes-256-cbc no IV', encrypt('aes-256-cbc', 'Secret', '12345678910121314151617181920212'));

Query:

  1. SELECT comment, hex(secret) FROM encryption_test;

Result:

  1. ┌─comment─────────────────────────────┬─hex(secret)──────────────────────┐
  2. aes-256-cfb128 no IV B4972BDC4459
  3. aes-256-cfb128 no IV, different key 2FF57C092DC9
  4. aes-256-cfb128 with IV 5E6CB398F653
  5. aes-256-cbc no IV 1BC0629A92450D9E73A00E7D02CF4142
  6. └─────────────────────────────────────┴──────────────────────────────────┘

Example with -gcm:

Query:

  1. INSERT INTO encryption_test VALUES('aes-256-gcm', encrypt('aes-256-gcm', 'Secret', '12345678910121314151617181920212', 'iviviviviviviviv')), \
  2. ('aes-256-gcm with AAD', encrypt('aes-256-gcm', 'Secret', '12345678910121314151617181920212', 'iviviviviviviviv', 'aad'));
  3. SELECT comment, hex(secret) FROM encryption_test WHERE comment LIKE '%gcm%';

Result:

  1. ┌─comment──────────────┬─hex(secret)──────────────────────────────────┐
  2. aes-256-gcm A8A3CCBC6426CFEEB60E4EAE03D3E94204C1B09E0254
  3. aes-256-gcm with AAD A8A3CCBC6426D9A1017A0A932322F1852260A4AD6837
  4. └──────────────────────┴──────────────────────────────────────────────┘

aes_encrypt_mysql

Compatible with mysql encryption and resulting ciphertext can be decrypted with AES_DECRYPT function.

Will produce the same ciphertext as encrypt on equal inputs. But when key or iv are longer than they should normally be, aes_encrypt_mysql will stick to what MySQL’s aes_encrypt does: ‘fold’ key and ignore excess bits of iv.

Supported encryption modes:

  • aes-128-ecb, aes-192-ecb, aes-256-ecb
  • aes-128-cbc, aes-192-cbc, aes-256-cbc
  • aes-128-cfb1, aes-192-cfb1, aes-256-cfb1
  • aes-128-cfb8, aes-192-cfb8, aes-256-cfb8
  • aes-128-cfb128, aes-192-cfb128, aes-256-cfb128
  • aes-128-ofb, aes-192-ofb, aes-256-ofb

Syntax

  1. aes_encrypt_mysql('mode', 'plaintext', 'key' [, iv])

Arguments

  • mode — Encryption mode. String.
  • plaintext — Text that needs to be encrypted. String.
  • key — Encryption key. If key is longer than required by mode, MySQL-specific key folding is performed. String.
  • iv — Initialization vector. Optional, only first 16 bytes are taken into account String.

Returned value

  • Ciphertext binary string. String.

Examples

Given equal input encrypt and aes_encrypt_mysql produce the same ciphertext:

Query:

  1. SELECT encrypt('aes-256-cfb128', 'Secret', '12345678910121314151617181920212', 'iviviviviviviviv') = aes_encrypt_mysql('aes-256-cfb128', 'Secret', '12345678910121314151617181920212', 'iviviviviviviviv') AS ciphertexts_equal;

Result:

  1. ┌─ciphertexts_equal─┐
  2. 1
  3. └───────────────────┘

But encrypt fails when key or iv is longer than expected:

Query:

  1. SELECT encrypt('aes-256-cfb128', 'Secret', '123456789101213141516171819202122', 'iviviviviviviviv123');

Result:

  1. Received exception from server (version 21.1.2):
  2. Code: 36. DB::Exception: Received from localhost:9000. DB::Exception: Invalid key size: 33 expected 32: While processing encrypt('aes-256-cfb128', 'Secret', '123456789101213141516171819202122', 'iviviviviviviviv123').

While aes_encrypt_mysql produces MySQL-compatitalbe output:

Query:

  1. SELECT hex(aes_encrypt_mysql('aes-256-cfb128', 'Secret', '123456789101213141516171819202122', 'iviviviviviviviv123')) AS ciphertext;

Result:

  1. ┌─ciphertext───┐
  2. 24E9E4966469
  3. └──────────────┘

Notice how supplying even longer IV produces the same result

Query:

  1. SELECT hex(aes_encrypt_mysql('aes-256-cfb128', 'Secret', '123456789101213141516171819202122', 'iviviviviviviviv123456')) AS ciphertext

Result:

  1. ┌─ciphertext───┐
  2. 24E9E4966469
  3. └──────────────┘

Which is binary equal to what MySQL produces on same inputs:

  1. mysql> SET block_encryption_mode='aes-256-cfb128';
  2. Query OK, 0 rows affected (0.00 sec)
  3. mysql> SELECT aes_encrypt('Secret', '123456789101213141516171819202122', 'iviviviviviviviv123456') as ciphertext;
  4. +------------------------+
  5. | ciphertext |
  6. +------------------------+
  7. | 0x24E9E4966469 |
  8. +------------------------+
  9. 1 row in set (0.00 sec)

decrypt

This function decrypts ciphertext into a plaintext using these modes:

  • aes-128-ecb, aes-192-ecb, aes-256-ecb
  • aes-128-cbc, aes-192-cbc, aes-256-cbc
  • aes-128-cfb1, aes-192-cfb1, aes-256-cfb1
  • aes-128-cfb8, aes-192-cfb8, aes-256-cfb8
  • aes-128-cfb128, aes-192-cfb128, aes-256-cfb128
  • aes-128-ofb, aes-192-ofb, aes-256-ofb
  • aes-128-gcm, aes-192-gcm, aes-256-gcm

Syntax

  1. decrypt('mode', 'ciphertext', 'key' [, iv, aad])

Arguments

  • mode — Decryption mode. String.
  • ciphertext — Encrypted text that needs to be decrypted. String.
  • key — Decryption key. String.
  • iv — Initialization vector. Required for -gcm modes, optinal for others. String.
  • aad — Additional authenticated data. Won’t decrypt if this value is incorrect. Works only in -gcm modes, for others would throw an exception. String.

Returned value

Examples

Re-using table from encrypt.

Query:

  1. SELECT comment, hex(secret) FROM encryption_test;

Result:

  1. ┌─comment──────────────┬─hex(secret)──────────────────────────────────┐
  2. aes-256-gcm A8A3CCBC6426CFEEB60E4EAE03D3E94204C1B09E0254
  3. aes-256-gcm with AAD A8A3CCBC6426D9A1017A0A932322F1852260A4AD6837
  4. └──────────────────────┴──────────────────────────────────────────────┘
  5. ┌─comment─────────────────────────────┬─hex(secret)──────────────────────┐
  6. aes-256-cfb128 no IV B4972BDC4459
  7. aes-256-cfb128 no IV, different key 2FF57C092DC9
  8. aes-256-cfb128 with IV 5E6CB398F653
  9. aes-256-cbc no IV 1BC0629A92450D9E73A00E7D02CF4142
  10. └─────────────────────────────────────┴──────────────────────────────────┘

Now let’s try to decrypt all that data.

Query:

  1. SELECT comment, decrypt('aes-256-cfb128', secret, '12345678910121314151617181920212') as plaintext FROM encryption_test

Result:

  1. ┌─comment─────────────────────────────┬─plaintext─┐
  2. aes-256-cfb128 no IV Secret
  3. aes-256-cfb128 no IV, different key 4
  4. aes-256-cfb128 with IV ���6�~
  5. aes-256-cbc no IV 2*4h3c4w��@
  6. └─────────────────────────────────────┴───────────┘

Notice how only a portion of the data was properly decrypted, and the rest is gibberish since either mode, key, or iv were different upon encryption.

aes_decrypt_mysql

Compatible with mysql encryption and decrypts data encrypted with AES_ENCRYPT function.

Will produce same plaintext as decrypt on equal inputs. But when key or iv are longer than they should normally be, aes_decrypt_mysql will stick to what MySQL’s aes_decrypt does: ‘fold’ key and ignore excess bits of IV.

Supported decryption modes:

  • aes-128-ecb, aes-192-ecb, aes-256-ecb
  • aes-128-cbc, aes-192-cbc, aes-256-cbc
  • aes-128-cfb1, aes-192-cfb1, aes-256-cfb1
  • aes-128-cfb8, aes-192-cfb8, aes-256-cfb8
  • aes-128-cfb128, aes-192-cfb128, aes-256-cfb128
  • aes-128-ofb, aes-192-ofb, aes-256-ofb

Syntax

  1. aes_decrypt_mysql('mode', 'ciphertext', 'key' [, iv])

Arguments

  • mode — Decryption mode. String.
  • ciphertext — Encrypted text that needs to be decrypted. String.
  • key — Decryption key. String.
  • iv — Initialization vector. Optinal. String.

Returned value

Examples

Let’s decrypt data we’ve previously encrypted with MySQL:

  1. mysql> SET block_encryption_mode='aes-256-cfb128';
  2. Query OK, 0 rows affected (0.00 sec)
  3. mysql> SELECT aes_encrypt('Secret', '123456789101213141516171819202122', 'iviviviviviviviv123456') as ciphertext;
  4. +------------------------+
  5. | ciphertext |
  6. +------------------------+
  7. | 0x24E9E4966469 |
  8. +------------------------+
  9. 1 row in set (0.00 sec)

Query:

  1. SELECT aes_decrypt_mysql('aes-256-cfb128', unhex('24E9E4966469'), '123456789101213141516171819202122', 'iviviviviviviviv123456') AS plaintext

Result:

  1. ┌─plaintext─┐
  2. Secret
  3. └───────────┘

Original article