Des In C++ - C++ Programming Tutorial
C++ Course / Miscellaneous / Des In C++

Des In C++

BLUF: Mastering Des In C++ is a critical step in becoming a proficient C++ developer. This lesson provides a deep dive into the syntax, performance considerations, and real-world applications of this concept.
Key Performance Insight: Des In C++

C++ is renowned for its efficiency. Learn how Des In C++ enables low-level control and high-performance computing in the tutorial below.

Introduction

A vast amount of data has been generated since the onset of the digital era. Safeguarding confidential data has gained significant importance owing to the necessity of upholding individuals' privacy. Consequently, ensuring the security of data during its transmission across networks and its storage in system memory has been emphasized greatly.

The DES (Data Encryption Standard) algorithm was created by IBM. This encryption method is symmetric-key-based and originated from an earlier algorithm designed by Horst Feistel in the 1970s during the rise of scientific computing advancements. Symmetric key algorithms employ an identical key for both encrypting and decrypting the message and resulting ciphertext. The American National Bureau of Standards officially adopted this algorithm.

After consultations with the National Security Agency (NSA), some modifications were applied to the DES encryption algorithm in 1976. Subsequently, this algorithm was officially selected and released as a Federal Information Processing Standard (FIPS) in the United States in 1977.

Describe the DES algorithm.

The Data Encryption Standard (DES) algorithm is a block cipher technique that employs a 48-bit key for each input. It processes plaintext in 64-bit blocks, converting them into cipher text in 64-bit blocks. The data requiring encryption is segmented into sections known as "blocks," with each block individually encrypted using block cipher methods.

The decryption step essentially reverses the encryption process. When decrypting, a 64-bit block of ciphertext is inputted, and a 64-bit block of plaintext is produced. Both the encryptor and the decryptor need to utilize the identical 48-bit key to ensure seamless communication.

The DES algorithm was effective during the initial stages of the internet, but in current times, it is unsuitable due to its insufficient key length of 56 bits. As technology has progressed and computing power has increased, a motivated attacker can swiftly decipher the key using appropriate resources. Nonetheless, it has played a crucial role in shaping the evolution and progress of cryptography.

How to Create a Key

Alphabets and numbers are not directly processed by computers. Prior to encryption, all textual characters undergo conversion to binary format. Information is segmented into chunks to facilitate processing during the encryption phase. In the DES cipher, the binary plaintext is segmented into 64-bit blocks, leading to a block size of 64 bits.

Before proceeding with any other operations, the final block is padded to achieve a length of exactly 64 bits if it falls short. This padding step guarantees the inclusion of extra data within the block, thereby extending it to 64 bits. The purpose of padding is to enhance the complexity of decrypting the encrypted data.

To thwart attempts at predicting the original message based solely on the encryption algorithm, encryption keys are utilized to modify the results of the encryption process.

In DES, a lone key is employed to generate new keys for each "round" iteration. The DES procedure is executed 16 times to produce the ultimate ciphertext; each execution is known as a round, with the outcomes becoming the input for the next round. This process enables a high level of confusion in the plaintext.

Now, the initial step involves choosing an arbitrary 64-bit value to function as our key K. It is important to note that for successful communication, both the sender and the receiver need to have an identical 64-bit key.

One distinct subkey is generated for each round, ensuring uniqueness and acting as a variation of the initial master key K. A total of 16 subkeys, denoted as K1 to K16, are assigned for individual rounds.

The initial step involves permuting the key with the Permuted Choice-1 (PC-1) table to generate the round keys. The provided PC1 table serves to rearrange the elements within the data block.

The data block's "Left" and "Right" segments are represented by the characters "C" and "D". The values in the table indicate which bits of the input key are assigned to the left or right sections of the key scheduling state.

57 49 41 33 25 17 09
01 58 50 42 34 26 18
10 02 59 51 43 35 27
19 11 03 60 52 44 36
63 55 47 39 31 23 15
07 62 54 46 38 30 22
14 06 61 53 45 37 29
21 13 13 28 20 12 04

TABLE:- PC1

Each individual bit in the original key undergoes rearrangement and is reassigned a new position based on the PC-1 permutation. The initial key's 57th bit is relocated to the first cell of the "C" table due to its value being 57. Subsequently, the second bit of the updated key corresponds to the 49th bit from the preceding block as the second cell indicates the number 49. Following this pattern, the remaining bits from the original block are sequentially placed in accordance with the "C" table configuration to generate the new key. Once the bits are organized according to the "C" table, we proceed to the "D" table to complete the second segment of our fresh key.

You might notice that the table and key are only 56 bits in length, rather than 64 bits. This is because every eighth bit - 8, 16, 24, 32, 40, 48, and 56 - is specifically reserved as parity bits to validate the successful transmission of the key. These parity bits provide the DES algorithm with the effective security equivalent of a 56-bit key.

We have now split our 56-bit key into two halves, with each half consisting of 28 bits. This division will serve as our reference point going forward.

Following this, the key undergoes a leftward shift, either by one bit or two bits, determined by the current round. The following table outlines the specific shift amounts:

In this series of rounds, the numerical values are shifted to the left based on the distances specified in the table. Each shift is applied to the result of the previous round.

If we are in the fifth encryption iteration with a key of 1101101101101101, the protocol indicates a required leftward shift of two positions. Consequently, the key value post this cycle will be: 0110110110110111.

As a consequence, sixteen unique subkeys are generated, with each one corresponding to a DES round. Subsequently, the key undergoes a permutation using the specified Permuted Choice-2 table. Following this, we once again reorganize the bits in our key based on the following table.

The key is rearranged in a similar manner to the previous permutation, resulting in the determination of the new position of the key bit as illustrated in the table below.

Table: PC2

14 17 11 24 01 05
03 28 15 06 21 10
23 19 12 04 26 08
16 07 27 20 13 02
41 52 31 37 47 55
30 40 51 45 33 48
44 49 39 56 34 53
46 42 50 36 29 32

As a result, the 14th bit of the key is placed in the first position of the new permuted key, the 17th bit is placed in the second position, etc. Further examination reveals that the newly produced key only has 48 bits , as opposed to the previous keys' 56 bits .

Encryption Process in DES Algorithm:

Initial Permutation Function:

The plaintext is segmented into 64-bit blocks, similar to how we segmented the key. The data designated for encryption and transmission from the sender to the receiver is referred to as the plaintext. In cases where the last block falls short of 64 bits, padding is applied to meet the required block size.

The text becomes muddled due to the initial permutation function applied before any further processing. The Initial Permutation, known as IP, holds no significance in cryptography.

The bits in the binary encoded message are arranged based on the Initial Permutation table, following a process similar to the one used for the key permutation. The result of this stage acts as the input for the subsequent step:

Block splitting:

Blocks are split into two components: a left block L0 comprising 32 bits and a right block R0 consisting of 32 bits after the IP process is finished.

We move on to the next function after the blocks are separated. During the first round , the right half of the block is taken and put through the following processes:

  • Expansion Permutation (E)
  • Key mixing
  • Substitution (S1, S2,...,S8)
  • Permutation (P)
  • A) Expansion Permutation (E):

The purpose of the expansion permutation is threefold. One of these objectives is to create the avalanche effect, where the outcome of two different bits is impacted by a single input bit. Additionally, to align with the subkey length for the subsequent stage, this process guarantees that the right half contains 48 bits.

A case study of the avalanche impact

Another notable outcome of the expansion permutation is its capacity to condense the data while extending the output in comparison to the input during the substitution process.

Following that, the blocks are reorganized based on the expansion function table provided below:

Upon thorough analysis, it becomes apparent that the table consists of several duplicated sections. Consequently, the block size will expand from 32 to 48 bits.

Table:E

32 01 02 03 04 05
04 05 06 07 08 09
08 09 10 11 12 13
12 13 14 15 16 17
16 17 18 19 20 21
20 21 22 23 24 25
24 25 26 27 28 29
28 29 30 31 32 01

B) Key Mixing :

After increasing the block size to 48 bits, we implement the subkey from the initial round that was generated during the previous key scheduling step. Subsequently, this subkey is employed to alter the block by applying the XOR operation based on the table.

For example, if the last eight bits of the expanded key are 01001011, the resulting key after the key blending process will be: 10110100.

The resulting block is progressed to the next phase.

C) Substitution (S):

Substitution is utilized to enhance the intricacy of the data, increasing its level of obfuscation. Every 6-bit input is transformed into a 4-bit output through the utilization of 8 predefined tables referred to as substitution boxes or S-Boxes.

The most significant bit (MSB) and least significant bit (LSB) are derived from the 6-bit input and converted into a decimal number represented by X. This X value indicates the row number in the S-box. Subsequently, the middle four bits of the input are converted into another decimal number denoted as Y. The column number to refer to is determined by the value of Y. Then, the corresponding S-box number is converted into a 4-bit binary number. Ultimately, this process allows for the successful conversion of a 6-bit input into a 4-bit output.

D) Permutation (P)

The F operation is once more rearranged utilizing the P permutation table provided below:

Now that all the stages of the F function have been executed, the encrypted data retrieved corresponds to a numerical value referred to as f(R0, K1). This value signifies that the outcome is influenced by the initial right side of block R0 and the subkey used in the first round.

Now, we utilize this value and proceed with the following steps:

i) XORing with the Left Block:

Next, by combining the previously retained left block section with the f(R0,K1) block derived from the permutation in the preceding stage, an XOR operation is performed. This results in the generation of R1, representing the result of the initial processing.

ii) 15 more times:

To finish 16 cycles of processing, the three mentioned tasks are reiterated an additional 15 times. The illustration below showcases the handling of the blocks:

iii) Final Permutation:

Utilizing the f(R0, K1) table given, the result from the final round undergoes one last permutation. Here, the f(R0, K1) table acts as the reverse of the initial table P. The ciphertext produced by the DES algorithm represents the ultimate result of this reverse permutation table.

Process of Decryption in the DES Algorithm

Now, we'll go over the DES decryption procedure:

The algorithm features a Feistel framework that simplifies the decryption process. The only contrast between encryption and decryption lies in the reversal of subkey utilization for decryption.

The information is split into two sections following the initial permutation, P. The F operation transfers the data from the right section. In this instance, the sixteenth subkey is introduced, and the remaining steps proceed as normal. Upon obtaining the output from the F operation, the left segment of the resultant block is subjected to XOR operation. By reversing the order of the subkeys used during encryption, this process is reiterated. The plaintext is ultimately obtained after completing all sixteen rounds.

Modes of Operation for DES

While DES algorithm secures plaintext, its vulnerability to brute force attacks stems from the limited key length, which is particularly concerning in today's era of advanced cloud computing and supercomputers that far surpass the capabilities of 1970s-era computers for which DES was originally created.

As a consequence, the DES algorithm was utilized extensively alongside different operation modes tailored to specific applications and needs.

Some of the key operational modes include:

Electronic Code Book (ECB).

Each section of the original plain text undergoes encryption using the DES algorithm to generate a corresponding block of cipher text.

Cipher Block Chaining (CBC):

It represents an enhancement in security compared to ECB. Following XOR operation with the plaintext, the preceding cipher block is fed into the next encryption round.

Example

Cipher Block (1) = Cipher Block (0) ⊕ PlainText Block (1) + Key.

CFB (Cipher Feedback Mode):

In this way, an Initialization Vector (IV) functions as a distinct variable. Its purpose is to initiate the encryption procedure. The IV plays a crucial role in the initial encryption step, where the output from previous encryptions serves as the input for all subsequent ones.

Each iteration's result is evenly divided into s-bits on the left side and b-bits on the right side. Subsequently, the subsequent encryption round employs the s-bits to perform an XOR operation on the plaintext.

OFB (Output Feedback Mode):

The only contrast between DES and Cipher Feedback lies in the fact that the output is fed back to the next round as feedback instead of the s-bits.

DES Algorithm Implementations and Testing

The DES algorithm is executed through the subsequent C++ code:

1) Generating a Key:

The method employs 16 distinct subkeys throughout its 16 encryption rounds.

Example

#include <iostream>
#include <string>
using namespace std;
string round_keys[16];
string shift_left_once(string key_chunk){
string shifted="";
for(int i = 1; i < 28; i++){
shifted += key_chunk[i];
}
shifted += key_chunk[0];
return shifted;
}
string shift_left_twice(string key_chunk){
string shifted="";
for(int i = 0; i < 2; i++){
for(int j = 1; j < 28; j++){
shifted += key_chunk[j];
}
shifted += key_chunk[0];
key_chunk= shifted;
shifted ="";
}
return key_chunk;
}
void generate_keys(string key){
int pc1[56] = {
57,49,41,33,25,17,9,
1,58,50,42,34,26,18,
10,2,59,51,43,35,27,
19,11,3,60,52,44,36,
63,55,47,39,31,23,15,
7,62,54,46,38,30,22,
14,6,61,53,45,37,29,
21,13,5,28,20,12,4
};
int pc2[48] = {
14,17,11,24,1,5,
3,28,15,6,21,10,
23,19,12,4,26,8,
16,7,27,20,13,2,
41,52,31,37,47,55,
30,40,51,45,33,48,
44,49,39,56,34,53,
46,42,50,36,29,32
};
string perm_key ="";
for(int i = 0; i < 56; i++){
perm_key+= key[pc1[i]-1];
}
string left= perm_key.substr(0, 28);
string right= perm_key.substr(28, 28);
for(int i=0; i<16; i++){
if(i == 0 || i == 1 || i==8 || i==15 ){
left= shift_left_once(left);
right= shift_left_once(right);
}
else{
left= shift_left_twice(left);
right= shift_left_twice(right);
}
string combined_key = left + right;
string round_key = "";
for(int i = 0; i < 48; i++){
round_key += combined_key[pc2[i]-1];
}
round_keys[i] = round_key;
cout<<"Key "<<i+1<<": "<<round_keys[i]<<endl;
}

}
int main(){
string key = "10101010101110110000100100011000001001110011"
"01101100110011011101";
generate_keys(key);
}

Output:

Output

Key 1: 000110010100110011010000011100101101111010001100
Key 2: 010001010110100001011000000110101011110011001110
Key 3: 000001101110110110100100101011001111010110110101
Key 4: 110110100010110100000011001010110110111011100011
Key 5: 011010011010011000101001111111101100100100010011
Key 6: 110000011001010010001110100001110100011101011110
Key 7: 011100001000101011010010110111011011001111000000
Key 8: 001101001111100000100010111100001100011001101101
Key 9: 100001001011101101000100011100111101110011001100
Key 10: 000000100111011001010111000010001011010110111111
Key 11: 011011010101010101100000101011110111110010100101
Key 12: 110000101100000111101001011010100100101111110011
Key 13: 100110011100001100010011100101111100100100011111
Key 14: 001001010001101110001011110001110001011111010000
Key 15: 001100110011000011000101110110011010001101101101
Key 16: 000110000001110001011101011101011100011001101101

2) Plaintext encryption to produce Ciphertext

The original text that requires encryption is divided into two equal parts and undergoes 16 rounds of encryption. Following this process, the resulting ciphertext is generated by merging the two halves.

Example

#include <iostream>
#include <string>
#include <cmath>
using namespace std;
string round_keys[16];
string pt;
string convertDecimalToBinary(int decimal)
{
string binary;
while(decimal != 0) {
binary = (decimal % 2 == 0 ? "0" : "1") + binary;
decimal = decimal/2;
}
while(binary.length() < 4){
binary = "0" + binary;
}
return binary;
}
int convertBinaryToDecimal(string binary)
{
int decimal = 0;
int counter = 0;
int size = binary.length();
for(int i = size-1; i >= 0; i--)
{
if(binary[i] == '1'){
decimal += pow(2, counter);
}
counter++;
}
return decimal;
}
string shift_left_once(string key_chunk){
string shifted="";
for(int i = 1; i < 28; i++){
shifted += key_chunk[i];
}
shifted += key_chunk[0];
return shifted;
}
string shift_left_twice(string key_chunk){
string shifted="";
for(int i = 0; i < 2; i++){
for(int j = 1; j < 28; j++){
shifted += key_chunk[j];
}
shifted += key_chunk[0];
key_chunk= shifted;
shifted ="";
}
return key_chunk;
}
string Xor(string a, string b){
string result = "";
int size = b.size();
for(int i = 0; i < size; i++){
if(a[i] != b[i]){
result += "1";
}
else{
result += "0";
}
}
return result;
}
void generate_keys(string key){
int pc1[56] = {
57,49,41,33,25,17,9,
1,58,50,42,34,26,18,
10,2,59,51,43,35,27,
19,11,3,60,52,44,36,
63,55,47,39,31,23,15,
7,62,54,46,38,30,22,
14,6,61,53,45,37,29,
21,13,5,28,20,12,4
};
int pc2[48] = {
14,17,11,24,1,5,
3,28,15,6,21,10,
23,19,12,4,26,8,
16,7,27,20,13,2,
41,52,31,37,47,55,
30,40,51,45,33,48,
44,49,39,56,34,53,
46,42,50,36,29,32
};
string perm_key ="";
for(int i = 0; i < 56; i++){
perm_key+= key[pc1[i]-1];
}
string left= perm_key.substr(0, 28);
string right= perm_key.substr(28, 28);
for(int i=0; i<16; i++){
if(i == 0 || i == 1 || i==8 || i==15 ){
left= shift_left_once(left);
right= shift_left_once(right);
}
else{
left= shift_left_twice(left);
right= shift_left_twice(right);
}
string combined_key = left + right;
string round_key = "";
for(int i = 0; i < 48; i++){
round_key += combined_key[pc2[i]-1];
}
round_keys[i] = round_key;
}

}
string DES(){
int initial_permutation[64] = {
58,50,42,34,26,18,10,2,
60,52,44,36,28,20,12,4,
62,54,46,38,30,22,14,6,
64,56,48,40,32,24,16,8,
57,49,41,33,25,17,9,1,
59,51,43,35,27,19,11,3,
61,53,45,37,29,21,13,5,
63,55,47,39,31,23,15,7
};
int expansion_table[48] = {
32,1,2,3,4,5,4,5,
6,7,8,9,8,9,10,11,
12,13,12,13,14,15,16,17,
16,17,18,19,20,21,20,21,
22,23,24,25,24,25,26,27,
28,29,28,29,30,31,32,1
};
int substition_boxes[8][4][16]=
{{
14,4,13,1,2,15,11,8,3,10,6,12,5,9,0,7,
0,15,7,4,14,2,13,1,10,6,12,11,9,5,3,8,
4,1,14,8,13,6,2,11,15,12,9,7,3,10,5,0,
15,12,8,2,4,9,1,7,5,11,3,14,10,0,6,13
},
{
15,1,8,14,6,11,3,4,9,7,2,13,12,0,5,10,
3,13,4,7,15,2,8,14,12,0,1,10,6,9,11,5,
0,14,7,11,10,4,13,1,5,8,12,6,9,3,2,15,
13,8,10,1,3,15,4,2,11,6,7,12,0,5,14,9
},
{
10,0,9,14,6,3,15,5,1,13,12,7,11,4,2,8,
13,7,0,9,3,4,6,10,2,8,5,14,12,11,15,1,
13,6,4,9,8,15,3,0,11,1,2,12,5,10,14,7,
1,10,13,0,6,9,8,7,4,15,14,3,11,5,2,12
},
{
7,13,14,3,0,6,9,10,1,2,8,5,11,12,4,15,
13,8,11,5,6,15,0,3,4,7,2,12,1,10,14,9,
10,6,9,0,12,11,7,13,15,1,3,14,5,2,8,4,
3,15,0,6,10,1,13,8,9,4,5,11,12,7,2,14
},
{
2,12,4,1,7,10,11,6,8,5,3,15,13,0,14,9,
14,11,2,12,4,7,13,1,5,0,15,10,3,9,8,6,
4,2,1,11,10,13,7,8,15,9,12,5,6,3,0,14,
11,8,12,7,1,14,2,13,6,15,0,9,10,4,5,3
},
{
12,1,10,15,9,2,6,8,0,13,3,4,14,7,5,11,
10,15,4,2,7,12,9,5,6,1,13,14,0,11,3,8,
9,14,15,5,2,8,12,3,7,0,4,10,1,13,11,6,
4,3,2,12,9,5,15,10,11,14,1,7,6,0,8,13
},
{
4,11,2,14,15,0,8,13,3,12,9,7,5,10,6,1,
13,0,11,7,4,9,1,10,14,3,5,12,2,15,8,6,
1,4,11,13,12,3,7,14,10,15,6,8,0,5,9,2,
6,11,13,8,1,4,10,7,9,5,0,15,14,2,3,12
},
{
13,2,8,4,6,15,11,1,10,9,3,14,5,0,12,7,
1,15,13,8,10,3,7,4,12,5,6,11,0,14,9,2,
7,11,4,1,9,12,14,2,0,6,10,13,15,3,5,8,
2,1,14,7,4,10,8,13,15,12,9,0,3,5,6,11
}};
int permutation_tab[32] = {
16,7,20,21,29,12,28,17,
1,15,23,26,5,18,31,10,
2,8,24,14,32,27,3,9,
19,13,30,6,22,11,4,25
};
int inverse_permutation[64]= {
40,8,48,16,56,24,64,32,
39,7,47,15,55,23,63,31,
38,6,46,14,54,22,62,30,
37,5,45,13,53,21,61,29,
36,4,44,12,52,20,60,28,
35,3,43,11,51,19,59,27,
34,2,42,10,50,18,58,26,
33,1,41,9,49,17,57,25
};
string perm = "";
for(int i = 0; i < 64; i++){
perm += pt[initial_permutation[i]-1];
}
string left = perm.substr(0, 32);
string right = perm.substr(32, 32);
for(int i=0; i<16; i++) {
string right_expanded = "";
for(int i = 0; i < 48; i++) {
right_expanded += right[expansion_table[i]-1];
};
string xored = Xor(round_keys[i], right_expanded);
string res = "";
for(int i=0;i<8; i++){
string row1= xored.substr(i*6,1) + xored.substr(i*6 + 5,1);
int row = convertBinaryToDecimal(row1);
string col1 = xored.substr(i*6 + 1,1) + xored.substr(i*6 + 2,1) + xored.substr(i*6 + 3,1) + xored.substr(i*6 + 4,1);;
int col = convertBinaryToDecimal(col1);
int val = substition_boxes[i][row][col];
res += convertDecimalToBinary(val);
}
string perm2 ="";
for(int i = 0; i < 32; i++){
perm2 += res[permutation_tab[i]-1];
}
xored = Xor(perm2, left);
left = xored;
if(i < 15){
string temp = right;
right = xored;
left = temp;
}
}
string combined_text = left + right;
string ciphertext ="";
for(int i = 0; i < 64; i++){
ciphertext+= combined_text[inverse_permutation[i]-1];
}
return ciphertext;
}
int main(){
string key= "1010101010111011000010010001100000100111001101101100110011011101";
pt= "1010101111001101111001101010101111001101000100110010010100110110";
generate_keys(key);
cout<<"Plain text: "<<pt<<endl;
string ct= DES();
cout<<"Ciphertext: "<<ct<<endl;
}

Output:

Output

Plaintext: 1010101111001101111001101010101111001101000100110010010100110110
Ciphertext: 1001111000100110100111110101101011111010010011011011101101110000

Input Required

This code uses input(). Please provide values below:

Logic Practice
Install Logic Practice
Add to home screen for a faster app-like experience