crypto.encrypt_base64
Available inall subroutines.
Symmetric key encryption.
The key and initialization vector (IV) must be hex-encoded strings. The plaintext must be Base64-encoded. The returned ciphertext is also Base64-encoded.
This function uses standard Base64 encoding as defined in RFC 4648 Section 4, with the alphabet A-Za-z0-9+/ and = padding. The URL-safe variant (-_ instead of +/) is not supported. On input, padding is optional. On output, padding is always included.
Parameters
| Parameter | Type | Description |
|---|---|---|
cipher | CIPHER | Encryption algorithm: aes128, aes192, or aes256 |
mode | MODE | Block cipher mode: cbc, ctr, gcm, or ccm |
padding | PADDING | Padding scheme: pkcs7 or nopad |
key_hex | STRING | Hex-encoded encryption key |
iv_hex | STRING | Hex-encoded initialization vector or nonce |
plaintext_base64 | STRING | Base64-encoded data to encrypt |
Supported combinations
| Cipher | Mode | Padding | Key length | IV length | Tag length | Description |
|---|---|---|---|---|---|---|
| aes128 | cbc | pkcs7 | 128 bits | 128 bits | — | AES-128-CBC |
| aes192 | cbc | pkcs7 | 192 bits | 128 bits | — | AES-192-CBC |
| aes256 | cbc | pkcs7 | 256 bits | 128 bits | — | AES-256-CBC |
| aes128 | cbc | nopad | 128 bits | 128 bits | — | AES-128-CBC raw |
| aes192 | cbc | nopad | 192 bits | 128 bits | — | AES-192-CBC raw |
| aes256 | cbc | nopad | 256 bits | 128 bits | — | AES-256-CBC raw |
| aes128 | ctr | nopad | 128 bits | 128 bits | — | AES-128-CTR |
| aes192 | ctr | nopad | 192 bits | 128 bits | — | AES-192-CTR |
| aes256 | ctr | nopad | 256 bits | 128 bits | — | AES-256-CTR |
| aes128 | gcm | nopad | 128 bits | 96 bits | 128 bits | AES-128-GCM |
| aes192 | gcm | nopad | 192 bits | 96 bits | 128 bits | AES-192-GCM |
| aes256 | gcm | nopad | 256 bits | 96 bits | 128 bits | AES-256-GCM |
| aes128 | ccm | nopad | 128 bits | 56 bits | 96 bits | AES-128-CCM |
| aes192 | ccm | nopad | 192 bits | 56 bits | 96 bits | AES-192-CCM |
| aes256 | ccm | nopad | 256 bits | 56 bits | 96 bits | AES-256-CCM |
Basic example
declare local var.key_hex STRING;declare local var.iv_hex STRING;declare local var.plaintext_base64 STRING;declare local var.ciphertext_base64 STRING;
set var.key_hex = "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4";set var.iv_hex = "000102030405060708090a0b";set var.plaintext_base64 = "aGVsbG8gd29ybGQ="; # "hello world"
set var.ciphertext_base64 = crypto.encrypt_base64(aes256, gcm, nopad, var.key_hex, var.iv_hex, var.plaintext_base64);Encryption modes
Each mode has different security properties and requirements for the iv parameter. See the sections on key generation and generating IVs and nonces below for guidance on producing these values correctly.
AES-GCM
AES-GCM is an authenticated encryption mode, meaning that an adversary cannot change the ciphertext (and thus, the decrypted plaintext) without this being detected. It performs well on modern CPUs as it can easily be parallelized.
For a given key, the iv parameter doesn't have to be unpredictable, but must be unique for every plaintext. Reusing a nonce with the same key would immediately reveal the difference between plaintexts from the ciphertexts.
AES-CTR
AES-CTR is an unauthenticated mode, meaning that an adversary can change the ciphertext, resulting in a different plaintext than the original one, without this being detectable. In fact, an adversary can trivially flip any bits they want: flipping a bit in the ciphertext will cause decryption to return the original plaintext with the same bits flipped at the same positions. For this reason, AES-CTR should never be used against an active adversary unless an additional authentication layer is implemented. Despite this limitation, performance is good on modern CPUs as it can easily be parallelized.
For a given key, the iv parameter doesn't have to be unpredictable, but must be unique for every plaintext. Reusing a nonce with the same key would immediately reveal the difference between plaintexts from the ciphertexts.
AES-CBC
AES-CBC is an unauthenticated mode, meaning that an adversary can change the ciphertext, resulting in a different plaintext than the original one, without this being detectable. Additionally, implementations of AES-CBC with padding are often vulnerable to side-channel attacks that are difficult to mitigate. Performance is also limited, as it cannot take advantage of the parallelism of modern CPUs. For these reasons, AES-CBC is not recommended for general use, but is still required by some protocols.
Unlike other modes, the iv parameter must be unpredictable by an adversary and appear to be randomly chosen from a uniform distribution. For a given key, it must also be different for every plaintext.
With the nopad padding, the plaintext length (after Base64 decoding) must be a multiple of 128 bits (16 bytes), and the output will have the same decoded length. With the pkcs7 padding, the plaintext can be of any length, but the output will have a decoded length equal to the plaintext length rounded up to 128 bits.
AES-CCM
AES-CCM is an authenticated encryption mode, meaning that an adversary cannot change the ciphertext (and thus, the decrypted plaintext) without this being detected. However, performance is limited as it cannot be parallelized. AES-CCM is still in use in legacy, constrained, and standards-driven environments, but rarely used in modern designs.
For a given key, the iv parameter doesn't have to be unpredictable, but must be unique for every plaintext. Reusing a nonce with the same key would immediately reveal the difference between plaintexts from the ciphertexts.
Key generation
Secret keys must be randomly sampled from a uniform distribution. This can be done in a terminal with the openssl command:
$ openssl rand -hex 32The provided number is a number of bytes, so the above command generates a 256-bit key.
Generating IVs and nonces
The CBC mode requires an unpredictable initialization vector (IV) for every message. Other modes require a nonce, which must be unique for every message but not necessarily unpredictable. In distributed systems, using random nonces is the easiest option.
However, with the currently supported ciphers, using random nonces with a static key has low usage limits. In particular, AES-CTR and AES-GCM cannot safely be used with more than 2^32 inputs.
The recommended approach is to derive a per-message key and nonce from a master key and a longer nonce, ensuring that (key, nonce) pairs are not reused with distinct messages:
sub compute_message_nonce192_hex(STRING var.key_hex, STRING var.plaintext_base64) STRING { declare local var.ikm STRING; set var.ikm = "nonce|" + randomstr(48, "0123456789abcdef") + var.plaintext_base64; return substr(digest.hmac_sha256(var.key_hex, var.ikm), 2, 48);}
sub compute_message_key256_hex(STRING var.key_hex, STRING var.nonce192_hex) STRING { return substr(digest.hmac_sha256(var.key_hex, "key256|" + var.nonce192_hex), 2, 64);}
sub compute_message_nonce96_hex(STRING var.key_hex, STRING var.nonce192_hex) STRING { return substr(digest.hmac_sha256(var.key_hex, "nonce96|" + var.nonce192_hex), 2, 24);}Complete example
The following example demonstrates encryption with automatic nonce derivation, using the helper functions defined above. The output is a string containing the 192-bit nonce (48 hex characters) followed by the Base64-encoded ciphertext:
sub encrypt_base64(STRING var.key_hex, STRING var.plaintext_base64) STRING { declare local var.nonce192_hex STRING; set var.nonce192_hex = compute_message_nonce192_hex(var.key_hex, var.plaintext_base64);
declare local var.msg_key_hex STRING; set var.msg_key_hex = compute_message_key256_hex(var.key_hex, var.nonce192_hex);
declare local var.msg_nonce_hex STRING; set var.msg_nonce_hex = compute_message_nonce96_hex(var.key_hex, var.nonce192_hex);
declare local var.encrypted STRING; set var.encrypted = crypto.encrypt_base64(aes256, gcm, nopad, var.msg_key_hex, var.msg_nonce_hex, var.plaintext_base64);
declare local var.result STRING; set var.result = var.nonce192_hex + var.encrypted; return var.result;}
sub decrypt_base64(STRING var.key_hex, STRING var.ciphertext) STRING { # Ciphertext must contain at least the 192-bit nonce (48 hex chars) and 128-bit GCM tag declare local var.nonce192_hex STRING; set var.nonce192_hex = substr(var.ciphertext, 0, 48);
declare local var.msg_key_hex STRING; set var.msg_key_hex = compute_message_key256_hex(var.key_hex, var.nonce192_hex);
declare local var.msg_nonce_hex STRING; set var.msg_nonce_hex = compute_message_nonce96_hex(var.key_hex, var.nonce192_hex);
return crypto.decrypt_base64(aes256, gcm, nopad, var.msg_key_hex, var.msg_nonce_hex, substr(var.ciphertext, 48));}
declare local var.key_hex STRING;set var.key_hex = "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4";
declare local var.plaintext_base64 STRING;set var.plaintext_base64 = "aGVsbG8gd29ybGQ="; # "hello world"
declare local var.ciphertext STRING;set var.ciphertext = encrypt_base64(var.key_hex, var.plaintext_base64);
declare local var.decrypted_base64 STRING;set var.decrypted_base64 = decrypt_base64(var.key_hex, var.ciphertext);
if (fastly.error == "EBADDECRYPT") { error 403 "Decryption failed";} else if (fastly.error) { error 503;}Errors
If the requirements for the given cipher and mode are not met, or if the hex-encoded arguments are not valid hex, then fastly.error will be set to EINVAL. The Base64 decoder is lenient and silently ignores non-Base64 characters rather than rejecting them.
If decryption fails, then fastly.error will be set to EBADDECRYPT.
Provided that the parameter lengths are correct, unauthenticated modes will never fail on decryption, even if the key is incorrect, but the computed plaintext may then not match the original one.
Related content
crypto.decrypt_base64- Decrypt Base64-encoded data.crypto.encrypt_hex- Encrypt with hex encoding.crypto.decrypt_hex- Decrypt hex-encoded data.