purepython-aes 0.2.0__tar.gz → 0.3.1__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.
Files changed (112) hide show
  1. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/.github/workflows/release.yml +0 -18
  2. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/PKG-INFO +1 -1
  3. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/src/purepython_aes/__init__.py +8 -0
  4. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/src/purepython_aes/aes/__init__.py +21 -4
  5. purepython_aes-0.3.1/src/purepython_aes/aes/core/__init__.py +20 -0
  6. purepython_aes-0.3.1/src/purepython_aes/aes/core/fast/__init__.py +4 -0
  7. purepython_aes-0.3.1/src/purepython_aes/aes/core/fast/aes.py +214 -0
  8. purepython_aes-0.3.1/src/purepython_aes/aes/core/fast/algorithms/__init__.py +5 -0
  9. {purepython_aes-0.2.0/src/purepython_aes/aes → purepython_aes-0.3.1/src/purepython_aes/aes/core/fast}/algorithms/aes128.py +2 -2
  10. {purepython_aes-0.2.0/src/purepython_aes/aes → purepython_aes-0.3.1/src/purepython_aes/aes/core/fast}/algorithms/aes192.py +2 -2
  11. {purepython_aes-0.2.0/src/purepython_aes/aes → purepython_aes-0.3.1/src/purepython_aes/aes/core/fast}/algorithms/aes256.py +2 -2
  12. purepython_aes-0.3.1/src/purepython_aes/aes/core/fast/expansion.py +156 -0
  13. purepython_aes-0.3.1/src/purepython_aes/aes/core/fast/keys.py +49 -0
  14. purepython_aes-0.3.1/src/purepython_aes/aes/core/fast/ttables.py +533 -0
  15. purepython_aes-0.3.1/src/purepython_aes/aes/core/reference/__init__.py +13 -0
  16. {purepython_aes-0.2.0/src/purepython_aes/aes/core → purepython_aes-0.3.1/src/purepython_aes/aes/core/reference}/aes.py +3 -3
  17. purepython_aes-0.3.1/src/purepython_aes/aes/core/reference/algorithms/__init__.py +5 -0
  18. purepython_aes-0.3.1/src/purepython_aes/aes/core/reference/algorithms/aes128.py +13 -0
  19. purepython_aes-0.3.1/src/purepython_aes/aes/core/reference/algorithms/aes192.py +13 -0
  20. purepython_aes-0.3.1/src/purepython_aes/aes/core/reference/algorithms/aes256.py +13 -0
  21. {purepython_aes-0.2.0/src/purepython_aes/aes/core → purepython_aes-0.3.1/src/purepython_aes/aes/core/reference}/state.py +1 -3
  22. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/src/purepython_aes/aes/modes/__init__.py +2 -2
  23. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/src/purepython_aes/aes/modes/_base.py +1 -1
  24. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/src/purepython_aes/aes/modes/block/__init__.py +2 -1
  25. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/src/purepython_aes/aes/modes/block/_base.py +4 -4
  26. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/src/purepython_aes/aes/modes/block/cbc.py +2 -2
  27. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/src/purepython_aes/aes/modes/block/ecb.py +2 -2
  28. purepython_aes-0.3.1/src/purepython_aes/aes/modes/block/pcbc.py +35 -0
  29. purepython_aes-0.3.1/src/purepython_aes/types/__init__.py +5 -0
  30. purepython_aes-0.3.1/src/purepython_aes/types/finite/__init__.py +3 -0
  31. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/src/purepython_aes/types/finite/aliases.py +7 -0
  32. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/src/purepython_aes.egg-info/PKG-INFO +1 -1
  33. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/src/purepython_aes.egg-info/SOURCES.txt +42 -18
  34. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/src/purepython_aes.egg-info/scm_file_list.json +42 -18
  35. purepython_aes-0.3.1/src/purepython_aes.egg-info/scm_version.json +8 -0
  36. purepython_aes-0.3.1/tests/unit/aes/core/fast/algorithms/strategies.py +10 -0
  37. {purepython_aes-0.2.0/tests/unit/aes → purepython_aes-0.3.1/tests/unit/aes/core/fast}/algorithms/test_aes128.py +2 -1
  38. {purepython_aes-0.2.0/tests/unit/aes → purepython_aes-0.3.1/tests/unit/aes/core/fast}/algorithms/test_aes192.py +2 -1
  39. {purepython_aes-0.2.0/tests/unit/aes → purepython_aes-0.3.1/tests/unit/aes/core/fast}/algorithms/test_aes256.py +2 -1
  40. purepython_aes-0.3.1/tests/unit/aes/core/fast/test_aes.py +14 -0
  41. purepython_aes-0.3.1/tests/unit/aes/core/fast/test_expansion.py +66 -0
  42. purepython_aes-0.3.1/tests/unit/aes/core/fast/test_keys.py +98 -0
  43. purepython_aes-0.3.1/tests/unit/aes/core/fast/test_ttables.py +118 -0
  44. purepython_aes-0.3.1/tests/unit/aes/core/reference/algorithms/strategies.py +16 -0
  45. purepython_aes-0.3.1/tests/unit/aes/core/reference/algorithms/test_aes128.py +63 -0
  46. purepython_aes-0.3.1/tests/unit/aes/core/reference/algorithms/test_aes192.py +60 -0
  47. purepython_aes-0.3.1/tests/unit/aes/core/reference/algorithms/test_aes256.py +64 -0
  48. {purepython_aes-0.2.0/tests/unit/aes/core → purepython_aes-0.3.1/tests/unit/aes/core/reference}/test_expansion.py +1 -1
  49. {purepython_aes-0.2.0/tests/unit/aes/core → purepython_aes-0.3.1/tests/unit/aes/core/reference}/test_operations.py +1 -1
  50. {purepython_aes-0.2.0/tests/unit/aes/core → purepython_aes-0.3.1/tests/unit/aes/core/reference}/test_state.py +1 -11
  51. purepython_aes-0.3.1/tests/unit/aes/modes/block/__init__.py +0 -0
  52. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/tests/unit/aes/modes/block/test_cbc.py +11 -1
  53. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/tests/unit/aes/modes/block/test_ecb.py +11 -1
  54. purepython_aes-0.3.1/tests/unit/aes/modes/block/test_pcbc.py +80 -0
  55. purepython_aes-0.3.1/tests/unit/aes/padding/__init__.py +0 -0
  56. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/tests/unit/aes/strategies.py +1 -6
  57. purepython_aes-0.3.1/tests/unit/const/__init__.py +0 -0
  58. purepython_aes-0.2.0/src/purepython_aes/aes/algorithms/__init__.py +0 -5
  59. purepython_aes-0.2.0/src/purepython_aes/aes/core/__init__.py +0 -3
  60. purepython_aes-0.2.0/src/purepython_aes/types/__init__.py +0 -5
  61. purepython_aes-0.2.0/src/purepython_aes/types/finite/__init__.py +0 -3
  62. purepython_aes-0.2.0/src/purepython_aes.egg-info/scm_version.json +0 -8
  63. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/.github/workflows/pre-commit.yml +0 -0
  64. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/.gitignore +0 -0
  65. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/.pre-commit-config.yaml +0 -0
  66. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/LISENCE.txt +0 -0
  67. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/README.md +0 -0
  68. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/pyproject.toml +0 -0
  69. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/scripts/check-test-marks.sh +0 -0
  70. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/setup.cfg +0 -0
  71. {purepython_aes-0.2.0/src/purepython_aes/aes → purepython_aes-0.3.1/src/purepython_aes/aes/core}/interface.py +0 -0
  72. {purepython_aes-0.2.0/src/purepython_aes/aes/core → purepython_aes-0.3.1/src/purepython_aes/aes/core/reference}/expansion.py +0 -0
  73. {purepython_aes-0.2.0/src/purepython_aes/aes/core → purepython_aes-0.3.1/src/purepython_aes/aes/core/reference}/operations.py +0 -0
  74. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/src/purepython_aes/aes/modes/operations.py +0 -0
  75. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/src/purepython_aes/aes/padding/__init__.py +0 -0
  76. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/src/purepython_aes/aes/padding/_base.py +0 -0
  77. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/src/purepython_aes/aes/padding/ansix923.py +0 -0
  78. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/src/purepython_aes/aes/padding/iso10126.py +0 -0
  79. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/src/purepython_aes/aes/padding/iso7816.py +0 -0
  80. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/src/purepython_aes/aes/padding/no.py +0 -0
  81. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/src/purepython_aes/aes/padding/pkcs7.py +0 -0
  82. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/src/purepython_aes/aes/padding/zero.py +0 -0
  83. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/src/purepython_aes/const/__init__.py +0 -0
  84. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/src/purepython_aes/const/aes.py +0 -0
  85. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/src/purepython_aes/const/sbox.py +0 -0
  86. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/src/purepython_aes/py.typed +0 -0
  87. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/src/purepython_aes.egg-info/dependency_links.txt +0 -0
  88. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/src/purepython_aes.egg-info/requires.txt +0 -0
  89. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/src/purepython_aes.egg-info/top_level.txt +0 -0
  90. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/tests/__init__.py +0 -0
  91. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/tests/test_initial.py +0 -0
  92. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/tests/unit/__init__.py +0 -0
  93. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/tests/unit/aes/__init__.py +0 -0
  94. {purepython_aes-0.2.0/tests/unit/aes/algorithms → purepython_aes-0.3.1/tests/unit/aes/core}/__init__.py +0 -0
  95. {purepython_aes-0.2.0/tests/unit/aes/algorithms → purepython_aes-0.3.1/tests/unit/aes/core}/conftest.py +0 -0
  96. {purepython_aes-0.2.0/tests/unit/aes/core → purepython_aes-0.3.1/tests/unit/aes/core/fast}/__init__.py +0 -0
  97. {purepython_aes-0.2.0/tests/unit/aes/modes → purepython_aes-0.3.1/tests/unit/aes/core/fast/algorithms}/__init__.py +0 -0
  98. {purepython_aes-0.2.0/tests/unit/aes/modes/block → purepython_aes-0.3.1/tests/unit/aes/core/reference}/__init__.py +0 -0
  99. {purepython_aes-0.2.0/tests/unit/aes/padding → purepython_aes-0.3.1/tests/unit/aes/core/reference/algorithms}/__init__.py +0 -0
  100. {purepython_aes-0.2.0/tests/unit/aes/core → purepython_aes-0.3.1/tests/unit/aes/core/reference}/test_aes.py +0 -0
  101. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/tests/unit/aes/core/strategies.py +0 -0
  102. {purepython_aes-0.2.0/tests/unit/const → purepython_aes-0.3.1/tests/unit/aes/modes}/__init__.py +0 -0
  103. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/tests/unit/aes/modes/test_operations.py +0 -0
  104. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/tests/unit/aes/padding/conftest.py +0 -0
  105. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/tests/unit/aes/padding/strategies.py +0 -0
  106. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/tests/unit/aes/padding/test_ansix923.py +0 -0
  107. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/tests/unit/aes/padding/test_iso10126.py +0 -0
  108. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/tests/unit/aes/padding/test_iso7816.py +0 -0
  109. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/tests/unit/aes/padding/test_no.py +0 -0
  110. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/tests/unit/aes/padding/test_pkcs7.py +0 -0
  111. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/tests/unit/aes/padding/test_zero.py +0 -0
  112. {purepython_aes-0.2.0 → purepython_aes-0.3.1}/tests/unit/const/test_sbox.py +0 -0
@@ -50,28 +50,10 @@ jobs:
50
50
  run: python -m pip install --editable ".[dev, test]"
51
51
  - name: Run pre-commit hooks
52
52
  run: pre-commit run --all-files --show-diff-on-failure
53
- slow-tests:
54
- name: Run release-only tests
55
- needs:
56
- - validate
57
- runs-on: ubuntu-latest
58
- steps:
59
- - name: Check out tagged commit
60
- uses: actions/checkout@v4
61
- - name: Set up Python
62
- uses: actions/setup-python@v5
63
- with:
64
- python-version: "3.10"
65
- cache: pip
66
- - name: Install dependencies
67
- run: python -m pip install --editable ".[dev, test]"
68
- - name: Run slow tests
69
- run: python -m pytest -n 8 -m "slow and not quick"
70
53
  build:
71
54
  name: Build distributions
72
55
  needs:
73
56
  - pre-commit
74
- - slow-tests
75
57
  runs-on: ubuntu-latest
76
58
  steps:
77
59
  - name: Check out tagged commit
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: purepython-aes
3
- Version: 0.2.0
3
+ Version: 0.3.1
4
4
  Summary: Pure-Python implementation of AES encryption algorithm.
5
5
  Author-email: Emelianov Artem <penguin5726@proton.me>
6
6
  License-Expression: MIT
@@ -12,7 +12,11 @@ from purepython_aes.aes import (
12
12
  Iso7816Padding,
13
13
  Iso10126Padding,
14
14
  NoPadding,
15
+ PcbcMode,
15
16
  Pkcs7Padding,
17
+ ReferenceAes128,
18
+ ReferenceAes192,
19
+ ReferenceAes256,
16
20
  ZeroPadding,
17
21
  )
18
22
 
@@ -30,6 +34,10 @@ __all__: list[str] = [
30
34
  'Iso7816Padding',
31
35
  'Iso10126Padding',
32
36
  'NoPadding',
37
+ 'PcbcMode',
33
38
  'Pkcs7Padding',
39
+ 'ReferenceAes128',
40
+ 'ReferenceAes192',
41
+ 'ReferenceAes256',
34
42
  'ZeroPadding',
35
43
  ]
@@ -1,6 +1,19 @@
1
- from purepython_aes.aes.algorithms import Aes128, Aes192, Aes256
2
- from purepython_aes.aes.interface import Aes
3
- from purepython_aes.aes.modes import AesMode, BlockCipherMode, CbcMode, EcbMode
1
+ from purepython_aes.aes.core import (
2
+ Aes,
3
+ Aes128,
4
+ Aes192,
5
+ Aes256,
6
+ ReferenceAes128,
7
+ ReferenceAes192,
8
+ ReferenceAes256,
9
+ )
10
+ from purepython_aes.aes.modes import (
11
+ AesMode,
12
+ BlockCipherMode,
13
+ CbcMode,
14
+ EcbMode,
15
+ PcbcMode,
16
+ )
4
17
  from purepython_aes.aes.padding import (
5
18
  AnsiX923Padding,
6
19
  BasePadding,
@@ -12,14 +25,18 @@ from purepython_aes.aes.padding import (
12
25
  )
13
26
 
14
27
  __all__: list[str] = [
28
+ 'Aes',
15
29
  'Aes128',
16
30
  'Aes192',
17
31
  'Aes256',
18
- 'Aes',
32
+ 'ReferenceAes128',
33
+ 'ReferenceAes192',
34
+ 'ReferenceAes256',
19
35
  'AesMode',
20
36
  'BlockCipherMode',
21
37
  'CbcMode',
22
38
  'EcbMode',
39
+ 'PcbcMode',
23
40
  'AnsiX923Padding',
24
41
  'BasePadding',
25
42
  'Iso7816Padding',
@@ -0,0 +1,20 @@
1
+ from purepython_aes.aes.core.fast import Aes128, Aes192, Aes256, FastAesCore
2
+ from purepython_aes.aes.core.interface import Aes
3
+ from purepython_aes.aes.core.reference import (
4
+ AesCore,
5
+ ReferenceAes128,
6
+ ReferenceAes192,
7
+ ReferenceAes256,
8
+ )
9
+
10
+ __all__: list[str] = [
11
+ 'FastAesCore',
12
+ 'Aes128',
13
+ 'Aes192',
14
+ 'Aes256',
15
+ 'Aes',
16
+ 'AesCore',
17
+ 'ReferenceAes128',
18
+ 'ReferenceAes192',
19
+ 'ReferenceAes256',
20
+ ]
@@ -0,0 +1,4 @@
1
+ from purepython_aes.aes.core.fast.aes import FastAesCore
2
+ from purepython_aes.aes.core.fast.algorithms import Aes128, Aes192, Aes256
3
+
4
+ __all__: list[str] = ['FastAesCore', 'Aes128', 'Aes192', 'Aes256']
@@ -0,0 +1,214 @@
1
+ from dataclasses import dataclass
2
+
3
+ from purepython_aes.aes.core.fast.expansion import (
4
+ expand_aes128_key,
5
+ expand_aes192_key,
6
+ expand_aes256_key,
7
+ )
8
+ from purepython_aes.aes.core.fast.keys import (
9
+ build_decryption_round_keys,
10
+ RoundKey,
11
+ RoundKeys,
12
+ )
13
+ from purepython_aes.aes.core.fast.ttables import (
14
+ DECRYPTION_TABLE_0,
15
+ DECRYPTION_TABLE_1,
16
+ DECRYPTION_TABLE_2,
17
+ DECRYPTION_TABLE_3,
18
+ ENCRYPTION_TABLE_0,
19
+ ENCRYPTION_TABLE_1,
20
+ ENCRYPTION_TABLE_2,
21
+ ENCRYPTION_TABLE_3,
22
+ )
23
+ from purepython_aes.aes.core.interface import Aes
24
+ from purepython_aes.const import (
25
+ AES_128_KEY_SIZE,
26
+ AES_192_KEY_SIZE,
27
+ AES_256_KEY_SIZE,
28
+ AES_BLOCK_SIZE,
29
+ INVERSE_SBOX,
30
+ SBOX,
31
+ )
32
+
33
+
34
+ @dataclass(init=False, slots=True)
35
+ class FastAesCore(Aes):
36
+ """Implement optimized `encrypt_block` and `decrypt_block` methods."""
37
+
38
+ __decryption_round_keys: RoundKeys
39
+ __encryption_round_keys: RoundKeys
40
+
41
+ def __init__(self, key: bytes) -> None:
42
+ if len(key) != self.__key_size__:
43
+ raise ValueError(
44
+ f'expected len(key) == {self.__key_size__}, got {len(key)}'
45
+ )
46
+ key_size: int = len(key)
47
+ if key_size == AES_128_KEY_SIZE:
48
+ self.__encryption_round_keys = expand_aes128_key(key)
49
+ if key_size == AES_192_KEY_SIZE:
50
+ self.__encryption_round_keys = expand_aes192_key(key)
51
+ if key_size == AES_256_KEY_SIZE:
52
+ self.__encryption_round_keys = expand_aes256_key(key)
53
+ self.__decryption_round_keys = build_decryption_round_keys(
54
+ self.__encryption_round_keys
55
+ )
56
+
57
+ def encrypt_block(self, plaintext: bytes) -> bytes:
58
+ """Encrypt one 16-byte block using fused T-table rounds."""
59
+
60
+ if len(plaintext) != AES_BLOCK_SIZE:
61
+ raise ValueError(
62
+ f'expected len(plaintext) == {AES_BLOCK_SIZE}, got {len(plaintext)}'
63
+ )
64
+ round_keys: RoundKeys = self.__encryption_round_keys
65
+ plaintext_integer: int = int.from_bytes(plaintext, byteorder='big')
66
+ initial_round_key: RoundKey = round_keys[0]
67
+ state_0: int = (plaintext_integer >> 96) ^ initial_round_key[0]
68
+ state_1: int = ((plaintext_integer >> 64) & 0xFFFFFFFF) ^ initial_round_key[1]
69
+ state_2: int = ((plaintext_integer >> 32) & 0xFFFFFFFF) ^ initial_round_key[2]
70
+ state_3: int = (plaintext_integer & 0xFFFFFFFF) ^ initial_round_key[3]
71
+ for round_index in range(1, self.__round_count__):
72
+ round_key: RoundKey = round_keys[round_index]
73
+ next_state_0: int = (
74
+ ENCRYPTION_TABLE_0[(state_0 >> 24) & 0xFF]
75
+ ^ ENCRYPTION_TABLE_1[(state_1 >> 16) & 0xFF]
76
+ ^ ENCRYPTION_TABLE_2[(state_2 >> 8) & 0xFF]
77
+ ^ ENCRYPTION_TABLE_3[state_3 & 0xFF]
78
+ ^ round_key[0]
79
+ )
80
+ next_state_1: int = (
81
+ ENCRYPTION_TABLE_0[(state_1 >> 24) & 0xFF]
82
+ ^ ENCRYPTION_TABLE_1[(state_2 >> 16) & 0xFF]
83
+ ^ ENCRYPTION_TABLE_2[(state_3 >> 8) & 0xFF]
84
+ ^ ENCRYPTION_TABLE_3[state_0 & 0xFF]
85
+ ^ round_key[1]
86
+ )
87
+ next_state_2: int = (
88
+ ENCRYPTION_TABLE_0[(state_2 >> 24) & 0xFF]
89
+ ^ ENCRYPTION_TABLE_1[(state_3 >> 16) & 0xFF]
90
+ ^ ENCRYPTION_TABLE_2[(state_0 >> 8) & 0xFF]
91
+ ^ ENCRYPTION_TABLE_3[state_1 & 0xFF]
92
+ ^ round_key[2]
93
+ )
94
+ next_state_3: int = (
95
+ ENCRYPTION_TABLE_0[(state_3 >> 24) & 0xFF]
96
+ ^ ENCRYPTION_TABLE_1[(state_0 >> 16) & 0xFF]
97
+ ^ ENCRYPTION_TABLE_2[(state_1 >> 8) & 0xFF]
98
+ ^ ENCRYPTION_TABLE_3[state_2 & 0xFF]
99
+ ^ round_key[3]
100
+ )
101
+ state_0 = next_state_0
102
+ state_1 = next_state_1
103
+ state_2 = next_state_2
104
+ state_3 = next_state_3
105
+ final_round_key: RoundKey = round_keys[self.__round_count__]
106
+ output_0: int = (
107
+ (SBOX[(state_0 >> 24) & 0xFF] << 24)
108
+ | (SBOX[(state_1 >> 16) & 0xFF] << 16)
109
+ | (SBOX[(state_2 >> 8) & 0xFF] << 8)
110
+ | SBOX[state_3 & 0xFF]
111
+ ) ^ final_round_key[0]
112
+ output_1: int = (
113
+ (SBOX[(state_1 >> 24) & 0xFF] << 24)
114
+ | (SBOX[(state_2 >> 16) & 0xFF] << 16)
115
+ | (SBOX[(state_3 >> 8) & 0xFF] << 8)
116
+ | SBOX[state_0 & 0xFF]
117
+ ) ^ final_round_key[1]
118
+ output_2: int = (
119
+ (SBOX[(state_2 >> 24) & 0xFF] << 24)
120
+ | (SBOX[(state_3 >> 16) & 0xFF] << 16)
121
+ | (SBOX[(state_0 >> 8) & 0xFF] << 8)
122
+ | SBOX[state_1 & 0xFF]
123
+ ) ^ final_round_key[2]
124
+ output_3: int = (
125
+ (SBOX[(state_3 >> 24) & 0xFF] << 24)
126
+ | (SBOX[(state_0 >> 16) & 0xFF] << 16)
127
+ | (SBOX[(state_1 >> 8) & 0xFF] << 8)
128
+ | SBOX[state_2 & 0xFF]
129
+ ) ^ final_round_key[3]
130
+ output_integer: int = (
131
+ (output_0 << 96) | (output_1 << 64) | (output_2 << 32) | output_3
132
+ )
133
+ return output_integer.to_bytes(AES_BLOCK_SIZE, byteorder='big')
134
+
135
+ def decrypt_block(self, ciphertext: bytes) -> bytes:
136
+ """Decrypt one block using equivalent-inverse T-table rounds."""
137
+
138
+ if len(ciphertext) != AES_BLOCK_SIZE:
139
+ raise ValueError(
140
+ f'expected len(ciphertext) == {AES_BLOCK_SIZE}, got {len(ciphertext)}'
141
+ )
142
+ round_keys: RoundKeys = self.__decryption_round_keys
143
+ ciphertext_integer: int = int.from_bytes(
144
+ ciphertext,
145
+ byteorder='big',
146
+ )
147
+ initial_round_key: RoundKey = round_keys[0]
148
+ state_0: int = (ciphertext_integer >> 96) ^ initial_round_key[0]
149
+ state_1: int = ((ciphertext_integer >> 64) & 0xFFFFFFFF) ^ initial_round_key[1]
150
+ state_2: int = ((ciphertext_integer >> 32) & 0xFFFFFFFF) ^ initial_round_key[2]
151
+ state_3: int = (ciphertext_integer & 0xFFFFFFFF) ^ initial_round_key[3]
152
+ for round_index in range(1, self.__round_count__):
153
+ round_key: RoundKey = round_keys[round_index]
154
+ next_state_0: int = (
155
+ DECRYPTION_TABLE_0[(state_0 >> 24) & 0xFF]
156
+ ^ DECRYPTION_TABLE_1[(state_3 >> 16) & 0xFF]
157
+ ^ DECRYPTION_TABLE_2[(state_2 >> 8) & 0xFF]
158
+ ^ DECRYPTION_TABLE_3[state_1 & 0xFF]
159
+ ^ round_key[0]
160
+ )
161
+ next_state_1: int = (
162
+ DECRYPTION_TABLE_0[(state_1 >> 24) & 0xFF]
163
+ ^ DECRYPTION_TABLE_1[(state_0 >> 16) & 0xFF]
164
+ ^ DECRYPTION_TABLE_2[(state_3 >> 8) & 0xFF]
165
+ ^ DECRYPTION_TABLE_3[state_2 & 0xFF]
166
+ ^ round_key[1]
167
+ )
168
+ next_state_2: int = (
169
+ DECRYPTION_TABLE_0[(state_2 >> 24) & 0xFF]
170
+ ^ DECRYPTION_TABLE_1[(state_1 >> 16) & 0xFF]
171
+ ^ DECRYPTION_TABLE_2[(state_0 >> 8) & 0xFF]
172
+ ^ DECRYPTION_TABLE_3[state_3 & 0xFF]
173
+ ^ round_key[2]
174
+ )
175
+ next_state_3: int = (
176
+ DECRYPTION_TABLE_0[(state_3 >> 24) & 0xFF]
177
+ ^ DECRYPTION_TABLE_1[(state_2 >> 16) & 0xFF]
178
+ ^ DECRYPTION_TABLE_2[(state_1 >> 8) & 0xFF]
179
+ ^ DECRYPTION_TABLE_3[state_0 & 0xFF]
180
+ ^ round_key[3]
181
+ )
182
+ state_0 = next_state_0
183
+ state_1 = next_state_1
184
+ state_2 = next_state_2
185
+ state_3 = next_state_3
186
+ final_round_key: RoundKey = round_keys[self.__round_count__]
187
+ output_0: int = (
188
+ (INVERSE_SBOX[(state_0 >> 24) & 0xFF] << 24)
189
+ | (INVERSE_SBOX[(state_3 >> 16) & 0xFF] << 16)
190
+ | (INVERSE_SBOX[(state_2 >> 8) & 0xFF] << 8)
191
+ | INVERSE_SBOX[state_1 & 0xFF]
192
+ ) ^ final_round_key[0]
193
+ output_1: int = (
194
+ (INVERSE_SBOX[(state_1 >> 24) & 0xFF] << 24)
195
+ | (INVERSE_SBOX[(state_0 >> 16) & 0xFF] << 16)
196
+ | (INVERSE_SBOX[(state_3 >> 8) & 0xFF] << 8)
197
+ | INVERSE_SBOX[state_2 & 0xFF]
198
+ ) ^ final_round_key[1]
199
+ output_2: int = (
200
+ (INVERSE_SBOX[(state_2 >> 24) & 0xFF] << 24)
201
+ | (INVERSE_SBOX[(state_1 >> 16) & 0xFF] << 16)
202
+ | (INVERSE_SBOX[(state_0 >> 8) & 0xFF] << 8)
203
+ | INVERSE_SBOX[state_3 & 0xFF]
204
+ ) ^ final_round_key[2]
205
+ output_3: int = (
206
+ (INVERSE_SBOX[(state_3 >> 24) & 0xFF] << 24)
207
+ | (INVERSE_SBOX[(state_2 >> 16) & 0xFF] << 16)
208
+ | (INVERSE_SBOX[(state_1 >> 8) & 0xFF] << 8)
209
+ | INVERSE_SBOX[state_0 & 0xFF]
210
+ ) ^ final_round_key[3]
211
+ output_integer: int = (
212
+ (output_0 << 96) | (output_1 << 64) | (output_2 << 32) | output_3
213
+ )
214
+ return output_integer.to_bytes(AES_BLOCK_SIZE, byteorder='big')
@@ -0,0 +1,5 @@
1
+ from purepython_aes.aes.core.fast.algorithms.aes128 import Aes128
2
+ from purepython_aes.aes.core.fast.algorithms.aes192 import Aes192
3
+ from purepython_aes.aes.core.fast.algorithms.aes256 import Aes256
4
+
5
+ __all__: list[str] = ['Aes128', 'Aes192', 'Aes256']
@@ -1,12 +1,12 @@
1
1
  from dataclasses import dataclass
2
2
  from typing import ClassVar
3
3
 
4
- from purepython_aes.aes.core import AesCore
4
+ from purepython_aes.aes.core.fast.aes import FastAesCore
5
5
  from purepython_aes.const import AES_128_KEY_SIZE, AES_128_ROUND_COUNT
6
6
 
7
7
 
8
8
  @dataclass(init=False, slots=True)
9
- class Aes128(AesCore):
9
+ class Aes128(FastAesCore):
10
10
  """AES-128 encryption algorithm."""
11
11
 
12
12
  __key_size__: ClassVar[int] = AES_128_KEY_SIZE
@@ -1,12 +1,12 @@
1
1
  from dataclasses import dataclass
2
2
  from typing import ClassVar
3
3
 
4
- from purepython_aes.aes.core import AesCore
4
+ from purepython_aes.aes.core.fast.aes import FastAesCore
5
5
  from purepython_aes.const import AES_192_KEY_SIZE, AES_192_ROUND_COUNT
6
6
 
7
7
 
8
8
  @dataclass(init=False, slots=True)
9
- class Aes192(AesCore):
9
+ class Aes192(FastAesCore):
10
10
  """AES-192 encryption algorithm."""
11
11
 
12
12
  __key_size__: ClassVar[int] = AES_192_KEY_SIZE
@@ -1,12 +1,12 @@
1
1
  from dataclasses import dataclass
2
2
  from typing import ClassVar
3
3
 
4
- from purepython_aes.aes.core import AesCore
4
+ from purepython_aes.aes.core.fast.aes import FastAesCore
5
5
  from purepython_aes.const import AES_256_KEY_SIZE, AES_256_ROUND_COUNT
6
6
 
7
7
 
8
8
  @dataclass(init=False, slots=True)
9
- class Aes256(AesCore):
9
+ class Aes256(FastAesCore):
10
10
  """AES-256 encryption algorithm."""
11
11
 
12
12
  __key_size__: ClassVar[int] = AES_256_KEY_SIZE
@@ -0,0 +1,156 @@
1
+ from struct import unpack
2
+ from typing import Final
3
+
4
+ from purepython_aes.const import AES_128_ROUND_COUNT, AES_256_ROUND_COUNT, SBOX
5
+ from purepython_aes.types import RoundKey, RoundKeys
6
+
7
+ AES_192_EXPANSION_COUNT: Final[int] = 8
8
+
9
+ ROUND_CONSTANT_WORDS: Final[tuple[int, ...]] = (
10
+ 0x00000000,
11
+ 0x01000000,
12
+ 0x02000000,
13
+ 0x04000000,
14
+ 0x08000000,
15
+ 0x10000000,
16
+ 0x20000000,
17
+ 0x40000000,
18
+ 0x80000000,
19
+ 0x1B000000,
20
+ 0x36000000,
21
+ )
22
+
23
+
24
+ def expand_aes128_key(key: bytes) -> RoundKeys:
25
+ """Expand one AES-128 key directly into four-word round keys."""
26
+
27
+ initial_words: tuple[int, ...] = unpack('>4I', key)
28
+ word_0: int = initial_words[0]
29
+ word_1: int = initial_words[1]
30
+ word_2: int = initial_words[2]
31
+ word_3: int = initial_words[3]
32
+ round_keys: list[RoundKey] = [initial_words] # type: ignore[list-item]
33
+ for round_index in range(1, AES_128_ROUND_COUNT + 1):
34
+ transformed_word: int = (
35
+ (SBOX[(word_3 >> 16) & 0xFF] << 24)
36
+ | (SBOX[(word_3 >> 8) & 0xFF] << 16)
37
+ | (SBOX[word_3 & 0xFF] << 8)
38
+ | SBOX[(word_3 >> 24) & 0xFF]
39
+ ) ^ ROUND_CONSTANT_WORDS[round_index]
40
+ word_0 ^= transformed_word
41
+ word_1 ^= word_0
42
+ word_2 ^= word_1
43
+ word_3 ^= word_2
44
+ round_keys.append((word_0, word_1, word_2, word_3))
45
+ return round_keys
46
+
47
+
48
+ def expand_aes192_key(key: bytes) -> RoundKeys:
49
+ """Expand one AES-192 key directly into four-word round keys."""
50
+
51
+ (
52
+ source_word_0,
53
+ source_word_1,
54
+ source_word_2,
55
+ source_word_3,
56
+ source_word_4,
57
+ source_word_5,
58
+ ) = unpack('>6I', key)
59
+ leftover_word_0: int = source_word_4
60
+ leftover_word_1: int = source_word_5
61
+ round_keys: list[RoundKey] = [
62
+ (source_word_0, source_word_1, source_word_2, source_word_3),
63
+ ]
64
+ for round_constant_index in range(1, AES_192_EXPANSION_COUNT):
65
+ transformed_word: int = (
66
+ (SBOX[(source_word_5 >> 16) & 0xFF] << 24)
67
+ | (SBOX[(source_word_5 >> 8) & 0xFF] << 16)
68
+ | (SBOX[source_word_5 & 0xFF] << 8)
69
+ | SBOX[(source_word_5 >> 24) & 0xFF]
70
+ ) ^ ROUND_CONSTANT_WORDS[round_constant_index]
71
+ next_word_0: int = source_word_0 ^ transformed_word
72
+ next_word_1: int = source_word_1 ^ next_word_0
73
+ next_word_2: int = source_word_2 ^ next_word_1
74
+ next_word_3: int = source_word_3 ^ next_word_2
75
+ next_word_4: int = source_word_4 ^ next_word_3
76
+ next_word_5: int = source_word_5 ^ next_word_4
77
+ if round_constant_index % 2 == 1:
78
+ round_keys.append(
79
+ (leftover_word_0, leftover_word_1, next_word_0, next_word_1),
80
+ )
81
+ round_keys.append((next_word_2, next_word_3, next_word_4, next_word_5))
82
+ else:
83
+ round_keys.append((next_word_0, next_word_1, next_word_2, next_word_3))
84
+ leftover_word_0 = next_word_4
85
+ leftover_word_1 = next_word_5
86
+ source_word_0 = next_word_0
87
+ source_word_1 = next_word_1
88
+ source_word_2 = next_word_2
89
+ source_word_3 = next_word_3
90
+ source_word_4 = next_word_4
91
+ source_word_5 = next_word_5
92
+ final_transformed_word: int = (
93
+ (SBOX[(source_word_5 >> 16) & 0xFF] << 24)
94
+ | (SBOX[(source_word_5 >> 8) & 0xFF] << 16)
95
+ | (SBOX[source_word_5 & 0xFF] << 8)
96
+ | SBOX[(source_word_5 >> 24) & 0xFF]
97
+ ) ^ ROUND_CONSTANT_WORDS[AES_192_EXPANSION_COUNT]
98
+ final_word_0: int = source_word_0 ^ final_transformed_word
99
+ final_word_1: int = source_word_1 ^ final_word_0
100
+ final_word_2: int = source_word_2 ^ final_word_1
101
+ round_keys.append(
102
+ (final_word_0, final_word_1, final_word_2, source_word_3 ^ final_word_2),
103
+ )
104
+ return round_keys
105
+
106
+
107
+ def expand_aes256_key(key: bytes) -> RoundKeys:
108
+ """Expand one AES-256 key directly into four-word round keys."""
109
+
110
+ (
111
+ source_word_0,
112
+ source_word_1,
113
+ source_word_2,
114
+ source_word_3,
115
+ previous_word_0,
116
+ previous_word_1,
117
+ previous_word_2,
118
+ previous_word_3,
119
+ ) = unpack('>8I', key)
120
+ round_keys: list[RoundKey] = [
121
+ (source_word_0, source_word_1, source_word_2, source_word_3),
122
+ (previous_word_0, previous_word_1, previous_word_2, previous_word_3),
123
+ ]
124
+ for round_index in range(2, AES_256_ROUND_COUNT + 1):
125
+ transformed_word: int = (
126
+ (
127
+ (
128
+ (SBOX[(previous_word_3 >> 16) & 0xFF] << 24)
129
+ | (SBOX[(previous_word_3 >> 8) & 0xFF] << 16)
130
+ | (SBOX[previous_word_3 & 0xFF] << 8)
131
+ | SBOX[(previous_word_3 >> 24) & 0xFF]
132
+ )
133
+ ^ ROUND_CONSTANT_WORDS[round_index >> 1]
134
+ )
135
+ if round_index & 0x01 == 0
136
+ else (
137
+ (SBOX[(previous_word_3 >> 24) & 0xFF] << 24)
138
+ | (SBOX[(previous_word_3 >> 16) & 0xFF] << 16)
139
+ | (SBOX[(previous_word_3 >> 8) & 0xFF] << 8)
140
+ | SBOX[previous_word_3 & 0xFF]
141
+ )
142
+ )
143
+ next_word_0: int = source_word_0 ^ transformed_word
144
+ next_word_1: int = source_word_1 ^ next_word_0
145
+ next_word_2: int = source_word_2 ^ next_word_1
146
+ next_word_3: int = source_word_3 ^ next_word_2
147
+ round_keys.append((next_word_0, next_word_1, next_word_2, next_word_3))
148
+ source_word_0 = previous_word_0
149
+ source_word_1 = previous_word_1
150
+ source_word_2 = previous_word_2
151
+ source_word_3 = previous_word_3
152
+ previous_word_0 = next_word_0
153
+ previous_word_1 = next_word_1
154
+ previous_word_2 = next_word_2
155
+ previous_word_3 = next_word_3
156
+ return round_keys
@@ -0,0 +1,49 @@
1
+ from purepython_aes.aes.core.fast.ttables import (
2
+ DECRYPTION_TABLE_0,
3
+ DECRYPTION_TABLE_1,
4
+ DECRYPTION_TABLE_2,
5
+ DECRYPTION_TABLE_3,
6
+ )
7
+ from purepython_aes.const import SBOX
8
+ from purepython_aes.types import RoundKey, RoundKeys
9
+
10
+
11
+ def build_decryption_round_keys(
12
+ encryption_round_keys: RoundKeys,
13
+ ) -> RoundKeys:
14
+ """Build the equivalent-inverse decryption key schedule."""
15
+
16
+ round_count: int = len(encryption_round_keys) - 1
17
+ decryption_round_keys: list[RoundKey] = [encryption_round_keys[round_count]]
18
+ for encryption_round_index in range(round_count - 1, 0, -1):
19
+ word_0, word_1, word_2, word_3 = encryption_round_keys[encryption_round_index]
20
+ decryption_round_keys.append(
21
+ (
22
+ (
23
+ DECRYPTION_TABLE_0[SBOX[(word_0 >> 24) & 0xFF]]
24
+ ^ DECRYPTION_TABLE_1[SBOX[(word_0 >> 16) & 0xFF]]
25
+ ^ DECRYPTION_TABLE_2[SBOX[(word_0 >> 8) & 0xFF]]
26
+ ^ DECRYPTION_TABLE_3[SBOX[word_0 & 0xFF]]
27
+ ),
28
+ (
29
+ DECRYPTION_TABLE_0[SBOX[(word_1 >> 24) & 0xFF]]
30
+ ^ DECRYPTION_TABLE_1[SBOX[(word_1 >> 16) & 0xFF]]
31
+ ^ DECRYPTION_TABLE_2[SBOX[(word_1 >> 8) & 0xFF]]
32
+ ^ DECRYPTION_TABLE_3[SBOX[word_1 & 0xFF]]
33
+ ),
34
+ (
35
+ DECRYPTION_TABLE_0[SBOX[(word_2 >> 24) & 0xFF]]
36
+ ^ DECRYPTION_TABLE_1[SBOX[(word_2 >> 16) & 0xFF]]
37
+ ^ DECRYPTION_TABLE_2[SBOX[(word_2 >> 8) & 0xFF]]
38
+ ^ DECRYPTION_TABLE_3[SBOX[word_2 & 0xFF]]
39
+ ),
40
+ (
41
+ DECRYPTION_TABLE_0[SBOX[(word_3 >> 24) & 0xFF]]
42
+ ^ DECRYPTION_TABLE_1[SBOX[(word_3 >> 16) & 0xFF]]
43
+ ^ DECRYPTION_TABLE_2[SBOX[(word_3 >> 8) & 0xFF]]
44
+ ^ DECRYPTION_TABLE_3[SBOX[word_3 & 0xFF]]
45
+ ),
46
+ )
47
+ )
48
+ decryption_round_keys.append(encryption_round_keys[0])
49
+ return decryption_round_keys