diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dd2c177 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +__pycache__/ +raw_data/ +restored_file/ +encrypted/ +files/ +key/ +uploads/ \ No newline at end of file diff --git a/README.md b/README.md index 6cda0b8..ebb86cf 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ # Methodology To achieve the above goal, the following methodology needs to be followed:
+ 1. Load the file on the server.
2. Dividing the uploaded file into N parts.
3. Encrypting all the parts of the file using any one of the selected algorithms (Algorithm is changed with every part in round robin fashion).
@@ -13,6 +14,7 @@ To achieve the above goal, the following methodology needs to be followed:
After the above 4 steps you will have a N files which are in encrypted form which are stored on the server and a key which is downloaded as public key for decrypting the file and downloading it.
To restore the file, follow the following steps:
+ 1. Load the key on the server.
2. Decrypt the keys of the algorithms.
3. Decrypt all the N parts of the file using the same algorithms which were used to encrypt them.
@@ -20,20 +22,20 @@ To restore the file, follow the following steps:
# How to Run -**NOTE:** The project is based on Python 2.7.15 plateform running it on any other plateform might create some issues.
+**NOTE:** The project is based on Python 3.8.10 platform running it on any other platform might create some issues.
Step 1: Install Requirements
`pip install -r requirements.txt`
Step 2: Run the application
-`python app.py`
+`python3 app.py`
Step 3: Visit the localhost from your browser
Step 4: Enjoy :) - -[//]: <> (*IF YOU ENCOUNTER ANY BUGS OR FOR ANY SUGGESTIONS REGARDING THE IMPROVEMENT OF THE PROJECT FEEL FREE TO CONTACT ME :**) + +[//]: <> (\*IF YOU ENCOUNTER ANY BUGS OR FOR ANY SUGGESTIONS REGARDING THE IMPROVEMENT OF THE PROJECT FEEL FREE TO CONTACT ME :\*\*) **THE PROJECT HAS ENCOUNTERED A BUG BECAUSE OF THE CRYPTOGRAPHY LIBRARY BEING UPDATED. IF YOU ARE INTRESTED IN COLLABORATING TO IMPROVE THIS PROJECT FEEL FREE TO CONTACT ME :** -Shivang Srivastava - shivang.8@geu.ac.in
+Shivang Srivastava - shivang.8@geu.ac.in
diff --git a/app.py b/app.py index bcdf1c4..7c22b37 100644 --- a/app.py +++ b/app.py @@ -1,5 +1,5 @@ import os -from flask import Flask, request, redirect, url_for, render_template, send_from_directory, send_file +from flask import Flask, request, redirect, url_for, render_template, send_from_directory, send_file, flash from werkzeug.utils import secure_filename import tools import divider as dv @@ -17,95 +17,109 @@ #port = int(os.getenv('PORT', 8000)) + def allowed_file(filename): - return '.' in filename and \ - filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS + return '.' in filename and \ + filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS + def start_encryption(): - dv.divide() - tools.empty_folder('uploads') - enc.encrypter() - return render_template('success.html') + dv.divide() + tools.empty_folder('uploads') + enc.encrypter() + return render_template('success.html') + def start_decryption(): - dec.decrypter() - tools.empty_folder('key') - rst.restore() - return render_template('restore_success.html') + dec.decrypter() + tools.empty_folder('key') + rst.restore() + return render_template('restore_success.html') + -@app.route('/return-key/My_Key.pem') +@app.route('/return-key') def return_key(): - list_directory = tools.list_dir('key') - filename = './key/' + list_directory[0] - return send_file(filename, attachment_filename='My_Key.pem') + print("reached") + list_directory = tools.list_dir('key') + filename = './key/' + list_directory[0] + print(filename) + return send_file(filename, attachment_filename="My_Key.pem", as_attachment=True, cache_timeout=0) + @app.route('/return-file/') def return_file(): - list_directory = tools.list_dir('restored_file') - filename = './restored_file/' + list_directory[0] - print "****************************************" - print list_directory[0] - print "****************************************" - return send_file(filename, attachment_filename=list_directory[0], as_attachment=True) + list_directory = tools.list_dir('restored_file') + filename = './restored_file/' + list_directory[0] + print("****************************************") + print(list_directory[0]) + print("****************************************") + return send_file(filename, attachment_filename=list_directory[0], as_attachment=True, cache_timeout=0) + @app.route('/download/') def downloads(): - return render_template('download.html') + return render_template('download.html') + @app.route('/upload') def call_page_upload(): - return render_template('upload.html') + return render_template('upload.html') + @app.route('/home') def back_home(): - tools.empty_folder('key') - tools.empty_folder('restored_file') - return render_template('index.html') + tools.empty_folder('key') + tools.empty_folder('restored_file') + return render_template('index.html') + @app.route('/') def index(): - return render_template('index.html') + return render_template('index.html') + @app.route('/data', methods=['GET', 'POST']) def upload_file(): - tools.empty_folder('uploads') - if request.method == 'POST': - # check if the post request has the file part - if 'file' not in request.files: - flash('No file part') - return redirect(request.url) - file = request.files['file'] - # if user does not select file, browser also - # submit a empty part without filename - if file.filename == '': - flash('No selected file') - return 'NO FILE SELECTED' - if file: - filename = secure_filename(file.filename) - file.save(os.path.join(app.config['UPLOAD_FOLDER'], file.filename)) - return start_encryption() - return 'Invalid File Format !' - + tools.empty_folder('uploads') + if request.method == 'POST': + # check if the post request has the file part + if 'file' not in request.files: + flash('No file part') + return redirect(request.url) + file = request.files['file'] + # if user does not select file, browser also + # submit a empty part without filename + if file.filename == '': + flash('No selected file') + return 'NO FILE SELECTED' + if file: + filename = secure_filename(file.filename) + file.save(os.path.join(app.config['UPLOAD_FOLDER'], file.filename)) + return start_encryption() + return 'Invalid File Format !' + + @app.route('/download_data', methods=['GET', 'POST']) def upload_key(): - tools.empty_folder('key') - if request.method == 'POST': - # check if the post request has the file part - if 'file' not in request.files: - flash('No file part') - return redirect(request.url) - file = request.files['file'] - # if user does not select file, browser also - # submit a empty part without filename - if file.filename == '': - flash('No selected file') - return 'NO FILE SELECTED' - if file and allowed_file(file.filename): - filename = secure_filename(file.filename) - file.save(os.path.join(app.config['UPLOAD_KEY'], file.filename)) - return start_decryption() - return 'Invalid File Format !' + tools.empty_folder('key') + if request.method == 'POST': + # check if the post request has the file part + if 'file' not in request.files: + flash('No file part') + return redirect(request.url) + file = request.files['file'] + # if user does not select file, browser also + # submit a empty part without filename + if file.filename == '': + flash('No selected file') + return 'NO FILE SELECTED' + if file and allowed_file(file.filename): + filename = secure_filename(file.filename) + file.save(os.path.join(app.config['UPLOAD_KEY'], file.filename)) + return start_decryption() + return 'Invalid File Format !' + if __name__ == '__main__': - app.run(host='127.0.0.1', port=8000, debug=True) - #app.run() \ No newline at end of file + app.run(host='127.0.0.1', port=8000, debug=True) + # app.run() diff --git a/decrypter.py b/decrypter.py index 822f0e4..adee1b8 100644 --- a/decrypter.py +++ b/decrypter.py @@ -4,100 +4,106 @@ from cryptography.hazmat.primitives.ciphers.aead import AESGCM from cryptography.hazmat.primitives.ciphers.aead import AESCCM + def Algo1(key): - f = Fernet(key) - target_file = open("raw_data/store_in_me.enc","rb") - secret_data = "" - for line in target_file: - secret_data = secret_data + line - data = f.decrypt(secret_data) - target_file.close() - return data + f = Fernet(key) + target_file = open("raw_data/store_in_me.enc", "rb") + secret_data = b"" + for line in target_file: + secret_data = secret_data + line + data = f.decrypt(secret_data) + target_file.close() + return data + def Algo1_extented(filename, key1, key2): - f = MultiFernet([Fernet(key1),Fernet(key2)]) - source_filename = 'encrypted/' + filename - target_filename = 'files/' + filename - file = open(source_filename,'rb') - target_file = open(target_filename,'wb') - raw = "" - for line in file: - raw = raw + line - secret_data = f.decrypt(line) - target_file.write(secret_data) - file.close() - target_file.close() - + f = MultiFernet([Fernet(key1), Fernet(key2)]) + source_filename = 'encrypted/' + filename + target_filename = 'files/' + filename + file = open(source_filename, 'rb') + target_file = open(target_filename, 'wb') + raw = b"" + for line in file: + raw = raw + line + secret_data = f.decrypt(line) + target_file.write(secret_data) + file.close() + target_file.close() + + def Algo2(filename, key, nonce): - aad = "authenticated but unencrypted data" - chacha = ChaCha20Poly1305(key) - source_filename = 'encrypted/' + filename - target_filename = 'files/' + filename - file = open(source_filename,'rb') - target_file = open(target_filename,'wb') - raw = "" - for line in file: - raw = raw + line - secret_data = chacha.decrypt(nonce, raw, aad) - target_file.write(secret_data) - file.close() - target_file.close() - + aad = b"authenticated but unencrypted data" + chacha = ChaCha20Poly1305(key) + source_filename = 'encrypted/' + filename + target_filename = 'files/' + filename + file = open(source_filename, 'rb') + target_file = open(target_filename, 'wb') + raw = b"" + for line in file: + raw = raw + line + secret_data = chacha.decrypt(nonce, raw, aad) + target_file.write(secret_data) + file.close() + target_file.close() + + def Algo3(filename, key, nonce): - aad = "authenticated but unencrypted data" - aesgcm = AESGCM(key) - source_filename = 'encrypted/' + filename - target_filename = 'files/' + filename - file = open(source_filename,'rb') - target_file = open(target_filename,'wb') - raw = "" - for line in file: - raw = raw + line - secret_data = aesgcm.decrypt(nonce, raw, aad) - target_file.write(secret_data) - file.close() - target_file.close() - + aad = b"authenticated but unencrypted data" + aesgcm = AESGCM(key) + source_filename = 'encrypted/' + filename + target_filename = 'files/' + filename + file = open(source_filename, 'rb') + target_file = open(target_filename, 'wb') + raw = b"" + for line in file: + raw = raw + line + secret_data = aesgcm.decrypt(nonce, raw, aad) + target_file.write(secret_data) + file.close() + target_file.close() + + def Algo4(filename, key, nonce): - aad = "authenticated but unencrypted data" - aesccm = AESCCM(key) - source_filename = 'encrypted/' + filename - target_filename = 'files/' + filename - file = open(source_filename,'rb') - target_file = open(target_filename,'wb') - raw = "" - for line in file: - raw = raw + line - secret_data = aesccm.decrypt(nonce, raw, aad) - target_file.write(secret_data) - file.close() - target_file.close() - + aad = b"authenticated but unencrypted data" + aesccm = AESCCM(key) + source_filename = 'encrypted/' + filename + target_filename = 'files/' + filename + file = open(source_filename, 'rb') + target_file = open(target_filename, 'wb') + raw = b"" + for line in file: + raw = raw + line + secret_data = aesccm.decrypt(nonce, raw, aad) + target_file.write(secret_data) + file.close() + target_file.close() + + def decrypter(): - tools.empty_folder('files') - key_1 = "" - list_directory = tools.list_dir('key') - filename = './key/' + list_directory[0] - public_key = open(filename,"rb") - for line in public_key: - key_1 = key_1 + line - public_key.close() - secret_information = Algo1(key_1) - list_information = secret_information.split(':::::') - key_1_1 = list_information[0] - key_1_2 = list_information[1] - key_2 = list_information[2] - key_3 = list_information[3] - key_4 = list_information[4] - nonce12 = list_information[5] - nonce13 = list_information[6] - files = sorted(tools.list_dir('encrypted')) - for index in range(0,len(files)): - if index%4 == 0: - Algo1_extented(files[index],key_1_1,key_1_2) - elif index%4 == 1: - Algo2(files[index],key_2,nonce12) - elif index%4 == 2: - Algo3(files[index],key_3,nonce12) - else: - Algo4(files[index],key_4,nonce13) \ No newline at end of file + tools.empty_folder('files') + key_1 = b"" + list_directory = tools.list_dir('key') + filename = './key/' + list_directory[0] + public_key = open(filename, "rb") + for line in public_key: + key_1 = key_1 + line + public_key.close() + secret_information = Algo1(key_1) + list_information = secret_information.split(b':::::') + key_1_1 = list_information[0] + key_1_2 = list_information[1] + key_2 = list_information[2] + key_3 = list_information[3] + key_4 = list_information[4] + nonce12 = list_information[5] + nonce13 = list_information[6] + files = sorted(tools.list_dir('encrypted')) + for index in range(0, len(files)): + if index % 4 == 0: + Algo1_extented(files[index], key_1_1, key_1_2) + elif index % 4 == 1: + Algo2(files[index], key_2, nonce12) + elif index % 4 == 2: + Algo3(files[index], key_3, nonce12) + else: + Algo4(files[index], key_4, nonce13) diff --git a/divider.py b/divider.py index 5f4b246..7f835e9 100644 --- a/divider.py +++ b/divider.py @@ -1,37 +1,38 @@ import sys import tools + def divide(): - tools.empty_folder('files') - tools.empty_folder('raw_data') - FILE = tools.list_dir('uploads') - FILE = './uploads/'+FILE[0] + tools.empty_folder('files') + tools.empty_folder('raw_data') + FILE = tools.list_dir('uploads') + FILE = './uploads/'+FILE[0] - MAX = 1024*32 # 1 MB - max chapter size - BUF = 50*1024*1024*1024 # 50GB - memory buffer size + MAX = 1024*32 # 1 MB - max chapter size + BUF = 50*1024*1024*1024 # 50GB - memory buffer size - chapters = 0 - uglybuf = '' - meta_data = open('raw_data/meta_data.txt','w') - file__name = FILE.split('/') - file__name = file__name[-1] - print file__name - meta_data.write("File_Name=%s\n" % (file__name)) - with open(FILE, 'rb') as src: - while True: - target_file = open('files/SECRET' + '%07d' % chapters, 'wb') - written = 0 - while written < MAX: - if len(uglybuf) > 0: - target_file.write(uglybuf) - target_file.write(src.read(min(BUF, MAX - written))) - written += min(BUF, MAX - written) - uglybuf = src.read(1) - if len(uglybuf) == 0: - break - target_file.close() - if len(uglybuf) == 0: - break - chapters += 1 - meta_data.write("chapters=%d" % (chapters+1)) - meta_data.close() \ No newline at end of file + chapters = 0 + uglybuf = '' + meta_data = open('raw_data/meta_data.txt', 'w') + file__name = FILE.split('/') + file__name = file__name[-1] + print(file__name) + meta_data.write("File_Name=%s\n" % (file__name)) + with open(FILE, 'rb') as src: + while True: + target_file = open('files/SECRET' + '%07d' % chapters, 'wb') + written = 0 + while written < MAX: + if len(uglybuf) > 0: + target_file.write(uglybuf) + target_file.write(src.read(min(BUF, MAX - written))) + written += min(BUF, MAX - written) + uglybuf = src.read(1) + if len(uglybuf) == 0: + break + target_file.close() + if len(uglybuf) == 0: + break + chapters += 1 + meta_data.write("chapters=%d" % (chapters+1)) + meta_data.close() diff --git a/encrypter.py b/encrypter.py index b645559..811dd56 100644 --- a/encrypter.py +++ b/encrypter.py @@ -5,96 +5,107 @@ from cryptography.hazmat.primitives.ciphers.aead import AESGCM from cryptography.hazmat.primitives.ciphers.aead import AESCCM + def Algo1(data, key): - f = Fernet(key) - target_file = open("raw_data/store_in_me.enc","wb") - secret_data = f.encrypt(data) - target_file.write(secret_data) - target_file.close() + f = Fernet(key) + # All keys stored in store_in_me.enc encrypted with key_1 + target_file = open("raw_data/store_in_me.enc", "wb") + secret_data = f.encrypt(data) + target_file.write(secret_data) + target_file.close() + def Algo1_extented(filename, key1, key2): - f = MultiFernet([Fernet(key1),Fernet(key2)]) - source_filename = 'files/' + filename - target_filename = 'encrypted/' + filename - file = open(source_filename,'rb') - target_file = open(target_filename,'wb') - raw = "" - for line in file: - raw = raw + line - secret_data = f.encrypt(raw) - target_file.write(secret_data) - file.close() - target_file.close() + f = MultiFernet([Fernet(key1), Fernet(key2)]) + source_filename = 'files/' + filename + target_filename = 'encrypted/' + filename + file = open(source_filename, 'rb') + target_file = open(target_filename, 'wb') + raw = b"" + for line in file: + raw = raw + line + secret_data = f.encrypt(raw) + target_file.write(secret_data) + file.close() + target_file.close() + def Algo2(filename, key, nonce): - aad = "authenticated but unencrypted data" - chacha = ChaCha20Poly1305(key) - source_filename = 'files/' + filename - target_filename = 'encrypted/' + filename - file = open(source_filename,'rb') - target_file = open(target_filename,'wb') - raw = "" - for line in file: - raw = raw + line - secret_data = chacha.encrypt(nonce, raw, aad) - target_file.write(secret_data) - file.close() - target_file.close() + aad = b"authenticated but unencrypted data" + chacha = ChaCha20Poly1305(key) + source_filename = 'files/' + filename + target_filename = 'encrypted/' + filename + file = open(source_filename, 'rb') + target_file = open(target_filename, 'wb') + raw = b"" + for line in file: + raw = raw + line + secret_data = chacha.encrypt(nonce, raw, aad) + target_file.write(secret_data) + file.close() + target_file.close() + def Algo3(filename, key, nonce): - aad = "authenticated but unencrypted data" - aesgcm = AESGCM(key) - source_filename = 'files/' + filename - target_filename = 'encrypted/' + filename - file = open(source_filename,'rb') - target_file = open(target_filename,'wb') - raw = "" - for line in file: - raw = raw + line - secret_data = aesgcm.encrypt(nonce, raw, aad) - target_file.write(secret_data) - file.close() - target_file.close() - + aad = b"authenticated but unencrypted data" + aesgcm = AESGCM(key) + source_filename = 'files/' + filename + target_filename = 'encrypted/' + filename + file = open(source_filename, 'rb') + target_file = open(target_filename, 'wb') + raw = b"" + for line in file: + raw = raw + line + secret_data = aesgcm.encrypt(nonce, raw, aad) + target_file.write(secret_data) + file.close() + target_file.close() + + def Algo4(filename, key, nonce): - aad = "authenticated but unencrypted data" - aesccm = AESCCM(key) - source_filename = 'files/' + filename - target_filename = 'encrypted/' + filename - file = open(source_filename,'rb') - target_file = open(target_filename,'wb') - raw = "" - for line in file: - raw = raw + line - secret_data = aesccm.encrypt(nonce, raw, aad) - target_file.write(secret_data) - file.close() - target_file.close() + aad = b"authenticated but unencrypted data" + aesccm = AESCCM(key) + source_filename = 'files/' + filename + target_filename = 'encrypted/' + filename + file = open(source_filename, 'rb') + target_file = open(target_filename, 'wb') + raw = b"" + for line in file: + raw = raw + line + secret_data = aesccm.encrypt(nonce, raw, aad) + target_file.write(secret_data) + file.close() + target_file.close() + def encrypter(): - tools.empty_folder('key') - tools.empty_folder('encrypted') - key_1 = Fernet.generate_key() - key_1_1 = Fernet.generate_key() - key_1_2 = Fernet.generate_key() - key_2 = ChaCha20Poly1305.generate_key() - key_3 = AESGCM.generate_key(bit_length=128) - key_4 = AESCCM.generate_key(bit_length=128) - nonce13 = os.urandom(13) - nonce12 = os.urandom(12) - files = sorted(tools.list_dir('files')) - for index in range(0,len(files)): - if index%4 == 0: - Algo1_extented(files[index],key_1_1,key_1_2) - elif index%4 == 1: - Algo2(files[index],key_2,nonce12) - elif index%4 == 2: - Algo3(files[index],key_3,nonce12) - else: - Algo4(files[index],key_4,nonce13) - secret_information = (key_1_1)+":::::"+(key_1_2)+":::::"+(key_2)+":::::"+(key_3)+":::::"+(key_4)+":::::"+(nonce12)+":::::"+(nonce13) - Algo1(secret_information,key_1) - public_key = open("./key/Taale_Ki_Chabhi.pem","wb") - public_key.write(key_1) - public_key.close() - tools.empty_folder('files') \ No newline at end of file + tools.empty_folder('key') + tools.empty_folder('encrypted') + key_1 = Fernet.generate_key() + key_1_1 = Fernet.generate_key() + key_1_2 = Fernet.generate_key() + key_2 = ChaCha20Poly1305.generate_key() + key_3 = AESGCM.generate_key(bit_length=128) + key_4 = AESCCM.generate_key(bit_length=128) + nonce13 = os.urandom(13) + nonce12 = os.urandom(12) + files = sorted(tools.list_dir('files')) + for index in range(0, len(files)): + if index % 4 == 0: + Algo1_extented(files[index], key_1_1, key_1_2) + elif index % 4 == 1: + Algo2(files[index], key_2, nonce12) + elif index % 4 == 2: + Algo3(files[index], key_3, nonce12) + else: + Algo4(files[index], key_4, nonce13) + secret_information = (key_1_1)+b":::::"+(key_1_2)+b":::::"+(key_2) + \ + b":::::"+(key_3)+b":::::"+(key_4)+b":::::" + \ + (nonce12)+b":::::"+(nonce13) # All the keys + + # Encrypting all the keys with algo1 using key_1 + Algo1(secret_information, key_1) + public_key = open("./key/Taale_Ki_Chabhi.pem", "wb") + public_key.write(key_1) # key_1 stored in Taale_Ki_Chabhi.pem + public_key.close() + tools.empty_folder('files') diff --git a/raw_data/meta_data.txt b/raw_data/meta_data.txt deleted file mode 100644 index 788d511..0000000 --- a/raw_data/meta_data.txt +++ /dev/null @@ -1,2 +0,0 @@ -File_Name=Bus Rke to DLI.pdf -chapters=12 \ No newline at end of file diff --git a/raw_data/store_in_me.enc b/raw_data/store_in_me.enc deleted file mode 100644 index 06716e4..0000000 --- a/raw_data/store_in_me.enc +++ /dev/null @@ -1 +0,0 @@ -gAAAAABbatuE_Xi9cMb1I_FbrmH_noowNDe5BoT9W0V3RbiWs1qDuviJWcgZPkGeCI7q47-4-alpFbDknRpexc4Pl5Y4DE2NkB44bVKgYtUnu_SuHY2CGyWBubW6wez2wu2DjKk5ulvfAjSXdFLtbNtQ7bN6rAlKAx-ANfL9oiBkQrJ12-RzteMZMkQNSRLYM3gS5l6MpkumpcQdk2HB5VbPDjZlYwvfZLJQ8T44fk0WWymghD1Aa8yo3MEuA1RkR3FIk8ysDuMgi_EEGYKvEHXx8s8RPSCQjWsDE6b4g8x2DYGFrEmQOouJbVNfk5umV_3ooQpsFADLx0mOe0GsetATxrOAuuTEKg== \ No newline at end of file diff --git a/templates/restore_success.html b/templates/restore_success.html index 6ef7ef7..b8c42be 100644 --- a/templates/restore_success.html +++ b/templates/restore_success.html @@ -1,16 +1,22 @@ - -
-

Secure File Storage Using Hybrid Cryptography



-

SUCCESS

- - - - - -
-

A Project by:

-
Shivang Srivastava
B.Tech (CSE)
GE-152010678
-
- - \ No newline at end of file + +
+

Secure File Storage Using Hybrid Cryptography

+

+

SUCCESS

+ + + + + +
+ + + +
+
+

A Project by:

+
Shivang Srivastava
B.Tech (CSE)
GE-152010678
+
+ + diff --git a/templates/success.html b/templates/success.html index 603757f..26d4936 100644 --- a/templates/success.html +++ b/templates/success.html @@ -1,16 +1,22 @@ - -
-

Secure File Storage Using Hybrid Cryptography



-

SUCCESS

- - - - - -
-

A Project by:

-
Shivang Srivastava
B.Tech (CSE)
GE-152010678
-
- - \ No newline at end of file + +
+

Secure File Storage Using Hybrid Cryptography

+

+

SUCCESS

+ + + + + +
+ + + +
+
+

A Project by:

+
Shivang Srivastava
B.Tech (CSE)
GE-152010678
+
+ +