From 02d9f77e9d3660dcae31b28e5dfc1087b179a125 Mon Sep 17 00:00:00 2001 From: Qi Zhao Date: Mon, 25 Mar 2024 11:37:58 +0800 Subject: [PATCH 1/9] =?UTF-8?q?test:=20=F0=9F=92=8D=20support=203.11=203.1?= =?UTF-8?q?2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index be5de53..70a9075 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -21,7 +21,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"] + python-version: ["3.6", "3.7", "3.8", "3.9", "3.10", "3.11", "3.12"] steps: - uses: actions/checkout@v3 From b4166d198d8584be3ca50742a2a27888a05665aa Mon Sep 17 00:00:00 2001 From: Qi Zhao Date: Mon, 25 Mar 2024 14:17:52 +0800 Subject: [PATCH 2/9] =?UTF-8?q?test:=20=F0=9F=92=8D=20update=20actions=20t?= =?UTF-8?q?o=20use=20Node.js=2020?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 70a9075..9583187 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -24,9 +24,9 @@ jobs: python-version: ["3.6", "3.7", "3.8", "3.9", "3.10", "3.11", "3.12"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v3 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install dependencies From 36e86edf21929d572874eca535de335fb215c49b Mon Sep 17 00:00:00 2001 From: Qi Zhao Date: Mon, 25 Mar 2024 14:39:03 +0800 Subject: [PATCH 3/9] =?UTF-8?q?test:=20=F0=9F=92=8D=20support=20macos=20&?= =?UTF-8?q?=20windows?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/main.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9583187..b84cd7d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -16,13 +16,12 @@ concurrency: jobs: build: - - runs-on: ubuntu-20.04 strategy: fail-fast: false matrix: + os: [ubuntu-20.04, macos-12, windows-2022] python-version: ["3.6", "3.7", "3.8", "3.9", "3.10", "3.11", "3.12"] - + runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} From 0b6e0c2183d78a2a5789cd1321d5f1382211b387 Mon Sep 17 00:00:00 2001 From: Qi Zhao Date: Mon, 25 Mar 2024 15:36:16 +0800 Subject: [PATCH 4/9] =?UTF-8?q?fix:=20=F0=9F=90=9B=20MAX=5FDATETIME?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyencrypt/license.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyencrypt/license.py b/pyencrypt/license.py index 37e4374..2d3abc4 100644 --- a/pyencrypt/license.py +++ b/pyencrypt/license.py @@ -12,7 +12,7 @@ DATE_FORMAT = '%Y-%m-%dT%H:%M:%S%z' MIN_DATETIME = datetime.now().astimezone() -MAX_DATETIME = datetime.max.replace(year=9998).astimezone() +MAX_DATETIME = datetime(year=2999, month=12, day=31).astimezone() def get_mac_address() -> str: From f9d73b4cad818cd5962bf0d14fa6e3fda956bf70 Mon Sep 17 00:00:00 2001 From: Qi Zhao Date: Tue, 26 Mar 2024 14:17:01 +0800 Subject: [PATCH 5/9] =?UTF-8?q?fix:=20=F0=9F=90=9B=20read=5Ftext=20encodin?= =?UTF-8?q?g=20in=20Windows?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyencrypt/encrypt.py | 20 +++++++++++++------- tests/conftest.py | 4 ++-- tests/test_license.py | 2 +- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/pyencrypt/encrypt.py b/pyencrypt/encrypt.py index 9d4dc28..48c6f7d 100644 --- a/pyencrypt/encrypt.py +++ b/pyencrypt/encrypt.py @@ -57,13 +57,14 @@ def generate_so_file(cipher_key: str, d: int, n: int, base_dir: Optional[Path] = need_import_files = ['ntt.py', 'aes.py', 'decrypt.py', 'license.py'] for file in need_import_files: file_path = path / file - decrypt_source_ls.append(REMOVE_SELF_IMPORT.sub('', file_path.read_text())) + decrypt_source_ls.append(REMOVE_SELF_IMPORT.sub('', file_path.read_text(encoding="utf-8"))) loader_source_path = path / 'loader.py' - loader_source = REMOVE_SELF_IMPORT.sub('', loader_source_path.read_text()).replace( - "__private_key = ''", f"__private_key = '{private_key}'", 1 - ).replace("__cipher_key = ''", f"__cipher_key = '{cipher_key}'", 1).replace( - 'license = None', f'license = {license}', 1 + loader_source = ( + REMOVE_SELF_IMPORT.sub("", loader_source_path.read_text(encoding="utf-8")) + .replace("__private_key = ''", f"__private_key = '{private_key}'", 1) + .replace("__cipher_key = ''", f"__cipher_key = '{cipher_key}'", 1) + .replace("license = None", f"license = {license}", 1) ) if base_dir is None: @@ -79,9 +80,14 @@ def generate_so_file(cipher_key: str, d: int, n: int, base_dir: Optional[Path] = # Origin file loader_origin_file_path = temp_dir / 'loader_origin.py' loader_origin_file_path.touch(exist_ok=True) - loader_origin_file_path.write_text(f"{decrypt_source}\n{loader_source}") + loader_origin_file_path.write_text( + f"{decrypt_source}\n{loader_source}", encoding="utf-8" + ) - loader_file_path.write_text(python_minifier.minify(loader_origin_file_path.read_text())) + loader_file_path.write_text( + python_minifier.minify(loader_origin_file_path.read_text(encoding="utf-8")), + encoding="utf-8", + ) from setuptools import setup # isort:skip from Cython.Build import cythonize diff --git a/tests/conftest.py b/tests/conftest.py index 6f15770..87ff186 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -30,7 +30,7 @@ def file_and_loader(request, tmp_path_factory): file_path.write_text("""\ def {function_name}(): {code} - """.format(function_name=function_name, code=code)) + """.format(function_name=function_name, code=code), encoding='utf-8') # generate loader.so key = generate_aes_key() new_path = file_path.with_suffix('.pye') @@ -74,7 +74,7 @@ def package_and_loader(request, tmp_path_factory): file_path.write_text("""\ def {function_name}(): {code} - """.format(function_name=function_name, code=code)) + """.format(function_name=function_name, code=code), encoding='utf-8') new_path = file_path.with_suffix('.pye') key = generate_aes_key() diff --git a/tests/test_license.py b/tests/test_license.py index 2ecd133..df5db90 100644 --- a/tests/test_license.py +++ b/tests/test_license.py @@ -81,7 +81,7 @@ def test_check_license_invalid(self, key, tmp_path): license_file_path = generate_license_file(key, path=tmp_path) license_data = json.loads(license_file_path.read_text()) license_data['signature'] = 'invalid' - license_file_path.write_text(json.dumps(license_data)) + license_file_path.write_text(json.dumps(license_data), encoding='utf-8') with pytest.raises(Exception) as excinfo: check_license(license_file_path, key) assert str(excinfo.value) == 'License signature is invalid.' From 0bfc19998a902f44c1990d7f10abdd9ef1dcbae2 Mon Sep 17 00:00:00 2001 From: Qi Zhao Date: Tue, 26 Mar 2024 14:51:43 +0800 Subject: [PATCH 6/9] =?UTF-8?q?test:=20=F0=9F=92=8D=20build=5Fext=20file?= =?UTF-8?q?=20path?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyencrypt/encrypt.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pyencrypt/encrypt.py b/pyencrypt/encrypt.py index 48c6f7d..af3a0b7 100644 --- a/pyencrypt/encrypt.py +++ b/pyencrypt/encrypt.py @@ -97,6 +97,7 @@ def generate_so_file(cipher_key: str, d: int, n: int, base_dir: Optional[Path] = script_args=['build_ext', '--build-lib', temp_dir.as_posix()], cmdclass={'build_ext': build_ext}, ) + print(list(temp_dir.iterdir())) return list(temp_dir.glob('loader.cpython-*-*.so'))[0].absolute() From 01d5c4fafc414af3ee26f19ad44f98c62cce15e5 Mon Sep 17 00:00:00 2001 From: Qi Zhao Date: Tue, 26 Mar 2024 14:52:14 +0800 Subject: [PATCH 7/9] =?UTF-8?q?ci:=20=F0=9F=8E=A1=20remopve=20python=203.1?= =?UTF-8?q?1=20&=203.12?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b84cd7d..c903176 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -20,7 +20,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-20.04, macos-12, windows-2022] - python-version: ["3.6", "3.7", "3.8", "3.9", "3.10", "3.11", "3.12"] + python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 From 9ea7fd47adddad4f85ab7405a7c0d1b6d39dd34c Mon Sep 17 00:00:00 2001 From: Qi Zhao Date: Tue, 26 Mar 2024 16:01:36 +0800 Subject: [PATCH 8/9] =?UTF-8?q?fix:=20=F0=9F=90=9B=20loader=20loader=5Fext?= =?UTF-8?q?ension=20path=20in=20Windows?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyencrypt/encrypt.py | 15 +++++++++++++-- tests/test_encrypt.py | 11 +++++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/pyencrypt/encrypt.py b/pyencrypt/encrypt.py index af3a0b7..1c328d3 100644 --- a/pyencrypt/encrypt.py +++ b/pyencrypt/encrypt.py @@ -1,4 +1,5 @@ import os +import sys import re from pathlib import Path from typing import Optional @@ -97,8 +98,18 @@ def generate_so_file(cipher_key: str, d: int, n: int, base_dir: Optional[Path] = script_args=['build_ext', '--build-lib', temp_dir.as_posix()], cmdclass={'build_ext': build_ext}, ) - print(list(temp_dir.iterdir())) - return list(temp_dir.glob('loader.cpython-*-*.so'))[0].absolute() + if sys.platform.startswith('win'): + # loader.cp36-win_amd64.pyd + pattern = 'loader.cp*-*.pyd' + else: + # loader.cpython-36m-x86_64-linux-gnu.so + # loader.cpython-36m-darwin.so + pattern = "loader.cpython-*-*.so" + + loader_extension = next(temp_dir.glob(pattern), None) + if loader_extension is None: + raise Exception(f"Can't find loader extension in {temp_dir.as_posix()}") + return loader_extension.absolute() def encrypt_file(path: Path, key: str, delete_origin: bool = False, new_path: Optional[Path] = None): diff --git a/tests/test_encrypt.py b/tests/test_encrypt.py index f5f7736..fe20324 100644 --- a/tests/test_encrypt.py +++ b/tests/test_encrypt.py @@ -1,6 +1,7 @@ import os from pathlib import Path import shutil +import sys import pytest from pyencrypt.encrypt import can_encrypt, encrypt_file, encrypt_key, generate_so_file @@ -47,7 +48,10 @@ def test_generate_so_file(self, key, tmp_path): assert generate_so_file(cipher_key, d, n, tmp_path) assert (tmp_path / 'encrypted' / 'loader.py').exists() is True assert (tmp_path / 'encrypted' / 'loader_origin.py').exists() is True - assert list((tmp_path / 'encrypted').glob('loader.cpython-*-*.so')) != [] + if sys.platform.startswith('win'): + assert next((tmp_path / 'encrypted').glob('loader.cp*-*.pyd'), None) is not None + else: + assert next((tmp_path / 'encrypted').glob('loader.cpython-*-*.so'), None) is not None @pytest.mark.parametrize('key', [ AES_KEY, @@ -58,7 +62,10 @@ def test_generate_so_file_default_path(self, key): assert generate_so_file(cipher_key, d, n) assert (Path(os.getcwd()) / 'encrypted' / 'loader.py').exists() is True assert (Path(os.getcwd()) / 'encrypted' / 'loader_origin.py').exists() is True - assert list((Path(os.getcwd()) / 'encrypted').glob('loader.cpython-*-*.so')) != [] + if sys.platform.startswith('win'): + assert next((Path(os.getcwd()) / 'encrypted').glob('loader.cp*-*.pyd'), None) is not None + else: + assert next((Path(os.getcwd()) / 'encrypted').glob('loader.cpython-*-*.so'), None) is not None @pytest.mark.parametrize( From 7abe3985e042d5dd2aff487dfd6588bb2f3d6a68 Mon Sep 17 00:00:00 2001 From: Qi Zhao Date: Tue, 26 Mar 2024 16:40:17 +0800 Subject: [PATCH 9/9] =?UTF-8?q?fix:=20=F0=9F=90=9B=20loader=20extension=20?= =?UTF-8?q?path=20in=20CLI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyencrypt/cli.py | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/pyencrypt/cli.py b/pyencrypt/cli.py index 74b780a..25cb125 100644 --- a/pyencrypt/cli.py +++ b/pyencrypt/cli.py @@ -10,8 +10,7 @@ from pyencrypt import __description__, __version__ from pyencrypt.decrypt import decrypt_file -from pyencrypt.encrypt import (can_encrypt, encrypt_file, encrypt_key, - generate_so_file) +from pyencrypt.encrypt import (can_encrypt, encrypt_file, encrypt_key, generate_so_file) from pyencrypt.generate import generate_aes_key from pyencrypt.license import MAX_DATETIME, MIN_DATETIME, generate_license_file @@ -34,13 +33,7 @@ """ PYTHON_MAJOR, PYTHON_MINOR = sys.version_info[:2] -LAODER_FILE_NAME = click.style( - "encrypted/loader.cpython-{major}{minor}{abi}-{platform}.so".format( - major=PYTHON_MAJOR, minor=PYTHON_MINOR, abi=sys.abiflags, platform=sys.platform - ), - blink=True, - fg='blue' -) +LOADER_FILE_NAME = click.style("encrypted/{}", blink=True, fg='blue') LICENSE_FILE_NAME = click.style("license.lic", blink=True, fg='blue') SUCCESS_ANSI = click.style('successfully', fg='green') @@ -55,17 +48,17 @@ FINISH_ENCRYPT_MSG = f""" Encryption completed {SUCCESS_ANSI}. -Please copy {LAODER_FILE_NAME} into your encrypted directory. +Please copy {LOADER_FILE_NAME} into your encrypted directory. And then remove `encrypted` directory. Finally, add `import loader` at the top of your entry file.\ """ FINISH_DECRYPT_MSG = f""" -Decryption completed {SUCCESS_ANSI}. Your origin source code has be put: %s +Decryption completed {SUCCESS_ANSI}. Your origin source code has be put: {{work_dir}} """ FINISH_GENERATE_LOADER_MSG = f""" -Generate loader file {SUCCESS_ANSI}. Your loader file is located in {LAODER_FILE_NAME} +Generate loader file {SUCCESS_ANSI}. Your loader file is located in {LOADER_FILE_NAME} """ FINISH_GENERATE_LICENSE_MSG = f""" @@ -187,11 +180,11 @@ def encrypt_command(ctx, pathname, replace, key, with_license, mac, ipv4, before raise Exception(f'{path} is not a valid path.') cipher_key, d, n = encrypt_key(key.encode()) # 需要放进导入器中 - generate_so_file(cipher_key, d, n, license=with_license) + loader_extension = generate_so_file(cipher_key, d, n, license=with_license) if with_license is True: generate_license_file(key, Path(os.getcwd()), after, before, mac, ipv4) click.echo(FINISH_GENERATE_LICENSE_MSG) - click.echo(FINISH_ENCRYPT_MSG) + click.echo(FINISH_ENCRYPT_MSG.format(loader_extension.name)) @cli.command(name='decrypt') @@ -227,7 +220,7 @@ def decrypt_command(ctx, pathname, replace, key): else: raise Exception(f'{path} is not a valid path.') - click.echo(FINISH_DECRYPT_MSG % work_dir) + click.echo(FINISH_DECRYPT_MSG.format(work_dir=work_dir)) @cli.command(name='generate') @@ -237,8 +230,8 @@ def decrypt_command(ctx, pathname, replace, key): def generate_loader(ctx, key): """Generate loader file using specified key""" cipher_key, d, n = encrypt_key(key.encode()) - generate_so_file(cipher_key, d, n, Path(os.getcwd())) - click.echo(FINISH_GENERATE_LOADER_MSG) + loader_extension = generate_so_file(cipher_key, d, n, Path(os.getcwd())) + click.echo(FINISH_GENERATE_LOADER_MSG.format(loader_extension.name)) @cli.command(name='license')