Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

CCS Practical Lab Manual

Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 34

1.

Perform encryption and decryption using one time pad algorithm

One Time Pad algorithm is the improvement of the Vernam Cipher, proposed by An Army
Signal Corp officer, Joseph Mauborgne. It is the only available algorithm that is
unbreakable(completely secure). It is a method of encrypting alphabetic plain text. It is one
of the Substitution techniques which converts plain text into ciphertext. In this mechanism,
we assign a number to each character of the Plain-Text.

The two requirements for the One-Time pad are

The key should be randomly generated as long as the size of the message.
The key is to be used to encrypt and decrypt a single message, and then it is discarded.
So encrypting every new message requires a new key of the same length as the new
message in one-time pad.

The ciphertext generated by the One-Time pad is random, so it does not have any statistical
relation with the plain text.
The assignment is as follows:

A B C D E F G H I J
0 1 2 3 4 5 6 7 8 9
K L M N O P Q R S T
10 11 12 13 14 15 16 17 18 19
U V W X Y Z
20 21 22 23 24 25
The relation between the key and plain text: In this algorithm, the length of the key should
be equal to that of plain text.

Examples:

Input: Message = HELLO, Key = MONEY Output: Cipher – TSYPM, Message – HELLO
Explanation: Part 1: Plain text to Ciphertext Plain text — H E L L O ? 7 4 11 11 14
Key — M O N E Y ? 12 14 13 4 24 Plain text + key ? 19 18 24 15 38 ? 19 18 24 15 12 (= 38
– 26) Cipher Text ? T S Y P M Part 2: Ciphertext to Message Cipher Text — T S Y P
M ? 19 18 24 15 12 Key — M O N E Y? 12 14 13 4 24 Cipher text – key ? 7 4 11 11 -
12 ? 7 4 11 11 14 Message ? H E L L O Input: Message = SAVE, Key = LIFE Output: Cipher
– DIAI Message – SAVE

Security of One-Time Pad

If any way cryptanalyst finds these two keys using which two plaintext are produced but if
the key was produced randomly, then the cryptanalyst cannot find which key is more likely
than the other. In fact, for any plaintext as the size of ciphertext, a key exists that produces
that plaintext.
So if a cryptanalyst tries the brute force attack(try using all possible keys), he would end up
with many legitimate plaintexts, with no way of knowing which plaintext is legitimate.
Therefore, the code is unbreakable.
The security of the one-time pad entirely depends on the randomness of the key. If the
characters of the key are truly random, then the characters of the ciphertext will be truly
random. Thus, there are no patterns or regularities that a cryptanalyst can use to attack the
ciphertext.
Advantages

One-Time Pad is the only algorithm that is truly unbreakable and can be used for low-
bandwidth channels requiring very high security(ex. for military uses).
Disadvantages

There is the practical problem of making large quantities of random keys. Any heavily used
system might require millions of random characters on a regular basis.
For every message to be sent, a key of equal length is needed by both sender and receiver.
Thus, a mammoth key distribution problem exists.
Below is the implementation of the Vernam Cipher:

Program
Python program Implementing One Time Pad Algorithm

# Importing required classes


# Method 1
# Returning encrypted text

def stringEncryption(text, key):


# Initializing cipherText
cipherText = ""

# Initialize cipher array of key length


# which stores the sum of corresponding no.'s
# of plainText and key.
cipher = []
for i in range(len(key)):
cipher.append(ord(text[i]) - ord('A') + ord(key[i])-ord('A'))

# If the sum is greater than 25


# subtract 26 from it
# and store that resulting value
for i in range(len(key)):
if cipher[i] > 25:
cipher[i] = cipher[i] - 26

# Converting the no.'s into integers


# Convert these integers to corresponding
# characters and add them up to cipherText

for i in range(len(key)):
x = cipher[i] + ord('A')
cipherText += chr(x)

# Returning the cipherText


return cipherText

# Method 2
# Returning plain text
def stringDecryption(s, key):

# Initializing plain text


plainText = ""

# Initializing integer array of key length


# which stores difference
# of corresponding no.'s of
# each character of cipherText and key

plain = []

# Running for loop for each character


# subtracting and storing in the array

for i in range(len(key)):
plain.append(ord(s[i]) - ord('A') - (ord(key[i]) - ord('A')))

# If the difference is less than 0


# add 26 and store it in the array.
for i in range(len(key)):
if (plain[i] < 0):
plain[i] = plain[i] + 26

# Converting int to corresponding char


# add them up to plainText

for i in range(len(key)):
x = plain[i] + ord('A')
plainText += chr(x)

# Returning plainText
return plainText

plainText = "Hello"

# Declaring key
key = "MONEY"

# Converting plain text to toUpperCase


# function call to stringEncryption
# with plainText and key as parameters
encryptedText = stringEncryption(plainText.upper(), key.upper())

# Printing cipher Text


print("Cipher Text - " + encryptedText)

# Calling above method to stringDecryption


# with encryptedText and key as parameters
print("Message - " + stringDecryption(encryptedText, key.upper()))

# This code is contributed by Pranay Arora


Output:
Cipher Text - TSYPM
Message - HELLO
Time Complexity: O(N)

Space Complexity: O(N)

2. Write a program to implement extended Euclidean Algorithm.

GCD of two numbers is the largest number that divides both of them. A simple way to find
GCD is to factorize both numbers and multiply common factors. GCD

// C program to demonstrate working of extended


// Euclidean Algorithm
#include <stdio.h>

// C function for extended Euclidean Algorithm


int gcdExtended(int a, int b, int* x, int* y)
{
// Base Case
if (a == 0) {
*x = 0;
*y = 1;
return b;
}

int x1, y1; // To store results of recursive call


int gcd = gcdExtended(b % a, a, &x1, &y1);

// Update x and y using results of recursive


// call
*x = y1 - (b / a) * x1;
*y = x1;
return gcd;
}

// Driver Program
int main()
{
int x, y;
int a = 35, b = 15;
int g = gcdExtended(a, b, &x, &y);
printf("gcd(%d, %d) = %d", a, b, g);
return 0;
}
Output:
gcd(35, 15) = 5
Time Complexity: O(Log min(a, b))

3. Write a program to find primitive root of a number

Given a prime number n, the task is to find its primitive root under modulo n. The primitive
root of a prime number n is an integer r between[1, n-1] such that the values of r^x(mod n)
where x is in the range[0, n-2] are different. Return -1 if n is a non-prime number.

Examples:

Input : 7
Output : Smallest primitive root = 3
Explanation: n = 7
3^0(mod 7) = 1
3^1(mod 7) = 3
3^2(mod 7) = 2
3^3(mod 7) = 6
3^4(mod 7) = 4
3^5(mod 7) = 5

Input : 761
Output : Smallest primitive root = 6
A simple solution is to try all numbers from 2 to n-1. For every number r, compute values of
r^x(mod n) where x is in the range[0, n-2]. If all these values are different, then return r, else
continue for the next value of r. If all values of r are tried, return -1.

An efficient solution is based on the below facts.


If the multiplicative order of a number r modulo n is equal to Euler Totient Function ?(n)
( note that the Euler Totient Function for a prime n is n-1), then it is a primitive root.
1- Euler Totient Function phi = n-1 [Assuming n is prime]
1- Find all prime factors of phi.
2- Calculate all powers to be calculated further
using (phi/prime-factors) one by one.
3- Check for all numbered for all powers from i=2
to n-1 i.e. (i^ powers) modulo n.
4- If it is 1 then 'i' is not a primitive root of n.
5- If it is never 1 then return i;.
Although there can be multiple primitive roots for a prime number, we are only concerned
with the smallest one. If you want to find all the roots, then continue the process till p-1
instead of breaking up by finding the first primitive root.

# Python3 program to find primitive root


# of a given number n
from math import sqrt

# Returns True if n is prime


def isPrime( n):

# Corner cases
if (n <= 1):
return False
if (n <= 3):
return True

# This is checked so that we can skip


# middle five numbers in below loop
if (n % 2 == 0 or n % 3 == 0):
return False
i=5
while(i * i <= n):
if (n % i == 0 or n % (i + 2) == 0) :
return False
i=i+6

return True

""" Iterative Function to calculate (x^n)%p


in O(logy) */"""
def power( x, y, p):

res = 1 # Initialize result

x = x % p # Update x if it is more
# than or equal to p

while (y > 0):

# If y is odd, multiply x with result


if (y & 1):
res = (res * x) % p

# y must be even now


y = y >> 1 # y = y/2
x = (x * x) % p

return res

# Utility function to store prime


# factors of a number
def findPrimefactors(s, n) :

# Print the number of 2s that divide n


while (n % 2 == 0) :
s.add(2)
n = n // 2

# n must be odd at this point. So we can


# skip one element (Note i = i +2)
for i in range(3, int(sqrt(n)), 2):

# While i divides n, print i and divide n


while (n % i == 0) :

s.add(i)
n = n // i

# This condition is to handle the case


# when n is a prime number greater than 2
if (n > 2) :
s.add(n)

# Function to find smallest primitive


# root of n
def findPrimitive( n) :
s = set()

# Check if n is prime or not


if (isPrime(n) == False):
return -1

# Find value of Euler Totient function


# of n. Since n is a prime number, the
# value of Euler Totient function is n-1
# as there are n-1 relatively prime numbers.
phi = n - 1

# Find prime factors of phi and store in a set


findPrimefactors(s, phi)

# Check for every number from 2 to phi


for r in range(2, phi + 1):
# Iterate through all prime factors of phi.
# and check if we found a power with value 1
flag = False
for it in s:

# Check if r^((phi)/primefactors)
# mod n is 1 or not
if (power(r, phi // it, n) == 1):

flag = True
break

# If there was no power with value 1.


if (flag == False):
return r

# If no primitive root found


return -1

# Driver Code
n = 761
print("Smallest primitive root of",
n, "is", findPrimitive(n))

# This code is contributed by


# Shubham Singh(SHUBHAMSINGH10)
Output:

Smallest primitive root of 761 is 6


Time Complexity : O(n^2 * logn)
Space Complexity : O(sqrt(n))

4. write a program to develop secure system by applying rsa algorithm

RSA algorithm is an 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 the Private key is kept private.

An example of asymmetric cryptography:

A client (for example browser) sends its public key to the server and requests some data.
The server encrypts the data using the client’s public key and sends the encrypted data.
The client receives this data and decrypts it.
Since this is asymmetric, nobody else except the browser can decrypt the data even if a third
party has the public key of the browser.

The idea! The idea of RSA is based on the fact that it is difficult to factorize a large integer.
The public key consists of two numbers where one number is a multiplication of two large
prime numbers. And private key is also derived from the same two prime numbers. So if
somebody can factorize the large number, the private key is compromised. Therefore
encryption strength totally lies on the key size and if we double or triple the key size, the
strength of encryption increases exponentially. RSA keys can be typically 1024 or 2048 bits
long, but experts believe that 1024-bit keys could be broken in the near future. But till now it
seems to be an infeasible task.
Let us learn the mechanism behind the RSA algorithm : >> Generating Public Key:

Select two prime no's. Suppose P = 53 and Q = 59.


Now First part of the Public key : n = P*Q = 3127.
We also need a small exponent say e :
But e Must be
An integer.
Not be a factor of Φ(n).
1 < e < Φ(n) [Φ(n) is discussed below],
Let us now consider it to be equal to 3.
Our Public Key is made of n and e
>> Generating Private Key:

We need to calculate Φ(n) :


Such that Φ(n) = (P-1)(Q-1)
so, Φ(n) = 3016
Now calculate Private Key, d :
d = (k*Φ(n) + 1) / e for some integer k
For k = 2, value of d is 2011.
Now we are ready with our – Public Key ( n = 3127 and e = 3) and Private Key(d = 2011) Now
we will encrypt “HI”:

Convert letters to numbers : H = 8 and I = 9


Thus Encrypted Data c = (89e)mod n
Thus our Encrypted Data comes out to be 1394

Now we will decrypt 1394 :


Decrypted Data = (cd)mod n
Thus our Encrypted Data comes out to be 89
8 = H and I = 9 i.e. "HI".
Below is the implementation of the RSA algorithm for

Method 1: Encrypting and decrypting small numeral values:


# Python for RSA asymmetric cryptographic algorithm.
# For demonstration, values are
# relatively small compared to practical application
import math

def gcd(a, h):


temp = 0
while(1):
temp = a % h
if (temp == 0):
return h
a=h
h = temp

p=3
q=7
n = p*q
e=2
phi = (p-1)*(q-1)

while (e < phi):

# e must be co-prime to phi and


# smaller than phi.
if(gcd(e, phi) == 1):
break
else:
e = e+1

# Private key (d stands for decrypt)


# choosing d such that it satisfies
# d*e = 1 + k * totient

k=2
d = (1 + (k*phi))/e

# Message to be encrypted
msg = 12.0

print("Message data = ", msg)

# Encryption c = (msg ^ e) % n
c = pow(msg, e)
c = math.fmod(c, n)
print("Encrypted data = ", c)

# Decryption m = (c ^ d) % n
m = pow(c, d)
m = math.fmod(m, n)
print("Original Message Sent = ", m)

# This code is contributed by Pranay Arora.


Output
Message data = 12.000000
Encrypted data = 3.000000
Original Message Sent = 12.000000
Method 2: Encrypting and decrypting plain text messages containing alphabets and numbers
using their ASCII value:

import random
import math

# A set will be the collection of prime numbers,


# where we can select random primes p and q
prime = set()

public_key = None
private_key = None
n = None

# We will run the function only once to fill the set of


# prime numbers
def primefiller():
# Method used to fill the primes set is Sieve of
# Eratosthenes (a method to collect prime numbers)
seive = [True] * 250
seive[0] = False
seive[1] = False
for i in range(2, 250):
for j in range(i * 2, 250, i):
seive[j] = False

# Filling the prime numbers


for i in range(len(seive)):
if seive[i]:
prime.add(i)

# Picking a random prime number and erasing that prime


# number from list because p!=q
def pickrandomprime():
global prime
k = random.randint(0, len(prime) - 1)
it = iter(prime)
for _ in range(k):
next(it)

ret = next(it)
prime.remove(ret)
return ret

def setkeys():
global public_key, private_key, n
prime1 = pickrandomprime() # First prime number
prime2 = pickrandomprime() # Second prime number

n = prime1 * prime2
fi = (prime1 - 1) * (prime2 - 1)

e=2
while True:
if math.gcd(e, fi) == 1:
break
e += 1

# d = (k*Φ(n) + 1) / e for some integer k


public_key = e

d=2
while True:
if (d * e) % fi == 1:
break
d += 1

private_key = d

# To encrypt the given number


def encrypt(message):
global public_key, n
e = public_key
encrypted_text = 1
while e > 0:
encrypted_text *= message
encrypted_text %= n
e -= 1
return encrypted_text

# To decrypt the given number


def decrypt(encrypted_text):
global private_key, n
d = private_key
decrypted = 1
while d > 0:
decrypted *= encrypted_text
decrypted %= n
d -= 1
return decrypted

# First converting each character to its ASCII value and


# then encoding it then decoding the number to get the
# ASCII and converting it to character
def encoder(message):
encoded = []
# Calling the encrypting function in encoding function
for letter in message:
encoded.append(encrypt(ord(letter)))
return encoded

def decoder(encoded):
s = ''
# Calling the decrypting function decoding function
for num in encoded:
s += chr(decrypt(num))
return s

if __name__ == '__main__':
primefiller()
setkeys()
message = "Test Message"
# Uncomment below for manual input
# message = input("Enter the message\n")
# Calling the encoding function
coded = encoder(message)

print("Initial message:")
print(message)
print("\n\nThe encoded message(encrypted by public key)\n")
print(''.join(str(p) for p in coded))
print("\n\nThe decoded message(decrypted by public key)\n")
print(''.join(str(p) for p in decoder(coded)))

Output
Initial message:
Test Message

The encoded message(encrypted by public key)


863312887135951593413927434912887135951359583051879012887

The decoded message(decrypted by private key)


Test Message

5. DES Algorithm

Data encryption standard (DES) has been found vulnerable to very powerful attacks
andhttps://media.geeksforgeeks.org/wp-content/uploads/20200306122641/DES-11.png
therefore, the popularity of DES has been found slightly on the decline. DES is a block cipher
and encrypts data in blocks of size of 64 bits each, which means 64 bits of plain text go as
the input to DES, which produces 64 bits of ciphertext. The same algorithm and key are used
for encryption and decryption, with minor differences. The key length is 56 bits.

The basic idea is shown in the figure:

We have mentioned that DES uses a 56-bit key. Actually, The initial key consists of 64 bits.
However, before the DES process even starts, every 8th bit of the key is discarded to
produce a 56-bit key. That is bit positions 8, 16, 24, 32, 40, 48, 56, and 64 are discarded.

Thus, the discarding of every 8th bit of the key produces a 56-bit key from the original 64-bit
key.
DES is based on the two fundamental attributes of cryptography: substitution (also called
confusion) and transposition (also called diffusion). DES consists of 16 steps, each of which is
called a round. Each round performs the steps of substitution and transposition. Let us now
discuss the broad-level steps in DES.

In the first step, the 64-bit plain text block is handed over to an initial Permutation (IP)
function.
The initial permutation is performed on plain text.
Next, the initial permutation (IP) produces two halves of the permuted block; saying Left
Plain Text (LPT) and Right Plain Text (RPT).
Now each LPT and RPT go through 16 rounds of the encryption process.
In the end, LPT and RPT are rejoined and a Final Permutation (FP) is performed on the
combined block
The result of this process produces 64-bit ciphertext.
Initial Permutation (IP):
As we have noted, the initial permutation (IP) happens only once and it happens before the
first round. It suggests how the transposition in IP should proceed, as shown in the figure.
For example, it says that the IP replaces the first bit of the original plain text block with the
58th bit of the original plain text, the second bit with the 50th bit of the original plain text
block, and so on.

This is nothing but jugglery of bit positions of the original plain text block. the same rule
applies to all the other bit positions shown in the figure.

As we have noted after IP is done, the resulting 64-bit permuted text block is divided into
two half blocks. Each half-block consists of 32 bits, and each of the 16 rounds, in turn,
consists of the broad-level steps outlined in the figure.
Step-1: Key transformation:
We have noted initial 64-bit key is transformed into a 56-bit key by discarding every 8th bit
of the initial key. Thus, for each a 56-bit key is available. From this 56-bit key, a different 48-
bit Sub Key is generated during each round using a process called key transformation. For
this, the 56-bit key is divided into two halves, each of 28 bits. These halves are circularly
shifted left by one or two positions, depending on the round.

For example: if the round numbers 1, 2, 9, or 16 the shift is done by only one position for
other rounds, the circular shift is done by two positions. The number of key bits shifted per
round is shown in the figure.

After an appropriate shift, 48 of the 56 bits are selected.From the 48 we might obtain 64 or
56 bits based on requirement which helps us to recognize that this model is very versatile
and can handle any range of requirements needed or provided. for selecting 48 of the 56
bits the table is shown in the figure given below. For instance, after the shift, bit number 14
moves to the first position, bit number 17 moves to the second position, and so on. If we
observe the table , we will realize that it contains only 48-bit positions. Bit number 18 is
discarded (we will not find it in the table), like 7 others, to reduce a 56-bit key to a 48-bit
key. Since the key transformation process involves permutation as well as a selection of a
48-bit subset of the original 56-bit key it is called Compression Permutation.

Because of this compression permutation technique, a different subset of key bits is used in
each round. That makes DES not easy to crack.

Step-2: Expansion Permutation:


Recall that after the initial permutation, we had two 32-bit plain text areas called Left Plain
Text(LPT) and Right Plain Text(RPT). During the expansion permutation, the RPT is expanded
from 32 bits to 48 bits. Bits are permuted as well hence called expansion permutation. This
happens as the 32-bit RPT is divided into 8 blocks, with each block consisting of 4 bits. Then,
each 4-bit block of the previous step is then expanded to a corresponding 6-bit block, i.e.,
per 4-bit block, 2 more bits are added.
his process results in expansion as well as a permutation of the input bit while creating
output. The key transformation process compresses the 56-bit key to 48 bits. Then the
expansion permutation process expands the 32-bit RPT to 48-bits. Now the 48-bit key is XOR
with 48-bit RPT and the resulting output is given to the next step, which is the S-Box
substitution.

# Python3 code for the above approach

# Hexadecimal to binary conversion

def hex2bin(s):
mp = {'0': "0000",
'1': "0001",
'2': "0010",
'3': "0011",
'4': "0100",
'5': "0101",
'6': "0110",
'7': "0111",
'8': "1000",
'9': "1001",
'A': "1010",
'B': "1011",
'C': "1100",
'D': "1101",
'E': "1110",
'F': "1111"}
bin = ""
for i in range(len(s)):
bin = bin + mp[s[i]]
return bin

# Binary to hexadecimal conversion

def bin2hex(s):
mp = {"0000": '0',
"0001": '1',
"0010": '2',
"0011": '3',
"0100": '4',
"0101": '5',
"0110": '6',
"0111": '7',
"1000": '8',
"1001": '9',
"1010": 'A',
"1011": 'B',
"1100": 'C',
"1101": 'D',
"1110": 'E',
"1111": 'F'}
hex = ""
for i in range(0, len(s), 4):
ch = ""
ch = ch + s[i]
ch = ch + s[i + 1]
ch = ch + s[i + 2]
ch = ch + s[i + 3]
hex = hex + mp[ch]

return hex

# Binary to decimal conversion

def bin2dec(binary):

binary1 = binary
decimal, i, n = 0, 0, 0
while(binary != 0):
dec = binary % 10
decimal = decimal + dec * pow(2, i)
binary = binary//10
i += 1
return decimal

# Decimal to binary conversion

def dec2bin(num):
res = bin(num).replace("0b", "")
if(len(res) % 4 != 0):
div = len(res) / 4
div = int(div)
counter = (4 * (div + 1)) - len(res)
for i in range(0, counter):
res = '0' + res
return res

# Permute function to rearrange the bits

def permute(k, arr, n):


permutation = ""
for i in range(0, n):
permutation = permutation + k[arr[i] - 1]
return permutation

# shifting the bits towards left by nth shifts

def shift_left(k, nth_shifts):


s = ""
for i in range(nth_shifts):
for j in range(1, len(k)):
s = s + k[j]
s = s + k[0]
k=s
s = ""
return k

# calculating xow of two strings of binary number a and b

def xor(a, b):


ans = ""
for i in range(len(a)):
if a[i] == b[i]:
ans = ans + "0"
else:
ans = ans + "1"
return ans

# Table of Position of 64 bits at initial level: Initial Permutation Table


initial_perm = [58, 50, 42, 34, 26, 18, 10, 2,
60, 52, 44, 36, 28, 20, 12, 4,
62, 54, 46, 38, 30, 22, 14, 6,
64, 56, 48, 40, 32, 24, 16, 8,
57, 49, 41, 33, 25, 17, 9, 1,
59, 51, 43, 35, 27, 19, 11, 3,
61, 53, 45, 37, 29, 21, 13, 5,
63, 55, 47, 39, 31, 23, 15, 7]
# Expansion D-box Table
exp_d = [32, 1, 2, 3, 4, 5, 4, 5,
6, 7, 8, 9, 8, 9, 10, 11,
12, 13, 12, 13, 14, 15, 16, 17,
16, 17, 18, 19, 20, 21, 20, 21,
22, 23, 24, 25, 24, 25, 26, 27,
28, 29, 28, 29, 30, 31, 32, 1]

# Straight Permutation Table


per = [16, 7, 20, 21,
29, 12, 28, 17,
1, 15, 23, 26,
5, 18, 31, 10,
2, 8, 24, 14,
32, 27, 3, 9,
19, 13, 30, 6,
22, 11, 4, 25]

# S-box Table
sbox = [[[14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7],
[0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8],
[4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0],
[15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13]],

[[15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10],


[3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5],
[0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15],
[13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9]],

[[10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8],


[13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1],
[13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7],
[1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12]],

[[7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15],


[13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9],
[10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4],
[3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14]],

[[2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9],


[14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6],
[4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14],
[11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3]],

[[12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11],


[10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8],
[9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6],
[4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13]],
[[4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1],
[13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6],
[1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2],
[6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12]],

[[13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7],


[1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2],
[7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8],
[2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11]]]

# Final Permutation Table


final_perm = [40, 8, 48, 16, 56, 24, 64, 32,
39, 7, 47, 15, 55, 23, 63, 31,
38, 6, 46, 14, 54, 22, 62, 30,
37, 5, 45, 13, 53, 21, 61, 29,
36, 4, 44, 12, 52, 20, 60, 28,
35, 3, 43, 11, 51, 19, 59, 27,
34, 2, 42, 10, 50, 18, 58, 26,
33, 1, 41, 9, 49, 17, 57, 25]

def encrypt(pt, rkb, rk):


pt = hex2bin(pt)

# Initial Permutation
pt = permute(pt, initial_perm, 64)
print("After initial permutation", bin2hex(pt))

# Splitting
left = pt[0:32]
right = pt[32:64]
for i in range(0, 16):
# Expansion D-box: Expanding the 32 bits data into 48 bits
right_expanded = permute(right, exp_d, 48)

# XOR RoundKey[i] and right_expanded


xor_x = xor(right_expanded, rkb[i])

# S-boxex: substituting the value from s-box table by calculating row and column
sbox_str = ""
for j in range(0, 8):
row = bin2dec(int(xor_x[j * 6] + xor_x[j * 6 + 5]))
col = bin2dec(
int(xor_x[j * 6 + 1] + xor_x[j * 6 + 2] + xor_x[j * 6 + 3] + xor_x[j * 6 + 4]))
val = sbox[j][row][col]
sbox_str = sbox_str + dec2bin(val)

# Straight D-box: After substituting rearranging the bits


sbox_str = permute(sbox_str, per, 32)
# XOR left and sbox_str
result = xor(left, sbox_str)
left = result

# Swapper
if(i != 15):
left, right = right, left
print("Round ", i + 1, " ", bin2hex(left),
" ", bin2hex(right), " ", rk[i])

# Combination
combine = left + right

# Final permutation: final rearranging of bits to get cipher text


cipher_text = permute(combine, final_perm, 64)
return cipher_text

pt = "123456ABCD132536"
key = "AABB09182736CCDD"

# Key generation
# --hex to binary
key = hex2bin(key)

# --parity bit drop table


keyp = [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]

# getting 56 bit key from 64 bit using the parity bits


key = permute(key, keyp, 56)

# Number of bit shifts


shift_table = [1, 1, 2, 2,
2, 2, 2, 2,
1, 2, 2, 2,
2, 2, 2, 1]

# Key- Compression Table : Compression of key from 56 bits to 48 bits


key_comp = [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]

# Splitting
left = key[0:28] # rkb for RoundKeys in binary
right = key[28:56] # rk for RoundKeys in hexadecimal

rkb = []
rk = []
for i in range(0, 16):
# Shifting the bits by nth shifts by checking from shift table
left = shift_left(left, shift_table[i])
right = shift_left(right, shift_table[i])

# Combination of left and right string


combine_str = left + right

# Compression of key from 56 to 48 bits


round_key = permute(combine_str, key_comp, 48)

rkb.append(round_key)
rk.append(bin2hex(round_key))

print("Encryption")
cipher_text = bin2hex(encrypt(pt, rkb, rk))
print("Cipher Text : ", cipher_text)

print("Decryption")
rkb_rev = rkb[::-1]
rk_rev = rk[::-1]
text = bin2hex(encrypt(cipher_text, rkb_rev, rk_rev))
print("Plain Text : ", text)

# This code is contributed by Aditya Jain


Output
...60AF7CA5
Round 12 FF3C485F 22A5963B C2C1E96A4BF3
Round 13 22A5963B 387CCDAA 99C31397C91F
Round 14 387CCDAA BD2DD2AB 251B8BC717D0
Round 15 BD2DD2AB CF26B472 3330C5D9A36D
Round 16 19BA9212 CF26B472 181C5D75C66D

Cipher Text: C0B7A8D05F3A829C

Decryption
After initial permutation: 19BA9212CF26B472
After splitting: L0=19BA9212 R0=CF26B472

Round 1 CF26B472 BD2DD2AB 181C5D75C66D


Round 2 BD2DD2AB 387CCDAA 3330C5D9A36D
Round 3 387CCDAA 22A5963B 251B8BC717D0
Round 4 22A5963B FF3C485F 99C31397C91F
Round 5 FF3C485F 6CA6CB20 C2C1E96A4BF3
Round 6 6CA6CB20 10AF9D37 6D5560AF7CA5
Round 7 10AF9D37 308BEE97 02765708B5BF
Round 8 308BEE97 A9FC20A3 84BB4473DCCC
Round 9 A9FC20A3 2E8F9C65 34F822F0C66D
Round 10 2E8F9C65 A15A4B87 708AD2DDB3C0
Round 11 A15A4B87 236779C2 C1948E87475E
Round 12 236779C2 B8089591 69A629FEC913
Round 13 B8089591 4A1210F6 DA2D032B6EE3
Round 14 4A1210F6 5A78E394 06EDA4ACF5B5
Round 15 5A78E394 18CA18AD 4568581ABCCE
Round 16 14A7D678 18CA18AD 194CD072DE8C

Plain Text: 123456ABCD132536


Output:

Encryption:

After initial permutation: 14A7D67818CA18AD


After splitting: L0=14A7D678 R0=18CA18AD

Round 1 18CA18AD 5A78E394 194CD072DE8C


Round 2 5A78E394 4A1210F6 4568581ABCCE
Round 3 4A1210F6 B8089591 06EDA4ACF5B5
Round 4 B8089591 236779C2 DA2D032B6EE3
Round 5 236779C2 A15A4B87 69A629FEC913
Round 6 A15A4B87 2E8F9C65 C1948E87475E
Round 7 2E8F9C65 A9FC20A3 708AD2DDB3C0
Round 8 A9FC20A3 308BEE97 34F822F0C66D
Round 9 308BEE97 10AF9D37 84BB4473DCCC
Round 10 10AF9D37 6CA6CB20 02765708B5BF
Round 11 6CA6CB20 FF3C485F 6D5560AF7CA5
Round 12 FF3C485F 22A5963B C2C1E96A4BF3
Round 13 22A5963B 387CCDAA 99C31397C91F
Round 14 387CCDAA BD2DD2AB 251B8BC717D0
Round 15 BD2DD2AB CF26B472 3330C5D9A36D
Round 16 19BA9212 CF26B472 181C5D75C66D

Cipher Text: C0B7A8D05F3A829C

Decryption
After initial permutation: 19BA9212CF26B472
After splitting: L0=19BA9212 R0=CF26B472

Round 1 CF26B472 BD2DD2AB 181C5D75C66D


Round 2 BD2DD2AB 387CCDAA 3330C5D9A36D
Round 3 387CCDAA 22A5963B 251B8BC717D0
Round 4 22A5963B FF3C485F 99C31397C91F
Round 5 FF3C485F 6CA6CB20 C2C1E96A4BF3
Round 6 6CA6CB20 10AF9D37 6D5560AF7CA5
Round 7 10AF9D37 308BEE97 02765708B5BF
Round 8 308BEE97 A9FC20A3 84BB4473DCCC
Round 9 A9FC20A3 2E8F9C65 34F822F0C66D
Round 10 2E8F9C65 A15A4B87 708AD2DDB3C0
Round 11 A15A4B87 236779C2 C1948E87475E
Round 12 236779C2 B8089591 69A629FEC913
Round 13 B8089591 4A1210F6 DA2D032B6EE3
Round 14 4A1210F6 5A78E394 06EDA4ACF5B5
Round 15 5A78E394 18CA18AD 4568581ABCCE
Round 16 14A7D678 18CA18AD 194CD072DE8C

Plain Text: 123456ABCD132536

6. Digital Signature

Digital Signatures are an Asymmetrically encrypted hash of a digital message(data). It is a


value that can provide a guarantee of authenticity, non-repudiation, and integrity. In other
terms, it means you can verify the sender, date & time and message content have not been
revealed or compromised.

Note: You can refer this link for better understanding of cryptographic terms.

Calculation of Digital Signature

Digital Signatures are often calculated using elliptical curve cryptography, especially in IoT
devices, but we will be using RSA for demonstration purposes. First, we will take the input
message and create a hash of it using SHA-256 because of its speed and security, and we will
then encrypt that hash with the private key from Asymmetric key pair. On the other side, the
receiver will decrypt it using the public key and compare the hash to ensure they are indeed
the same.
Digital Signature Flow

Let “A” and “B” be the fictional actors in the cryptography system for better understanding.
“A” is the sender and calculates the hash of the message and attaches signature which he
wants to send using his private key.
The other side “B” hashes the message and then decrypts the signature with A’s public key
and compares the two hashes
If “B” finds the hashes matching then the message has not been altered or compromised.
Implementing Digital Signatures
Let us implement the digital signature using algorithms SHA and RSA and also verify if the
hash matches with a public key.

Approach:

Create a method named Create_Digital_Signature() to implement Digital Signature by


passing two parameters input message and the private key. In this method we will get an
instance of the signature object passing the signing algorithm and assign it with a private key
and finally pass the input this will return byte array.
public static byte[] Create_Digital_Signature(byte[] input, PrivateKey privateKey);
signature.initSign(privateKey);
signature.update(input);
The next step is to generate asymmetric key pair using RSA algorithm and SecureRandom
class functions.
SecureRandom secureRandom =new SecureRandom();
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);
Finally verifying the signature using public key. Verify_Digital_Signature() method is used to
check whether the signature matches by passing it the input, signature, and public key.
Signature signature = Signature.getInstance(SIGNING_ALGORITHM);
signature.initVerify(publickey);
signature.update(input);
Example:

Input:msg = “GEEKSFORGEEEKS IS A COMPUTER SCIENCE PORTAL”


Output:
Signature Value:
80429D3FA203437B4098CAF774D96C827B6CC2489F437A82926DA2EFCE64EF68FB33235B9
F6BA8E3B033235B9F6BA8
Verification: true

Below is the implementation:

// Java implementation for Generating


// and verifying the digital signature

package java_cryptography;

// Imports
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.util.Scanner;

import javax.xml.bind.DatatypeConverter;

public class Digital_Signature_GeeksforGeeks {

// Signing Algorithm
private static final String
SIGNING_ALGORITHM
= "SHA256withRSA";
private static final String RSA = "RSA";
private static Scanner sc;

// Function to implement Digital signature


// using SHA256 and RSA algorithm
// by passing private key.
public static byte[] Create_Digital_Signature(
byte[] input,
PrivateKey Key)
throws Exception
{
Signature signature
= Signature.getInstance(
SIGNING_ALGORITHM);
signature.initSign(Key);
signature.update(input);
return signature.sign();
}

// Generating the asymmetric key pair


// using SecureRandom class
// functions and RSA algorithm.
public static KeyPair Generate_RSA_KeyPair()
throws Exception
{
SecureRandom secureRandom
= new SecureRandom();
KeyPairGenerator keyPairGenerator
= KeyPairGenerator
.getInstance(RSA);
keyPairGenerator
.initialize(
2048, secureRandom);
return keyPairGenerator
.generateKeyPair();
}

// Function for Verification of the


// digital signature by using the public key
public static boolean
Verify_Digital_Signature(
byte[] input,
byte[] signatureToVerify,
PublicKey key)
throws Exception
{
Signature signature
= Signature.getInstance(
SIGNING_ALGORITHM);
signature.initVerify(key);
signature.update(input);
return signature
.verify(signatureToVerify);
}

// Driver Code
public static void main(String args[])
throws Exception
{

String input
= "GEEKSFORGEEKS IS A"
+ " COMPUTER SCIENCE PORTAL";
KeyPair keyPair
= Generate_RSA_KeyPair();

// Function Call
byte[] signature
= Create_Digital_Signature(
input.getBytes(),
keyPair.getPrivate());

System.out.println(
"Signature Value:\n "
+ DatatypeConverter
.printHexBinary(signature));

System.out.println(
"Verification: "
+ Verify_Digital_Signature(
input.getBytes(),
signature, keyPair.getPublic()));
}
}
Output:

7. write a program to implement diffie hellman key exchange algorithm.

Elliptic Curve Cryptography (ECC) is an approach to public-key cryptography, based on the


algebraic structure of elliptic curves over finite fields. ECC requires a smaller key as
compared to non-ECC cryptography to provide equivalent security (a 256-bit ECC security
has equivalent security attained by 3072-bit RSA cryptography).

For a better understanding of Elliptic Curve Cryptography, it is very important to understand


the basics of the Elliptic Curve. An elliptic curve is a planar algebraic curve defined by an
equation of the form
y2 = x3 + ax + b

Where ‘a’ is the co-efficient of x and ‘b’ is the constant of the equation
The curve is non-singular; that is, its graph has no cusps or self-intersections (when the
characteristic of the Coefficient field is equal to 2 or 3).

In general, an elliptic curve looks like as shown below. Elliptic curves can intersect almost 3
points when a straight line is drawn intersecting the curve. As we can see, the elliptic curve is
symmetric about the x-axis. This property plays a key role in the algorithm.
Diffie-Hellman algorithm:
The Diffie-Hellman algorithm is being used to establish a shared secret that can be used for
secret communications while exchanging data over a public network using the elliptic curve
to generate points and get the secret key using the parameters.

For the sake of simplicity and practical implementation of the algorithm, we will consider
only 4 variables, one prime P and G (a primitive root of P) and two private values a and b.
P and G are both publicly available numbers. Users (say Alice and Bob) pick private values a
and b and they generate a key and exchange it publicly. The opposite person receives the
key and that generates a secret key, after which they have the same secret key to encrypt.
Step-by-Step explanation is as follows:

Alice Bob
Public Keys available = P, G Public Keys available = P, G
Private Key Selected = a Private Key Selected = b
Key generated =

x = G^a mod P

Key generated =

y = G^b mod P

Exchange of generated keys takes place


Key received = y key received = x
Generated Secret Key =

k_a = y^a mod P

Generated Secret Key =

k_b = x^b mod P

Algebraically, it can be shown that

k_a = k_b

Users now have a symmetric secret key to encrypt


Example:

Step 1: Alice and Bob get public numbers P = 23, G = 9

Step 2: Alice selected a private key a = 4 and


Bob selected a private key b = 3

Step 3: Alice and Bob compute public values


Alice: x =(9^4 mod 23) = (6561 mod 23) = 6
Bob: y = (9^3 mod 23) = (729 mod 23) = 16
Step 4: Alice and Bob exchange public numbers

Step 5: Alice receives public key y =16 and


Bob receives public key x = 6

Step 6: Alice and Bob compute symmetric keys


Alice: ka = y^a mod p = 65536 mod 23 = 9
Bob: kb = x^b mod p = 216 mod 23 = 9

Step 7: 9 is the shared secret.


Implementation:
# Diffie-Hellman Code

def prime_checker(p):
# Checks If the number entered is a Prime Number or not
if p < 1:
return -1
elif p > 1:
if p == 2:
return 1
for i in range(2, p):
if p % i == 0:
return -1
return 1

def primitive_check(g, p, L):


# Checks If The Entered Number Is A Primitive Root Or Not
for i in range(1, p):
L.append(pow(g, i) % p)
for i in range(1, p):
if L.count(i) > 1:
L.clear()
return -1
return 1

l = []
while 1:
P = int(input("Enter P : "))
if prime_checker(P) == -1:
print("Number Is Not Prime, Please Enter Again!")
continue
break

while 1:
G = int(input(f"Enter The Primitive Root Of {P} : "))
if primitive_check(G, P, l) == -1:
print(f"Number Is Not A Primitive Root Of {P}, Please Try Again!")
continue
break

# Private Keys
x1, x2 = int(input("Enter The Private Key Of User 1 : ")), int(
input("Enter The Private Key Of User 2 : "))
while 1:
if x1 >= P or x2 >= P:
print(f"Private Key Of Both The Users Should Be Less Than {P}!")
continue
break

# Calculate Public Keys


y1, y2 = pow(G, x1) % P, pow(G, x2) % P

# Generate Secret Keys


k1, k2 = pow(y2, x1) % P, pow(y1, x2) % P

print(f"\nSecret Key For User 1 Is {k1}\nSecret Key For User 2 Is {k2}\n")

if k1 == k2:
print("Keys Have Been Exchanged Successfully")
else:
print("Keys Have Not Been Exchanged Successfully")
Output:

The value of P : 23
The value of G : 9

The private key a for Alice : 4


The private key b for Bob : 3

Secret key for the Alice is : 9


Secret Key for the Bob is : 9

8. write a program to implement Chinese remainder theorem

We are given two arrays num[0..k-1] and rem[0..k-1]. In num[0..k-1], every pair is coprime
(gcd for every pair is 1). We need to find minimum positive number x such that:

x % num[0] = rem[0],
x % num[1] = rem[1],
.......................
x % num[k-1] = rem[k-1]
Basically, we are given k numbers which are pairwise coprime, and given remainders of
these numbers when an unknown number x is divided by them. We need to find the
minimum possible value of x that produces given remainders.
Examples :
Input: num[] = {5, 7}, rem[] = {1, 3}
Output: 31
Explanation:
31 is the smallest number such that:
(1) When we divide it by 5, we get remainder 1.
(2) When we divide it by 7, we get remainder 3.

Input: num[] = {3, 4, 5}, rem[] = {2, 3, 1}


Output: 11
Explanation:
11 is the smallest number such that:
(1) When we divide it by 3, we get remainder 2.
(2) When we divide it by 4, we get remainder 3.
(3) When we divide it by 5, we get remainder 1.
Chinese Remainder Theorem states that there always exists an x that satisfies given
congruences. Below is theorem statement adapted from wikipedia.
Let num[0], num[1], …num[k-1] be positive integers that are pairwise coprime. Then, for any
given sequence of integers rem[0], rem[1], … rem[k-1], there exists an integer x solving the
following system of simultaneous congruences.

The first part is clear that there exists an x. The second part basically states that all solutions
(including the minimum one) produce the same remainder when divided by-product of
num[0], num[1], .. num[k-1]. In the above example, the product is 3*4*5 = 60. And 11 is one
solution, other solutions are 71, 131, .. etc. All these solutions produce the same remainder
when divided by 60, i.e., they are of form 11 + m*60 where m >= 0.
A Naive Approach to find x is to start with 1 and one by one increment it and check if
dividing it with given elements in num[] produces corresponding remainders in rem[]. Once
we find such an x, we return it.
Below is the implementation of Naive Approach.

# A Python3 program to demonstrate


# working of Chinise remainder Theorem

# k is size of num[] and rem[].


# Returns the smallest number x
# such that:
# x % num[0] = rem[0],
# x % num[1] = rem[1],
# ..................
# x % num[k-2] = rem[k-1]
# Assumption: Numbers in num[]
# are pairwise coprime (gcd for
# every pair is 1)
def findMinX(num, rem, k):
x = 1; # Initialize result

# As per the Chinise remainder


# theorem, this loop will
# always break.
while(True):

# Check if remainder of
# x % num[j] is rem[j]
# or not (for all j from
# 0 to k-1)
j = 0;
while(j < k):
if (x % num[j] != rem[j]):
break;
j += 1;

# If all remainders
# matched, we found x
if (j == k):
return x;

# Else try next number


x += 1;

# Driver Code
num = [3, 4, 5];
rem = [2, 3, 1];
k = len(num);
print("x is", findMinX(num, rem, k));

# This code is contributed by mits


Output :

x is 11
Time Complexity : O(M), M is the product of all elements of num[] array.

Auxiliary Space : O(1)

You might also like