From a2162b4a3e9d3c24d597de058a8516eef8469ae4 Mon Sep 17 00:00:00 2001 From: Timmoc Date: Mon, 25 Nov 2024 14:29:07 +0800 Subject: [PATCH 1/6] =?UTF-8?q?=E6=96=87=E4=BB=B6=E7=8E=B0=E5=9C=A8?= =?UTF-8?q?=E9=BB=98=E8=AE=A4=E4=BF=9D=E5=AD=98=E5=88=B0download=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E5=A4=B9=20=E5=A2=9E=E5=8A=A0=E5=8F=91=E9=80=81?= =?UTF-8?q?=E6=96=B9=E6=8F=90=E9=86=92=EF=BC=8C=E4=BD=BF=E7=94=A8Segwit?= =?UTF-8?q?=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- recv/recv.py | 27 ++++++++++----- tool/DownloadPathTool.py | 29 ++++++++++++++++ tool/hash/Segwit.py | 72 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 119 insertions(+), 9 deletions(-) create mode 100644 tool/DownloadPathTool.py create mode 100644 tool/hash/Segwit.py diff --git a/recv/recv.py b/recv/recv.py index 5bf5675..91bfc4a 100644 --- a/recv/recv.py +++ b/recv/recv.py @@ -1,9 +1,12 @@ import base64 +from Crypto.Hash import SHA256 + from config import config from entity.Letter import Letter -from tool import PriKeyHelper +from tool import PriKeyHelper, DownloadPathTool from tool.asymmetric import RSA +from tool.hash import Segwit from tool.symmetric import SM4 @@ -15,14 +18,15 @@ def getLetter(): handleLetter(letter) pass -def handleLetter(letter:Letter): + +def handleLetter(letter: Letter): # 解析信件 确认收信人 # 获取自身key pki = PriKeyHelper.getUserKey() if pki[1] != letter.recvPubKey: raise Exception("信件不属于自己") # 用自己的私钥解密key 获得对称加密秘钥。 - key = RSA.decrypt_message(base64.b64decode(letter.encryptKey),pki[0]) + key = RSA.decrypt_message(base64.b64decode(letter.encryptKey), pki[0]) # 根据不同的对称加密算法 try: @@ -32,9 +36,9 @@ def handleLetter(letter:Letter): # 进行解密fileBase64 data = b"" if type == config.EncryptType.SM4_ECB: - data = base64.b64decode(SM4.decrypt_ecb(letter.fileBase64,key)) + data = base64.b64decode(SM4.decrypt_ecb(letter.fileBase64, key)) elif type == config.EncryptType.SM4_CBC: - data = base64.b64decode(SM4.decrypt_cbc_with_iv(letter.fileBase64,key)) + data = base64.b64decode(SM4.decrypt_cbc_with_iv(letter.fileBase64, key)) elif type == config.EncryptType.AES_ECB: raise NotImplementedError("未实现") elif type == config.EncryptType.AES_CBC: @@ -43,12 +47,17 @@ def handleLetter(letter:Letter): raise KeyError("不支持的对称加密算法") # 用发信人的公钥验签摘要 - result = RSA.verify_signature(data,letter.sign,letter.senderPubKey) + result = RSA.verify_signature(data, letter.sign, letter.senderPubKey) if not result: raise Exception("签名验证失败,文件不可信") # 保存文件 - with open(f"./{letter.fileName}","wb") as f: + + # 默认下载目录 + download_dir = DownloadPathTool.get_download_directory() + + with open(f"{download_dir}/{letter.fileName}", "wb") as f: f.write(data) - print(f"签名验证有效,已将文件 {letter.fileName} 保存至当前目录下") - return \ No newline at end of file + print(f"确认收到来自 {Segwit.encodeSegwit(letter.senderPubKey.encode("utf-8"))} 的文件") + print(f"签名验证有效,已将文件 {letter.fileName} 保存至 {download_dir} 下") + return diff --git a/tool/DownloadPathTool.py b/tool/DownloadPathTool.py new file mode 100644 index 0000000..1f9db0a --- /dev/null +++ b/tool/DownloadPathTool.py @@ -0,0 +1,29 @@ +import sys +import winreg +from pathlib import Path + + +def get_download_directory(): + if sys.platform == 'win32': + # 打开注册表项 + key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, + r'Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders') + + try: + # 读取 "Downloads" 路径 + download_dir, _ = winreg.QueryValueEx(key, '{374DE290-123F-4565-9164-39C4925E467B}') + + # 将路径从注册表中的格式转换为标准格式 + download_dir = Path(download_dir).resolve() + + finally: + # 关闭注册表项 + winreg.CloseKey(key) + else: + # 对于非 Windows 系统,使用 ~\Downloads 作为默认下载目录 + home_dir = Path.home() + download_dir = home_dir / 'Downloads' + dir_path = Path(download_dir) + if not dir_path.exists(): + dir_path.mkdir(parents=True, exist_ok=True) + return str(download_dir) diff --git a/tool/hash/Segwit.py b/tool/hash/Segwit.py new file mode 100644 index 0000000..f2e5319 --- /dev/null +++ b/tool/hash/Segwit.py @@ -0,0 +1,72 @@ +import hashlib +import bech32 + +# https://liaoxuefeng.com/books/blockchain/bitcoin/segwit/index.html + +def hash160(data): + """ + 计算输入数据的SHA-160哈希值。 + + 参数: + data (bytes): 输入数据 + + 返回: + bytes: SHA-160哈希值 + """ + return hashlib.new('ripemd160', hashlib.sha256(data).digest()).digest() + +def encodeSegwit(data_bytes, hrp="fst"): + """ + 将字节数据编码为Bech32格式。 + + 参数: + hrp (str): 人类可读部分 (Human-readable part) + data_bytes (bytes): 要编码的数据 + + 返回: + str: Bech32编码后的字符串 + """ + + # 将字节转换为5位整数列表 + data_ints = bech32.convertbits(hash160(data_bytes), 8, 5) + if not data_ints: + raise ValueError("Failed to convert bytes to 5-bit integers") + + return bech32.bech32_encode(hrp, data_ints) + + +def verifySegwit(bech32_str): + """ + 验证Bech32编码的字符串是否有效。 + + 参数: + bech32_str (str): Bech32编码的字符串 + + 返回: + bool: 如果字符串有效则返回True,否则返回False + """ + try: + hrp, data_ints = bech32.bech32_decode(bech32_str) + if not hrp or not data_ints: + return False + + # 将5位整数列表转换回字节 + data_bytes = bech32.convertbits(data_ints, 5, 8, False) + if not data_bytes: + return False + + return True + except Exception as e: + print(f"Verification failed: {e}") + return False + + +# 示例用法 +if __name__ == "__main__": + message = b"Hello, Bech32!" + + encoded = encodeSegwit(message) + print(f"Encoded: {encoded}") + + is_valid = verifySegwit(encoded) + print(f"Is valid: {is_valid}") From 637fe0b0c8342b076f323526c52b1151f16cbb6e Mon Sep 17 00:00:00 2001 From: Timmoc Date: Tue, 26 Nov 2024 10:29:29 +0800 Subject: [PATCH 2/6] =?UTF-8?q?=E6=96=87=E4=BB=B6=E7=8E=B0=E5=9C=A8?= =?UTF-8?q?=E4=B8=8D=E4=BC=9A=E8=A6=86=E7=9B=96=E7=8E=B0=E6=9C=89=E7=9A=84?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- recv/recv.py | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/recv/recv.py b/recv/recv.py index 91bfc4a..28abeb9 100644 --- a/recv/recv.py +++ b/recv/recv.py @@ -1,6 +1,5 @@ import base64 - -from Crypto.Hash import SHA256 +import os.path from config import config from entity.Letter import Letter @@ -55,9 +54,23 @@ def handleLetter(letter: Letter): # 默认下载目录 download_dir = DownloadPathTool.get_download_directory() - - with open(f"{download_dir}/{letter.fileName}", "wb") as f: + filename = letter.fileName + base_name, ext = os.path.splitext(filename) # 分离文件名和扩展名 + newName = None + count = 0 + while True: + if count: + newName = f"{base_name}({count}){ext}" + else: + newName = filename + path = f"{download_dir}/{newName}" + if os.path.exists(path): + print(f"文件已存在,自动避免覆盖写入") + count += 1 + else: + break + with open(path, "wb") as f: f.write(data) print(f"确认收到来自 {Segwit.encodeSegwit(letter.senderPubKey.encode("utf-8"))} 的文件") - print(f"签名验证有效,已将文件 {letter.fileName} 保存至 {download_dir} 下") + print(f"签名验证有效,已将文件 {newName} 保存至 {download_dir} 下") return From b37acaaf22edae2dfc52cec2d538f8e3deae8a84 Mon Sep 17 00:00:00 2001 From: Timmoc Date: Tue, 26 Nov 2024 10:29:29 +0800 Subject: [PATCH 3/6] =?UTF-8?q?=E6=96=87=E4=BB=B6=E7=8E=B0=E5=9C=A8?= =?UTF-8?q?=E4=B8=8D=E4=BC=9A=E8=A6=86=E7=9B=96=E7=8E=B0=E6=9C=89=E7=9A=84?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- recv/recv.py | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/recv/recv.py b/recv/recv.py index 91bfc4a..8e085d6 100644 --- a/recv/recv.py +++ b/recv/recv.py @@ -1,6 +1,6 @@ import base64 - -from Crypto.Hash import SHA256 +import logging +import os.path from config import config from entity.Letter import Letter @@ -55,9 +55,23 @@ def handleLetter(letter: Letter): # 默认下载目录 download_dir = DownloadPathTool.get_download_directory() - - with open(f"{download_dir}/{letter.fileName}", "wb") as f: + filename = letter.fileName + base_name, ext = os.path.splitext(filename) # 分离文件名和扩展名 + newName = None + count = 0 + while True: + if count: + newName = f"{base_name}({count}){ext}" + else: + newName = filename + path = f"{download_dir}/{newName}" + if os.path.exists(path): + logging.debug("文件已存在,自动避免覆盖写入") + count += 1 + else: + break + with open(path, "wb") as f: f.write(data) print(f"确认收到来自 {Segwit.encodeSegwit(letter.senderPubKey.encode("utf-8"))} 的文件") - print(f"签名验证有效,已将文件 {letter.fileName} 保存至 {download_dir} 下") + print(f"签名验证有效,已将文件 {newName} 保存至 {download_dir} 下") return From b60d827192a91c660e4d00d774e9de5c94a66b77 Mon Sep 17 00:00:00 2001 From: Timmoc Date: Tue, 26 Nov 2024 11:03:09 +0800 Subject: [PATCH 4/6] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E4=B8=80=E4=B8=8B?= =?UTF-8?q?=E6=9C=AA=E8=83=BD=E9=80=9A=E8=BF=87=E6=B5=8B=E8=AF=95=E7=9A=84?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E7=94=A8=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- recv/recv.py | 6 +++--- recv/test_recv.py | 25 ++++++++++++++++++++++--- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/recv/recv.py b/recv/recv.py index 8e085d6..98679c3 100644 --- a/recv/recv.py +++ b/recv/recv.py @@ -7,7 +7,7 @@ from entity.Letter import Letter from tool import PriKeyHelper, DownloadPathTool from tool.asymmetric import RSA from tool.hash import Segwit -from tool.symmetric import SM4 +from tool.symmetric import SM4, AES def getLetter(): @@ -40,9 +40,9 @@ def handleLetter(letter: Letter): elif type == config.EncryptType.SM4_CBC: data = base64.b64decode(SM4.decrypt_cbc_with_iv(letter.fileBase64, key)) elif type == config.EncryptType.AES_ECB: - raise NotImplementedError("未实现") + data = AES.AESUtils(key).decrypt(letter.fileBase64) elif type == config.EncryptType.AES_CBC: - raise NotImplementedError("未实现") + data = AES.AESUtils(key).decrypt(letter.fileBase64,"cbc") else: raise KeyError("不支持的对称加密算法") diff --git a/recv/test_recv.py b/recv/test_recv.py index ca0ad29..1106e10 100644 --- a/recv/test_recv.py +++ b/recv/test_recv.py @@ -2,12 +2,31 @@ from unittest.mock import patch import recv from sender import sender -from tool import PriKeyHelper + + +@patch('builtins.input', side_effect=['./public.pem', 'aes', 'ecb']) +def test_handleLetter_aes_ecb(mock_input): + letter = sender.main() + recv.handleLetter(letter) + pass + + +@patch('builtins.input', side_effect=['./public.pem', 'aes', 'cbc']) +def test_handleLetter_aes_cbc(mock_input): + letter = sender.main() + recv.handleLetter(letter) + pass + + +@patch('builtins.input', side_effect=['./public.pem', 'sm4', 'ecb']) +def test_handleLetter_sm4_ecb(mock_input): + letter = sender.main() + recv.handleLetter(letter) + pass @patch('builtins.input', side_effect=['./public.pem', 'sm4', 'cbc']) -def test_handleLetter(mock_input): - pki = PriKeyHelper.getUserKey() +def test_handleLetter_sm4_cbc(mock_input): letter = sender.main() recv.handleLetter(letter) pass From 80a971702e13984f626dc8610da8017319e47b59 Mon Sep 17 00:00:00 2001 From: Timmoc Date: Tue, 26 Nov 2024 12:37:17 +0800 Subject: [PATCH 5/6] aes base64 decode --- recv/recv.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/recv/recv.py b/recv/recv.py index 98679c3..c22db5f 100644 --- a/recv/recv.py +++ b/recv/recv.py @@ -40,9 +40,9 @@ def handleLetter(letter: Letter): elif type == config.EncryptType.SM4_CBC: data = base64.b64decode(SM4.decrypt_cbc_with_iv(letter.fileBase64, key)) elif type == config.EncryptType.AES_ECB: - data = AES.AESUtils(key).decrypt(letter.fileBase64) + data = base64.b64decode(AES.AESUtils(key).decrypt(letter.fileBase64)) elif type == config.EncryptType.AES_CBC: - data = AES.AESUtils(key).decrypt(letter.fileBase64,"cbc") + data = base64.b64decode(AES.AESUtils(key).decrypt(letter.fileBase64,"cbc")) else: raise KeyError("不支持的对称加密算法") From 9a53fb2e12fb4f1bc523a052e54668c214939a78 Mon Sep 17 00:00:00 2001 From: Timmoc Date: Tue, 26 Nov 2024 12:39:16 +0800 Subject: [PATCH 6/6] =?UTF-8?q?python3.12=E6=83=8A=E5=96=9C=E5=8F=8C?= =?UTF-8?q?=E5=BC=95=E5=8F=B7=E5=86=85=E5=8F=8C=E5=BC=95=E5=8F=B7=E4=B8=8D?= =?UTF-8?q?=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- recv/recv.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/recv/recv.py b/recv/recv.py index c22db5f..c19b027 100644 --- a/recv/recv.py +++ b/recv/recv.py @@ -72,6 +72,6 @@ def handleLetter(letter: Letter): break with open(path, "wb") as f: f.write(data) - print(f"确认收到来自 {Segwit.encodeSegwit(letter.senderPubKey.encode("utf-8"))} 的文件") + print(f"确认收到来自 {Segwit.encodeSegwit(letter.senderPubKey.encode('utf-8'))} 的文件") print(f"签名验证有效,已将文件 {newName} 保存至 {download_dir} 下") return