0% found this document useful (0 votes)
7 views35 pages

CS305 Lab: Cryptography Experiments

The document is a lab file for CS 305: Information Network and Security, detailing various cryptographic experiments including Caesar cipher, Monoalphabetic decryption, Playfair cipher, and Polyalphabetic cipher. Each experiment includes an aim, theory, code implementation, and learning outcomes that highlight the understanding of encryption techniques and their vulnerabilities. The lab file serves as a practical guide for students to implement and analyze different encryption algorithms.

Uploaded by

Aayush Bansal
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
7 views35 pages

CS305 Lab: Cryptography Experiments

The document is a lab file for CS 305: Information Network and Security, detailing various cryptographic experiments including Caesar cipher, Monoalphabetic decryption, Playfair cipher, and Polyalphabetic cipher. Each experiment includes an aim, theory, code implementation, and learning outcomes that highlight the understanding of encryption techniques and their vulnerabilities. The lab file serves as a practical guide for students to implement and analyze different encryption algorithms.

Uploaded by

Aayush Bansal
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

DEPARTMENT OF COMPUTER SCIENCE

AND ENGINEERING

CS 305:Information Network and


Security

CS305
LAB FILE

SUBMITTED TO: SUBMITTED BY:


Prof. Shailender Kumar Aayush Bansal
(23/CS/010)
INDEX
S Experiment Date
no
To implement Caesar cipher encryption
1 11/08/2025

To implement Monoalphabetic decryption.


2 18/08/2025

To implement Play fair cipher encryption-decryption.


3 01/09/2025

To implement Polyalphabetic cipher encryption


4 decryption. 08/09/2025

To implement Hill- cipher encryption


5 decryption 06/10/2025

To implement S-DES sub key Generation


6 13/10/2025

To implement Diffie-hallman key exchange


7 algorithm 13/10/2025

To implement RSA encryption-decryption


8 03/11/2025

Write a program to generate SHA-1 hash.


9 03/11/2025

Implement a digital signature algorithm


10 10/11/2025

2
Experiment 1

Aim : To implement Caesar cipher encryption.

Theory: The Caesar Cipher is one of the earliest and simplest encryption techniques,
named after Julius Caesar, who reportedly used it to protect his military communications. It is
a type of substitution cipher, where each letter in the plaintext is shifted by a fixed number
of positions down the alphabet.

Code:

#include <bits/stdc++.h>
using namespace std;

class caeserCipher{
private:
int key;
char shiftChar(char c, int shift){
if(c >= 'a' && c<='z'){
return 'a' + (c - 'a' + shift + 26) % 26;
}
else if(c >= 'A' && c <= 'Z'){
return 'A' + (c - 'A' + shift + 26) % 26;
}
return c;
}

public:
caeserCipher(int n){
key = n;
}

string encryptText(string plainText){


int n = [Link]();
string cypherText = "";
for(int i=0; i<n; i++){
cypherText += shiftChar(plainText[i], key);
}

return cypherText;
}

string decryptText(string cypherText){


int n = [Link]();
string plainText = "";
for(int i=0; i<n; i++){
plainText += shiftChar(cypherText[i], -key);

3
}
return plainText;
}

};

int main(){
caeserCipher cipher(20);
string plainText = "Aayushbansal23cs010";
string encryptedText = [Link](plainText);
string decryptedText = [Link](encryptedText);
cout << "Encrypted Text: " << encryptedText << endl;
cout << "Decrypted Text: " << decryptedText << endl;
return 0;
}

Output:

Learning Outcome: Through this experiment, students will gain an understanding of


symmetric key encryption using substitution ciphers, particularly the Caesar cipher. They will
be able to explain its working with the help of mathematical representation and learn to
implement both encryption and decryption processes using a programming language. By
analyzing the strengths and weaknesses of the Caesar cipher, students will also recognize the
limitations of classical cryptographic techniques and understand the necessity of stronger and
more secure algorithms for modern communication systems.

4
Experiment 2

Aim: To implement Monoalphabetic decryption.

Theory: A monoalphabetic cipher is a type of substitution cipher where each letter of the
plaintext is replaced with another unique letter of the alphabet. Unlike the Caesar cipher,
which shifts characters by a fixed number of positions, the monoalphabetic cipher allows any
random permutation of the 26 letters to act as the key, thus providing 26!26!26! possible
keys. This makes it more secure than Caesar cipher against brute force attacks. However, it is
still vulnerable to frequency analysis because in English and most languages, certain letters
and letter combinations occur more frequently than others. By analyzing the frequency of
ciphertext letters and comparing them with the known frequency distribution of the language,
an attacker can often break the cipher. Decryption in a monoalphabetic cipher requires the
exact key mapping used during encryption, which substitutes each ciphertext character back
to its corresponding plaintext character.

Code:

#include <bits/stdc++.h>
using namespace std;

class monoAlphabeticCypher{
public:
string key = "qwertyuiopasdfghjklzxcvbnm";
string key2 = "QWERTYUIOPASDFGHJKLZXCVBNM";
string key3 = "9876123450";
unordered_map<char, char> encMap;
unordered_map<char, char> decMap;

monoAlphabeticCypher(){
mapCreation();
}

monoAlphabeticCypher(string k, string k2, string k3){


key3 = k3;
key2 = k2;
key = k;
mapCreation();
}

void mapCreation(){
int i=0;
for(char c: key){
encMap['a' + i] = c;
decMap[c] = 'a' + i;
i++;
}

5
i=0;
for(char c: key2){
encMap['A' + i] = c;
decMap[c] = 'A' + i;
i++;
}
i=0;
for(char c: key3){
encMap['0' + i] = c;
decMap[c] = '0' + i;
i++;
}
}
string encrypt(string plainText){
string cypherText = "";
for(char c: plainText){
if(isalnum(c))
cypherText += encMap[c];
else
cypherText += c;
}
return cypherText;
}

string decrypt(string cypherText){


string plainText = "";
for(char c: cypherText){
if(isalnum(c)){
plainText+=decMap[c];
}
else
plainText += c;
}
return plainText;
}
};

int main(){
monoAlphabeticCypher ac;
string encryptedText = [Link]("AayushBansal 23/cs/010");
string decryptedText = [Link](encryptedText);
cout << "Encrypted Text: " << encryptedText << endl;
cout << "Decrypted Text: " << decryptedText << endl;
return 0;
}

6
Output:

Learning Outcome: Through this experiment, students will learn the working principle
of monoalphabetic substitution ciphers and understand how decryption can be performed
using the appropriate key mapping. They will gain hands-on experience in implementing
decryption algorithms and appreciate the differences between Caesar cipher and
monoalphabetic substitution in terms of key space and security. Students will also understand
the weaknesses of monoalphabetic ciphers, particularly their susceptibility to frequency
analysis, and recognize why such classical cryptographic methods are insufficient for secure
communication in modern systems.

7
Experiment 3

Aim: To implement Play fair cipher encryption-decryption.

Theory: The Playfair cipher is a classical encryption technique invented by Charles


Wheatstone in 1854 and later popularized by Lord Playfair. It is a digraph substitution
cipher, meaning it encrypts pairs of letters (digraphs) instead of single letters, thereby
increasing security compared to simple monoalphabetic ciphers. The cipher uses a 5×5 key
matrix constructed from a keyword, where the letters of the alphabet are placed (with I and J
usually treated as the same letter). To encrypt a message, the plaintext is divided into pairs of
letters, and substitution rules are applied based on the position of the letters in the matrix: if
both letters appear in the same row, they are replaced with the letters to their immediate right;
if in the same column, they are replaced with the letters immediately below; and if they form
a rectangle, they are replaced by the letters in the same row but at the opposite corners.
Decryption follows the reverse process. Since the Playfair cipher works on digraphs, it
reduces the effectiveness of frequency analysis attacks by hiding single-letter statistics,
making it more secure than monoalphabetic substitution. However, with modern
computational power, it is still considered insecure for practical use.

Code:

#include <bits/stdc++.h>
using namespace std;

class playFairCypher {
private:
char matrix[5][5];
unordered_map<char, pair<int, int>> charPos;

void generateMatrix(string key) {


set<char> used;
string adjustedKey = "";

for (char c : key) {


if (c == 'J') c = 'I';
c = toupper(c);
if (isalpha(c) && [Link](c) == [Link]()) {
adjustedKey += c;
[Link](c);
}
}

for (char c = 'A'; c <= 'Z'; c++) {


if (c == 'J') continue;
if ([Link](c) == [Link]()) {
adjustedKey += c;
[Link](c);

8
}
}

int idx = 0;
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
matrix[i][j] = adjustedKey[idx];
charPos[adjustedKey[idx]] = {i, j};
idx++;
}
}
}

string prepareText(string text, bool forEncryption = true) {


string prepared = "";
for (char c : text) {
if (isalpha(c)) {
c = toupper(c);
if (c == 'J') c = 'I';
prepared += c;
}
}

if (!forEncryption) return prepared;

string result = "";


for (int i = 0; i < [Link](); i++) {
result += prepared[i];
if (i + 1 < [Link]()) {
if (prepared[i] == prepared[i + 1]) {
result += 'X';
}
}
}

if ([Link]() % 2 != 0) result += 'X';


return result;
}

string processDigraphs(string text, bool encrypt = true) {


string result = "";
int shift = encrypt ? 1 : -1;

for (int i = 0; i < [Link](); i += 2) {


char a = text[i];
char b = text[i + 1];

int row1 = charPos[a].first;

9
int col1 = charPos[a].second;
int row2 = charPos[b].first;
int col2 = charPos[b].second;

if (row1 == row2) {
result += matrix[row1][(col1 + shift + 5) % 5];
result += matrix[row2][(col2 + shift + 5) % 5];
} else if (col1 == col2) {
result += matrix[(row1 + shift + 5) % 5][col1];
result += matrix[(row2 + shift + 5) % 5][col2];
} else {
result += matrix[row1][col2];
result += matrix[row2][col1];
}
}

return result;
}

public:
void setKey(string key) {
generateMatrix(key);
}

string encrypt(string plaintext) {


string prepared = prepareText(plaintext, true);
return processDigraphs(prepared, true);
}

string decrypt(string ciphertext) {


string prepared = prepareText(ciphertext, false);
return processDigraphs(prepared, false);
}

void displayMatrix() {
cout << "Key Matrix:\n";
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
cout << matrix[i][j] << ' ';
}
cout << '\n';
}
}
};

int main() {
playFairCypher cipher;
string key = "AAYUSHBANSAL";

10
[Link](key);
[Link]();

string plaintext = "My name is Aayush Bansal";


string encrypted = [Link](plaintext);
cout << "Encrypted: " << encrypted << endl;

string decrypted = [Link](encrypted);


cout << "Decrypted: " << decrypted << endl;

return 0;
}

Output:

Learning Outcome: By performing this experiment, students will understand the concept
of digraph substitution and the construction of the Playfair cipher key matrix. They will learn
how to implement both encryption and decryption algorithms using the substitution rules of
the cipher. This experiment helps students analyze how Playfair cipher improves upon simple
substitution techniques by encrypting letter pairs, thereby providing better resistance against
frequency analysis. Furthermore, students will develop insight into the limitations of classical
ciphers and appreciate the evolution toward stronger and more complex cryptographic
methods in modern systems.

11
Experiment 4

Aim: To implement Polyalphabetic cipher encryption decryption. Encryption/Decryption:


Based on substitution, using multiple substitution Alphabets

Theory: A polyalphabetic cipher is a type of substitution cipher that uses multiple


substitution alphabets to encrypt the data, making it more secure than simple
monoalphabetic ciphers. In this cipher, each letter in the plaintext is encrypted using a
different alphabet depending on a repeating key. The most well-known example of a
polyalphabetic cipher is the Vigenère cipher, which uses a key word to determine the shift
for each letter. For example, if the key is “KEY,” the first letter of the plaintext is shifted
according to ‘K’, the second by ‘E’, and the third by ‘Y’, after which the key repeats.
Mathematically, the encryption process can be expressed as
Ei = (Pi + Ki )mod 26
and the decryption process as
Di = (Ci − Ki + 26)mod 26
where Pi, Ki, and Cirepresent the positions of the plaintext, key, and ciphertext letters
respectively. By using multiple alphabets, the polyalphabetic cipher disguises the frequency
of letters in the plaintext, making frequency analysis attacks less effective. However, with
long ciphertexts and short keys, statistical methods can still be used to break it.

Code:

#include <bits/stdc++.h>
using namespace std;

class VignereCipher{
private:
string key;

char shiftChar(char c, int shift){


if(c >= 'a' && c<='z'){
return 'a' + (c - 'a' + shift + 26) % 26;
}
else if(c >= 'A' && c <= 'Z'){
return 'A' + (c - 'A' + shift + 26) % 26;
}

return c;
}

string generateStr(int n){


string str = "";

for(int i=0; i<n; i++){


str+= key[i%n];
}

12
return str;
}
public:

VignereCipher(string k = "random"){
string lowerKey = "";
for(int i=0; i<[Link](); i++){
lowerKey += tolower(k[i]);
}
key = lowerKey;
}

string plainToCipher(string plainText){


int n = [Link]();

string cipherText = "";


string str = generateStr(n);
for(int i=0; i<n; i++){
int shift = str[i] - 'a';
cipherText += shiftChar(plainText[i], shift);
}
return cipherText;
}

string cipherToPlain(string cipherText){


int n = [Link]();
string plainText = "";
string str = generateStr(n);
for(int i=0; i<n; i++){
int shift = str[i] - 'a';
plainText += shiftChar(cipherText[i], -shift);
}
return plainText;
}

};

int main(){
VignereCipher v1("Aayushbansal");
string plainText = “Hello Aayush Bansal";
cout << "Plain Text: " << plainText << endl;
string a = [Link](plainText);
cout << "Cipher Text: " << a << endl;

string b = [Link](a);
cout << "Decrypted Text: " << b << endl;
return 0;
}

13
Output:

Learning Outcome: Through this experiment, students will learn how multiple
substitution alphabets are used to enhance the security of encryption schemes. They will
understand the working principle of polyalphabetic substitution, including both encryption
and decryption processes. Students will gain hands-on experience in implementing the
algorithm, constructing and applying a key for repeated use, and observing how this
technique reduces the weaknesses of monoalphabetic ciphers. This experiment will help them
appreciate the historical evolution of cryptography from simple substitution to more secure
and complex systems, laying the foundation for understanding modern encryption algorithms.

14
Experiment 5

Aim: To implement Hill- cipher encryption decryption

Theory: The Hill cipher is a polyalphabetic substitution cipher based on linear algebra.
It was invented by Lester S. Hill in 1929 and operates on blocks of letters rather than
individual characters, making it a block cipher. In this method, each letter is first converted
into a numerical value (A = 0, B = 1, ..., Z = 25). The plaintext is then divided into equal-
sized blocks, and each block is represented as a column vector. A key matrix of the same size
(n × n) is used for encryption. The ciphertext is obtained by multiplying the key matrix with
the plaintext vector and taking the result modulo 26. Mathematically, it is expressed as:
C = K × P (mod 26)
where Cis the ciphertext vector, Kis the key matrix, and Pis the plaintext vector. For
decryption, the inverse of the key matrix (mod 26) is used:
P = K −1 × C (mod 26)
The key matrix must be invertible modulo 26 (i.e., its determinant should be coprime with
26) to ensure successful decryption. The Hill cipher provides high diffusion and security
compared to classical ciphers like Caesar or Playfair, as it mixes multiple letters at once.
However, it can be vulnerable to known plaintext attacks if a sufficient number of plaintext–
ciphertext pairs are known.

Code:

#include <bits/stdc++.h>
using namespace std;

class HillCipher
{
private:
vector<vector<int>> keyMatrix;
int matrixSize;

vector<vector<int>> getKeyMatrix(const string &key)


{
vector<vector<int>> matrix(matrixSize, vector<int>(matrixSize));
int k = 0;
for (int i = 0; i < matrixSize; i++)
{
for (int j = 0; j < matrixSize; j++)
{
matrix[i][j] = (toupper(key[k]) - 'A') % 26;
k++;
}
}
return matrix;

15
}

vector<int> multiplyMatrix(const vector<vector<int>> &matrix, const vector<int> &vec)


{
vector<int> result(matrixSize);
for (int i = 0; i < matrixSize; i++)
{
result[i] = 0;
for (int j = 0; j < matrixSize; j++)
{
result[i] += matrix[i][j] * vec[j];
}
result[i] = ((result[i] % 26) + 26) % 26;
}
return result;
}

int modInverse(int a, int m = 26)


{
a = (a % m + m) % m;
for (int x = 1; x < m; x++)
if ((a * x) % m == 1)
return x;
throw invalid_argument("Matrix not invertible mod 26");
}

// Compute determinant for 2x2 or 3x3


int determinant(const vector<vector<int>> &mat)
{
if (matrixSize == 2)
return (mat[0][0] * mat[1][1] - mat[0][1] * mat[1][0]) % 26;

if (matrixSize == 3)
{
int det = mat[0][0] * (mat[1][1] * mat[2][2] - mat[1][2] * mat[2][1]) - mat[0][1] *
(mat[1][0] * mat[2][2] - mat[1][2] * mat[2][0]) + mat[0][2] * (mat[1][0] * mat[2][1] - mat[1]
[1] * mat[2][0]);
return ((det % 26) + 26) % 26;
}

throw invalid_argument("Only 2x2 or 3x3 supported.");


}

// Get cofactor matrix for 3x3


vector<vector<int>> cofactor3x3(const vector<vector<int>> &mat)
{
vector<vector<int>> cof(3, vector<int>(3));
cof[0][0] = (mat[1][1] * mat[2][2] - mat[1][2] * mat[2][1]);

16
cof[0][1] = -(mat[1][0] * mat[2][2] - mat[1][2] * mat[2][0]);
cof[0][2] = (mat[1][0] * mat[2][1] - mat[1][1] * mat[2][0]);
cof[1][0] = -(mat[0][1] * mat[2][2] - mat[0][2] * mat[2][1]);
cof[1][1] = (mat[0][0] * mat[2][2] - mat[0][2] * mat[2][0]);
cof[1][2] = -(mat[0][0] * mat[2][1] - mat[0][1] * mat[2][0]);
cof[2][0] = (mat[0][1] * mat[1][2] - mat[0][2] * mat[1][1]);
cof[2][1] = -(mat[0][0] * mat[1][2] - mat[0][2] * mat[1][0]);
cof[2][2] = (mat[0][0] * mat[1][1] - mat[0][1] * mat[1][0]);
return cof;
}

// Inverse matrix for 2x2 or 3x3


vector<vector<int>> inverseMatrix()
{
int det = determinant(keyMatrix);
int invDet = modInverse(det);

vector<vector<int>> inv(matrixSize, vector<int>(matrixSize));


if (matrixSize == 2)
{
inv[0][0] = keyMatrix[1][1];
inv[0][1] = -keyMatrix[0][1];
inv[1][0] = -keyMatrix[1][0];
inv[1][1] = keyMatrix[0][0];
}
else if (matrixSize == 3)
{
vector<vector<int>> cof = cofactor3x3(keyMatrix);
// Transpose cofactor to get adjugate
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
inv[i][j] = cof[j][i];
}

for (int i = 0; i < matrixSize; i++)


for (int j = 0; j < matrixSize; j++)
inv[i][j] = ((inv[i][j] * invDet) % 26 + 26) % 26;

return inv;
}

public:
HillCipher(const string &key, int size)
{
matrixSize = size;
keyMatrix = getKeyMatrix(key);
}

17
string encrypt(const string &text)
{
string result = "";
for (size_t i = 0; i < [Link](); i += matrixSize)
{
vector<int> block(matrixSize, 0);
for (int j = 0; j < matrixSize; j++)
{
if (i + j < [Link]() && isalpha(text[i + j]))
block[j] = (toupper(text[i + j]) - 'A') % 26;
}
vector<int> encryptedBlock = multiplyMatrix(keyMatrix, block);
for (int val : encryptedBlock)
result += char(val + 'A');
}
return result;
}

string decrypt(const string &text)


{
string result = "";
vector<vector<int>> invKey = inverseMatrix();
for (size_t i = 0; i < [Link](); i += matrixSize)
{
vector<int> block(matrixSize, 0);
for (int j = 0; j < matrixSize; j++)
{
if (i + j < [Link]() && isalpha(text[i + j]))
block[j] = (toupper(text[i + j]) - 'A') % 26;
}
vector<int> decryptedBlock = multiplyMatrix(invKey, block);
for (int val : decryptedBlock)
result += char(val + 'A');
}
return result;
}
};

int main()
{
string text, key;
int size;

cout << "Enter plaintext: ";


getline(cin, text);
cout << "Enter key (perfect square length, e.g., 4 for 2x2 or 9 for 3x3): ";
getline(cin, key);

18
size = int(sqrt([Link]()));
if ((size * size) != [Link]())
{
cout << "Invalid key length.\n";
return 1;
}

HillCipher cipher(key, size);


string encrypted = [Link](text);
cout << "Encrypted Text: " << encrypted << endl;
cout << "Decrypted Text: " << [Link](encrypted) << endl;

return 0;
}

Output:

Learning Outcome: By performing this experiment, students will learn how linear
algebra concepts such as matrices, determinants, and modular arithmetic are applied in
cryptography. They will understand the process of encrypting and decrypting messages using
the Hill cipher and how to construct and validate the key matrix. This experiment helps
students appreciate how mathematical operations can enhance the strength of cryptographic
systems by introducing complexity and diffusion. Additionally, they will gain practical
experience in implementing matrix-based encryption techniques and understanding the
advantages and limitations of the Hill cipher in secure communication.

19
Experiment 6

Aim: To implement S-DES sub key Generation

Theory: Simplified Data Encryption Standard (S-DES) is a reduced version of the DES
algorithm, designed for educational purposes to help understand the working principles of
modern block ciphers. It operates on 8-bit blocks of plaintext using a 10-bit key, and
performs two rounds of encryption or decryption. The key generation process is a crucial
part of S-DES, as it produces two 8-bit subkeys (K1 and K2) from the initial 10-bit key,
which are later used in each round of encryption and decryption. The subkey generation
involves several permutation and shifting operations. First, the 10-bit key undergoes a P10
permutation, rearranging its bits according to a predefined pattern. The result is divided into
two 5-bit halves, each of which is circularly left-shifted by one position to produce the input
for P8 permutation, generating the first subkey (K1). For the second subkey (K2), both
halves are again circularly shifted left by two positions and passed through the same P8
permutation pattern. These operations ensure that each subkey is unique and derived
systematically from the main key. The subkey generation process in S-DES demonstrates
how permutation, shifting, and key scheduling contribute to the overall security and diffusion
of symmetric key algorithms.

Code:

#include <bits/stdc++.h>
using namespace std;

class DESKeyGenerator {
private:
const vector<int> PC1 = {
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
};

const vector<int> PC2 = {


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

20
};

const vector<int> shifts = {


1, 1, 2, 2, 2, 2, 2, 2,
1, 2, 2, 2, 2, 2, 2, 1
};

vector<int> key64;
vector<vector<int>> roundKeys;

vector<int> permute(const vector<int>& input, const vector<int>& table) {


vector<int> output;
for (int pos : table)
output.push_back(input[pos - 1]);
return output;
}

vector<int> leftShift(const vector<int>& bits, int shiftCount) {


vector<int> shifted([Link]());
int n = [Link]();
for (int i = 0; i < n; i++)
shifted[i] = bits[(i + shiftCount) % n];
return shifted;
}

public:
DESKeyGenerator(const string& keyStr) {
[Link](64);
for (char c : keyStr) {
key64.push_back(c - '0');
}

void generateRoundKeys() {
vector<int> key56 = permute(key64, PC1);

vector<int> C([Link](), [Link]() + 28);


vector<int> D([Link]() + 28, [Link]());

[Link]();
for (int i = 0; i < 16; i++) {
C = leftShift(C, shifts[i]);
D = leftShift(D, shifts[i]);

vector<int> combined(C);
[Link]([Link](), [Link](), [Link]());

21
vector<int> roundKey = permute(combined, PC2);
roundKeys.push_back(roundKey);
}
}

const vector<vector<int>>& getRoundKeys() const {


return roundKeys;
}

void printRoundKeys() const {


for (int i = 0; i < (int)[Link](); ++i) {
cout << "Round " << setw(2) << i+1 << ": ";
for (int bit : roundKeys[i]) cout << bit;
cout << "\n";
}
}
};

int main() {
string key =
"0001001100110100010101110111100110011011101111001101111111110001";
DESKeyGenerator des(key);
[Link]();
[Link]();

return 0;
}

Output:

22
Learning Outcome: Through this experiment, students will understand the process of
key generation in symmetric key encryption, particularly how subkeys are derived from an
original key using permutations and shifts. They will learn how the two subkeys (K1 and K2)
are produced in the S-DES algorithm and how these keys contribute to the encryption and
decryption rounds. This experiment helps students gain practical knowledge of key
scheduling, which is a fundamental concept in modern cryptographic algorithms. By
implementing S-DES subkey generation, students will appreciate the importance of key
transformation in enhancing security and preventing predictable encryption patterns in block
cipher systems.

23
Experiment 7

Aim: To implement Diffie-hallman key exchange algorithm

Theory: The Diffie–Hellman Key Exchange algorithm, proposed by Whitfield Diffie and
Martin Hellman in 1976, is one of the earliest public-key cryptographic protocols. It allows
two parties to securely establish a shared secret key over an insecure communication
channel without transmitting the key itself. The algorithm is based on the principles of
modular arithmetic and the discrete logarithm problem, which is computationally hard to
reverse. In this method, both users publicly agree on a large prime number pand a primitive
root g(also called a generator). Each user then selects a private key (say aand b), computes
their public key using the formula A = g a mod pand B = g b mod p, and exchanges these
public keys. Finally, both users compute the shared secret key using the received public key
and their private key:
K = (B a )mod p = (A b )mod p
Since both expressions yield the same result, a common secret key is established that can
later be used for symmetric encryption. The security of the Diffie–Hellman algorithm lies in
the difficulty of computing the discrete logarithm, making it infeasible for an attacker to
determine the private key even with knowledge of the public parameters. However, Diffie–
Hellman is vulnerable to man-in-the-middle attacks if authentication is not implemented.

Code:

#include <bits/stdc++.h>
using namespace std;

long long power(long long a, long long b,


long long P)
{
if (b == 1)
return a;

return (((long long)pow(a, b)) % P);


}

int main()
{
long long P, G, x, a, y, b, ka, kb;

P = 31;
cout << "The value of P(Prime) is : " << P << endl;

G = 11;
cout << "The value of G(Primitive Root for P) : " << G << endl;

a = 6;

24
cout << "The private key for A: " << a << endl;

x = power(G, a, P);

b = 7;
cout << "The private key for B : " << b << endl;

y = power(G, b, P);

ka = power(y, a, P);
kb = power(x, b, P);
cout << "Secret key for A is : " << ka << endl;
cout << "Secret key for B is : " << kb << endl;

return 0;
}

Output:

Learning Outcome: Through this experiment, students will understand the concept of
public key cryptography and how two parties can securely generate a shared secret key
without prior communication. They will learn the step-by-step working of the Diffie–
Hellman Key Exchange algorithm using modular exponentiation and the role of prime
numbers and primitive roots in ensuring security. By implementing this algorithm, students
will appreciate how mathematical problems like the discrete logarithm form the foundation of
modern cryptographic systems. This experiment also helps them recognize potential
vulnerabilities in unauthenticated key exchange and the importance of combining Diffie–
Hellman with authentication mechanisms in practical secure communication protocols.

25
Experiment 8

Aim: To implement RSA encryption and decryption.

Theory:
RSA (Rivest‒Shamir‒Adleman) is a widely used public-key cryptosystem that enables
secure data transmission. It is based on the mathematical difficulty of factoring large prime
numbers. Unlike symmetric key algorithms, RSA uses two separate keys: a public key for
encryption and a private key for decryption. Working Principle:

[Link] Generation: Two large prime numbers (p and q) are selected. The modulus n = p ×
q iscomputed. Euler’ s totient function φ (n) = (p-1)(q-1) is calculated. A public key
exponent e is chosen such that 1 < e < φ (n) and gcd(e, φ (n)) = 1. The private key d is
computed as the modular multiplicative inverse of e modulo φ (n).

[Link]: The plaintext message M is converted to a number m and the ciphertext c


iscomputed using the formula:
c = me mod n

[Link]: The receiver uses the private key d to retrieve the original message:
m = cd mod n

Since decryption can only be performed with the private key, RSA ensures both
confidentiality and authentication. It is widely used in secure communications, digital
signatures, and SSL/TLS protocols.

Code:

#include <bits/stdc++.h>
using namespace std;

int gcd(int a,int b){


return b==0?a:gcd(b,a%b);
}
long long modExp(long long base,long long exp,long long mod){
long long result=1;base%=mod;
while(exp>0){
if(exp&1)result=(result*base)%mod;exp>>=1;base=(base*base)%mod;
}
return result;
}
int modInverse(int e,int phi){
int t=0,newt=1,r=phi,newr=e;
while(newr!=0){
int q=r/newr;tie(t,newt)=make_tuple(newt,t-q*newt);tie(r,newr)=make_tuple(newr,r-q*newr);
}
if(r>1)throw runtime_error("e has no inverse mod phi");
if(t<0)t+=phi;
return t;
}

26
int main(){
cout<<"=== RSA Key Generation and Encryption ===\n\n”;

int p,q,e;
cout<<"Enter first prime number (p): “;cin>>p;

cout<<"Enter second prime number (q): “;cin>>q;

int n=p*q;int phi=(p-1)*(q-1);


cout<<"\nComputed values:\n";
cout<<"n = "<<n<<endl;
cout<<"phi(n) = "<<phi<<endl;
cout<<"\nEnter public key exponent (e): ";cin>>e;
if(gcd(e,phi)!=1){
cout<<"❌ Invalid e! Must be coprime with phi(n).\n";return 0;
}
int d=modInverse(e,phi);
cout<<"\nPublic Key (e, n) = ("<<e<<", "<<n<<")";
cout<<"\nPrivate Key (d, n) = ("<<d<<", "<<n<<")\n";
[Link]();
string msg;
cout<<"\nEnter the text message to encrypt: ";
getline(cin,msg);
cout<<"\nEncrypting message…\n";

vector<long long> encrypted;


for(char ch:msg){
int m=int(ch);long long c=modExp(m,e,n);encrypted.push_back(c);cout<<ch<<" ->
“<<c<<endl;
}
cout<<"\nEncrypted Cipher Text (numeric form): ";
for(auto c:encrypted)cout<<c<<" ";
cout<<"\n";
cout<<"\nDecrypting message...\n";
string decrypted=“";

for(auto c:encrypted){
long long m=modExp(c,d,n);decrypted+=char(m);cout<<c<<" -> “<<char(m)<<endl;
}
cout<<"\nDecrypted Text: “<<decrypted<<endl;

cout<<"\n=== RSA Execution Complete ===\n";


return 0;
}

27
Output:

Learning Outcome:
Understood the working of the RSA algorithm, including key generation,
encryption, and decryption. Learned how asymmetric encryption provides
secure communication through public and private key pairs.

28
Experiment 9

Aim: Write a program to generate SHA-1 hash.

Theory:
SHA-1 (Secure Hash Algorithm 1) is a cryptographic hash function designed by the
National Security Agency (NSA). It produces a fixed 160-bit (20-byte) hash value from
input data of any size. The SHA-1 algorithm is commonly used to verify data integrity and
generate digital signatures. Working Principle:

Preprocessing: The message is padded to ensure its length is congruent to 448 modulo
512,followed by appending the message length as a 64-bit value.

Initialization: The algorithm uses five 32-bit variables (A, B, C, D, E) initialized


withconstant values.

Processing: The padded message is divided into 512-bit blocks. Each block undergoes 80
rounds of processing involving bitwise operations, modular additions, and logical functions.

Output:The final output is the concatenation of A, B, C, D, and E, forming a 160-bit


hashvalue.
SHA-1 is widely used in older security applications such as TLS and SSL certificates,
version control systems like Git, and file integrity verification. However, due to
vulnerabilities found in its design, it has largely been replaced by more secure algorithms
like SHA-256 and SHA-3.

Code:

#include <iostream>
#include <iomanip>
#include <sstream>
#include <cstring>
#include <cstdint>
#include <vector>
using namespace std;

class SHA1 {
private:
static uint32_t leftRotate(uint32_t value, unsigned int count) {
return (value << count) | (value >> (32 - count));
}

public:
string hash(const string &input) {
// Initial hash values (SHA-1 standard)
uint32_t h0 = 0x67452301;
uint32_t h1 = 0xEFCDAB89;

29
uint32_t h2 = 0x98BADCFE;
uint32_t h3 = 0x10325476;
uint32_t h4 = 0xC3D2E1F0;

uint64_t originalBitLen = static_cast<uint64_t>([Link]()) * 8ULL;


string msg = input;

msg.push_back(static_cast<char>(0x80));
while (([Link]() % 64) != 56) msg.push_back(static_cast<char>(0x00));

for (int i = 7; i >= 0; --i) {


msg.push_back(static_cast<char>((originalBitLen >> (i * 8)) & 0xFF));
}

for (size_t chunkStart = 0; chunkStart < [Link](); chunkStart += 64) {


uint32_t w[80];
// Break chunk into sixteen 32-bit big-endian words
for (int i = 0; i < 16; ++i) {
size_t idx = chunkStart + i * 4;
w[i] = (static_cast<uint32_t>(static_cast<uint8_t>(msg[idx + 0])) << 24) |
(static_cast<uint32_t>(static_cast<uint8_t>(msg[idx + 1])) << 16) |
(static_cast<uint32_t>(static_cast<uint8_t>(msg[idx + 2])) << 8) |
(static_cast<uint32_t>(static_cast<uint8_t>(msg[idx + 3])));
}
// Extend the sixteen 32-bit words into eighty 32-bit words
for (int i = 16; i < 80; ++i) {
w[i] = leftRotate(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1);
}

uint32_t a = h0;
uint32_t b = h1;
uint32_t c = h2;
uint32_t d = h3;
uint32_t e = h4;

// Main loop
for (int i = 0; i < 80; ++i) {
uint32_t f, k;
if (i < 20) {
f = (b & c) | ((~b) & d);
k = 0x5A827999;
} else if (i < 40) {
f = b ^ c ^ d;
k = 0x6ED9EBA1;
} else if (i < 60) {
f = (b & c) | (b & d) | (c & d);
k = 0x8F1BBCDC;
} else {

30
f = b ^ c ^ d;
k = 0xCA62C1D6;
}
uint32_t temp = leftRotate(a, 5) + f + e + k + w[i];
e = d;
d = c;
c = leftRotate(b, 30);
b = a;
a = temp;
}

// Add this chunk's hash to result so far


h0 += a;
h1 += b;
h2 += c;
h3 += d;
h4 += e;
}

// Produce final hash value (big-endian) as hex string


stringstream ss;
ss << hex << setfill('0') << nouppercase;
ss << setw(8) << (h0);
ss << setw(8) << (h1);
ss << setw(8) << (h2);
ss << setw(8) << (h3);
ss << setw(8) << (h4);
return [Link]();
}
};

int main() {
SHA1 sha1;
string input = "Aayushbansal 23cs010";
cout << "Input: " << input << endl;
string hashValue = [Link](input);
cout << "SHA-1: " << hashValue << endl;
return 0;
}

31
Output:

Learning Outcome:
Understood the concept of cryptographic hash functions and the working of
[Link] how to generate a fixed-size hash from variable-length input
and its applications in ensuring data integrity and authentication.

32
Experiment 10

Aim:
To implement a Digital Signature Algorithm (DSA) for signing and verifying messages.

Theory:
A digital signature provides a mathematical scheme for verifying the authenticity and
integrity of digital messages or documents. Digital signatures are the asymmetric-key
equivalent of handwritten signatures and assure the recipient that the message was
created by a known sender (authentication) and was not altered in transit (integrity).
Digital Signature Algorithms (DSAs) broadly follow three steps: key generation,
signing, and verification. One widely used family is the Digital Signature Algorithm
(DSA) defined by NIST; another common approach uses RSA for signing and
verification.
Working Principle:
Key Generation: Parameters (such as prime p, subgroup q, and generator g) are
selected according to the algorithm's specifications. A private key x is chosen
randomly from the appropriate range and the corresponding public key y is
computed (for example, y = g^x mod p).

Signing: To sign a message M, a cryptographic hash function H is applied to obtain


afixedsize digest h = H(M). A per-message random value k is chosen and
intermediate values are computed (for example, r = (g^k mod p) mod q). The
signature component s is calculated using the private key x, r, k, and h (for DSA: s =
k^{-1}(h + x·r) mod q). The signature is the pair (r, s).

Verification: The verifier computes the hash h = H(M) and uses the signer's public
key along with r and s to check signature validity. Using algorithm-specific
computations (for DSA: w = s^{-1} mod q; u1 = h·w mod q; u2 = r·w mod q; v =
(g^{u1}·y^{u2} mod p) mod q), the signature is valid if v equals [Link] depends on
using a strong hash function, securely generating random k for eachsignature, and
protecting the private key. Digital signatures are used in secure email,software
distribution, certificates (PKI), blockchain transactions, and many authentication
protocols.

Code:

#include <iostream>
#include <cmath>
#include <cstdlib>
using namespace std;

long long modExp(long long base, long long exp, long long mod) {
long long res = 1;
base %= mod;
while (exp > 0) {
if (exp % 2 == 1)
res = (res * base) % mod;
exp /= 2;

33
base = (base * base) % mod;
}
return res;
}

// Compute modular inverse of a mod m (assuming m is prime)


long long modInverse(long long a, long long m) {
// Fermat’s little theorem: a^(m-2) mod m
return modExp(a, m - 2, m);
}

int main() {
// ----- Simple hardcoded parameters -----
long long p = 23; // prime modulus
long long q = 11; // prime divisor of (p−1)
long long g = 4; // generator of order q mod p

cout << "Simple DSA Demo\n";


cout << "Parameters:\n";
cout << "p = " << p << ", q = " << q << ", g = " << g << "\n\n";

// ----- Key Generation -----


long long x = 6; // private key (random in [1, q−1])
long long y = modExp(g, x, p); // public key

cout << "Private key x = " << x << "\n";


cout << "Public key y = " << y << "\n\n";

// ----- Signing -----


long long H = 9; // pretend message hash (normally use SHA-1)
long long k = 3; // random per-message secret (in [1, q−1])
long long r = modExp(g, k, p) % q;
long long kInv = modInverse(k, q);
long long s = (kInv * (H + x * r)) % q;

cout << "Message hash H = " << H << "\n";


cout << "Random k = " << k << "\n";
cout << "Signature: (r, s) = (" << r << ", " << s << ")\n\n";

// ----- Verification -----


long long w = modInverse(s, q);
long long u1 = (H * w) % q;
long long u2 = (r * w) % q;
long long v = ((modExp(g, u1, p) * modExp(y, u2, p)) % p) % q;

cout << "Verification computed v = " << v << "\n";


if (v == r)
cout << "Signature is VALID!\n";

34
else
cout << "Signature is INVALID!\n";

return 0;
}

Output:

Learning Outcome:
Understood how digital signatures provide authentication and integrity for
digital [Link] the steps of key generation, signing, and verification
in a digital signature scheme (e.g., DSA), and the importance of secure
parameter selection and randomness.

35

You might also like