From ba2dd4c682771067db6712c13bc07cefd7dd55c2 Mon Sep 17 00:00:00 2001
From: Mming-145460 <175156969+Mming-145460@users.noreply.github.com>
Date: Fri, 27 Dec 2024 13:43:55 +0800
Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.idea/.gitignore | 3 +
.idea/.name | 1 +
.idea/dbnavigator.xml | 400 ++++++++++++++++++
.../inspectionProfiles/profiles_settings.xml | 6 +
.idea/misc.xml | 4 +
.idea/modules.xml | 8 +
.idea/实验.iml | 8 +
GUI.py | 47 ++
NEV.py | 195 +++++++++
NEW.py | 269 ++++++++++++
__pycache__/NEV.cpython-311.pyc | Bin 0 -> 12011 bytes
__pycache__/NEW.cpython-311.pyc | Bin 0 -> 12926 bytes
__pycache__/receiver.cpython-311.pyc | Bin 0 -> 11882 bytes
__pycache__/sender.cpython-311.pyc | Bin 0 -> 12093 bytes
main.py | 77 ++++
receiver_private_key.pem | 28 ++
receiver_public_key.pem | 9 +
sender_private_key.pem | 28 ++
sender_public_key.pem | 9 +
yicixing.py | 25 ++
20 files changed, 1117 insertions(+)
create mode 100644 .idea/.gitignore
create mode 100644 .idea/.name
create mode 100644 .idea/dbnavigator.xml
create mode 100644 .idea/inspectionProfiles/profiles_settings.xml
create mode 100644 .idea/misc.xml
create mode 100644 .idea/modules.xml
create mode 100644 .idea/实验.iml
create mode 100644 GUI.py
create mode 100644 NEV.py
create mode 100644 NEW.py
create mode 100644 __pycache__/NEV.cpython-311.pyc
create mode 100644 __pycache__/NEW.cpython-311.pyc
create mode 100644 __pycache__/receiver.cpython-311.pyc
create mode 100644 __pycache__/sender.cpython-311.pyc
create mode 100644 main.py
create mode 100644 receiver_private_key.pem
create mode 100644 receiver_public_key.pem
create mode 100644 sender_private_key.pem
create mode 100644 sender_public_key.pem
create mode 100644 yicixing.py
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..359bb53
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,3 @@
+# 默认忽略的文件
+/shelf/
+/workspace.xml
diff --git a/.idea/.name b/.idea/.name
new file mode 100644
index 0000000..5ffb332
--- /dev/null
+++ b/.idea/.name
@@ -0,0 +1 @@
+GUI.py
\ No newline at end of file
diff --git a/.idea/dbnavigator.xml b/.idea/dbnavigator.xml
new file mode 100644
index 0000000..c7a8a85
--- /dev/null
+++ b/.idea/dbnavigator.xml
@@ -0,0 +1,400 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000..105ce2d
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..a971a2c
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..5a6690a
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/实验.iml b/.idea/实验.iml
new file mode 100644
index 0000000..d0876a7
--- /dev/null
+++ b/.idea/实验.iml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/GUI.py b/GUI.py
new file mode 100644
index 0000000..798613d
--- /dev/null
+++ b/GUI.py
@@ -0,0 +1,47 @@
+import tkinter as tk
+import subprocess
+import threading
+
+# 使用线程来运行 NEW.py和 NEV.py。
+def run_new():
+ try:
+ threading.Thread(target=lambda: subprocess.run(["python", "NEW.py"])).start()
+ except FileNotFoundError:
+ print("找不到NEW.py文件")
+
+
+def run_nev():
+ try:
+ threading.Thread(target=lambda: subprocess.run(["python", "NEV.py"])).start()
+ except FileNotFoundError:
+ print("找不到NEV.py文件")
+
+
+# 创建主窗口
+root = tk.Tk()
+root.title("文件安全传输工具")
+# 设置窗口大小
+root.geometry("300x150")
+
+# 创建标签
+label = tk.Label(root, text="请选择您的身份:")
+label.pack(pady=10)
+
+# 创建按钮框架,用于在同一行放置按钮
+button_frame = tk.Frame(root)
+button_frame.pack()
+
+# 创建发送方按钮
+sender_button = tk.Button(button_frame, text="发送方", command=run_new)
+sender_button.pack(side=tk.LEFT, padx=10)
+
+# 创建接收方按钮
+receiver_button = tk.Button(button_frame, text="接收方", command=run_nev)
+receiver_button.pack(side=tk.LEFT, padx=10)
+
+# 创建关闭按钮
+confirm_button = tk.Button(button_frame, text="关闭", command=root.destroy)
+confirm_button.pack(side=tk.LEFT, padx=10)
+
+# 运行主循环
+root.mainloop()
\ No newline at end of file
diff --git a/NEV.py b/NEV.py
new file mode 100644
index 0000000..a3876c4
--- /dev/null
+++ b/NEV.py
@@ -0,0 +1,195 @@
+import os
+import pickle
+import socket
+import tkinter as tk
+from tkinter import filedialog, ttk, messagebox
+from cryptography.hazmat.primitives.asymmetric import padding
+from cryptography.hazmat.primitives import hashes, serialization
+from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
+import hashlib
+
+
+# 去除填充函数
+def unpad(data):
+ padding_length = data[-1]
+ if padding_length > len(data):
+ return data
+ return data[:-padding_length]
+
+
+# 解密文件函数
+def decrypt_file(encrypted_data, key, iv, algorithm, mode):
+ mode_mapping = {
+ "CBC": modes.CBC,
+ "CFB": modes.CFB
+ }
+ if algorithm == "AES":
+ # 对于AES算法,IV长度为16字节,无需处理
+ cipher = Cipher(algorithms.AES(key), mode_mapping[mode](iv))
+ elif algorithm == "DES":
+ # 确保IV长度为8字节
+ iv = iv[:8]
+ cipher = Cipher(algorithms.TripleDES(key), mode_mapping[mode](iv))
+ else:
+ raise ValueError("不支持该算法")
+
+ decryptor = cipher.decryptor()
+ decrypted_data = decryptor.update(encrypted_data) + decryptor.finalize()
+ return decrypted_data
+
+
+# 计算哈希值函数
+def calculate_hash(data):
+ digest = hashes.Hash(hashes.SHA256())
+ digest.update(data)
+ return digest.finalize()
+
+
+# 验证签名函数
+def verify_signature(public_key, signature, hash_value):
+ try:
+ public_key.verify(
+ signature,
+ hash_value,
+ padding.PSS(
+ mgf=padding.MGF1(hashes.SHA256()),
+ salt_length=padding.PSS.MAX_LENGTH
+ ),
+ hashes.SHA256()
+ )
+ return True
+ except Exception:
+ return False
+
+
+# 解密对称密钥函数
+def decrypt_symmetric_key(private_key, encrypted_key):
+ decrypted_key = private_key.decrypt(
+ encrypted_key,
+ padding.OAEP(
+ mgf=padding.MGF1(algorithm=hashes.SHA256()),
+ algorithm=hashes.SHA256(),
+ label=None
+ )
+ )
+ return decrypted_key
+
+
+# 计算文件的 MD5 校验和
+def calculate_md5(data):
+ md5 = hashlib.md5()
+ md5.update(data)
+ return md5.hexdigest()
+
+
+# 接收文件函数
+def receive_file(server_ip, server_port, output_dir, algorithm, mode):
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
+ s.bind((server_ip, server_port))
+ s.listen(1)
+ print(f"等待连接,监听端口 {server_port}...")
+ conn, addr = s.accept()
+ with conn:
+ print(f"接收到连接来自: {addr}")
+ data = b""
+ while True:
+ packet = conn.recv(4096)
+ if not packet:
+ break
+ data += packet
+
+ package = pickle.loads(data)
+
+ encrypted_file = package['encrypted_file']
+ iv = package['iv']
+ encrypted_symmetric_key = package['encrypted_symmetric_key']
+ signature = package['signature']
+ received_algorithm = package['algorithm']
+ received_mode = package['mode']
+ received_md5_checksum = package['md5_checksum']
+ file_extension = package['file_extension']
+ # 从封装包中提取发送方公钥
+ sender_public_pem = package['sender_public_key']
+ sender_public_key = serialization.load_pem_public_key(sender_public_pem)
+
+ if algorithm!= received_algorithm or mode!= received_mode:
+ raise ValueError("选择的解密算法或模式与发送方不一致")
+
+ with open("receiver_private_key.pem", "rb") as f:
+ receiver_private_key = serialization.load_pem_private_key(
+ f.read(),
+ password=None
+ )
+
+ symmetric_key = decrypt_symmetric_key(receiver_private_key, encrypted_symmetric_key)
+
+ decrypted_file = decrypt_file(encrypted_file, symmetric_key, iv, algorithm, mode)
+ decrypted_file = unpad(decrypted_file)
+
+ file_hash = calculate_hash(encrypted_file)
+
+ is_valid = verify_signature(sender_public_key, signature, file_hash)
+ return is_valid, output_dir, file_extension, decrypted_file
+
+
+# 主函数,用于启动 GUI 界面
+def receiver_main():
+ def select_output_dir():
+ output_dir.set(filedialog.askdirectory())
+
+ def start_receiving():
+ if not output_dir.get():
+ print("请选择保存文件的路径!")
+ return
+ algorithm = algorithm_var.get()
+ mode = mode_var.get()
+ try:
+ is_valid, output_dir_path, file_extension, decrypted_file = receive_file(
+ server_ip.get(), int(server_port.get()), output_dir.get(), algorithm, mode
+ )
+ result_label.config(text=f"接收方验证签名结果: {is_valid}")
+ if is_valid:
+ output_file_path = os.path.join(output_dir_path, f"decrypted_file.{file_extension}")
+ with open(output_file_path, "wb") as f:
+ f.write(decrypted_file)
+ messagebox.showinfo("提示", "接收成功")
+ except Exception as e:
+ print(f"接收过程中发生错误: {e}")
+
+ root = tk.Tk()
+ root.title("接收方")
+ root.geometry("450x300") # 适当增加窗口高度以显示新标签
+
+ server_ip = tk.StringVar(value="127.0.0.1")
+ server_port = tk.StringVar(value="12345")
+ output_dir = tk.StringVar()
+ algorithm_var = tk.StringVar(value="AES")
+ mode_var = tk.StringVar(value="CBC")
+
+ ttk.Label(root, text="监听 IP:").grid(row=0, column=0, sticky=tk.W, pady=5)
+ ttk.Entry(root, textvariable=server_ip).grid(row=0, column=1, pady=5)
+
+ ttk.Label(root, text="监听端口:").grid(row=1, column=0, sticky=tk.W, pady=5)
+ ttk.Entry(root, textvariable=server_port).grid(row=1, column=1, pady=5)
+
+ ttk.Label(root, text="选择保存文件的路径:").grid(row=2, column=0, sticky=tk.W, pady=5)
+ ttk.Entry(root, textvariable=output_dir, width=30).grid(row=2, column=1, pady=5)
+ ttk.Button(root, text="选择目录", command=select_output_dir).grid(row=2, column=2, pady=5)
+
+ ttk.Label(root, text="选择解密算法:").grid(row=3, column=0, sticky=tk.W, pady=5)
+ ttk.Combobox(root, textvariable=algorithm_var, values=["AES", "DES"], state="readonly").grid(row=3, column=1, pady=5)
+
+ ttk.Label(root, text="选择解密模式:").grid(row=4, column=0, sticky=tk.W, pady=5)
+ ttk.Combobox(root, textvariable=mode_var, values=["CBC", "CFB"], state="readonly").grid(row=4, column=1, pady=5)
+
+ ttk.Button(root, text="开始接收文件", command=start_receiving).grid(row=5, columnspan=3, pady=20)
+
+ # 新增显示验证结果的标签
+ result_label = ttk.Label(root, text="")
+ result_label.grid(row=6, columnspan=3, pady=10)
+
+ root.mainloop()
+
+
+if __name__ == "__main__":
+ receiver_main()
\ No newline at end of file
diff --git a/NEW.py b/NEW.py
new file mode 100644
index 0000000..e5e5a4b
--- /dev/null
+++ b/NEW.py
@@ -0,0 +1,269 @@
+import os
+import pickle
+import socket
+import tkinter as tk
+from tkinter import filedialog, ttk, messagebox
+from cryptography.hazmat.primitives.asymmetric import rsa, padding
+from cryptography.hazmat.primitives import hashes, serialization
+from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
+import hashlib
+
+
+# 生成并保存发送方密钥对
+def generate_and_save_sender_keys():
+ private_key = rsa.generate_private_key(
+ public_exponent=65537,
+ key_size=2048,
+ )
+ public_key = private_key.public_key()
+
+ with open('sender_private_key.pem', 'wb') as f:
+ f.write(private_key.private_bytes(
+ encoding=serialization.Encoding.PEM,
+ format=serialization.PrivateFormat.PKCS8,
+ encryption_algorithm=serialization.NoEncryption()
+ ))
+
+ with open('sender_public_key.pem', 'wb') as f:
+ f.write(public_key.public_bytes(
+ encoding=serialization.Encoding.PEM,
+ format=serialization.PublicFormat.SubjectPublicKeyInfo
+ ))
+
+ return private_key, public_key
+
+
+# 加载发送方密钥对
+def load_sender_keys():
+ try:
+ with open('sender_private_key.pem', 'rb') as f:
+ private_key = serialization.load_pem_private_key(
+ f.read(),
+ password=None
+ )
+ with open('sender_public_key.pem', 'rb') as f:
+ public_key = serialization.load_pem_public_key(f.read())
+ return private_key, public_key
+ except FileNotFoundError:
+ return generate_and_save_sender_keys()
+
+
+sender_private_key, sender_public_key = load_sender_keys()
+
+# 用于存储接收方公钥的变量
+receiver_public_key = None
+
+
+# 填充数据函数
+def pad(data, block_size):
+ padding_length = block_size - (len(data) % block_size)
+ if padding_length == 0:
+ padding_length = block_size
+ padding = bytes([padding_length]) * padding_length
+ return data + padding
+
+
+# 加密文件函数
+def encrypt_file(file_path, key, algorithm, mode):
+ with open(file_path, 'rb') as f:
+ file_data = f.read()
+ if algorithm == "AES":
+ block_size = 16
+ elif algorithm == "DES":
+ block_size = 8
+ else:
+ raise ValueError("Unsupported algorithm")
+ file_data = pad(file_data, block_size)
+
+ mode_mapping = {
+ "CBC": modes.CBC,
+ "CFB": modes.CFB
+ }
+ if algorithm == "AES":
+ iv = os.urandom(16)
+ elif algorithm == "DES":
+ iv = os.urandom(8) # 生成8字节的IV用于DES算法
+ else:
+ raise ValueError("Unsupported algorithm")
+
+ if algorithm == "AES":
+ cipher = Cipher(algorithms.AES(key), mode_mapping[mode](iv))
+ elif algorithm == "DES":
+ cipher = Cipher(algorithms.TripleDES(key), mode_mapping[mode](iv))
+ else:
+ raise ValueError("Unsupported algorithm")
+
+ encryptor = cipher.encryptor()
+ encrypted_data = encryptor.update(file_data) + encryptor.finalize()
+ return encrypted_data, iv
+
+
+# 计算哈希值函数
+def calculate_hash(data):
+ digest = hashes.Hash(hashes.SHA256())
+ digest.update(data)
+ return digest.finalize()
+
+
+# 对哈希值进行签名函数
+def sign_hash(private_key, hash_value):
+ signature = private_key.sign(
+ hash_value,
+ padding.PSS(
+ mgf=padding.MGF1(hashes.SHA256()),
+ salt_length=padding.PSS.MAX_LENGTH
+ ),
+ hashes.SHA256()
+ )
+ return signature
+
+
+# 加密对称密钥函数
+def encrypt_symmetric_key(public_key, symmetric_key):
+ encrypted_key = public_key.encrypt(
+ symmetric_key,
+ padding.OAEP(
+ mgf=padding.MGF1(algorithm=hashes.SHA256()),
+ algorithm=hashes.SHA256(),
+ label=None
+ )
+ )
+ return encrypted_key
+
+
+# 计算文件的 MD5 校验和
+def calculate_md5(data):
+ md5 = hashlib.md5()
+ md5.update(data)
+ return md5.hexdigest()
+
+
+# 发送文件函数
+def send_file(server_ip, server_port, package):
+ try:
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
+ s.connect((server_ip, server_port))
+ s.sendall(pickle.dumps(package))
+ return True
+ except Exception as e:
+ print(f"连接失败: {e}")
+ return False
+
+
+# 导入接收方公钥的函数
+def import_receiver_public_key():
+ global receiver_public_key
+ file_path = filedialog.askopenfilename(filetypes=[("PEM files", "*.pem")])
+ if file_path:
+ try:
+ with open(file_path, 'rb') as f:
+ receiver_public_key = serialization.load_pem_public_key(f.read())
+ messagebox.showinfo("提示", "接收方公钥导入成功")
+ except Exception as e:
+ messagebox.showerror("错误", f"导入接收方公钥失败: {e}")
+
+
+# 主函数,用于启动 GUI 界面
+def sender_main():
+ def select_file():
+ file_path.set(filedialog.askopenfilename())
+
+ def send():
+ global receiver_public_key
+ file = file_path.get()
+ algorithm = algorithm_var.get()
+ mode = mode_var.get()
+
+ if not file:
+ messagebox.showerror("错误", "请选择文件")
+ return
+
+ if algorithm == "AES":
+ key_size = 32
+ elif algorithm == "DES":
+ key_size = 24
+ else:
+ print("不支持的算法!")
+ return
+
+ if mode == "CBC":
+ pass
+ elif mode == "CFB":
+ pass
+ else:
+ print("不支持的模式!")
+ return
+
+ if not receiver_public_key:
+ messagebox.showerror("错误", "请导入证书")
+ return
+
+ symmetric_key = os.urandom(key_size)
+ encrypted_file, iv = encrypt_file(file, symmetric_key, algorithm, mode)
+
+ file_hash = calculate_hash(encrypted_file)
+ signature = sign_hash(sender_private_key, file_hash)
+
+ sender_public_pem = sender_public_key.public_bytes(
+ encoding=serialization.Encoding.PEM,
+ format=serialization.PublicFormat.SubjectPublicKeyInfo
+ )
+
+ encrypted_symmetric_key = encrypt_symmetric_key(receiver_public_key, symmetric_key)
+
+ md5_checksum = calculate_md5(encrypted_file)
+
+ file_extension = os.path.splitext(file)[1][1:]
+ # 在封装包中添加发送方公钥
+ package = {
+ 'encrypted_file': encrypted_file,
+ 'iv': iv,
+ 'encrypted_symmetric_key': encrypted_symmetric_key,
+ 'signature': signature,
+ 'algorithm': algorithm,
+ 'mode': mode,
+ 'md5_checksum': md5_checksum,
+ 'file_extension': file_extension,
+ 'sender_public_key': sender_public_pem
+ }
+
+ if send_file(server_ip.get(), int(server_port.get()), package):
+ messagebox.showinfo("提示", "发送成功")
+ else:
+ messagebox.showerror("错误", "连接失败")
+
+
+ root = tk.Tk()
+ root.title("发送方")
+ root.geometry("490x250")
+
+ file_path = tk.StringVar()
+ algorithm_var = tk.StringVar(value="AES")
+ mode_var = tk.StringVar(value="CBC")
+ server_ip = tk.StringVar(value="127.0.0.1")
+ server_port = tk.StringVar(value="12345")
+
+ ttk.Label(root, text="选择文件:").grid(row=0, column=0, sticky=tk.W, pady=5)
+ ttk.Entry(root, textvariable=file_path, width=30).grid(row=0, column=1, pady=5)
+ ttk.Button(root, text="浏览", command=select_file).grid(row=0, column=2, pady=5)
+ ttk.Button(root, text="导入证书", command=import_receiver_public_key).grid(row=0, column=3, pady=5)
+
+ ttk.Label(root, text="选择加密算法:").grid(row=1, column=0, sticky=tk.W, pady=5)
+ ttk.Combobox(root, textvariable=algorithm_var, values=["AES", "DES"], state="readonly").grid(row=1, column=1, pady=5)
+
+ ttk.Label(root, text="选择加密模式:").grid(row=2, column=0, sticky=tk.W, pady=5)
+ ttk.Combobox(root, textvariable=mode_var, values=["CBC", "CFB"], state="readonly").grid(row=2, column=1, pady=5)
+
+ ttk.Label(root, text="接收方 IP:").grid(row=3, column=0, sticky=tk.W, pady=5)
+ ttk.Entry(root, textvariable=server_ip).grid(row=3, column=1, pady=5)
+
+ ttk.Label(root, text="接收方端口:").grid(row=4, column=0, sticky=tk.W, pady=5)
+ ttk.Entry(root, textvariable=server_port).grid(row=4, column=1, pady=5)
+
+ ttk.Button(root, text="发送文件", command=send).grid(row=5, columnspan=3, pady=20)
+
+ root.mainloop()
+
+
+if __name__ == "__main__":
+ sender_main()
\ No newline at end of file
diff --git a/__pycache__/NEV.cpython-311.pyc b/__pycache__/NEV.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..43c5a10841e59a72bf6a563859b580916819bf48
GIT binary patch
literal 12011
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
YN;{{t$52kBB_mXGN&*!@w#3^110Y)vGynhq
literal 0
HcmV?d00001
diff --git a/__pycache__/NEW.cpython-311.pyc b/__pycache__/NEW.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..5d57d0d2a935fe6053b9fe0718cd85d0d796266c
GIT binary patch
literal 12926
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
literal 0
HcmV?d00001
diff --git a/__pycache__/receiver.cpython-311.pyc b/__pycache__/receiver.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..230bbba44d5060c714e474bc881360923c59e217
GIT binary patch
literal 11882
zcmcIqeRLDom7kHGq|vu*OSa_?EItetiA9V7=Ys&o*w_XP!6Z=EU9a#6Ted8DM-f!l8qftjd_;0nZ=fg6B_%lwV0!l`{_%Dc@Cuo8u
zeME{olcV!2HyTYd&P<5yV)E#OP^C^9r0jh(N;>f2R&~@kr^c{LBt@0TL
zj2*@SQ-^85++mjTNS~#{LaSdPI`U`@(0rN#T0m=oT4^0n8?6UgNE?6_(MF)f9y@L7
zSKz)fB?`h*41d&DlI%Ni86sCw3F0ydDfr6)+9cbhztemdXSF#MqV9;-=b^oBU!X@+
zvTVPT6g7ixn)dp8pj6%K4)%J2qA}=UfX92n&3XfVr&3fmcn5nujHq+_dIF4>?Hve;
zs(}FQ33jL3gBGw(mGJ*!Kalf87+SYOOb$b2m}IlK!wdS`FO3MJ2ovqNCJ`p}#H)%-
z{dU5sXuJEr5TMgapT`d!MzouCi>BlgbU|hh+Z)8{ayTArhFinaozKI=1UuWCpMS39
zU}H4o|S34Geb1e=~l2d@_Fh2e+dy#jjoNjDPsn-48}P8Be#zd(^|Y29JxXkRK#7
zc_7CK|KJ875h7-@PplX}6lsfTY!{UiJ6_jb(9URX2xfZ1sBf?`M&R?>>3S*&L@nd+=I
z6Yop67$$Q~LqUX9VU$sASdm+P8CtuNZgEKM-wEw0|7v^qsig4#ZqLk`Q?lTRQEu;E
zCd29|l_}4}a;L+PCaj5O=_A9p(DZ;$augX3IhHH$1Y{0TVG0aUh3As8VS59RrtPA#
zzOh|Y?r3ZeCW{_w?8~T|1am9dIrm9teDs^!QE$Y-{
z60-0!1bHA4B5t;h9-qS`p;B7a2?9uDcY-3Qys^d^jh)lj=kkg!>Mod~LB9Agq4+Vb
zdh2B8^eUlx>kS28-5^vq@Oe9gydA^pVRcNKKjxXymU7xssg_Xu_+&d@Tq6|MaJ7w7
zn{ObYw(-VczP3fEZQ=7;g}hc=Ep962?A5%
znw^`TIl1eQCw!$l)_aq*A+fe
z3ouky(!&L-u%ew#~JL;MdiNsb`kB_*;FG9G~>jXx;^!Wx^_3V?L5#dY8zkd_6#N$Zrxz$
zu+Q7wg*}5d=Ue6ReSiuWpbYmH#7nJZg)VV6+09vp+wbdLaX
zACm_Z0j9KPWbf$SduB?jyH5Z~NF<-IScln%b9!rnAXT;uiJ7g#q2W+M3Aqm+J`Cbn
z`f~lc`dZ=-wYu%3@+Lt7?M@FN6k|swnc1fh!JCmN+!qJfl%x$P+X!-U(;Sat0AMs=
z*qM#kqtu~^tBT9e%9V6WxrR5S2!k0pm2FOvalk`3l}t6I%?Tc
z(BAsSeM}8z!v-x`3hd?dgN*kmp5)jjjF}}wN@f-VNamV`Ewu^+j}HuZSVp!gW-Exs
z|KK4YusgC=%v#sXSl7fT+Z<&cHOyIx#toN_zBlyF_uu6dqvUQyZLjA=*~=!KF0&nYb3Cpw<8g7d
z9Z1Ri_j&9{piL4*&
zA5sDD2_^ju!q_j+Pm1Sj?K^YraU%hHP*rqE9fe`NIO~=UggRloht#kWRBt2}NJe4Jo5aOM{iIbx6f8;VXx6Se
z(+I7}?Z=QdtPPTWb(jikPrxBzh#Fm{#fL-qGCTYsJ*2f+X#=EnS!pAr^;u~XqzzeVGo+1KX$z!HS?N4T
zo3qmSVP)9dFWE;>9Y$e`9B0fd-a94(ZOQHR%OqQrDuaE09-W`7L$JC6Hq!{vhmU1a
z2p2@N7T8qo!ubkbwjfvHkoD_nUrXfl(E3+lpwUJQiT+6SknoPkWfI%zhlR
z0g6Du0|YSsCaRAFm;uvpB2^a
zZhXoS5sbi5Q9bDG?)Q0MCme9oK?W}oA_@$8{0v^jMJvwk0)1x>6Rzr}#ez&(=BQ*y
z4%6Y|EWn{uV`_+$`x^I%hJ6xalQdeQwLNsW&(qDynY%p4Tl_}?VqOy{v@O6k1wwvW
z0&}E|vIL$;fTDD|mJU~6E9SpHuw3o@Ua1x|+2mF2!0iMewkNJacS+V%;?(r}?K^+X9I7~Apc#??uNh7Aa
zGO&>`ohRW(woEc>8ZoW7I(bC2CX2zmv(6TIi%eczkcB6TF=yIl!%^5KMXfYb@$Oor
zyg__q_0pmxsQ{@AYc7Z#O$xMH>YfC$lKqr{tT5DRhkx*=urY=0w$?mae(vb_$xBCt
zHBa%{rv>fP3BsVNn0Ch~?HToH^_X_z8P2?hr>X?1ileGWB4nl#w$%
zJNaE8^2JkI1ZoRMZHd_m&ma2np|c(19V5D7G)%{gc?m+Jt&qr^A&<*nKe+=4T)d%H
zFw}B}+E_v1@Q#E687pG;^0)18*rOXT@-n856_vxmIIkpTwZ|;h3H4di7)oe?FQF!E
zB?)6nt|a^(ZU#kIBx#Ge9+VMg+o*41
z18;H&CP!rF=e9Dgyjrli0DjY2#;l{|k?pZ%mE7vB!m@1~Dveo+CiH@36(D$aN3>M1
zS97Q^W-Xm)8s8h)6DwO8Z4=6B0k(@}1bwcta0Qh!no3SnIcc3T&N!dtoX^f-gs_b_
z?G;RWIcjfQ3u9F^xr*1W6SV6R1XAgyYUU~)7b@yw2IHBg(@i7Iqs;*I19stn!zmEJ
zE*v_JDoZGVS*jrsIJm0t!8JQCQ<{lMDp`Wdpi`OWj9>xI$ql^m3BmY8f>3EqQ}uI>
zYQa%|WBDIf-(3BNs(-EG9lHd_F3wOMbJWZ_w#+!T%-L59_H}~2cCKiZP*funH6>KU
z@~zO@gBn+r?ZDA3iVZxfjZ*CUM&5`Cgq~&xQCaZwJ
z#iNY^+Q^}epKG9NYE?5iS6Y6d?(NNQY@XC{8(z5K2LcygdQd1m$Wg_T(pN-x$WSKC
zDDA4r=kVa~JiT+QjW?_m3@bUqN`RcStE5ti+{1kLG0EgD6_XUWm@AHb#NSVC0fMi#
zzmp?8KQ6+8>R>VtZFL#gtnazg`B-F(sj@lOzHptPS*N*vdd
zIBt;QrKq7o^T`Sl=)YAun@W_QmY}9(nopOLn69=ouO&X+RA1J-O!-;90TQ1rL(MBR
zpRG{hcqNJBtEKo_dy7W-?`jh0f2=Irty2C;g?4K-e=?9jbD(m3AeTS~7i`f_DNmtO
zs#EGyn)Af4>Ig}buTVqeuoC>A3hYC3L`5qgN3k@ZPAji2*nFN=e+j2*5vOKp&Rl5V
zv}$QCTD?e~@=G{1i#QRRKAZrUn1)B-eI|8Po4cX^5;#P6b-_Uej#b$*)A2AdqP}YQ
zMr3C9)A%j)Q}ZqK)ATL$Q$?GvT5=D8f8!{np(z;oyfAq+{~M8-Jx&GR(DSEZF0dyh
zt){I#3fgwH@G|U)GH)^dMlEFbx9D5wFQPSc@i*At>`}9a6^}S-C5vY)ylhw+>@20r
zu9oM{+2oOVSz9-F&vd}m3*i670U+nd^TY@dh1lYd^PU0_1v%upx~0zOGIFgHl|jJr
z@j!_Ncd#Fh6mXUaFvsCNLmy-Y@i9Ik@19}v?t{Xv%<1DqReIw;;P(1mn|%R5w1Zn*
zi#UIZWvv485MB{b`DaY0P3KMsMJ~SR38Cl-o~jY38ZLFoXf`mX12$4g8}C7X&Z$5f
zs%W|}JCgg{!MtqkEJm6!nlYk$6iGwG2zn$w5)i_)izdKDKT56aFE~atk5VIdL?2BX
z7?;n*&t%gBBrof03PAG8VNp&`UBF@C-P2OY7rekEh7>2^6Dj!2NQxaJQ|KPNsS07-
z1uzO3SCOFu83Vu=iv(G~=|2mmxbyo{w$bNMb|asLSf+qKz#=H%&FB3hnr&7<+Ikx8EYkPT_af63~OWNqBG&s;gOT0Cx=l&q14vR8Hy+DZ?^(Sn3P6R}nX>bPZWI^`!i6~s?y
zYu4Afo`7EsU|lNdb+V|cSzo(hqx4ye^u0^S03u}2&(X5)FCV=DtcV5Z?Jk}sI04^S
zfx8h6I1>iT>ptxBgfMQN4HZJg5XL^g(wg45yB^C_Fh7DD;3!_2?UmY3Vta3X82{Zl
zD1HqWYq|pi18zSZvZcjlBY)5!gMmqr!Z1<5?>i3fTqU_``MtTQ0-Iqyi>k|bA+C-E
zOJ4-VKZ?ZPe=P;ZVfgMYM&be43b4Hg@ONy0WcWn2=fy$j5WMkMu>GR89p16}dtLzZ
z#fyQc+Ku1xVs&-*i>gML0>?%5_7KYk{36v57&sg_9C#7xs*W)30T1&M)M17(`2i-U
zFu@*u@if?mgFFpVunz;@3VHp^JHU_s!S`Wd!FB;wo=}jg^Y#siZ8fsL(imzJq-ytcNT4%LECP)Ikb&O%>ruX5FBWw{zunNZRb!OkLmVL_+;iXMP$!Q%1=Kl<
z+%w3{_4IPc%_FaXyd3g=O|gfjLerip|7WcnI>@7g0y;R0I%iNPceq<>hZYdcAsS1C
z$Kp!YO;>QJg-0y{YMDiCGpLO_@B)Y0;ocd*nU_)JPh+X&cf@QJinasr*=Jzd$9MotJi%3uy_~XnFC%H
zJ_rrE+-aav{=8z@SM$r?T;Pq1xcaL(UGnF6!K{x0b+}jcnBaGNGHU!7j^VFyWF&b9
zj^X7{nx*)G{V?sjxWUG&xi7zBgefBZ5)>j=0A`jV786;LAkt)&
zk_7B2O-x48mLSq(g_g8~|4ES&C0UA@lf=|YlFej-$hmbY){xEwktX{|0vU!+%qZ=g
m(jG&3kv(ITV=KlhoC?!=n-bmB7aq@|92$5LL<_4E`Sr*!q$H
literal 0
HcmV?d00001
diff --git a/__pycache__/sender.cpython-311.pyc b/__pycache__/sender.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..0d36cb616447ac42d55387c2ad9e6a2a00e0dfcf
GIT binary patch
literal 12093
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