Introduction to Encryption Algorithms
An encryption algorithm is a method of encryption. In cryptography, encryption is the process of hiding plaintext information so that it is unreadable without special information. Encryption algorithms can be divided into two categories: symmetric encryption and asymmetric encryption.
1. Symmetric Encryption
Information is encrypted using a key, and the same key is used for decryption with the same algorithm.
2. Asymmetric Encryption
Asymmetric encryption, also known as public key encryption, divides the key into a private key and a public key. The public key is used for encryption, and the private key is used for decryption, with different algorithms for encryption and decryption.
About Symmetric Encryption
Symmetric encryption means that both the encryption and decryption parties hold the same private key. The encryption party performs the encryption process using the key, while the decryption party uses the same key for decryption. Because both parties hold the same key, it is called symmetric encryption. Furthermore, since the key is theoretically only held by the encrypting and decrypting parties, it is also referred to as private key encryption. Common symmetric encryption algorithms include: DES, 3DES, AES, and RC4.
Symmetric encryption is divided into stream ciphers and block ciphers, as shown in the figure below:

1. Stream Ciphers
Stream ciphers encrypt each bit individually. It is achieved by adding each bit of the key stream to each bit of the plaintext. The key stream of synchronous stream ciphers depends only on the key, while that of asynchronous stream ciphers depends on both the key and the ciphertext, such as RC4.
2. Block Ciphers
Block ciphers encrypt an entire block of plaintext bits using the same key each time. This means that the encryption of any plaintext bit within a given block depends on all other plaintext bits within the same block. Most block ciphers have a block length of either 128 bits (16 bytes), such as AES, or 64 bits (8 bytes), such as DES and 3DES.
Characteristics of Symmetric Encryption
-
The algorithm is reversible, meaning that data can be restored through decryption after encryption. -
High encryption efficiency and fast encryption speed. -
Security is lower than asymmetric encryption due to potential security risks during key distribution.
Symmetric Encryption Algorithms
Before writing symmetric encryption algorithms, it is necessary to mention the operational modes of block encryption, as block encryption is the most commonly used encryption method in symmetric encryption and can have different operational modes.
Block Encryption Modes
1. Electronic Codebook Mode (ECB)
ECB mode requires that the plaintext length must be an integer multiple of the block size of the cipher used. For example, in AES (to be mentioned later), the plaintext length should be an integer multiple of 16 bytes. If this length requirement is not met, padding must be applied in various ways, such as adding a single “1” at the end of the plaintext, followed by enough “0” bits until the length of the plaintext reaches an integer multiple of the block length. If the length of the plaintext is already an integer multiple of the block length, all padding bits will form a separate extra block. The encryption and decryption of ECB are shown in the figure below:

Advantages and Disadvantages of ECB:
-
Synchronization between encryption and decryption of blocks is not required, meaning that if data is lost, the receiver may still decrypt the received blocks. -
Encryption is highly deterministic; as long as the key does not change, the same plaintext block will always produce the same ciphertext block. -
The encryption of plaintext blocks is completely independent, with no relation to previous blocks. If an attacker reorders the ciphertext blocks, it may yield valid plaintext. -
Cipher Block Chaining Mode (CBC)
2. Cipher Block Chaining Mode (CBC)
CBC mode is mainly based on two ideas. First, the encryption of all blocks is linked together, meaning that the ciphertext depends not only on
but also on all previous plaintext blocks. Second, the initial vector (IV) used in the encryption process is randomized.

3. Output Feedback Mode (OFB)
OFB mode uses block ciphers to construct a stream key encryption scheme. Note that in OFB mode, the key stream is not generated bit by bit, but in blocks. The output of the cipher is b bits of key stream, where b is the width of the block cipher used. The plaintext of b bits is XORed with the key stream of b bits to achieve encryption of the plaintext. As shown in the figure:

OFB mode first uses the block cipher to encrypt the IV, obtaining the first set of b-bit key stream output. The previous key output is fed back to the block cipher for encryption to calculate the next block of key stream bits. This process is repeated. Since synchronous stream ciphers are used, the encryption and decryption operations are identical, and both involve XOR operations. One advantage of OFB is that the computation of the block cipher is independent of the plaintext. Therefore, one or more blocks of key stream material can be pre-calculated.
4. Cipher Feedback Mode (CFB)
CFB mode also uses block ciphers as the basic element to construct stream ciphers. Similar to OFB mode, CFB also uses feedback; however, the difference is that OFB feeds back the output of the block cipher, while CFB feeds back the ciphertext. The basic idea of OFB is that to generate the first key stream block S1, the IV must be encrypted first; all subsequent key stream blocks S2, S3, … are generated by encrypting the previous ciphertext. As shown in the figure:

According to stream cipher encryption, the encryption and decryption operations of CFB are also identical, but CFB mode is an asynchronous stream key encryption.
5. Counter Mode (CTR)
Like OFB and CFB modes, the key stream is also calculated in blocks. The input to the block cipher is a counter, and each time the block cipher computes a new key stream block, the counter generates a different value, as shown in the figure:

When initializing the input for the block cipher, it is essential to avoid using the same input value twice. If an attacker knows any plaintext encrypted with the same input, they can compute the key stream block, allowing them to decrypt other plaintext. The most notable feature of counter mode is its parallelization, as it does not require any feedback. By having two block encryption engines, both can simultaneously encrypt the first block cipher counter values CTR1 and CTR2. After these two block cipher engines finish, one continues encrypting CTR3 while the other encrypts CTR4.
6. Galois/Counter Mode (GCM)
GCM is an encryption mode that computes a message authentication code. The MAC (message authentication code) is calculated by the sender as a checksum and is appended to the message. The receiver also calculates the checksum for this message and checks whether it matches the one sent by the sender, thus confirming whether the message is indeed sent by the sender and whether the ciphertext has been modified.
Mainstream Symmetric Encryption Algorithm Implementations (Go Language)
The following will introduce the implementations of the symmetric encryption algorithms mentioned above one by one.
1. RC4
RC4 is a stream cipher, as mentioned earlier. It consists of a pseudorandom number generator and XOR operations, with variable key lengths ranging from [1,255]. RC4 encrypts and decrypts byte by byte, and because it uses XOR operations, the same algorithm is employed.
func Encrypt(bytes []byte, key []byte) ([]byte, error) {
c, err := rc4.NewCipher(key)
if err != nil {
return nil, err
}
var dst = make([]byte, len(bytes))
c.XORKeyStream(dst, bytes)
return dst, nil
}
//RC4 is a stream cipher encryption using XOR operation, so here directly call the decryption algorithm
func Decrypt(encryptData, key []byte) ([]byte, error) {
return Encrypt(encryptData, key)
}
2. DES (3DES)
DES is an algorithm that encrypts blocks of 64 bits (8 bytes) using a key of 56 bits (7 bytes) (Note: Actually, the input key for DES is 64 bits (8 bytes), but the 8th bit of each byte serves as a parity bit for the preceding 7 bits, so the effective key length is 56 bits.). The encryption and decryption processes use the same key.
The mechanism of the DES algorithm is iterative, with a total of 16 rounds. Each round operates identically but uses different subkeys, which are derived from the main key by selecting 48 permutation bits from the input key. The DES encryption process is as follows:

3DES refers to triple DES encryption, with a key length of 192 bits (24 bytes).
The decryption process mainly involves reversing the key arrangement, meaning that the first round of decryption requires subkey 16; the second round requires subkey 15; and so on.
func Encrypt(plainText []byte, key []byte) ([]byte, error) {
//block, err := des.NewTripleDESCipher(key) for triple DES encryption
block, err := des.NewCipher(key)
if err != nil {
return nil, err
}
blockSize := block.BlockSize() //block size
plainText = paddingWithPKCS5(plainText, blockSize) //padding
var cipherText = make([]byte, len(plainText))
//ECB encrypts each block separately
var temp = cipherText
for len(plainText) > 0 {
block.Encrypt(temp, plainText[:blockSize])
plainText = plainText[blockSize:]
temp = temp[blockSize:]
}
return cipherText, nil
}
func Decrypt(cipherText []byte, key []byte) ([]byte, error) {
//block, err := des.NewTripleDESCipher(key) for triple DES encryption
block, err := des.NewCipher(key)
if err != nil {
return nil, err
}
blockSize := block.BlockSize() //block size
var plainText = make([]byte, len(cipherText))
//ECB decrypts each block separately
var temp = plainText
for len(cipherText) > 0 {
block.Decrypt(temp, cipherText[:blockSize])
cipherText = cipherText[blockSize:]
temp = temp[blockSize:]
}
plainText = unPaddingWithPKCS5(plainText)
return plainText, nil
}
func paddingWithPKCS5(data []byte, blockSize int) []byte {
//padding value and count needed
padding := blockSize - len(data)%blockSize
//assemble padding value ([]byte)
var paddingData = []byte{byte(padding)}
paddingData = bytes.Repeat(paddingData, padding)
//append padding
data = append(data, paddingData...)
return data
}
func unPaddingWithPKCS5(data []byte) []byte {
padding := int(data[len(data)-1])
return data[:len(data)-padding]
}
3. AES
AES has three different key lengths to resist brute-force attacks: 128 bits, 192 bits, and 256 bits, with a block size of 128 bits.
func Encrypt(plainText []byte, key []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
blockSize := block.BlockSize() //block size
plainText = paddingWithPKCS5(plainText, blockSize) //padding
var cipherText = make([]byte, len(plainText))
//CBC mode
blockMode := cipher.NewCBCDecrypter(block, key[:blockSize])
blockMode.CryptBlocks(cipherText,plainText)
return cipherText, nil
}
func Decrypt(cipherText []byte, key []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
blockSize := block.BlockSize() //block size
var plainText = make([]byte, len(cipherText))
//CBC mode
blockMode := cipher.NewCBCEncrypter(block, key[:blockSize])
blockMode.CryptBlocks(plainText, cipherText)
plainText = unPaddingWithPKCS5(plainText)
return plainText, nil
}
References
“Wikipedia” “In-Depth Cryptography – Principles and Applications of Common Encryption Technologies”
Finally
If there are any mistakes, please feel free to correct me! Thanks!
Here is the code link: https://github.com/pyihe/secret.
Recommended Reading
-
What are the good solutions for encrypting int type data in Go?
Site owner: polarisxu
Original articles
Not limited to Go technology
Work and entrepreneurship experiences
Go Language Chinese Network
Sharing Go knowledge for you every day
Go enthusiasts worth following