Skip to content

Commit

Permalink
fix: 🐛 Support Windows (#31)
Browse files Browse the repository at this point in the history
  • Loading branch information
ZhaoQi99 authored Mar 26, 2024
1 parent ffc1083 commit ccee618
Show file tree
Hide file tree
Showing 7 changed files with 53 additions and 36 deletions.
9 changes: 4 additions & 5 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,16 @@ 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"]

runs-on: ${{ matrix.os }}
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
Expand Down
27 changes: 10 additions & 17 deletions pyencrypt/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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')
Expand All @@ -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"""
Expand Down Expand Up @@ -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')
Expand Down Expand Up @@ -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')
Expand All @@ -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')
Expand Down
34 changes: 26 additions & 8 deletions pyencrypt/encrypt.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
import sys
import re
from pathlib import Path
from typing import Optional
Expand Down Expand Up @@ -57,13 +58,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:
Expand All @@ -79,9 +81,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
Expand All @@ -91,7 +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},
)
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):
Expand Down
2 changes: 1 addition & 1 deletion pyencrypt/license.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
4 changes: 2 additions & 2 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down Expand Up @@ -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()
Expand Down
11 changes: 9 additions & 2 deletions tests/test_encrypt.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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,
Expand All @@ -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(
Expand Down
2 changes: 1 addition & 1 deletion tests/test_license.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.'
Expand Down

0 comments on commit ccee618

Please sign in to comment.