apppy-generic 0.12.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- apppy_generic-0.12.0/.gitignore +28 -0
- apppy_generic-0.12.0/PKG-INFO +11 -0
- apppy_generic-0.12.0/README.md +0 -0
- apppy_generic-0.12.0/generic.mk +26 -0
- apppy_generic-0.12.0/pyproject.toml +33 -0
- apppy_generic-0.12.0/src/apppy/generic/__init__.py +0 -0
- apppy_generic-0.12.0/src/apppy/generic/encrypt.py +77 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
__generated__/
|
|
2
|
+
dist/
|
|
3
|
+
*.egg-info
|
|
4
|
+
.env
|
|
5
|
+
.env.*
|
|
6
|
+
*.env
|
|
7
|
+
!.github/ci/.env.local
|
|
8
|
+
.file_store/
|
|
9
|
+
*.pid
|
|
10
|
+
.python-version
|
|
11
|
+
*.secrets
|
|
12
|
+
.secrets
|
|
13
|
+
*.tar.gz
|
|
14
|
+
*.test_output/
|
|
15
|
+
.test_output/
|
|
16
|
+
uv.lock
|
|
17
|
+
*.whl
|
|
18
|
+
|
|
19
|
+
# System files
|
|
20
|
+
__pycache__
|
|
21
|
+
.DS_Store
|
|
22
|
+
|
|
23
|
+
# Editor files
|
|
24
|
+
*.sublime-project
|
|
25
|
+
*.sublime-workspace
|
|
26
|
+
.vscode/*
|
|
27
|
+
!.vscode/settings.json
|
|
28
|
+
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: apppy-generic
|
|
3
|
+
Version: 0.12.0
|
|
4
|
+
Summary: Generic Python definitions for server development
|
|
5
|
+
Project-URL: Homepage, https://github.com/spals/apppy
|
|
6
|
+
Author: Tim Kral
|
|
7
|
+
License: MIT
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Requires-Python: >=3.11
|
|
11
|
+
Requires-Dist: cryptography==43.0.3
|
|
File without changes
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
ifndef APPPY_GENERIC_MK_INCLUDED
|
|
2
|
+
APPPY_GENERIC_MK_INCLUDED := 1
|
|
3
|
+
GENERIC_PKG_DIR := $(patsubst %/,%,$(dir $(abspath $(lastword $(MAKEFILE_LIST)))))
|
|
4
|
+
|
|
5
|
+
.PHONY: generic generic-dev generic/build generic/clean generic/install generic/install-dev
|
|
6
|
+
|
|
7
|
+
generic: generic/clean generic/install
|
|
8
|
+
|
|
9
|
+
generic-dev: generic/clean generic/install-dev
|
|
10
|
+
|
|
11
|
+
generic/build:
|
|
12
|
+
cd $(GENERIC_PKG_DIR) && uvx --from build pyproject-build
|
|
13
|
+
|
|
14
|
+
generic/clean:
|
|
15
|
+
cd $(GENERIC_PKG_DIR) && rm -rf dist/ *.egg-info .venv
|
|
16
|
+
|
|
17
|
+
generic/install: generic/build
|
|
18
|
+
cd $(GENERIC_PKG_DIR) && uv pip install dist/*.whl
|
|
19
|
+
|
|
20
|
+
generic/install-dev:
|
|
21
|
+
cd $(GENERIC_PKG_DIR) && uv pip install -e .
|
|
22
|
+
|
|
23
|
+
generic/publish: generic/clean generic/install
|
|
24
|
+
twine upload $(GENERIC_PKG_DIR)/dist/* || exit 1
|
|
25
|
+
|
|
26
|
+
endif # APPPY_GENERIC_MK_INCLUDED
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.25"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[dependency-groups]
|
|
6
|
+
dev = []
|
|
7
|
+
|
|
8
|
+
[project]
|
|
9
|
+
name = "apppy-generic"
|
|
10
|
+
description = "Generic Python definitions for server development"
|
|
11
|
+
dynamic = ["version"]
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
requires-python = ">=3.11"
|
|
14
|
+
license = {text = "MIT"}
|
|
15
|
+
authors = [{ name = "Tim Kral" }]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
]
|
|
20
|
+
dependencies = [
|
|
21
|
+
"cryptography==43.0.3",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
[project.urls]
|
|
25
|
+
Homepage = "https://github.com/spals/apppy"
|
|
26
|
+
|
|
27
|
+
[tool.hatch.build.targets.wheel]
|
|
28
|
+
packages = ["src/apppy"]
|
|
29
|
+
|
|
30
|
+
[tool.hatch.version]
|
|
31
|
+
path = "../../VERSION"
|
|
32
|
+
pattern = "^(?P<version>\\d+\\.\\d+\\.\\d+)$"
|
|
33
|
+
source = "regex"
|
|
File without changes
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
from os import urandom
|
|
2
|
+
|
|
3
|
+
from cryptography.hazmat.backends import default_backend
|
|
4
|
+
from cryptography.hazmat.primitives import hashes, padding
|
|
5
|
+
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
|
6
|
+
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
|
|
7
|
+
|
|
8
|
+
BLOCK_SIZE = 16 # AES block size in bytes
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class DecryptionFailedError(Exception):
|
|
12
|
+
"""Raised when there is an error while trying ot decrypt bytes"""
|
|
13
|
+
|
|
14
|
+
def __init__(self):
|
|
15
|
+
super().__init__("unable_to_decrypt")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class BytesEncrypter:
|
|
19
|
+
"""
|
|
20
|
+
Service to encrypt data bytes.integers into strings based on a static
|
|
21
|
+
alphabet. This allows the system to ofuscate the integer
|
|
22
|
+
values to outside parties (e.g. database primary keys)
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(self, passphrase: str, salt: str | bytes) -> None:
|
|
26
|
+
self._passphrase = passphrase
|
|
27
|
+
if isinstance(salt, str):
|
|
28
|
+
self._salt = salt.encode("utf-8")
|
|
29
|
+
else:
|
|
30
|
+
self._salt = salt
|
|
31
|
+
|
|
32
|
+
self._encryption_key = self.__derive_encryption_key()
|
|
33
|
+
|
|
34
|
+
def __derive_encryption_key(self) -> bytes:
|
|
35
|
+
passphrase_encoded = self._passphrase.encode()
|
|
36
|
+
kdf = PBKDF2HMAC(
|
|
37
|
+
algorithm=hashes.SHA256(),
|
|
38
|
+
length=32, # AES-256 requires 32 bytes
|
|
39
|
+
salt=self._salt,
|
|
40
|
+
iterations=100000,
|
|
41
|
+
backend=default_backend(),
|
|
42
|
+
)
|
|
43
|
+
return kdf.derive(passphrase_encoded)
|
|
44
|
+
|
|
45
|
+
def encrypt_bytes(self, data: bytes) -> bytes:
|
|
46
|
+
"""
|
|
47
|
+
Encrypt the entire data stream with AES-CBC and PKCS7 padding.
|
|
48
|
+
A new IV is generated for each encryption operation.
|
|
49
|
+
"""
|
|
50
|
+
iv = urandom(BLOCK_SIZE)
|
|
51
|
+
cipher = Cipher(
|
|
52
|
+
algorithms.AES(self._encryption_key), modes.CBC(iv), backend=default_backend()
|
|
53
|
+
)
|
|
54
|
+
encryptor = cipher.encryptor()
|
|
55
|
+
padder = padding.PKCS7(BLOCK_SIZE * 8).padder()
|
|
56
|
+
padded_data = padder.update(data) + padder.finalize()
|
|
57
|
+
encrypted_data = encryptor.update(padded_data) + encryptor.finalize()
|
|
58
|
+
return iv + encrypted_data
|
|
59
|
+
|
|
60
|
+
def decrypt_bytes(self, data: bytes) -> bytes:
|
|
61
|
+
"""
|
|
62
|
+
Decrypt the data stream with AES-CBC and PKCS7 unpadding.
|
|
63
|
+
The IV is extracted from the first block of the encrypted data.
|
|
64
|
+
"""
|
|
65
|
+
iv = data[:BLOCK_SIZE]
|
|
66
|
+
encrypted_data = data[BLOCK_SIZE:]
|
|
67
|
+
|
|
68
|
+
cipher = Cipher(
|
|
69
|
+
algorithms.AES(self._encryption_key), modes.CBC(iv), backend=default_backend()
|
|
70
|
+
)
|
|
71
|
+
decryptor = cipher.decryptor()
|
|
72
|
+
decrypted_data = decryptor.update(encrypted_data) + decryptor.finalize()
|
|
73
|
+
unpadder = padding.PKCS7(BLOCK_SIZE * 8).unpadder()
|
|
74
|
+
try:
|
|
75
|
+
return unpadder.update(decrypted_data) + unpadder.finalize()
|
|
76
|
+
except ValueError as e:
|
|
77
|
+
raise DecryptionFailedError() from e
|