diff --git a/README.md b/README.md index 13b3b4e..2ca7371 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ similar to `python3 -mcompileall -b` where `-b` does an in place compilation. ```python from pyce import encrypt_path encrypt_path('pyce/hello.pyc') -[('pyce/hello.pyce', '443df1d5f9914d13ed27950dd81aa2dd9d3b708be416c388f3226ad398d71a14')] +[('da78a5bfe655d01334d7121dc0f021de870dc11515424f528045eb5f29098504', 'd11d969ab1d345df86f4180db549f5ffc400da10d03d43e1fa93eae373199dae')] ``` Second, register your keys and try importing from the encrypted module or @@ -33,7 +33,7 @@ package: ```python from pyce import PYCEPathFinder -PYCEPathFinder.KEYS = {'pyce/hello.pyce' : '443df1d5f9914d13ed27950dd81aa2dd9d3b708be416c388f3226ad398d71a14'} +PYCEPathFinder.KEYS = {'da78a5bfe655d01334d7121dc0f021de870dc11515424f528045eb5f29098504': 'd11d969ab1d345df86f4180db549f5ffc400da10d03d43e1fa93eae373199dae'} import sys sys.meta_path.insert(0, PYCEPathFinder) diff --git a/pyce/_crypto.py b/pyce/_crypto.py index 7626a96..b97c1d7 100644 --- a/pyce/_crypto.py +++ b/pyce/_crypto.py @@ -24,8 +24,8 @@ Example use: >>> from pyce.crypto import encrypt_path, decryptf ->>> path, key = encrypt_path('pyce/hello.pyc')[0] ->>> print(decryptf(path, key)) +>>> hash, key = encrypt_path('pyce/hello.pyc')[0] +>>> print(decryptf('pyce/hello.pyce', key)) """ @@ -79,7 +79,7 @@ def encrypt_path(path: str, extensions: Set[str] = ENC_EXT, exclusions: exclusions: files that should not be encrypted Returns: - List of all paths and their encryption key + List of hashes of all encrypted files with their encryption key """ if exclusions is None: exclusions = set() @@ -118,8 +118,8 @@ def encrypt_path(path: str, extensions: Set[str] = ENC_EXT, exclusions: data = openf.read() # hash - hashv = srchash(data) - key = hashv.digest() + plaintext_hash = srchash(data) + key = plaintext_hash.digest() # encrypt cipher = Cipher(CIPHER(key), MODE(key[0:16]), backend=BACKEND) @@ -131,12 +131,21 @@ def encrypt_path(path: str, extensions: Set[str] = ENC_EXT, exclusions: openf.write(ciphertext) # append HMAC - openf.write(hmac(key, ciphertext, HMAC_HS).digest()) + ciphertext_hmac = hmac(key, ciphertext, HMAC_HS).digest() + openf.write(ciphertext_hmac) new_absolute_path, ext = splitext(absolute_path) new_absolute_path += ext + 'e' rename(absolute_path, new_absolute_path) - manifest.append((new_absolute_path, hashv.hexdigest())) + + # add the decryption key for this file together with a hash of its + # encrypted contents. The import mechanism will then look up the + # required key by producing this same hash again at runtime. + encrypted_data = ciphertext + ciphertext_hmac + ciphertext_hash = srchash(encrypted_data) + manifest.append( + (ciphertext_hash.hexdigest(), plaintext_hash.hexdigest()) + ) return manifest diff --git a/pyce/_imports.py b/pyce/_imports.py index 06dd52a..72ca534 100644 --- a/pyce/_imports.py +++ b/pyce/_imports.py @@ -26,10 +26,9 @@ _classify_pyc) from importlib.machinery import (FileFinder, ModuleSpec, PathFinder, SourcelessFileLoader) -from os.path import normcase, relpath from typing import Any, Dict, List, Optional, Tuple -from pyce._crypto import decrypt +from pyce._crypto import decrypt, srchash # Globals EXTENSIONS = ['.pyce'] @@ -69,8 +68,10 @@ def get_code(self, fullname: str) -> Any: path = self.get_filename(fullname) data = self.get_data(path) - # It is important to normalize path case for platforms like Windows - data = decrypt(data, PYCEPathFinder.KEYS[normcase(relpath(path))]) + # Decrypt the code. Use a hash of the encrypted data to look up the + # required encryption key. + data_hash = srchash(data).hexdigest() + data = decrypt(data, PYCEPathFinder.KEYS[data_hash]) # Call _classify_pyc to do basic validation of the pyc but ignore the # result. There's no source to check against.