purepython-aes 0.3.1__tar.gz → 0.5.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.
Files changed (118) hide show
  1. {purepython_aes-0.3.1/src/purepython_aes.egg-info → purepython_aes-0.5.0}/PKG-INFO +1 -1
  2. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes/__init__.py +8 -0
  3. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes/aes/__init__.py +8 -0
  4. purepython_aes-0.5.0/src/purepython_aes/aes/modes/__init__.py +15 -0
  5. purepython_aes-0.5.0/src/purepython_aes/aes/modes/_base.py +31 -0
  6. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes/aes/modes/block/_base.py +2 -6
  7. purepython_aes-0.5.0/src/purepython_aes/aes/modes/stream/__init__.py +5 -0
  8. purepython_aes-0.5.0/src/purepython_aes/aes/modes/stream/_base.py +40 -0
  9. purepython_aes-0.5.0/src/purepython_aes/aes/modes/stream/cfb.py +76 -0
  10. purepython_aes-0.5.0/src/purepython_aes/aes/modes/stream/ofb.py +45 -0
  11. purepython_aes-0.5.0/src/purepython_aes/types/__init__.py +9 -0
  12. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes/types/finite/__init__.py +2 -1
  13. purepython_aes-0.5.0/src/purepython_aes/types/finite/literals.py +6 -0
  14. {purepython_aes-0.3.1 → purepython_aes-0.5.0/src/purepython_aes.egg-info}/PKG-INFO +1 -1
  15. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes.egg-info/SOURCES.txt +8 -0
  16. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes.egg-info/scm_file_list.json +8 -0
  17. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes.egg-info/scm_version.json +2 -2
  18. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/tests/unit/aes/core/fast/test_keys.py +2 -2
  19. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/tests/unit/aes/core/reference/test_expansion.py +5 -5
  20. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/tests/unit/aes/core/reference/test_operations.py +6 -6
  21. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/tests/unit/aes/core/reference/test_state.py +1 -1
  22. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/tests/unit/aes/modes/block/test_cbc.py +8 -8
  23. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/tests/unit/aes/modes/block/test_ecb.py +2 -2
  24. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/tests/unit/aes/modes/block/test_pcbc.py +9 -9
  25. purepython_aes-0.5.0/tests/unit/aes/modes/stream/test_cfb.py +48 -0
  26. purepython_aes-0.5.0/tests/unit/aes/modes/stream/test_ofb.py +28 -0
  27. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/tests/unit/aes/modes/test_operations.py +2 -2
  28. purepython_aes-0.5.0/tests/unit/const/__init__.py +0 -0
  29. purepython_aes-0.3.1/src/purepython_aes/aes/modes/__init__.py +0 -4
  30. purepython_aes-0.3.1/src/purepython_aes/aes/modes/_base.py +0 -14
  31. purepython_aes-0.3.1/src/purepython_aes/types/__init__.py +0 -5
  32. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/.github/workflows/pre-commit.yml +0 -0
  33. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/.github/workflows/release.yml +0 -0
  34. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/.gitignore +0 -0
  35. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/.pre-commit-config.yaml +0 -0
  36. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/LISENCE.txt +0 -0
  37. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/README.md +0 -0
  38. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/pyproject.toml +0 -0
  39. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/scripts/check-test-marks.sh +0 -0
  40. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/setup.cfg +0 -0
  41. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes/aes/core/__init__.py +0 -0
  42. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes/aes/core/fast/__init__.py +0 -0
  43. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes/aes/core/fast/aes.py +0 -0
  44. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes/aes/core/fast/algorithms/__init__.py +0 -0
  45. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes/aes/core/fast/algorithms/aes128.py +0 -0
  46. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes/aes/core/fast/algorithms/aes192.py +0 -0
  47. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes/aes/core/fast/algorithms/aes256.py +0 -0
  48. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes/aes/core/fast/expansion.py +0 -0
  49. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes/aes/core/fast/keys.py +0 -0
  50. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes/aes/core/fast/ttables.py +0 -0
  51. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes/aes/core/interface.py +0 -0
  52. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes/aes/core/reference/__init__.py +0 -0
  53. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes/aes/core/reference/aes.py +0 -0
  54. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes/aes/core/reference/algorithms/__init__.py +0 -0
  55. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes/aes/core/reference/algorithms/aes128.py +0 -0
  56. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes/aes/core/reference/algorithms/aes192.py +0 -0
  57. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes/aes/core/reference/algorithms/aes256.py +0 -0
  58. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes/aes/core/reference/expansion.py +0 -0
  59. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes/aes/core/reference/operations.py +0 -0
  60. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes/aes/core/reference/state.py +0 -0
  61. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes/aes/modes/block/__init__.py +0 -0
  62. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes/aes/modes/block/cbc.py +0 -0
  63. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes/aes/modes/block/ecb.py +0 -0
  64. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes/aes/modes/block/pcbc.py +0 -0
  65. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes/aes/modes/operations.py +0 -0
  66. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes/aes/padding/__init__.py +0 -0
  67. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes/aes/padding/_base.py +0 -0
  68. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes/aes/padding/ansix923.py +0 -0
  69. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes/aes/padding/iso10126.py +0 -0
  70. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes/aes/padding/iso7816.py +0 -0
  71. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes/aes/padding/no.py +0 -0
  72. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes/aes/padding/pkcs7.py +0 -0
  73. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes/aes/padding/zero.py +0 -0
  74. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes/const/__init__.py +0 -0
  75. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes/const/aes.py +0 -0
  76. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes/const/sbox.py +0 -0
  77. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes/py.typed +0 -0
  78. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes/types/finite/aliases.py +0 -0
  79. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes.egg-info/dependency_links.txt +0 -0
  80. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes.egg-info/requires.txt +0 -0
  81. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/src/purepython_aes.egg-info/top_level.txt +0 -0
  82. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/tests/__init__.py +0 -0
  83. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/tests/test_initial.py +0 -0
  84. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/tests/unit/__init__.py +0 -0
  85. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/tests/unit/aes/__init__.py +0 -0
  86. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/tests/unit/aes/core/__init__.py +0 -0
  87. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/tests/unit/aes/core/conftest.py +0 -0
  88. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/tests/unit/aes/core/fast/__init__.py +0 -0
  89. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/tests/unit/aes/core/fast/algorithms/__init__.py +0 -0
  90. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/tests/unit/aes/core/fast/algorithms/strategies.py +0 -0
  91. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/tests/unit/aes/core/fast/algorithms/test_aes128.py +0 -0
  92. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/tests/unit/aes/core/fast/algorithms/test_aes192.py +0 -0
  93. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/tests/unit/aes/core/fast/algorithms/test_aes256.py +0 -0
  94. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/tests/unit/aes/core/fast/test_aes.py +0 -0
  95. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/tests/unit/aes/core/fast/test_expansion.py +0 -0
  96. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/tests/unit/aes/core/fast/test_ttables.py +0 -0
  97. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/tests/unit/aes/core/reference/__init__.py +0 -0
  98. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/tests/unit/aes/core/reference/algorithms/__init__.py +0 -0
  99. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/tests/unit/aes/core/reference/algorithms/strategies.py +0 -0
  100. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/tests/unit/aes/core/reference/algorithms/test_aes128.py +0 -0
  101. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/tests/unit/aes/core/reference/algorithms/test_aes192.py +0 -0
  102. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/tests/unit/aes/core/reference/algorithms/test_aes256.py +0 -0
  103. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/tests/unit/aes/core/reference/test_aes.py +0 -0
  104. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/tests/unit/aes/core/strategies.py +0 -0
  105. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/tests/unit/aes/modes/__init__.py +0 -0
  106. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/tests/unit/aes/modes/block/__init__.py +0 -0
  107. {purepython_aes-0.3.1/tests/unit/aes/padding → purepython_aes-0.5.0/tests/unit/aes/modes/stream}/__init__.py +0 -0
  108. {purepython_aes-0.3.1/tests/unit/const → purepython_aes-0.5.0/tests/unit/aes/padding}/__init__.py +0 -0
  109. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/tests/unit/aes/padding/conftest.py +0 -0
  110. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/tests/unit/aes/padding/strategies.py +0 -0
  111. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/tests/unit/aes/padding/test_ansix923.py +0 -0
  112. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/tests/unit/aes/padding/test_iso10126.py +0 -0
  113. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/tests/unit/aes/padding/test_iso7816.py +0 -0
  114. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/tests/unit/aes/padding/test_no.py +0 -0
  115. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/tests/unit/aes/padding/test_pkcs7.py +0 -0
  116. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/tests/unit/aes/padding/test_zero.py +0 -0
  117. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/tests/unit/aes/strategies.py +0 -0
  118. {purepython_aes-0.3.1 → purepython_aes-0.5.0}/tests/unit/const/test_sbox.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: purepython-aes
3
- Version: 0.3.1
3
+ Version: 0.5.0
4
4
  Summary: Pure-Python implementation of AES encryption algorithm.
5
5
  Author-email: Emelianov Artem <penguin5726@proton.me>
6
6
  License-Expression: MIT
@@ -8,15 +8,19 @@ from purepython_aes.aes import (
8
8
  BasePadding,
9
9
  BlockCipherMode,
10
10
  CbcMode,
11
+ CfbMode,
12
+ CipherMode,
11
13
  EcbMode,
12
14
  Iso7816Padding,
13
15
  Iso10126Padding,
14
16
  NoPadding,
17
+ OfbMode,
15
18
  PcbcMode,
16
19
  Pkcs7Padding,
17
20
  ReferenceAes128,
18
21
  ReferenceAes192,
19
22
  ReferenceAes256,
23
+ StreamCipherMode,
20
24
  ZeroPadding,
21
25
  )
22
26
 
@@ -30,14 +34,18 @@ __all__: list[str] = [
30
34
  'BasePadding',
31
35
  'BlockCipherMode',
32
36
  'CbcMode',
37
+ 'CfbMode',
38
+ 'CipherMode',
33
39
  'EcbMode',
34
40
  'Iso7816Padding',
35
41
  'Iso10126Padding',
36
42
  'NoPadding',
43
+ 'OfbMode',
37
44
  'PcbcMode',
38
45
  'Pkcs7Padding',
39
46
  'ReferenceAes128',
40
47
  'ReferenceAes192',
41
48
  'ReferenceAes256',
49
+ 'StreamCipherMode',
42
50
  'ZeroPadding',
43
51
  ]
@@ -11,8 +11,12 @@ from purepython_aes.aes.modes import (
11
11
  AesMode,
12
12
  BlockCipherMode,
13
13
  CbcMode,
14
+ CfbMode,
15
+ CipherMode,
14
16
  EcbMode,
17
+ OfbMode,
15
18
  PcbcMode,
19
+ StreamCipherMode,
16
20
  )
17
21
  from purepython_aes.aes.padding import (
18
22
  AnsiX923Padding,
@@ -35,8 +39,12 @@ __all__: list[str] = [
35
39
  'AesMode',
36
40
  'BlockCipherMode',
37
41
  'CbcMode',
42
+ 'CfbMode',
43
+ 'CipherMode',
38
44
  'EcbMode',
45
+ 'OfbMode',
39
46
  'PcbcMode',
47
+ 'StreamCipherMode',
40
48
  'AnsiX923Padding',
41
49
  'BasePadding',
42
50
  'Iso7816Padding',
@@ -0,0 +1,15 @@
1
+ from purepython_aes.aes.modes._base import AesMode, CipherMode
2
+ from purepython_aes.aes.modes.block import BlockCipherMode, CbcMode, EcbMode, PcbcMode
3
+ from purepython_aes.aes.modes.stream import CfbMode, OfbMode, StreamCipherMode
4
+
5
+ __all__: list[str] = [
6
+ 'AesMode',
7
+ 'CipherMode',
8
+ 'BlockCipherMode',
9
+ 'CbcMode',
10
+ 'EcbMode',
11
+ 'PcbcMode',
12
+ 'CfbMode',
13
+ 'OfbMode',
14
+ 'StreamCipherMode',
15
+ ]
@@ -0,0 +1,31 @@
1
+ from __future__ import annotations
2
+
3
+ from abc import ABC, abstractmethod
4
+ from dataclasses import dataclass
5
+
6
+ from purepython_aes.aes.core.interface import Aes
7
+
8
+
9
+ @dataclass
10
+ class AesMode(ABC):
11
+ """Base class for all AES modes of operation."""
12
+
13
+ algorithm: Aes
14
+ """Either AES-128, AES-192, or AES-256."""
15
+
16
+
17
+ @dataclass(slots=True)
18
+ class CipherMode(AesMode):
19
+ """Base class for all AES Cipher modes of operation."""
20
+
21
+ @abstractmethod
22
+ def encrypt(self, plaintext: bytes) -> bytes:
23
+ """Encrypt a byte string."""
24
+
25
+ raise NotImplementedError
26
+
27
+ @abstractmethod
28
+ def decrypt(self, ciphertext: bytes) -> bytes:
29
+ """Decrypt a byte string."""
30
+
31
+ raise NotImplementedError
@@ -1,13 +1,13 @@
1
1
  from abc import ABC, abstractmethod
2
2
  from dataclasses import dataclass
3
3
 
4
- from purepython_aes.aes.modes._base import AesMode
4
+ from purepython_aes.aes.modes._base import CipherMode
5
5
  from purepython_aes.aes.padding import BasePadding
6
6
  from purepython_aes.const import AES_BLOCK_SIZE
7
7
 
8
8
 
9
9
  @dataclass(slots=True)
10
- class BlockCipherMode(AesMode, ABC):
10
+ class BlockCipherMode(CipherMode, ABC):
11
11
  padding: BasePadding
12
12
 
13
13
  @abstractmethod
@@ -23,13 +23,9 @@ class BlockCipherMode(AesMode, ABC):
23
23
  raise NotImplementedError
24
24
 
25
25
  def encrypt(self, plaintext: bytes) -> bytes:
26
- """Pad and encrypt a byte string."""
27
-
28
26
  return self.__encrypt_blocks__(self.padding.pad(plaintext))
29
27
 
30
28
  def decrypt(self, ciphertext: bytes) -> bytes:
31
- """Decrypt and unpad a byte string."""
32
-
33
29
  if len(ciphertext) % AES_BLOCK_SIZE != 0:
34
30
  raise ValueError(f'len(ciphertext) % {AES_BLOCK_SIZE} must be 0')
35
31
  return self.padding.unpad(self.__decrypt_blocks__(ciphertext))
@@ -0,0 +1,5 @@
1
+ from purepython_aes.aes.modes.stream._base import StreamCipherMode
2
+ from purepython_aes.aes.modes.stream.cfb import CfbMode
3
+ from purepython_aes.aes.modes.stream.ofb import OfbMode
4
+
5
+ __all__: list[str] = ['StreamCipherMode', 'CfbMode', 'OfbMode']
@@ -0,0 +1,40 @@
1
+ from abc import ABC, abstractmethod
2
+ from dataclasses import dataclass
3
+ from secrets import token_bytes
4
+
5
+ from purepython_aes.aes.modes._base import CipherMode
6
+ from purepython_aes.const import AES_BLOCK_SIZE
7
+
8
+
9
+ @dataclass(slots=True)
10
+ class StreamCipherMode(CipherMode, ABC):
11
+ def encrypt(self, plaintext: bytes) -> bytes:
12
+ iv: bytes = token_bytes(AES_BLOCK_SIZE)
13
+ ciphertext: bytes = self.__encrypt_stream__(iv, plaintext)
14
+ return iv + ciphertext
15
+
16
+ def decrypt(self, ciphertext: bytes) -> bytes:
17
+ return self.__decrypt_stream__(
18
+ initialization_value=ciphertext[:AES_BLOCK_SIZE],
19
+ ciphertext=ciphertext[AES_BLOCK_SIZE:],
20
+ )
21
+
22
+ @abstractmethod
23
+ def __encrypt_stream__(
24
+ self,
25
+ initialization_value: bytes,
26
+ plaintext: bytes,
27
+ ) -> bytes:
28
+ """Transform plaintext into ciphertext."""
29
+
30
+ raise NotImplementedError
31
+
32
+ @abstractmethod
33
+ def __decrypt_stream__(
34
+ self,
35
+ initialization_value: bytes,
36
+ ciphertext: bytes,
37
+ ) -> bytes:
38
+ """Transform ciphertext into plaintext."""
39
+
40
+ raise NotImplementedError
@@ -0,0 +1,76 @@
1
+ from dataclasses import dataclass
2
+ from typing import final
3
+
4
+ from purepython_aes.aes.modes.stream._base import StreamCipherMode
5
+ from purepython_aes.const import AES_BLOCK_SIZE
6
+ from purepython_aes.types import CfbSegmentSize
7
+
8
+
9
+ @final
10
+ @dataclass(slots=True)
11
+ class CfbMode(StreamCipherMode):
12
+ """Cipher FeedBack mode of operation."""
13
+
14
+ segment_size: CfbSegmentSize = AES_BLOCK_SIZE # type: ignore[assignment]
15
+ """Feedback segment size in bytes."""
16
+
17
+ def __post_init__(self) -> None:
18
+ if self.segment_size < 1 or self.segment_size > AES_BLOCK_SIZE:
19
+ raise ValueError(
20
+ ', '.join(
21
+ (
22
+ f'expected 1 <= segment_size <= {AES_BLOCK_SIZE}',
23
+ f'got {self.segment_size}',
24
+ )
25
+ )
26
+ )
27
+
28
+ def __encrypt_stream__(
29
+ self,
30
+ initialization_value: bytes,
31
+ plaintext: bytes,
32
+ ) -> bytes:
33
+ feedback_register: bytes = initialization_value
34
+ plaintext_size: int = len(plaintext)
35
+ ciphertext: bytearray = bytearray(plaintext_size)
36
+ for segment_start in range(0, plaintext_size, self.segment_size):
37
+ segment_end: int = min(segment_start + self.segment_size, plaintext_size)
38
+ segment_length: int = segment_end - segment_start
39
+ encrypted_register: bytes = self.algorithm.encrypt_block(feedback_register)
40
+ for segment_index in range(segment_length):
41
+ ciphertext[segment_start + segment_index] = (
42
+ plaintext[segment_start + segment_index]
43
+ ^ encrypted_register[segment_index]
44
+ )
45
+ ciphertext_segment: bytes = bytes(ciphertext[segment_start:segment_end])
46
+ feedback_register = (
47
+ feedback_register[segment_length:] + bytes(ciphertext_segment)
48
+ if segment_length != AES_BLOCK_SIZE
49
+ else ciphertext_segment
50
+ )
51
+ return bytes(ciphertext)
52
+
53
+ def __decrypt_stream__(
54
+ self,
55
+ initialization_value: bytes,
56
+ ciphertext: bytes,
57
+ ) -> bytes:
58
+ feedback_register: bytes = initialization_value
59
+ ciphertext_size: int = len(ciphertext)
60
+ plaintext: bytearray = bytearray(ciphertext_size)
61
+ for segment_start in range(0, ciphertext_size, self.segment_size):
62
+ segment_end: int = min(segment_start + self.segment_size, ciphertext_size)
63
+ segment_length: int = segment_end - segment_start
64
+ encrypted_register: bytes = self.algorithm.encrypt_block(feedback_register)
65
+ for segment_index in range(segment_length):
66
+ plaintext[segment_start + segment_index] = (
67
+ ciphertext[segment_start + segment_index]
68
+ ^ encrypted_register[segment_index]
69
+ )
70
+ ciphertext_segment: bytes = ciphertext[segment_start:segment_end]
71
+ feedback_register = (
72
+ feedback_register[segment_length:] + ciphertext_segment
73
+ if segment_length != AES_BLOCK_SIZE
74
+ else ciphertext_segment
75
+ )
76
+ return bytes(plaintext)
@@ -0,0 +1,45 @@
1
+ from dataclasses import dataclass
2
+ from typing import final
3
+
4
+ from purepython_aes.aes.modes.stream._base import StreamCipherMode
5
+ from purepython_aes.const import AES_BLOCK_SIZE
6
+
7
+
8
+ @final
9
+ @dataclass(slots=True)
10
+ class OfbMode(StreamCipherMode):
11
+ """Output FeedBack mode of operation."""
12
+
13
+ def __encrypt_stream__(
14
+ self,
15
+ initialization_value: bytes,
16
+ plaintext: bytes,
17
+ ) -> bytes:
18
+ return self.__transform_stream__(initialization_value, plaintext)
19
+
20
+ def __decrypt_stream__(
21
+ self,
22
+ initialization_value: bytes,
23
+ ciphertext: bytes,
24
+ ) -> bytes:
25
+ return self.__transform_stream__(initialization_value, ciphertext)
26
+
27
+ def __transform_stream__(
28
+ self,
29
+ initialization_value: bytes,
30
+ data: bytes,
31
+ ) -> bytes:
32
+ """XOR data with the OFB keystream."""
33
+
34
+ feedback_register: bytes = initialization_value
35
+ data_size: int = len(data)
36
+ transformed_data: bytearray = bytearray(data_size)
37
+ for block_start in range(0, data_size, AES_BLOCK_SIZE):
38
+ block_end: int = min(block_start + AES_BLOCK_SIZE, data_size)
39
+ block_length: int = block_end - block_start
40
+ feedback_register = self.algorithm.encrypt_block(feedback_register)
41
+ for block_index in range(block_length):
42
+ transformed_data[block_start + block_index] = (
43
+ data[block_start + block_index] ^ feedback_register[block_index]
44
+ )
45
+ return bytes(transformed_data)
@@ -0,0 +1,9 @@
1
+ # noqa: A005
2
+ from purepython_aes.types.finite import (
3
+ CfbSegmentSize,
4
+ IntLookupTable256,
5
+ RoundKey,
6
+ RoundKeys,
7
+ )
8
+
9
+ __all__: list[str] = ['CfbSegmentSize', 'IntLookupTable256', 'RoundKey', 'RoundKeys']
@@ -1,3 +1,4 @@
1
1
  from purepython_aes.types.finite.aliases import IntLookupTable256, RoundKey, RoundKeys
2
+ from purepython_aes.types.finite.literals import CfbSegmentSize
2
3
 
3
- __all__: list[str] = ['IntLookupTable256', 'RoundKey', 'RoundKeys']
4
+ __all__: list[str] = ['IntLookupTable256', 'RoundKey', 'RoundKeys', 'CfbSegmentSize']
@@ -0,0 +1,6 @@
1
+ from typing import Literal, TypeAlias
2
+
3
+ CfbSegmentSize: TypeAlias = Literal[
4
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
5
+ ]
6
+ """Allowed value for `segment_size` field of CfbMode."""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: purepython-aes
3
- Version: 0.3.1
3
+ Version: 0.5.0
4
4
  Summary: Pure-Python implementation of AES encryption algorithm.
5
5
  Author-email: Emelianov Artem <penguin5726@proton.me>
6
6
  License-Expression: MIT
@@ -44,6 +44,10 @@ src/purepython_aes/aes/modes/block/_base.py
44
44
  src/purepython_aes/aes/modes/block/cbc.py
45
45
  src/purepython_aes/aes/modes/block/ecb.py
46
46
  src/purepython_aes/aes/modes/block/pcbc.py
47
+ src/purepython_aes/aes/modes/stream/__init__.py
48
+ src/purepython_aes/aes/modes/stream/_base.py
49
+ src/purepython_aes/aes/modes/stream/cfb.py
50
+ src/purepython_aes/aes/modes/stream/ofb.py
47
51
  src/purepython_aes/aes/padding/__init__.py
48
52
  src/purepython_aes/aes/padding/_base.py
49
53
  src/purepython_aes/aes/padding/ansix923.py
@@ -58,6 +62,7 @@ src/purepython_aes/const/sbox.py
58
62
  src/purepython_aes/types/__init__.py
59
63
  src/purepython_aes/types/finite/__init__.py
60
64
  src/purepython_aes/types/finite/aliases.py
65
+ src/purepython_aes/types/finite/literals.py
61
66
  tests/__init__.py
62
67
  tests/test_initial.py
63
68
  tests/unit/__init__.py
@@ -92,6 +97,9 @@ tests/unit/aes/modes/block/__init__.py
92
97
  tests/unit/aes/modes/block/test_cbc.py
93
98
  tests/unit/aes/modes/block/test_ecb.py
94
99
  tests/unit/aes/modes/block/test_pcbc.py
100
+ tests/unit/aes/modes/stream/__init__.py
101
+ tests/unit/aes/modes/stream/test_cfb.py
102
+ tests/unit/aes/modes/stream/test_ofb.py
95
103
  tests/unit/aes/padding/__init__.py
96
104
  tests/unit/aes/padding/conftest.py
97
105
  tests/unit/aes/padding/strategies.py
@@ -11,6 +11,7 @@
11
11
  "src/purepython_aes/types/__init__.py",
12
12
  "src/purepython_aes/types/finite/aliases.py",
13
13
  "src/purepython_aes/types/finite/__init__.py",
14
+ "src/purepython_aes/types/finite/literals.py",
14
15
  "src/purepython_aes/const/__init__.py",
15
16
  "src/purepython_aes/const/aes.py",
16
17
  "src/purepython_aes/const/sbox.py",
@@ -46,6 +47,10 @@
46
47
  "src/purepython_aes/aes/modes/_base.py",
47
48
  "src/purepython_aes/aes/modes/__init__.py",
48
49
  "src/purepython_aes/aes/modes/operations.py",
50
+ "src/purepython_aes/aes/modes/stream/_base.py",
51
+ "src/purepython_aes/aes/modes/stream/cfb.py",
52
+ "src/purepython_aes/aes/modes/stream/__init__.py",
53
+ "src/purepython_aes/aes/modes/stream/ofb.py",
49
54
  "src/purepython_aes/aes/modes/block/_base.py",
50
55
  "src/purepython_aes/aes/modes/block/__init__.py",
51
56
  "src/purepython_aes/aes/modes/block/cbc.py",
@@ -92,6 +97,9 @@
92
97
  "tests/unit/aes/padding/test_no.py",
93
98
  "tests/unit/aes/modes/test_operations.py",
94
99
  "tests/unit/aes/modes/__init__.py",
100
+ "tests/unit/aes/modes/stream/test_cfb.py",
101
+ "tests/unit/aes/modes/stream/__init__.py",
102
+ "tests/unit/aes/modes/stream/test_ofb.py",
95
103
  "tests/unit/aes/modes/block/__init__.py",
96
104
  "tests/unit/aes/modes/block/test_pcbc.py",
97
105
  "tests/unit/aes/modes/block/test_ecb.py",
@@ -1,7 +1,7 @@
1
1
  {
2
- "tag": "0.3.1",
2
+ "tag": "0.5.0",
3
3
  "distance": 0,
4
- "node": "g35d4036fe4f9868f92a439cff80b412c016aa475",
4
+ "node": "g393fbd90ca44525a385004d5d26622a21b782356",
5
5
  "dirty": false,
6
6
  "branch": "HEAD",
7
7
  "node_date": "2026-07-14"
@@ -19,11 +19,11 @@ from tests.unit.aes.strategies import aes128key, aes192key, aes256key
19
19
  @mark.quick
20
20
  @mark.parametrize(
21
21
  'round_count',
22
- (
22
+ [
23
23
  AES_128_ROUND_COUNT,
24
24
  AES_192_ROUND_COUNT,
25
25
  AES_256_ROUND_COUNT,
26
- ),
26
+ ],
27
27
  )
28
28
  def test_preserves_round_key_count(round_count: int) -> None:
29
29
  encryption_round_keys: RoundKeys = [
@@ -13,15 +13,15 @@ from purepython_aes.const import (
13
13
  AES_BLOCK_SIZE,
14
14
  )
15
15
 
16
- AES_CONFIGURATIONS: Final[tuple[tuple[int, int], tuple[int, int], tuple[int, int]]] = (
16
+ AES_CONFIGURATIONS: Final[list[tuple[int, int]]] = [
17
17
  (AES_128_KEY_SIZE, AES_128_ROUND_COUNT),
18
18
  (AES_192_KEY_SIZE, AES_192_ROUND_COUNT),
19
19
  (AES_256_KEY_SIZE, AES_256_ROUND_COUNT),
20
- )
20
+ ]
21
21
 
22
22
 
23
23
  @mark.quick
24
- @mark.parametrize(('key_size', 'round_count'), AES_CONFIGURATIONS)
24
+ @mark.parametrize(['key_size', 'round_count'], AES_CONFIGURATIONS)
25
25
  def test_expand_key_returns_corrent_number_of_keys(
26
26
  key_size: int,
27
27
  round_count: int,
@@ -31,14 +31,14 @@ def test_expand_key_returns_corrent_number_of_keys(
31
31
 
32
32
 
33
33
  @mark.quick
34
- @mark.parametrize(('key_size', 'round_count'), AES_CONFIGURATIONS)
34
+ @mark.parametrize(['key_size', 'round_count'], AES_CONFIGURATIONS)
35
35
  def test_expand_key_returns_16_byte_round_keys(key_size: int, round_count: int) -> None:
36
36
  round_keys: tuple[bytes, ...] = expand_key(bytes(key_size), round_count)
37
37
  assert all(len(round_key) == AES_BLOCK_SIZE for round_key in round_keys)
38
38
 
39
39
 
40
40
  @mark.quick
41
- @mark.parametrize(('key_size', 'round_count'), AES_CONFIGURATIONS)
41
+ @mark.parametrize(['key_size', 'round_count'], AES_CONFIGURATIONS)
42
42
  def test_expanded_schedule_starts_with_original_key(
43
43
  key_size: int,
44
44
  round_count: int,
@@ -29,8 +29,8 @@ def xor_columns(
29
29
 
30
30
  @mark.quick
31
31
  @mark.parametrize(
32
- ('left', 'right', 'expected'),
33
- (
32
+ ['left', 'right', 'expected'],
33
+ [
34
34
  (0x00, 0x00, 0x00),
35
35
  (0x00, 0x01, 0x00),
36
36
  (0x01, 0x00, 0x00),
@@ -45,7 +45,7 @@ def xor_columns(
45
45
  (0xFF, 0xFF, 0x13),
46
46
  (0x80, 0x02, 0x1B),
47
47
  (0x80, 0x80, 0x9A),
48
- ),
48
+ ],
49
49
  )
50
50
  def test_galois256_multiply_known_values(left: int, right: int, expected: int) -> None:
51
51
  assert galois256_multiply(left, right) == expected
@@ -107,8 +107,8 @@ def test_galois256_multiply_right_distributes_over_xor() -> None:
107
107
 
108
108
  @mark.quick
109
109
  @mark.parametrize(
110
- ('values', 'expected'),
111
- (
110
+ ['values', 'expected'],
111
+ [
112
112
  ((), 0x00),
113
113
  ((0x00,), 0x00),
114
114
  ((0x57,), 0x57),
@@ -117,7 +117,7 @@ def test_galois256_multiply_right_distributes_over_xor() -> None:
117
117
  ((0xFF, 0x0F, 0xF0), 0x00),
118
118
  ((0x01, 0x02, 0x04, 0x08), 0x0F),
119
119
  ((0xDE, 0xAD, 0xBE, 0xEF), 0x22),
120
- ),
120
+ ],
121
121
  )
122
122
  def test_xor_bytes_known_values(values: tuple[int, ...], expected: int) -> None:
123
123
  assert xor_bytes(*values) == expected
@@ -17,7 +17,7 @@ class TestAesState:
17
17
  assert state.to_bytes() == block
18
18
 
19
19
  @staticmethod
20
- @mark.parametrize('block', (bytes(), bytes(1), bytes(15), bytes(17), bytes(32)))
20
+ @mark.parametrize('block', [bytes(), bytes(1), bytes(15), bytes(17), bytes(32)])
21
21
  def test_from_bytes_rejects_invalid_block_lengths(block: bytes) -> None:
22
22
  with raises(
23
23
  expected_exception=ValueError,
@@ -50,11 +50,11 @@ class TestCbcMode:
50
50
  key: bytes,
51
51
  data: bytes,
52
52
  ) -> None:
53
- ecb: CbcMode = CbcMode(aes(key[:aes.__key_size__]), padding()) # fmt: skip
54
- assert ecb.decrypt(ecb.encrypt(data)) == data
53
+ cbc: CbcMode = CbcMode(aes(key[:aes.__key_size__]), padding()) # fmt: skip
54
+ assert cbc.decrypt(cbc.encrypt(data)) == data
55
55
 
56
56
  @staticmethod
57
- @mark.parametrize(['aes'], [[Aes128], [Aes192], [Aes256]])
57
+ @mark.parametrize('aes', [Aes128, Aes192, Aes256])
58
58
  @given(key=aes256key, data=binary())
59
59
  def test_decrypt_inverts_encrypt_zero_padding(
60
60
  aes: type[Aes],
@@ -62,16 +62,16 @@ class TestCbcMode:
62
62
  data: bytes,
63
63
  ) -> None:
64
64
  assume(not data.endswith(bytes(1)))
65
- ecb: CbcMode = CbcMode(aes(key[:aes.__key_size__]), ZeroPadding()) # fmt: skip
66
- assert ecb.decrypt(ecb.encrypt(data)) == data
65
+ cbc: CbcMode = CbcMode(aes(key[:aes.__key_size__]), ZeroPadding()) # fmt: skip
66
+ assert cbc.decrypt(cbc.encrypt(data)) == data
67
67
 
68
68
  @staticmethod
69
- @mark.parametrize(['aes'], [[Aes128], [Aes192], [Aes256]])
69
+ @mark.parametrize('aes', [Aes128, Aes192, Aes256])
70
70
  @given(key=aes256key, data=aes_blocks)
71
71
  def test_decrypt_inverts_encrypt_no_padding(
72
72
  aes: type[Aes],
73
73
  key: bytes,
74
74
  data: bytes,
75
75
  ) -> None:
76
- ecb: CbcMode = CbcMode(aes(key[:aes.__key_size__]), NoPadding()) # fmt: skip
77
- assert ecb.decrypt(ecb.encrypt(data)) == data
76
+ cbc: CbcMode = CbcMode(aes(key[:aes.__key_size__]), NoPadding()) # fmt: skip
77
+ assert cbc.decrypt(cbc.encrypt(data)) == data
@@ -54,7 +54,7 @@ class TestEcbMode:
54
54
  assert ecb.decrypt(ecb.encrypt(data)) == data
55
55
 
56
56
  @staticmethod
57
- @mark.parametrize(['aes'], [[Aes128], [Aes192], [Aes256]])
57
+ @mark.parametrize('aes', [Aes128, Aes192, Aes256])
58
58
  @given(key=aes256key, data=binary())
59
59
  def test_decrypt_inverts_encrypt_zero_padding(
60
60
  aes: type[Aes],
@@ -66,7 +66,7 @@ class TestEcbMode:
66
66
  assert ecb.decrypt(ecb.encrypt(data)) == data
67
67
 
68
68
  @staticmethod
69
- @mark.parametrize(['aes'], [[Aes128], [Aes192], [Aes256]])
69
+ @mark.parametrize('aes', [Aes128, Aes192, Aes256])
70
70
  @given(key=aes256key, data=aes_blocks)
71
71
  def test_decrypt_inverts_encrypt_no_padding(
72
72
  aes: type[Aes],
@@ -25,7 +25,7 @@ from tests.unit.aes.strategies import aes256key, aes_blocks
25
25
 
26
26
 
27
27
  @mark.quick
28
- class TestCbcMode:
28
+ class TestPcbcMode:
29
29
  @staticmethod
30
30
  @mark.parametrize(
31
31
  ['aes', 'padding'],
@@ -50,11 +50,11 @@ class TestCbcMode:
50
50
  key: bytes,
51
51
  data: bytes,
52
52
  ) -> None:
53
- ecb: PcbcMode = PcbcMode(aes(key[:aes.__key_size__]), padding()) # fmt: skip
54
- assert ecb.decrypt(ecb.encrypt(data)) == data
53
+ pcbc: PcbcMode = PcbcMode(aes(key[:aes.__key_size__]), padding()) # fmt: skip
54
+ assert pcbc.decrypt(pcbc.encrypt(data)) == data
55
55
 
56
56
  @staticmethod
57
- @mark.parametrize(['aes'], [[Aes128], [Aes192], [Aes256]])
57
+ @mark.parametrize('aes', [Aes128, Aes192, Aes256])
58
58
  @given(key=aes256key, data=binary())
59
59
  def test_decrypt_inverts_encrypt_zero_padding(
60
60
  aes: type[Aes],
@@ -62,19 +62,19 @@ class TestCbcMode:
62
62
  data: bytes,
63
63
  ) -> None:
64
64
  assume(not data.endswith(bytes(1)))
65
- ecb: PcbcMode = PcbcMode(
65
+ pcbc: PcbcMode = PcbcMode(
66
66
  algorithm=aes(key[:aes.__key_size__]), # fmt: skip
67
67
  padding=ZeroPadding(),
68
68
  )
69
- assert ecb.decrypt(ecb.encrypt(data)) == data
69
+ assert pcbc.decrypt(pcbc.encrypt(data)) == data
70
70
 
71
71
  @staticmethod
72
- @mark.parametrize(['aes'], [[Aes128], [Aes192], [Aes256]])
72
+ @mark.parametrize('aes', [Aes128, Aes192, Aes256])
73
73
  @given(key=aes256key, data=aes_blocks)
74
74
  def test_decrypt_inverts_encrypt_no_padding(
75
75
  aes: type[Aes],
76
76
  key: bytes,
77
77
  data: bytes,
78
78
  ) -> None:
79
- ecb: PcbcMode = PcbcMode(aes(key[:aes.__key_size__]), NoPadding()) # fmt: skip
80
- assert ecb.decrypt(ecb.encrypt(data)) == data
79
+ pcbc: PcbcMode = PcbcMode(aes(key[:aes.__key_size__]), NoPadding()) # fmt: skip
80
+ assert pcbc.decrypt(pcbc.encrypt(data)) == data
@@ -0,0 +1,48 @@
1
+ from itertools import product
2
+
3
+ from hypothesis import given
4
+ from hypothesis.strategies import binary
5
+ from pytest import mark
6
+
7
+ from purepython_aes import (
8
+ Aes,
9
+ Aes128,
10
+ Aes192,
11
+ Aes256,
12
+ CfbMode,
13
+ ReferenceAes128,
14
+ ReferenceAes192,
15
+ ReferenceAes256,
16
+ )
17
+ from purepython_aes.types import CfbSegmentSize
18
+ from tests.unit.aes.strategies import aes256key
19
+
20
+
21
+ @mark.quick
22
+ class TestCfbMode:
23
+ @staticmethod
24
+ @mark.parametrize(
25
+ ['aes', 'segment_size'],
26
+ tuple(
27
+ product(
28
+ [
29
+ Aes128,
30
+ Aes192,
31
+ Aes256,
32
+ ReferenceAes128,
33
+ ReferenceAes192,
34
+ ReferenceAes256,
35
+ ],
36
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
37
+ ),
38
+ ),
39
+ )
40
+ @given(key=aes256key, data=binary())
41
+ def test_decrypt_inverts_encrypt(
42
+ aes: type[Aes],
43
+ segment_size: CfbSegmentSize,
44
+ key: bytes,
45
+ data: bytes,
46
+ ) -> None:
47
+ cfb: CfbMode = CfbMode(aes(key[:aes.__key_size__]), segment_size) # fmt: skip
48
+ assert cfb.decrypt(cfb.encrypt(data)) == data
@@ -0,0 +1,28 @@
1
+ from hypothesis import given
2
+ from hypothesis.strategies import binary
3
+ from pytest import mark
4
+
5
+ from purepython_aes import (
6
+ Aes,
7
+ Aes128,
8
+ Aes192,
9
+ Aes256,
10
+ OfbMode,
11
+ ReferenceAes128,
12
+ ReferenceAes192,
13
+ ReferenceAes256,
14
+ )
15
+ from tests.unit.aes.strategies import aes256key
16
+
17
+
18
+ @mark.quick
19
+ class TestOfbMode:
20
+ @staticmethod
21
+ @mark.parametrize(
22
+ 'aes',
23
+ [Aes128, Aes192, Aes256, ReferenceAes128, ReferenceAes192, ReferenceAes256],
24
+ )
25
+ @given(key=aes256key, data=binary())
26
+ def test_decrypt_inverts_encrypt(aes: type[Aes], key: bytes, data: bytes) -> None:
27
+ ofb: OfbMode = OfbMode(aes(key[:aes.__key_size__])) # fmt: skip
28
+ assert ofb.decrypt(ofb.encrypt(data)) == data
@@ -13,7 +13,7 @@ def test_xor_empty_inputs() -> None:
13
13
 
14
14
  @mark.quick
15
15
  @mark.parametrize(
16
- ('left', 'right', 'expected'),
16
+ ['left', 'right', 'expected'],
17
17
  [
18
18
  (b'\x00', b'\x00', b'\x00'),
19
19
  (b'\x00', b'\xff', b'\xff'),
@@ -45,7 +45,7 @@ def test_xor_known_values(left: bytes, right: bytes, expected: bytes) -> None:
45
45
 
46
46
  @mark.quick
47
47
  @mark.parametrize(
48
- ('left', 'right'),
48
+ ['left', 'right'],
49
49
  [
50
50
  (b'\x00', bytes(0)),
51
51
  (bytes(0), b'\x00'),
File without changes
@@ -1,4 +0,0 @@
1
- from purepython_aes.aes.modes._base import AesMode
2
- from purepython_aes.aes.modes.block import BlockCipherMode, CbcMode, EcbMode, PcbcMode
3
-
4
- __all__: list[str] = ['AesMode', 'BlockCipherMode', 'CbcMode', 'EcbMode', 'PcbcMode']
@@ -1,14 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from abc import ABC
4
- from dataclasses import dataclass
5
-
6
- from purepython_aes.aes.core.interface import Aes
7
-
8
-
9
- @dataclass
10
- class AesMode(ABC):
11
- """Base class for all AES modes of operation."""
12
-
13
- algorithm: Aes
14
- """Either AES-128, AES-192, or AES-256."""
@@ -1,5 +0,0 @@
1
- # noqa: A005
2
-
3
- from purepython_aes.types.finite import IntLookupTable256, RoundKey, RoundKeys
4
-
5
- __all__: list[str] = ['IntLookupTable256', 'RoundKey', 'RoundKeys']
File without changes
File without changes