Everything You Want To Know About AES, But Were Afraid To Ask …

The workhorse of the cybersecurity industry is AES (Advanced Encryption Standard) and which is used to encrypt and decrypt data. The…

Everything You Want To Know About AES, But Were Afraid To Ask …

The workhorse of the cybersecurity industry is AES (Advanced Encryption Standard) and which is used to encrypt and decrypt data. The method comes from the winner of a NIST competition in 2001, and was previously known as the Rijndael (“Rain Dahl”) cipher.

AES is a symmetric key method, and where Bob and Alice have the same encryption key. In the following, Bob and Alice share an encryption key, and where Bob converts his plaintext into ciphertext, and then Alice converts the ciphertext back into plaintext using a shared secret key:

The problem with this setup is that the same plaintext will always result in the same ciphertext, so we typically add salt into the encryption process. We also need a way for Bob and Alice to generate the same secret key. This is either typically done through a key exchange method (such as with the Diffie-Hellman method) or by a KDF (Key Derivation Function). One of the most popular KDFs is PBKDF2 and which allows a password to be converted into an encryption key of a defined size:

Another mode of encryption is defined as AEAD (Authenticated Encryption with Additional Data), and where we can add some plaintext authenication data to the ciphering process, and where we need the same authentication data for the decrpytion process:

The size of the key is typically either 128 bits or 256 bits. AES (Advanced Encryption Standard) can be applied into three modes:

  • Block cipher. This can be implemented with CBC (Cipher Block Chaining) and which implements a block cipher. In the case of AES, the block size will be 16 bytes, and thus the ciphertext will be a multiple of the block size. As we use a block cipher, we need to pad the data for the cipher process, and then unpad the decrypted data. The most common method is PKCS7, and which pads with the value which equals the number of padding bytes.
  • AEAD (Authenticated Encryption with Additional Data). This can be implemented with GCM (Galois Counter Mode) and which implements with a stream cipher with the addition of additional data. This additional data can include something related to the ciphertext, such as with a session ID or TCP port. There is no padding required for the plaintext text, and thus the ciphertext will have the same length as the plaintext.
  • Stream cipher. This can be implemented with CFB (Cipher FeedBack) and which implements a stream cipher. As we use a stream cipher, we do not need to pad the data, and where the ciphertext size will be the same as the plaintext. Overall we use a simple XOR operation between the plaintext stream and a key stream (derived from the main encryption key). The decryption is performed by just XOR’ing the ciphertext stream with the same key stream.

Here is the code [here]:

package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/sha256"
"fmt"
"io"
"os"
"strconv"
"strings"
	"github.com/enceve/crypto/pad"
"golang.org/x/crypto/pbkdf2"
)
func getSalt(n int) []byte {
nonce := make([]byte, n)
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
panic(err.Error())
}
return (nonce)
}
func main() {
	msg := "hello"
passwd := "qwerty"
mode := "cbc"
size := 16
	argCount := len(os.Args[1:])
if argCount > 0 {
msg = os.Args[1]
}
if argCount > 1 {
passwd = os.Args[2]
}
if argCount > 2 {
mode = os.Args[3]
}
if argCount > 3 {
size, _ = strconv.Atoi(os.Args[4])
}
	pwsalt := getSalt(12) // 96 bits for nonce/IV
	key := pbkdf2.Key([]byte(passwd), pwsalt, 10000, size, sha256.New)
	block, _ := aes.NewCipher(key)
	var salt []byte
var plain []byte
var ciphertext []byte
	plaintext := []byte(msg)
	if mode == "gcm" {
// AEAD
salt = getSalt(12)
aesgcm, _ := cipher.NewGCM(block)
ciphertext = aesgcm.Seal(nil, salt, plaintext, nil)
plain, _ = aesgcm.Open(nil, salt, ciphertext, nil)
} else if mode == "cbc" {
// Block cipher
plain = make([]byte, (len(plaintext)/16+1)*aes.BlockSize)
ciphertext = make([]byte, (len(plaintext)/16+1)*aes.BlockSize)
salt = getSalt(16)
pkcs7 := pad.NewPKCS7(aes.BlockSize)
pad1 := pkcs7.Pad(plaintext)
blk := cipher.NewCBCEncrypter(block, salt)
blk.CryptBlocks(ciphertext, pad1)
blk = cipher.NewCBCDecrypter(block, salt)
blk.CryptBlocks(plain, ciphertext)
plain, _ = pkcs7.Unpad(plain)
	} else if mode == "cfb" {
// Stream cipher
salt = getSalt(aes.BlockSize)
plain = make([]byte, len(plaintext))
ciphertext = make([]byte, len(plaintext))
		stream := cipher.NewCFBEncrypter(block, salt)
stream.XORKeyStream(ciphertext, plaintext)
stream = cipher.NewCFBDecrypter(block, salt)
stream.XORKeyStream(plain, ciphertext)
}
	fmt.Printf("Mode:\t\t%s\n", strings.ToUpper(mode))
fmt.Printf("Key size:\t%d bits\n", size*8)
fmt.Printf("Message:\t%s\n", msg)
	fmt.Printf("Password:\t%s\n", passwd)
fmt.Printf("Password Salt:\t%x\n", pwsalt)
fmt.Printf("\nKey:\t\t%x\n", key)
fmt.Printf("\nCipher:\t\t%x\n", ciphertext)
	fmt.Printf("Salt:\t\t%x\n", salt)
fmt.Printf("\nDecrypted:\t%s\n", plain)
}

A sample run for AES GCM (Galois Counter Mode) shows that the ciphertext size is the same as the plaintext size [here]:

Mode:		GCM
Key size: 128 bits
Message: The quick brown fox jumps over the lazy dog
Password: Qwerty123
Password Salt: 1ef49a22fb2bc0eb23d8592b
Key:		f6a8bfd068df313f9238aa9dbc57c495
Cipher:		099149a5cfc01bf0faf5f20b9f1cf409ca5b29ee04743dc5db270d0bff27ff455a2c46e22d3c1321a7eab98fb67a17dc9461ba1f9b399e7863aad2
Salt: d57657896a98ab34148086ba
Decrypted:	The quick brown fox jumps over the lazy dog

With CBC (Cipher Block Chain) we have a block cipher, and thus the ciphertext is a multiple of the block size (16 bytes):

Mode:		CBC
Key size: 128 bits
Message: The quick brown fox jumps over the lazy dog
Password: Qwerty123
Password Salt: bf283a43f50c3af4d201e9d8
Key:		e9eeb0e6f3d4bcd707ed13788fdb5226
Cipher:		aae9754f7a662c267338451be65bc021558e0e33e1ef9b9001dd98fff5a95963cf6f3ce0db4dd8da80392cc81f6443bd
Salt: 29bf216555e7e58ba60ca29569bb618e
Decrypted:	The quick brown fox jumps over the lazy dog

For CFB (Cipher Feedback) we have a stream cipher and where we basically X-OR the plaintext stream with a keystream derived from the encryption key. As we have a stream cipher, the ciphertext is the same length as the plaintext:

Mode:		CFB
Key size: 128 bits
Message: The quick brown fox jumps over the lazy dog
Password: Qwerty123
Password Salt: 8d993b9baed83ff3be546952
Key:		afe68502e4587df339c6b6739ecbcda1
Cipher:		21215fdb73b086b70f9e25f8428c10b11ae64a0cf9accde173ab2043e439394fd7df28d06cf0049b8423f5
Salt: 2f8e3f87d3f271d61ed4c2d9388deef5
Decrypted:	The quick brown fox jumps over the lazy dog

And, the gives you a basic background of the key size, the encryption method (block, stream or AEAD), the application of salt, and the derivation of the key. If you are interested in knowing more, try here:

https://asecuritysite.com/aes/

Overall, it is GCM that is being increasingly used, as we are generally moving away from the block cipher modes, as these are relatively slow and also require padding. The addition of the additional data also allows us bind our cipher to sesssions, TCP ports, and so on.