Вы находитесь на странице: 1из 70

DELHI TECHNOLOGICAL UNIVERSITY

DISTRIBUTED SYSTEMS
LAB FILE

CHIRAG YADAV
2K16/CO/84
INDEX

S.NO. EXPERIMENT DATE SIGN.


DELHI TECHNOLOGICAL UNIVERSITY
INFORMATION AND NETWORK SECURITY
LAB FILE

CHIRAG YADAV
2K16/CO/84
INDEX

S.NO. EXPERIMENT DATE SIGN.


PROGRAM - 6

Aim :
To implement S-DES subkey Generation.

Description:

S-DES is a version of the DES with all parameters significantly reduced, but at the same time
preserving the structure of DES. The goal of S-DES is to allow a beginner to understand the
structure of DES, thus laying a foundation for a thorough study of DES. Its goal is as a teaching
tool in the same spirit as Phan’s.
The encryption algorithm involves five functions: an initial permutation (IP); a complex
function labeled fK, which involves both permutation and substitution operations and depends on
a key input; a simple permutation function that switches (SW) the two halves of the data; the
function fK again; and finally a permutation function that is the inverse of the initial permutation
(IP–1).

Algorithm :

Step 1: S-DES Key Generation

S-DES depends on the use of a 10-bit key shared between the sender and the receiver. From this
key, two 8-bit subkeys are produced for use in particular stages of the encryption and decryption
algorithm. The above figure depicts the stages followed to produce the subkeys. First, permute
the key in the following fashion. Let the 10-bit key be designated as (k1, k2, k3, k4, k5, k6, k7,
k8, k9, k10). Then the permutation P10 is defined as: P10(k1, k2, k3, k4, k5, k6, k7, k8, k9, k10)
= (k3, k5, k2, k7, k4, k10, k1, k9, k8, k6).

Step 2: S-DES Encryption

Initial and Final Permutations

The input to the algorithm is an 8-bit block of plaintext, which we first permute using the IP
function: IP(1 2 3 4 5 6 7 8) = (2 6 3 1 4 8 5 7).

The Function fK

The most complex component of S-DES is the function fK, which consists of a combination of
permutation and substitution functions. The functions can be expressed as follows. Let L and R
be the leftmost 4 bits and rightmost 4 bits of the 8-bit input to fK, and let F be a mapping (not
necessarily one to one) from 4-bit strings to 4-bit strings.

The S-box

The first 4 bits (first row of the preceding matrix) are fed into the S-box S0 to produce a 2-bit
output, and the remaining 4 bits (second row) are fed into S1 to produce another 2-bit output.

The Switch Function

The function fK only alters the leftmost 4 bits of the input. The switch function (SW)
interchanges the left and right 4 bits so that the second instance of fK operates on a different 4
bits. In this second instance, the E/P, S0, S1, and P4 functions are the same. The key input is K2.

Code:

#include<stdio.h>

int main()

int i, cnt=0, p8[8]={6,7,8,9,1,2,3,4};

int p10[10]={6,7,8,9,10,1,2,3,4,5};

char input[11], k1[10], k2[10], temp[11];

char LS1[5], LS2[5];

//k1, k2 are for storing interim keys

//p8 and p10 are for storing permutation key

//Read 10 bits from user...

printf("Enter 10 bits input:");

scanf("%s",input);
input[10]='\0';

//Applying p10...

for(i=0; i<10; i++)

cnt = p10[i];

temp[i] = input[cnt-1];

temp[i]='\0';

printf("\nYour p10 key is :");

for(i=0; i<10; i++)

{ printf("%d,",p10[i]); }

printf("\nBits after p10 :");

puts(temp);

//Performing LS-1 on first half of temp

for(i=0; i<5; i++)

if(i==4)

temp[i]=temp[0];

else

temp[i]=temp[i+1];

//Performing LS-1 on second half of temp


for(i=5; i<10; i++)

if(i==9)

temp[i]=temp[5];

else

temp[i]=temp[i+1];

printf("Output after LS-1 :");

puts(temp);

printf("\nYour p8 key is :");

for(i=0; i<8; i++)

{ printf("%d,",p8[i]); }

//Applying p8...

for(i=0; i<8; i++)

cnt = p8[i];

k1[i] = temp[cnt-1];

printf("\nYour key k1 is :");

puts(k1);

//This program can be extended to generate k2 as per DES algorithm.

}
Output:

Discussion:

A brute-force attack on simplified DES is certainly feasible. With a 10-bit key, there are only
210
= 1024 possibilities. Given a ciphertext, an attacker can try each possibility and analyze the
result to determine if it is reasonable plaintext.
Let us consider a known plaintext attack in which a single plaintext (p1, p2, p3, p4, p5, p6, p7,
p8) and its ciphertext output (c1, c2, c3, c4, c5, c6, c7, c8) are known and the key (k1, k2, k3, k4,
k5, k6, k7, k8, k9, k10) is unknown. We can therefore express the encryption algorithm as 8
nonlinear equations in 10 unknowns. There are a number of possible solutions, but each of these
could be calculated and then analyzed. Each of the permutations and additions in the algorithm is
a linear G-8 mapping. The nonlinearity comes from the S-boxes.

Finding and Learning

Simplified DES, developed by Professor Edward Schaefer of Santa Clara University [SCHA96],
is an educational rather than a secure encryption algorithm. It has similar properties and structure
to DES with much smaller parameters.
PROGRAM - 1

Aim :
To implement Caesar Cipher encryption decryption.

Description:
Thus to cipher a given text we need an integer value, known as shift which indicates the

number of position each letter of the text has been moved down.

The encryption can be represented using modular arithmetic by first transforming the letters

into numbers, according to the scheme, A = 0, B = 1,…, Z = 25. Encryption of a letter by a

shift n can be described mathematically as.

(Encryption Phase with shift n)

(Decryption Phase with shift n)

Algorithm :
● Traverse the given text one character at a time .
● For each character, transform the given character as per the rule, depending on
whether we’re encrypting or decrypting the text.
● Return the new string generated.

Code:

#include <iostream>
#include <cstring>

std::string encoder(std::string P, int key)


{
std::string result = "";
for (int i = 0; i < P.length(); i++)
{
if (isupper(P[i]))
result += (P[i] + key - 65) % 26 + 65;
else
result += (P[i] + key - 97) % 26 + 97;
}
return result;
}

std::string decoder(std::string C, int key)


{
std::string result = "";

for (int i = 0; i < C.length(); i++)


{
if (isupper(C[i]))
result += (C[i] + 26 - key - 65) % 26 + 65;
else
result += (C[i] + 26 - key - 97) % 26 + 97;
}
return result;
}

int main()
{
std::string input, encoded, decoded;
int key;
std::cout << "Enter the string : ";
std::getline(std::cin, input);
std::cout << "Enter the key : ";
std::cin >> key;
key%=26;
encoded = encoder(input, key);
std::cout << "Encoded string : " << encoded << std::endl;
decoded = decoder(encoded, key);
std::cout << "Decoded string : " << decoded << std::endl;
return 0;
}
Output:

Discussion:
The Caesar cipher can be easily broken even in a ciphertext-only scenario. Two situations can
be considered:
● An attacker knows (or guesses) that some sort of simple substitution cipher has been
used, but not specifically that it is a Caesar scheme;
● An attacker knows that a Caesar cipher is in use, but does not know the shift value.

Finding and Learning :


The Caesar cipher is one of the earliest known and simplest ciphers. It is a type of
substitution cipher in which each letter in the plaintext is 'shifted' a certain number of places
down the alphabet. For example, with a shift of 1, A would be replaced by B, B would
become C, and so on. The method is named after Julius Caesar, who apparently used it to
communicate with his generals.
PROGRAM - 2

Aim :
To implement Monoalphabetic encryption decryption.

Description:
A monoalphabetic substitution cipher, also known as a simple substitution cipher, relies on a

fixed replacement structure. That is, the substitution is fixed for each letter of the alphabet.

Thus, if "a" is encrypted to "R", then every time we see the letter "a" in the plaintext, we

replace it with the letter "R" in the ciphertext.

Algorithm :
● Traverse the given text one character at a time .
● For each character, transform the given character as per the key, depending on
whether we’re encrypting or decrypting the text.
● Return the new string generated.

Code:
#include <iostream>
#include <cstring>

int enc_array[] = {9, 5, 25, 11, 8, 16, 19, 12, 6, 10, 18, 15, 20, 14, 7, 2, 4, 21, 23, 17, 3, 22,
24, 0, 1, 13};
int dec_array[26];

std::string Encrypt(std::string str, int n)


{
std::string ans = "";
for (int i = 0; i < n; i++)
{
ans += char('a' + enc_array[str[i] - 'a']);
}
return ans;
}
std::string Decrypt(std::string str, int n)
{
std::string ans = "";
for (int i = 0; i < n; i++)
{
ans += char('a' + dec_array[str[i] - 'a']);
}
return ans;
}
int main()
{
for (int i = 0; i < 26; ++i)
{
dec_array[enc_array[i]] = i;
}
std::string input, enc, dec;
std::cout << "Enter the string : ";
std::cin>>input;
enc = Encrypt(input, input.length());
std::cout << "Encoded string : " << enc << std::endl;
dec = Decrypt(enc, enc.length());
std::cout << "Decoded string : " << dec << std::endl;
std::cout<<"Encrypting array: ";
for (int i = 0; i < 26; ++i)
{
std::cout << enc_array[i] << ' ' ;
}
std::cout << std::endl;
std::cout<<"Decrypting array: ";
for (int i = 0; i < 26; ++i)
{
std::cout << dec_array[i] << ' ' ;
}
std::cout << std::endl;
return 0;
}

Output:
Discussion:
One problem with a mono-alphabetic substitution cipher is that an attacker would gain a lot
of information if (s)he gets just one plain-cipher pair. Given few plain-cipher pairs, the
attacker can probably break your full cipher. If you have also data of different size (which
seems to be the case), you should consider taking a family of block ciphers such that you
have a (different) block cipher for each byte-length and encrypt the data with the block cipher
according to its bytelength.

Finding and Learning :


There are many different monoalphabetic substitution ciphers, in fact infinitely many, as each
letter can be encrypted to any symbol, not just another letter. The history of simple
substitution ciphers can be traced back to the very earliest civilizations, and for a long time
they were more than adequate for the purposes for which they were needed. By today's
standards they are very weak, and incredibly easy to break, but they were a very important
step in developing cryptography..
PROGRAM - 3

AIM :
To implement Playfair cipher encryption decryption.

DESCRIPTION:
The Playfair cipher was the first practical digraph substitution cipher. The scheme was invented
in 1854 by Charles Wheatstone but was named after Lord Playfair who promoted the use of the
cipher. In playfair cipher unlike traditional cipher we encrypt a pair of alphabets(digraphs)
instead of a single alphabet.
It was used for tactical purposes by British forces in the Second Boer War and in World War I
and for the same purpose by the Australians during World War II. This was because Playfair is
reasonably fast to use and requires no special equipment.

ALGORITHM :
The Algorithm consistes of 2 steps:
1. Generate the key Square(5×5):
○ The key square is a 5×5 grid of alphabets that acts as the key for encrypting the
plaintext. Each of the 25 alphabets must be unique and one letter of the alphabet
(usually J) is omitted from the table (as the table can hold only 25 alphabets). If
the plaintext contains J, then it is replaced by I.
○ The initial alphabets in the key square are the unique alphabets of the key in the
order in which they appear followed by the remaining letters of the alphabet in
order.
2. Algorithm to encrypt the plain text: The plaintext is split into pairs of two letters
(digraphs). If there is an odd number of letters, a Z is added to the last letter.

CODE:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include <cstring>

#define SIZE 30
// Function to convert the string to lowercase
void toLowerCase(char plain[], int ps)
{
int i;
for (i = 0; i < ps; i++) {
if (plain[i] > 64 && plain[i] < 91)
plain[i] += 32;
}
}

// Function to remove all spaces in a string


int removeSpaces(char* plain, int ps)
{
int i, count = 0;
for (i = 0; i < ps; i++)
if (plain[i] != ' ')
plain[count++] = plain[i];
plain[count] = '\0';
return count;
}

// Function to generate the 5x5 key square


void generateKeyTable(char key[], int ks, char keyT[5][5])
{
int i, j, k, flag = 0, *dicty;

// a 26 character hashmap
// to store count of the alphabet
dicty = (int*)calloc(26, sizeof(int));
for (i = 0; i < ks; i++) {
if (key[i] != 'j')
dicty[key[i] - 97] = 2;
}

dicty['j' - 97] = 1;

i = 0;
j = 0;
for (k = 0; k < ks; k++) {
if (dicty[key[k] - 97] == 2) {
dicty[key[k] - 97] -= 1;
keyT[i][j] = key[k];
j++;
if (j == 5) {
i++;
j = 0;
}
}
}

for (k = 0; k < 26; k++) {


if (dicty[k] == 0) {
keyT[i][j] = (char)(k + 97);
j++;
if (j == 5) {
i++;
j = 0;
}
}
}
}

// Function to search for the characters of a digraph


// in the key square and return their position
void search(char keyT[5][5], char a, char b, int arr[])
{
int i, j;

if (a == 'j')
a = 'i';
else if (b == 'j')
b = 'i';

for (i = 0; i < 5; i++) {

for (j = 0; j < 5; j++) {


if (keyT[i][j] == a) {
arr[0] = i;
arr[1] = j;
}
else if (keyT[i][j] == b) {
arr[2] = i;
arr[3] = j;
}
}
}
}

// Function to find the modulus with 5


int mod5(int a)
{
return (a % 5);
}

// Function to make the plain text length to be even


int prepare(char str[], int ptrs)
{
if (ptrs % 2 != 0) {
str[ptrs++] = 'z';
str[ptrs] = '\0';
}
return ptrs;
}

// Function for performing the encryption


void encrypt(char str[], char keyT[5][5], int ps)
{
int i, a[4];

for (i = 0; i < ps; i += 2) {

search(keyT, str[i], str[i + 1], a);

if (a[0] == a[2]) {
str[i] = keyT[a[0]][mod5(a[1] + 1)];
str[i + 1] = keyT[a[0]][mod5(a[3] + 1)];
}
else if (a[1] == a[3]) {
str[i] = keyT[mod5(a[0] + 1)][a[1]];
str[i + 1] = keyT[mod5(a[2] + 1)][a[1]];
}
else {
str[i] = keyT[a[0]][a[3]];
str[i + 1] = keyT[a[2]][a[1]];
}
}
}

// Function to encrypt using Playfair Cipher


void encryptByPlayfairCipher(char str[], char key[])
{
char ps, ks, keyT[5][5];

// Key
ks = strlen(key);
ks = removeSpaces(key, ks);
toLowerCase(key, ks);

// Plaintext
ps = strlen(str);
toLowerCase(str, ps);
ps = removeSpaces(str, ps);

ps = prepare(str, ps);

generateKeyTable(key, ks, keyT);

encrypt(str, keyT, ps);


}

// Driver code
int main()
{
char str[SIZE], key[SIZE];

// Key to be encrypted
strcpy(key, "Monarchy");
printf("Key text: %s\n", key);

// Plaintext to be encrypted
strcpy(str, "instruments");
printf("Plain text: %s\n", str);

// encrypt using Playfair Cipher


encryptByPlayfairCipher(str, key);

printf("Cipher text: %s\n", str);

return 0;
}

OUTPUT:

DISCUSSION:
The Playfair cipher was used mainly to protect important, yet non-critical secrets, as it is quick to
use and requires no special equipment. By the time enemy cryptanalysts could break the code the
information it was protecting would often no longer be relevant.

FINDING AND LEARNING :


The playfair cipher starts with creating a key table. The key table is a 5×5 grid of letters that will
act as the key for encrypting your plaintext. Each of the 25 letters must be unique and one letter
of the alphabet (usually Q) is omitted from the table (as there are 25 spots and 26 letters in the
alphabet).
PROGRAM - 4

Aim :
To implement polyalphabetic cipher encryption decryption ( Vigenere Cipher )

Description:
A polyalphabetic cipher is any cipher based on substitution, using multiple substitution
alphabets. Properties of polyalphabetic ciphers are :

• In a polyalphabetic cipher, multiple “alphabets” are used to encipher.


• If two letters are the same in the ciphertext it does not mean they must decipher to the
same plaintext letter.

The Vigenère cipher is probably the best-known example of a polyalphabetic cipher, though it is
a simplified special case. The Enigma machine is more complex but is still fundamentally a
polyalphabetic substitution cipher.

Algorithm :
The cipher accomplishes encryption using uses a text string (for example, a word) as a key,
which is then used for doing a number of alphabet shifts on the plaintext. Similar to the Caesar
Cipher, but instead of performing a single alphabet shift across the entire plaintext, the Vigenère
cipher uses a key to determine several different shift amounts across the entirety of the message.

Should the key be shorter than the plaintext, it is repeated until the length matches. In this way,
each letter in the plaintext is shifted by the alphabet number of the corresponding letter in the
key.

Expressed mathematically, the encryption of the message at letter *i*, is equal to the alphabetic
value of *i* in the plaintext plus the alphabetic value of the corresponding *i* in the key.

Decryption is the same process reversed, subtracting the key instead of adding to arrive back at
the original, plaintext value.

Code:

#include<bits/stdc++.h>

using namespace std;


string generateKey(string str, string key)

int x = str.size();

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

if (x == i)

i = 0;

if (key.size() == str.size())

break;

key.push_back(key[i]);

return key;

string cipherText(string str, string key)

string cipher_text;

for (int i = 0; i < str.size(); i++)

int x = (str[i] + key[i]) %26;

x += 'A';

cipher_text.push_back(x);

}
return cipher_text;

string originalText(string cipher_text, string key)

string orig_text;

for (int i = 0 ; i < cipher_text.size(); i++)

int x = (cipher_text[i] - key[i] + 26) %26;

x += 'A';

orig_text.push_back(x);

return orig_text;

int main()

string str, keyword;

cout<<"Enter plaintext : ";

cin>>str;

cout<<"Enter keyword : ";

cin>>keyword;

string key = generateKey(str, keyword);

string cipher_text = cipherText(str, key);

cout << "Ciphertext : "


<< cipher_text << "\n";

cout << "Original/Decrypted Text : "

<< originalText(cipher_text, key);

cout<<endl;

return 0;

Output:

Discussion:
The idea behind the Vigenère cipher, like all other polyalphabetic ciphers, is to disguise the
plaintext letter frequency to interfere with a straightforward application of frequency analysis.
For instance, if P is the most frequent letter in a ciphertext whose plaintext is in English, one
might suspect that P corresponds to E since E is the most frequently used letter in English.
However, by using the Vigenère cipher, E can be enciphered as different ciphertext letters at
different points in the message, which defeats simple frequency analysis.

The primary weakness of the Vigenère cipher is the repeating nature of its key. If a cryptanalyst
correctly guesses the key's length, the cipher text can be treated as interwoven Caesar ciphers,
which can easily be broken individually. The Kasiski examination and Friedman test can help to
determine the key length (see below: § Kasiski examination and § Friedman test).

Finding and Learning


A cipher is polyalphabetic if a given letter of the alphabet will not always enciphered by the
same ciphertext letter, and, as a consequence, cannot be described by a single set of ciphertext
alphabet corresponding to a single set of plaintext alphabet.
The simplest way to produce a polyalphabetic cipher is to combine different monoalphabetic
ciphers.

One of the problems with monoalphabetic ciphers is that the letters occur with certain frequency
in a language. This frequency can be graphed for both plaintext letters and the ciphertext letters
of the enciphered message, and, after some analysis, the cipher is relatively easily broken.
PROGRAM - 5

AIM :
To implement hill cipher encryption decryption.

DESCRIPTION:
Hill cipher is a polygraphic substitution cipher based on linear algebra.Each letter is represented
by a number modulo 26. Often the simple scheme A = 0, B = 1, …, Z = 25 is used, but this is not
an essential feature of the cipher. To encrypt a message, each block of n letters (considered as an
n-component vector) is multiplied by an invertible n × n matrix, against modulus 26. To decrypt
the message, each block is multiplied by the inverse of the matrix used for encryption.
The matrix used for encryption is the cipher key, and it should be chosen randomly from the set
of invertible n × n matrices (modulo 26).

ALGORITHM :
To encrypt a message, each block of n letters (considered as an n-component vector) is
multiplied by an invertible n × n matrix, against modulus 26. To decrypt the message, each block
is multiplied by the inverse of the matrix used for encryption.
The matrix used for encryption is the cipher key, and it should be chosen randomly from the set
of invertible n × n matrices (modulo 26). The cipher can, of course, be adapted to an alphabet
with any number of letters; all arithmetic just needs to be done modulo the number of letters
instead of modulo 26.

CODE:

#include <iostream>
using namespace std;

void getKeyMatrix(string key, int keyMatrix[][3])


{
int k = 0;
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
keyMatrix[i][j] = (key[k]) % 65;
k++;
}
}
}

void encrypt(int cipherMatrix[][1],


int keyMatrix[][3],
int messageVector[][1])
{
int x, i, j;
for (i = 0; i < 3; i++)
{
for (j = 0; j < 1; j++)
{
cipherMatrix[i][j] = 0;

for (x = 0; x < 3; x++)


{
cipherMatrix[i][j] +=
keyMatrix[i][x] * messageVector[x][j];
}

cipherMatrix[i][j] = cipherMatrix[i][j] % 26;


}
}
}

void HillCipher(string message, string key)


{
int keyMatrix[3][3];
getKeyMatrix(key, keyMatrix);

int messageVector[3][1];

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


messageVector[i][0] = (message[i]) % 65;

int cipherMatrix[3][1];

encrypt(cipherMatrix, keyMatrix, messageVector);


string CipherText;
for (int i = 0; i < 3; i++)
CipherText += cipherMatrix[i][0] + 65;

cout << " Ciphertext:" << CipherText;


}

int main()
{
string message;
cout<<"enter message: ";
cin>>message;
string key;
cout<<"\n enter key: ";
cin>>key;

HillCipher(message, key);

return 0;
}

OUTPUT:

DISCUSSION:
In classical cryptography, the Hill cipher is a polygraphic substitution cipher based on linear
algebra. Invented by Lester S. Hill in 1929, it was the first polygraphic cipher in which it was
practical (though barely) to operate on more than three symbols at once. The following
discussion assumes an elementary knowledge of matrices.
FINDING AND LEARNING
The Hill Cipher was invented by Lester S. Hill in 1929, and like the other Digraphic Ciphers it
acts on groups of letters. Unlike the others though it is extendable to work on different sized
blocks of letters. So, technically it is a polygraphic substitution cipher, as it can work on
digraphs, trigraphs (3 letter blocks) or theoretically any sized blocks.
PROGRAM - 7

Aim :
To implement diffie-hellman key exchange program

Description:
Diffie–Hellman key exchange (DH) is a method of securely exchanging cryptographic keys over
a public channel and was one of the first public-key protocols as originally conceptualized by
Ralph Merkle and named after Whitfield Diffie and Martin Hellman. DH is one of the earliest
practical examples of public key exchange implemented within the field of cryptography.

Also called exponential key exchange, it is a method of digital encryption that uses numbers
raised to specific powers to produce decryption keys on the basis of components that are never
directly transmitted, making the task of a would-be code breaker mathematically overwhelming.

Algorithm :
To implement Diffie-Hellman, the two end users Alice and Bob, while communicating over a
channel they know to be private, mutually agree on positive whole numbers p and q, such that p
is a prime number and q is a generator of p. The generator q is a number that, when raised to
positive whole-number powers less than p, never produces the same result for any two such
whole numbers. The value of p may be large but the value of q is usually small.

Once Alice and Bob have agreed on p and q in private, they choose positive whole-number
personal keys a and b, both less than the prime-number modulus p. Neither user divulges their
personal key to anyone; ideally they memorize these numbers and do not write them down or
store them anywhere. Next, Alice and Bob compute public keys a* and b* based on their
personal keys according to the formulas

a* = qa mod p

and

b* = qb mod p

The two users can share their public keys a* and b* over a communications medium assumed to
be insecure, such as the Internet or a corporate wide area network (WAN). From these public
keys, a number x can be generated by either user on the basis of their own personal keys. Alice
computes x using the formula

x = (b*)a mod p

Bob computes x using the formula


x = (a*)b mod p

The value of x turns out to be the same according to either of the above two formulas.

Code:

#include<bits/stdc++.h>

using namespace std;

long long int power(long long int a, long long int b, long long int P)

if (b == 1)

return a;

else

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

int main()

long long int P, G, x, a, y, b, ka, kb;

cout<<"Enter value of P : ";

cin>>P;

cout<<"Enter value of G : ";

cin>>G;

cout<<"Enter private key a for Alice : ";

cin>>a;

x = power(G, a, P);
cout<<"Enter private key b for Bob : ";

cin>>b;

y = power(G, b, P);

ka = power(y, a, P);

kb = power(x, b, P);

printf("Secret key for the Alice is : %lld\n", ka);

printf("Secret Key for the Bob is : %lld\n", kb);

return 0;

Output:

Discussion:
On a mathematical level, the Diffie-Hellman key exchange relies on one-way functions as the
basis for its security. These are calculations which are simple to do one way, but much more
difficult to calculate in reverse.

More specifically, it relies on the Diffie-Hellman problem, which assumes that under the right
parameters, it is infeasible to calculate gab from the separate values of g, ga and gb. There is
currently no publicly known way to easily find gab from the other values, which is why the
Diffie-Hellman key exchange is considered secure, despite the fact that attackers can intercept
the values p, g, A, and B.

Finding and Learning


The Diffie-Hellmann key exchange is a secure method for exchanging cryptographic keys. This
method allows two parties which have no prior knowledge of each other to establish a shared,
secret key, even over an insecure channel. The concept uses multiplicative group of integers
modulo, which without knowledge of the private keys of any of the parties, would present a
mathematically overwhelming task to a code breaker .
PROGRAM- 8

AIM

To implement RSA encryption-decryption

DESCRIPTION

RSA algorithm is asymmetric cryptography algorithm. Asymmetric actually means that it


works on two different keys i.e. Public Key and Private Key. As the name describes that the
Public Key is given to everyone and Private key is kept private.
An example of asymmetric cryptography :
1. A client (for example browser) sends its public key to the server and requests for
some data.
2. The server encrypts the data using client’s public key and sends the encrypted data.
3. Client receives this data and decrypts it.
Since this is asymmetric, nobody else except browser can decrypt the data even if a third
party has public key of browser.

ALGORITHM

● Generating Public Key :


1. Select two prime no's. Suppose P = 53 and Q = 59.
2. Now First part of the Public key : n = P*Q = 3127
3. We also need a small exponent say e :
4. But e Must be
○ An integer.
○ Not be a factor of n.
5. 1 < e < Φ(n) [Φ(n) is discussed below],
6. Our Public Key is made of n and e

● Generating Private Key :


1. We need to calculate Φ(n) :
Such that Φ(n) = (P-1)(Q-1)
so, Φ(n) = 3016
2. Now calculate Private Key, d :
d = (k*Φ(n) + 1) / e for some integer k
CODE

#include<iostream>
#include<math.h>
#include<string.h>
#include<stdlib.h>

using namespace std;

long int p, q, n, t, flag, e[100], d[100], temp[100], j, m[100], en[100], i;


char msg[100];
int prime(long int);
void ce();
long int cd(long int);
void encrypt();
void decrypt();
int prime(long int pr)
{
int i;
j = sqrt(pr);
for (i = 2; i <= j; i++)
{
if (pr % i == 0)
return 0;
}
return 1;
}
int main()
{
cout << "\nENTER FIRST PRIME NUMBER\n";
cin >> p;
flag = prime(p);
if (flag == 0)
{
cout << "\nWRONG INPUT\n";
exit(1);
}
cout << "\nENTER ANOTHER PRIME NUMBER\n";
cin >> q;
flag = prime(q);
if (flag == 0 || p == q)
{
cout << "\nWRONG INPUT\n";
exit(1);
}
cout << "\nENTER MESSAGE\n";
fflush(stdin);
cin >> msg;
for (i = 0; msg[i] != '\0'; i++)
m[i] = msg[i];
n = p * q;
t = (p - 1) * (q - 1);
ce();
cout << "\nPOSSIBLE VALUES OF e AND d ARE\n";
for (i = 0; i < j - 1; i++)
cout << e[i] << "\t" << d[i] << "\n";
encrypt();
decrypt();
cout<<"\n";
return 0;
}
void ce()
{
int k;
k = 0;
for (i = 2; i < t; i++)
{
if (t % i == 0)
continue;
flag = prime(i);
if (flag == 1 && i != p && i != q)
{
e[k] = i;
flag = cd(e[k]);
if (flag > 0)
{
d[k] = flag;
k++;
}
if (k == 99)
break;
}
}
}
long int cd(long int x)
{
long int k = 1;
while (1)
{
k = k + t;
if (k % x == 0)
return (k / x);
}
}
void encrypt()
{
long int pt, ct, key = e[0], k, len;
i = 0;
len = strlen(msg);
while (i != len)
{
pt = m[i];
pt = pt - 96;
k = 1;
for (j = 0; j < key; j++)
{
k = k * pt;
k = k % n;
}
temp[i] = k;
ct = k + 96;
en[i] = ct;
i++;
}
en[i] = -1;
cout << "\nTHE ENCRYPTED MESSAGE IS\n";
for (i = 0; en[i] != -1; i++)
cout<<char(en[i]);
}
void decrypt()
{
long int pt, ct, key = d[0], k;
i = 0;
while (en[i] != -1)
{
ct = temp[i];
k = 1;
for (j = 0; j < key; j++) {
k = k * ct;
k = k % n;
}
pt = k + 96;
m[i] = pt;
i++;
}
m[i] = -1;
cout << "\nTHE DECRYPTED MESSAGE IS\n";
for (i = 0; m[i] != -1; i++)
cout<<char(m[i]);
}

OUTPUT:
DISCUSSION

RSA derives its security from the difficulty of factoring large integers that are the product of
two large prime numbers. Multiplying these two numbers is easy, but determining the
original prime numbers from the total -- or factoring -- is considered infeasible due to the
time it would take using even today's supercomputers. The public and private key generation
algorithm is the most complex part of RSA cryptography. Two large prime numbers, p and q,
are generated using the Rabin-Miller primality test algorithm. A modulus, n, is calculated by
multiplying p and q. This number is used by both the public and private keys and provides the
link between them. Its length, usually expressed in bits, is called the key length.
FINDING AND LEARNING

RSA security relies on the computational difficulty of factoring large integers. As computing
power increases and more efficient factoring algorithms are discovered, the ability to factor
larger and larger numbers also increases. Encryption strength is directly tied to key size, and
doubling key length can deliver an exponential increase in strength, although it does impair
performance. RSA keys are typically 1024- or 2048-bits long, but experts believe that 1024-
bit keys are no longer fully secure against all attacks. This is why the government and some
industries are moving to a minimum key length of 2048-bits. Barring an unforeseen
breakthrough in quantum computing, it will be many years before longer keys are required.
PROGRAM – 1

AIM:
Implement concurrent day-time client-server application.

DESCRIPTION:
s
There are two major transport layer protocols to communicate between hosts : TCP and UDP.
In UDP, the client does not form a connection with the server like in TCP and instead just sends
a datagram. Similarly, the server need not accept a connection and just waits for datagrams to
arrive. Datagrams upon arrival contain the address of sender which the server uses to send data
to the correct client.

Socket
A socket is a combination of IP address and
port on one system. On each system a socket
exists for a process interacting with the socket
on other system over the network. A
combination of local socket and the socket at
the remote system is also known a ‘Four tuple’
or ‘4-tuple’. Each connection between two
processes running at different systems can be
uniquely identified through their 4-tuple.
Function Descriptions

socket()
Creates an UN-named socket inside the kernel and returns an integer known as
socket descriptor. This function takes domain/family as its first argument. For
Internet family of ipv4 addresses we use AF_INET. The second argument
‘SOCK_STREAM’ specifies that the transport layer protocol that we want
should be reliable i.e. It should have acknowledgement techniques. The third
argument is generally left zero to let the kernel decide the default protocol to
use for this connection. For connection oriented reliable connections, the default
protocol used is TCP.
bind()
Assigns the details specified in the structure ‘serv_addr’ to the socket created in
the step above. The details include, the family/domain, the interface to listen
on(in case the system has multiple interfaces to network) and the port on which
the server will wait for the client requests to come.
listen()
With second argument as ’10’ specifies maximum number of client connections
that server will queue for this listening socket. After the call to listen(), this
socket becomes a fully functional listening socket.
accept()
The server is put to sleep and when for an incoming client request, the three-
way TCP handshake is complete, the function accept () wakes up and returns
the socket descriptor representing the client socket. Accept() is run in an infinite
loop so that the server is always running and the delay or sleep of 1 sec ensures
that this server does not eat up all your CPU processing. As soon as server gets
a request from client, it prepares the date and time and writes on the client socket
through the descriptor returned by accept().

ALGORITHM:

Server
1. Create UDP socket
2. Bind socket to address
3. Wait for datagram from client process and reply to client request
4. Repeat while server is active

Client
1. Create UDP socket
2. Send request to server
3. Wait for datagram from server
4. Process and reply from server
5. Close socket and exit

CODE:

server.c
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <time.h>

int main()
{
struct sockaddr_in sa;

int sockfd, coontfd;


char str[1025];
time_t tick;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
{
printf("Error in creating socket\n");
exit(0);
}
else
{
printf("Socket Created\n");
}

printf("Socket created\n");
bzero(&sa, sizeof(sa));
memset(str, '0', sizeof(str));
sa.sin_family = AF_INET;
sa.sin_port = htons(5600);
sa.sin_addr.s_addr = htonl(INADDR_ANY);

if (bind(sockfd, (struct sockaddr*)&sa, sizeof(sa))<0)


{
printf("Bind Error\n");
}
else
printf("Binded\n");

listen(sockfd, 10);

while(1)
{
coontfd = accept(sockfd, (struct sockaddr*)NULL ,NULL); // Accept a request
from client
printf("Accepted\n");
tick = time(NULL);
snprintf(str, sizeof(str), "%.24s\r\n", ctime(&tick)); // read sys time and write to buffer
printf("sent\n");
printf("%s\n", str);
write(coontfd, str, strlen(str)); // send buffer to client
}

close(sockfd); // close the socket


return 0;
}

client.c

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>

int main()
{
struct sockaddr_in sa;

int n, sockfd;
char buff[1025];

sockfd = socket(PF_INET, SOCK_STREAM, 0);

if (sockfd < 0)
{
printf("Error in creation\n");
exit(0);
}
else
printf("Socket created\n");

bzero(&sa, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_port = htons(5600);

if (connect(sockfd, (struct sockaddr_in*)&sa, sizeof(sa)) < 0)


{
printf("Connection failed\n");
exit(0);
}
else
printf("Connection made\n");

if ( n = read(sockfd, buff, sizeof(buff)) < 0)


{
printf("Read Error\n");
exit(0);
}
else
{
printf("Read message: %s\n", buff);
printf("%s\n", buff);
printf("Done with connection, exiting\n");
}
close(sockfd);
return 0;
}

OUTPUT:

DISCUSSION:

If we are creating a connection between client and server using TCP then it has few functionality
like, TCP is suited for applications that require high reliability, and transmission time is relatively
less critical. It is used by other protocols like HTTP, HTTPs, FTP, SMTP, Telnet. TCP rearranges
data packets in the order specified. There is absolute guarantee that the data transferred remains
intact and arrives in the same order in which it was sent. TCP does Flow Control and requires
three packets to set up a socket connection, before any user data can be sent. TCP handles
reliability and congestion control. It also does error checking and error recovery. Erroneous
packets are retransmitted from the source to the destination.

FINDING AND LEARNING:

1. We successfully implemented a date-time client-server.


2. UDP is a connectionless protocol where the server waits for a request from a client to
become active. Each connection is treated as a new one\
3. On a local system i.e. within the same computer, the loop back address should be used
as the argument to the client.
4. The connect procedure follows the Three way handshake process to establish the
connection
PROGRAM - 2

AIM :
Write a program to implement Lamport Logical clock in a distributed system

DESCRIPTION:
A Lamport logical clock is an incrementing counter maintained in each process. Conceptually,
this logical clock can be thought of as a clock that only has meaning in relation to messages
moving between processes. When a process receives a message, it resynchronizes its logical
clock with that sender (causality).

ALOGRITHM :
• All the process counters start with value 0.
• A process increments its counter for each event (internal event, message sending,
message receiving) in that process.
• When a process sends a message, it includes its (incremented) counter value with the
message.
• On receiving a message, the counter of the recipient is updated to the greater of its
current counter and the timestamp in the received message, and then incremented by one.

CODE:

#include<stdio.h>

int max1(int a, int b) //to find the maximum timestamp between two events

if (a>b)

return a;

else

return b;

int main()
{

int i,j,k,p1[20],p2[20],e1,e2,dep[20][20];

printf("enter the events : ");

scanf("%d %d",&e1,&e2);

for(i=0;i<e1;i++)

p1[i]=i+1;

for(i=0;i<e2;i++)

p2[i]=i+1;

printf("enter the dependency matrix:\n");

printf("\t enter 1 if e1->e2 \n\t enter -1, if e2->e1 \n\t else enter 0 \n\n");

for(i=0;i<e2;i++)

printf("\te2%d",i+1);

for(i=0;i<e1;i++)

printf("\n e1%d \t",i+1);

for(j=0;j<e2;j++)

scanf("%d",&dep[i][j]);

for(i=0;i<e1;i++)

for(j=0;j<e2;j++)

{
if(dep[i][j]==1) //change the timestamp if dependency exist

{ p2[j]=max1(p2[j],p1[i]+1);

for(k=j;k<e2;k++)

p2[k+1]=p2[k]+1;

if(dep[i][j]==-1) //change the timestamp if dependency exist

p1[i]=max1(p1[i],p2[j]+1);

for(k=i;k<e1;k++)

p2[k+1]=p1[k]+1;

printf("P1 : "); //to print the outcome of Lamport Logical Clock

for(i=0;i<e1;i++)

printf("%d",p1[i]);

printf("\n P2 : ");

for(j=0;j<e2;j++)

printf("%d",p2[j]);
return 0 ;

OUTPUT:

DISCUSSION:
One of the shortcomings of Lamport Timestamps is rooted in the fact that they only partially
order events (as opposed to total order). Partial order indicates that not every pair of events need
be comparable. If two events can’t be compared, we call these events concurrent. The problem
with Lamport Timestamps is that they can’t tell if events are concurrent or not. This problem is
solved by Vector Clocks.

FINDING AND LEARNING


The algorithm of Lamport timestamps is a simple algorithm used to determine the order of
events in a distributed computer system. As different nodes or processes will typically not be
perfectly synchronized, this algorithm is used to provide a partial ordering of events with
minimal overhead, and conceptually provide a starting point for the more advanced vector clock
method. They are named after their creator, Leslie Lamport.
PROGRAM-3

AIM :
To implement bi-directional ring election Algorithm.

DESCRIPTION :
Another election algorithm is based on the use of a ring, but without a token. We assume that
the processes are physically or logically ordered, so that each process knows who its
successor is. When any process notices that the coordinator is not functioning, it builds an
ELECTION message containing its own process number and sends the message to its
successor. If the successor is down, the sender skips over the successor and goes to the next
member along the ring, or the one after that, until a running process is located. At each step,
the sender adds its own process number to the list in the message. Eventually, the message
gets back to the process that started it all. That process recognizes this event when it receives
an incoming message containing its own process number. At that point, the message type is
changed to COORDINATOR and circulated once again, this time to inform everyone else
who the coordinator is (the list member with the highest number) and who the members of
the new ring are. When this message has circulated once, it is removed and everyone goes
back to work.

CODE :
#include<string.h>
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
using namespace std;

struct rr
{
int index;
int id;
int f;
char state[10];
}proc[10];

int i,j,k,m,n;
int main(){
int temp;
char str[10];
cout<<"enter the number of process\n";
cin>>n;
for(i=0;i<n;i++){
cout<<"enter id of process\t";
cin>>proc[i].id;
strcpy(proc[i].state,"active");
proc[i].f=0;
}
// sorting
for(i=0;i<n-1;i++){
for(j=0;j<n-1;j++){
if(proc[j].id>proc[j+1].id){
temp=proc[j].id;
proc[j].id=proc[j+1].id;
proc[j+1].id=temp;
}
}
}

for(i=0;i<n;i++)
printf("[%d] %d\t",i,proc[i].id);

int init;
int ch;
int temp1;
int temp2;
int arr[10];
strcpy(proc[n-1].state,"inactive");
cout<<"\nprocess "<<proc[n-1].id<<" select as coordinator";

while(1) {
cout<<"\n1)election 2)quit\n";
scanf("%d",&ch);
for(i=0;i<n;i++){
proc[i].f=0;
}
switch(ch)
{
case 1: cout<<"\nenter the process Number who intialised election";
scanf("%d",&init);
temp2=init;
temp1=init+1;
i=0;
while(temp2!=temp1){
if(strcmp(proc[temp1].state,"active")==0 && proc[temp1].f==0 ){
cout<<"process "<<proc[init].id<<" send message to
"<<proc[temp1].id<<"\n";
proc[temp1].f=1;
init=temp1;
arr[i]=proc[temp1].id;
i++;
}
if(temp1==n)
temp1=0;
else
temp1++;
}
cout<<"process "<<proc[init].id<<" send message to "<<proc[temp1].id<<"\n";
arr[i]=proc[temp1].id;
i++;
int max=-1;
for(j=0;j<i;j++){
if(max<arr[j])
max=arr[j];
}
cout<<"\nprocess "<<max<<" select as coordinator";
for(i=0;i<n;i++){
if(proc[i].id==max){
strcpy(proc[i].state,"inactive");
// cout<<"\n"<<i<<" "<<proc[i].id<<"deactivate\n";
}
}
break;
break;
}

}
return 0;
}

OUTPUT:
DISCUSSION :
Eventually, the message gets back to the process that started it all. That process recognizes
this event when it receives an incoming message containing its own process number. At that
point, the message type is changed to COORDINATOR and circulated once again, this time
to inform everyone else who the coordinator is (the list member with the highest number) and
who the members of the new ring are. When this message has circulated once, it is removed
and everyone goes back to work.

FINDING AND LEARNING :


Bi-Directional Ring Election algorithm is very useful for implementing distributed
systems.Here we have successfully implemented the algorithm.
PROGRAM - 4

AIM :
Write a program to implement Bully Election algorithm in a distributed system

DESCRIPTION:
Election algorithms choose a process from group of processors to act as a coordinator. If the
coordinator process crashes due to some reasons, then a new coordinator is elected on other
processor. Election algorithm basically determines where a new copy of coordinator should
be restarted. Bully algorithm applies to system where every process can send a message to
every other process in the system.

ALOGRITHM :
Suppose process P sends a message to the coordinator.

● If coordinator does not respond to it within a time interval T, then it is assumed that
coordinator has failed.
● Now process P sends election message to every process with high priority number.
● It waits for responses, if no one responds for time interval T then process P elects
itself as a coordinator.
● Then it sends a message to all lower priority number processes that it is elected as
their new coordinator.
● However, if an answer is received within time T from any other process Q,
● (I) Process P again waits for time interval T’ to receive another message from Q that it
has been elected as coordinator.
● (II) If Q doesn’t responds within time interval T’ then it is assumed to have failed and
algorithm is restarted.

CODE:

#include<bits/stdc++.h>

#include<stdio.h>

#include<stdlib.h>

using namespace std;

struct process {

int no;
int priority;

int active;

struct process *next;

};

typedef struct process proc;

struct priority {

int pri;

struct priority *next;

proc *pp;

};

typedef struct priority pri;

pri* find_priority(proc *head, pri *head1) {

proc *p1;

pri *p2, *p3;

p1 = head;

while (p1->next != head) {

if (p1->active == 1) {

if (head1 == NULL) {

head1 = (pri*) malloc(sizeof(pri));

head1->pri = p1->priority;

head1->next = NULL;

head1->pp = p1;
p2 = head1;

} else {

p3 = (pri*) malloc(sizeof(pri));

p3->pri = p1->priority;

p3->pp = p1;

p3->next = NULL;

p2->next = p3;

p2 = p2->next;

p1 = p1->next;

} else

p1 = p1->next;

} //end while

p3 = (pri*) malloc(sizeof(pri));

p3->pri = p1->priority;

p3->pp = p1;

p3->next = NULL;

p2->next = p3;

p2 = p2->next;

p3 = head1;

return head1;

} //end find_priority()
int find_max_priority(pri *head) {

pri *p1;

int max = -1;

int i = 0;

p1 = head;

while (p1 != NULL) {

if (max < p1->pri && p1->pp->active == 1) {

max = p1->pri;

i = p1->pp->no;

p1 = p1->next;

return i;

void bully() {

proc *head;

proc *p1;

proc *p2;

int n, i, pr, maxpri, a, pid, max, o;

char ch;
head = p1 = p2 = NULL;

printf("\nEnter how many process: ");

scanf("%d", &n);

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

printf("\nEnter priority of process %d: ", i + 1);

scanf("%d", &pr);

printf("\nIs process with id %d is active ?(0/1) :", i + 1);

scanf("%d", &a);

if (head == NULL) {

head = (proc*) malloc(sizeof(proc));

if (head == NULL) {

printf("\nMemory cannot be allocated");

exit(0);

head->no = i + 1;

head->priority = pr;

head->active = a;

head->next = head;

p1 = head;

} else {

p2 = (proc*) malloc(sizeof(proc));

if (p2 == NULL) {
printf("\nMemory cannot be allocated");

exit(0);

p2->no = i + 1;

p2->priority = pr;

p2->active = a;

p1->next = p2;

p2->next = head;

p1 = p2;

} //end for

printf("\nEnter the process id that invokes election algorithm: ");

scanf("%d", &pid);

p2 = head;

while (p2->next != head) {

if (p2->no == pid) {

p2 = p2->next;

break;

p2 = p2->next;

printf("\nProcess with id %d has invoked election algorithm", pid);


printf("\t\nElection message is sent to processes");

while (p2->next != head) {

if (p2->no > pid)

printf("%d, ", p2->no);

p2 = p2->next;

printf("%d", p2->no);

p2 = head;

max = 0;

while (1) {

if (p2->priority > max && p2->active == 1)

max = p2->no;

p2 = p2->next;

if (p2 == head)

break;

printf("\nProcess with the id %d is the co-ordinator", max);

int main() {

bully();

return 0;

}
OUTPUT:

DISCUSSION:
In distributed computing, the bully algorithm is a method for dynamically electing a
coordinator or leader from a group of distributed computer processes. The process with the
highest process ID number from amongst the non-failed processes is selected as the
coordinator.
The algorithm assumes that:
● the system is synchronous.
● processes may fail at any time, including during execution of the algorithm.
● a process fails by stopping and returns from failure by restarting.

FINDING AND LEARNING


The safety property expected of leader election protocols is that every non-faulty process
either elects a process Q, or elects none at all. Note that all processes that elect a leader must
decide on the same process Q as the leader. The Bully algorithm satisfies this property (under
the system model specified), and at no point in time is it possible for two processes in the
group to have a conflicting view of who the leader is, except during an election. This is true
because if it weren't, there are two processes X and Y such that both sent the Coordinator
(victory) message to the group. This means X and Y must also have sent each other victory
messages.
PROGRAM- 5

AIM

Write a program to implement central mutual exclusion Algorithm

DESCRIPTION

Ricart–Agrawala algorithm is an algorithm for mutual exclusion in a distributed system


proposed by Glenn Ricart and Ashok Agrawala. This algorithm is an extension and
optimization of Lamport’s Distributed Mutual Exclusion Algorithm. Like Lamport’s
Algorithm, it also follows permission based approach to ensure mutual exclusion.
In this algorithm:
● Two types of messages ( REQUEST and REPLY) are used and communication
channels are assumed to follow FIFO order.
● A site sends a REQUEST message to all other site to get their permission to enter the
critical section.
● A site send a REPLY message to other sites to give its permission to enter the critical
section.
● A timestamp is given to each critical section request using Lamport’s logical clock.
● Timestamp is used to determine priority of critical section requests. Smaller
timestamp gets high priority over larger timestamp. The execution of critical section
request is always in the order of their timestamps.

ALGORITHM

● To enter Critical section:


○ When a site Si wants to enter the critical section, it sends a timestamped
REQUEST message to all other sites.
○ When a site Sj receives a REQUEST message from site Si, It sends a REPLY
message to site Si if and only if
■ Site Sj is neither requesting nor currently executing the critical section.
■ In case Site Sj is requesting, the timestamp of Site Si‘s request is
smaller than its own request.
○ Otherwise the request is deferred by site Sj.
● To execute the critical section:
○ Site Si enters the critical section if it has received the REPLY message from
all other sites.
● To release the critical section:
○ Upon exiting site Si sends REPLY message to all the deferred requests.
CODE

///************** Ricart–Agrawala Algorithm**************///


#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define NUM_PROCESS (3)
typedef struct
{
int clock[ NUM_PROCESS ]; //Receive Message Timestamp
int RD[ NUM_PROCESS ]; //Request Deffereed Array
int reply[ NUM_PROCESS ];
int cs_flag;
}PROCESS;

PROCESS processes[ NUM_PROCESS ] = {0};


//Local functions
static void GetTimestamp(int *timeStamp);
static ProcessRequest( int processId, int reqProcessId );
static void SendReply( int processId, int reqProcessId );
static void ExecuteCriticalSection( int processId );

//APIs
void CS_RequestCriticalSection( int processId, int *clock );
void CS_ReceivedRequest( int processId, int reqProcessId, int clock);
void CS_ReceivedReply( int processId, int fromProcessId );

int main( void ){


int timeStamp0 = 0;
int timeStamp1 = 0;
printf("\n\n\t************** Ricart Agrawala Algorithm **************\t\n\n");
printf("The distributed system has 3 Processes with Ids 0, 1, and 2\n\n");
//Process 0 makes a request for the CS
CS_RequestCriticalSection( 0, &timeStamp0 );
sleep(1);
//Process 1 makes a request the CS
CS_RequestCriticalSection( 1, &timeStamp1 );
//Process 2 receives request from 1
CS_ReceivedRequest( 2, 1, timeStamp1 );
sleep(2);
//Process 0 receives request from 1
CS_ReceivedRequest( 0, 1, timeStamp1 );

sleep(2);
//Process 1 receives request from 0
CS_ReceivedRequest( 1, 0, timeStamp0 );
sleep(2);
//Process 0 receives reply from 1
CS_ReceivedReply( 0, 1);
sleep(2);
//Process 1 receives reply from 2
CS_ReceivedReply( 1, 2 );
sleep(2);
//Process 0 receives reply from 2
CS_ReceivedReply( 0, 2 );
sleep(2);
//Process 1 receives reply from 0
CS_ReceivedReply( 1, 0 );
sleep(5);
}
void CS_RequestCriticalSection( int processId, int *clock ){
GetTimestamp( clock );
//Update requesting process's clock
processes[ processId ].clock[ processId ] = *clock;
printf( "Process [%d] at Timestamp [%d] Broadcasting REQUEST message\n", processId,
*clock );
}
void CS_ReceivedRequest( int processId, int reqProcessId, int clock){
printf("Process [%d] received REQUEST from Process [%d] with Timestamp [%d]\n",
processId, reqProcessId, clock );
//Updae clock
processes[ processId ].clock[ reqProcessId ] = clock;
//Process Request
ProcessRequest( processId, reqProcessId );
}
void CS_ReceivedReply( int processId, int fromProcessId ){
int exe_cs = 1;
//Set Reply flag
processes[ processId ].reply[ fromProcessId ] = 1;
printf("Process [%d] received REPLY from process [%d]\n", processId, fromProcessId);
//Check reply from all the other processes
for( int i = 0; i < NUM_PROCESS; i++ ) {
if( ( i != processId ) && ( processes[ processId ].reply[ i ] != 1 ) ){
exe_cs = 0;
}
}
if( exe_cs == 1 ){
printf("Process [%d] received REPLY from All the processes\n", processId);
ExecuteCriticalSection( processId );
}
}
static ProcessRequest( int processId, int reqProcessId ){
if( ( processes[ processId ].cs_flag == 1 ) || (processes[ processId ].clock[ processId ] != 0 )
){
if( processes[ processId ].clock[ reqProcessId ] < processes[ processId ].clock[
processId ] ){
//Send REPLY
SendReply( processId, reqProcessId);
}
else {
//Set request deffered flag for requesting process, at receiving process
processes[ processId ].RD[ reqProcessId ] = 1;
printf( "Process [%d] REQUEST from Process [%d] is DEFFERED\n", processId,
reqProcessId ); } } }
static void SendReply( int processId, int reqProcessId ) {
/* Reset requesting process's clock and RD flag at process receiving process */
processes[ processId ].clock[ reqProcessId ] = 0;
processes[ processId ].RD[ reqProcessId ] = 0;
printf( "Process [%d] sending REPLY to Process [%d]\n", processId, reqProcessId );
}
static void ExecuteCriticalSection( int processId ){
//Entering critical section
processes[ processId ].cs_flag = 1;
printf( "Process [%d] enters critical section\n", processId );
//Executing critical section
sleep(1);
printf( "Process [%d] is executing critical section\n", processId );
sleep(1);
//Exiting critical section
printf( "Process [%d] exits critical section\n", processId );
processes[ processId ].cs_flag = 0;
//Send reply to deffered processes
for( int i = 0; i < NUM_PROCESS; i++ ){
if( ( i != processId ) && ( processes[ processId ].RD[i] != 0 ) ){
SendReply( processId, processes[ processId ].RD[i]);
processes[ processId ].RD[i] = 0;
processes[ processId ].reply[ i ] = 0;
}}
}
static void GetTimestamp(int *clock) {
time_t t = time(NULL);
struct tm tm = *localtime(&t);
*clock = tm.tm_hour * 60 * 60 + tm.tm_min * 60 + tm.tm_sec; //In seconds
}

OUTPUT:

DISCUSSION
Message Complexity:
Ricart–Agrawala algorithm requires invocation of 2(N – 1) messages per critical section
execution. These 2(N – 1) messages involves
● (N – 1) request messages
● (N – 1) reply messages

FINDING AND LEARNING


1. Drawbacks of Ricart–Agrawala algorithm:
● Unreliable approach: failure of any one of nodes in the system can halt the progress of
the system. In this situation, the process will starve forever.
The problem of failure of node can be solved by detecting failure after some timeout.
2. Performance:
● Synchronization delay is equal to maximum message transmission time
● It requires 2(N – 1) messages per Critical section execution

Вам также может понравиться