purepython-aes 0.2.0__py3-none-any.whl → 0.3.0__py3-none-any.whl

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.
@@ -12,6 +12,7 @@ from purepython_aes.aes import (
12
12
  Iso7816Padding,
13
13
  Iso10126Padding,
14
14
  NoPadding,
15
+ PcbcMode,
15
16
  Pkcs7Padding,
16
17
  ZeroPadding,
17
18
  )
@@ -30,6 +31,7 @@ __all__: list[str] = [
30
31
  'Iso7816Padding',
31
32
  'Iso10126Padding',
32
33
  'NoPadding',
34
+ 'PcbcMode',
33
35
  'Pkcs7Padding',
34
36
  'ZeroPadding',
35
37
  ]
@@ -1,6 +1,12 @@
1
1
  from purepython_aes.aes.algorithms import Aes128, Aes192, Aes256
2
2
  from purepython_aes.aes.interface import Aes
3
- from purepython_aes.aes.modes import AesMode, BlockCipherMode, CbcMode, EcbMode
3
+ from purepython_aes.aes.modes import (
4
+ AesMode,
5
+ BlockCipherMode,
6
+ CbcMode,
7
+ EcbMode,
8
+ PcbcMode,
9
+ )
4
10
  from purepython_aes.aes.padding import (
5
11
  AnsiX923Padding,
6
12
  BasePadding,
@@ -20,6 +26,7 @@ __all__: list[str] = [
20
26
  'BlockCipherMode',
21
27
  'CbcMode',
22
28
  'EcbMode',
29
+ 'PcbcMode',
23
30
  'AnsiX923Padding',
24
31
  'BasePadding',
25
32
  'Iso7816Padding',
@@ -1,4 +1,4 @@
1
1
  from purepython_aes.aes.modes._base import AesMode
2
- from purepython_aes.aes.modes.block import BlockCipherMode, CbcMode, EcbMode
2
+ from purepython_aes.aes.modes.block import BlockCipherMode, CbcMode, EcbMode, PcbcMode
3
3
 
4
- __all__: list[str] = ['AesMode', 'BlockCipherMode', 'CbcMode', 'EcbMode']
4
+ __all__: list[str] = ['AesMode', 'BlockCipherMode', 'CbcMode', 'EcbMode', 'PcbcMode']
@@ -1,5 +1,6 @@
1
1
  from purepython_aes.aes.modes.block._base import BlockCipherMode
2
2
  from purepython_aes.aes.modes.block.cbc import CbcMode
3
3
  from purepython_aes.aes.modes.block.ecb import EcbMode
4
+ from purepython_aes.aes.modes.block.pcbc import PcbcMode
4
5
 
5
- __all__: list[str] = ['BlockCipherMode', 'CbcMode', 'EcbMode']
6
+ __all__: list[str] = ['BlockCipherMode', 'CbcMode', 'EcbMode', 'PcbcMode']
@@ -11,13 +11,13 @@ class BlockCipherMode(AesMode, ABC):
11
11
  padding: BasePadding
12
12
 
13
13
  @abstractmethod
14
- def encrypt_blocks(self, padded_plaintext: bytes) -> bytes:
14
+ def __encrypt_blocks__(self, padded_plaintext: bytes) -> bytes:
15
15
  """Encrypt plaintext containing complete blocks."""
16
16
 
17
17
  raise NotImplementedError
18
18
 
19
19
  @abstractmethod
20
- def decrypt_blocks(self, ciphertext: bytes) -> bytes:
20
+ def __decrypt_blocks__(self, ciphertext: bytes) -> bytes:
21
21
  """Decrypt ciphertext containing complete blocks."""
22
22
 
23
23
  raise NotImplementedError
@@ -25,11 +25,11 @@ class BlockCipherMode(AesMode, ABC):
25
25
  def encrypt(self, plaintext: bytes) -> bytes:
26
26
  """Pad and encrypt a byte string."""
27
27
 
28
- return self.encrypt_blocks(self.padding.pad(plaintext))
28
+ return self.__encrypt_blocks__(self.padding.pad(plaintext))
29
29
 
30
30
  def decrypt(self, ciphertext: bytes) -> bytes:
31
31
  """Decrypt and unpad a byte string."""
32
32
 
33
33
  if len(ciphertext) % AES_BLOCK_SIZE != 0:
34
34
  raise ValueError(f'len(ciphertext) % {AES_BLOCK_SIZE} must be 0')
35
- return self.padding.unpad(self.decrypt_blocks(ciphertext))
35
+ return self.padding.unpad(self.__decrypt_blocks__(ciphertext))
@@ -12,7 +12,7 @@ from purepython_aes.const import AES_BLOCK_SIZE
12
12
  class CbcMode(BlockCipherMode):
13
13
  """Cipher Block Chaining mode of operation."""
14
14
 
15
- def encrypt_blocks(self, padded_plaintext: bytes) -> bytes:
15
+ def __encrypt_blocks__(self, padded_plaintext: bytes) -> bytes:
16
16
  initialization_vector: bytes = token_bytes(AES_BLOCK_SIZE)
17
17
  previous_ciphertext_block: bytes = initialization_vector
18
18
  ciphertext_blocks: list[bytes] = [initialization_vector]
@@ -23,7 +23,7 @@ class CbcMode(BlockCipherMode):
23
23
  previous_ciphertext_block = ciphertext_block
24
24
  return bytes(0).join(ciphertext_blocks)
25
25
 
26
- def decrypt_blocks(self, ciphertext: bytes) -> bytes:
26
+ def __decrypt_blocks__(self, ciphertext: bytes) -> bytes:
27
27
  initialization_vector, *ciphertext_blocks = split_into_blocks(ciphertext)
28
28
  previous_ciphertext_block: bytes = initialization_vector
29
29
  plaintext_blocks: list[bytes] = []
@@ -10,12 +10,12 @@ from purepython_aes.aes.modes.operations import split_into_blocks
10
10
  class EcbMode(BlockCipherMode):
11
11
  """Electronic Codebook mode of operation."""
12
12
 
13
- def encrypt_blocks(self, padded_plaintext: bytes) -> bytes:
13
+ def __encrypt_blocks__(self, padded_plaintext: bytes) -> bytes:
14
14
  return bytes(0).join(
15
15
  map(self.algorithm.encrypt_block, split_into_blocks(padded_plaintext))
16
16
  )
17
17
 
18
- def decrypt_blocks(self, ciphertext: bytes) -> bytes:
18
+ def __decrypt_blocks__(self, ciphertext: bytes) -> bytes:
19
19
  return bytes(0).join(
20
20
  map(self.algorithm.decrypt_block, split_into_blocks(ciphertext))
21
21
  )
@@ -0,0 +1,35 @@
1
+ from dataclasses import dataclass
2
+ from secrets import token_bytes
3
+ from typing import final
4
+
5
+ from purepython_aes.aes.modes.block._base import BlockCipherMode
6
+ from purepython_aes.aes.modes.operations import split_into_blocks, xor
7
+ from purepython_aes.const import AES_BLOCK_SIZE
8
+
9
+
10
+ @final
11
+ @dataclass(slots=True)
12
+ class PcbcMode(BlockCipherMode):
13
+ """Propagating Cipher Block Chaining mode of operation."""
14
+
15
+ def __encrypt_blocks__(self, padded_plaintext: bytes) -> bytes:
16
+ initialization_vector: bytes = token_bytes(AES_BLOCK_SIZE)
17
+ previous_ciphertext_block: bytes = initialization_vector
18
+ ciphertext_blocks: list[bytes] = [initialization_vector]
19
+ for block in split_into_blocks(padded_plaintext):
20
+ chained_block: bytes = xor(block, previous_ciphertext_block)
21
+ ciphertext_block: bytes = self.algorithm.encrypt_block(chained_block)
22
+ ciphertext_blocks.append(ciphertext_block)
23
+ previous_ciphertext_block = xor(block, ciphertext_block)
24
+ return bytes(0).join(ciphertext_blocks)
25
+
26
+ def __decrypt_blocks__(self, ciphertext: bytes) -> bytes:
27
+ initialization_vector, *ciphertext_blocks = split_into_blocks(ciphertext)
28
+ previous_ciphertext_block: bytes = initialization_vector
29
+ plaintext_blocks: list[bytes] = []
30
+ for block in ciphertext_blocks:
31
+ decrypted_block: bytes = self.algorithm.decrypt_block(block)
32
+ plaintext_block: bytes = xor(decrypted_block, previous_ciphertext_block)
33
+ plaintext_blocks.append(plaintext_block)
34
+ previous_ciphertext_block = xor(block, plaintext_block)
35
+ return bytes(0).join(plaintext_blocks)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: purepython-aes
3
- Version: 0.2.0
3
+ Version: 0.3.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
@@ -1,6 +1,6 @@
1
- purepython_aes/__init__.py,sha256=ZTJt8dU-dVMCjbnBSdKgLCmPbDqvi_k6ayrqovV9al8,559
1
+ purepython_aes/__init__.py,sha256=lbfX5q06erbHFR_LA1u7-sKFRT2OnnS8kKD6z8ERhIA,589
2
2
  purepython_aes/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- purepython_aes/aes/__init__.py,sha256=u5445FnmaG02b5Rb10IIV1axMYQC8pOj8koZTtWO44M,652
3
+ purepython_aes/aes/__init__.py,sha256=8LpdNbGVXkISwlL5HQ1up_F0XEG1nOi7q_5Y-PBqmf8,703
4
4
  purepython_aes/aes/interface.py,sha256=OzNhRtS2xu5pOXFF7sAiHkNdSuMHLwaYSRm-0Ldz0Sg,699
5
5
  purepython_aes/aes/algorithms/__init__.py,sha256=L21rL4gaCdoRmV2wyFJyuzFpBI4jXry1uQSi0b0eqds,221
6
6
  purepython_aes/aes/algorithms/aes128.py,sha256=rmv-LJ2dcHIYmTfSTg2xAn-M94ank89ULYZKgpFuoVs,387
@@ -11,13 +11,14 @@ purepython_aes/aes/core/aes.py,sha256=xU1K9cNIek7moVeu-ZDrv3TIQOHTb6SoJsswXX10V3
11
11
  purepython_aes/aes/core/expansion.py,sha256=JmNxuHIV3EzFFDFX2RAnbwKECimovBLrL4O3mfE8UOA,1979
12
12
  purepython_aes/aes/core/operations.py,sha256=4jGJZ3xwRb3z0BLMorF-QCbTdPKAWUVPEvxkUe-5JjU,1989
13
13
  purepython_aes/aes/core/state.py,sha256=j79LDXRabfGAkF2qx8iVKgrSDfEUfFnfKU1XQ-5OidM,5488
14
- purepython_aes/aes/modes/__init__.py,sha256=LkLzTBOyS1cuPbk0oxwTCHvm1w5-eHh_NwbTsyLqBX0,203
14
+ purepython_aes/aes/modes/__init__.py,sha256=NMoX0SgSjN3SLjt4PbeBhBSKTFIr0SBkFL7Ots5LVDs,225
15
15
  purepython_aes/aes/modes/_base.py,sha256=L81chTROXrIgNL5GJ1AT9icmixbTT7okfcZ0QyeVaiA,289
16
16
  purepython_aes/aes/modes/operations.py,sha256=VYnoYB0Bb9bfwnApE3tL-2koLK6dDRotit1Pc1cAfhA,460
17
- purepython_aes/aes/modes/block/__init__.py,sha256=CsXGcONKMd5QnKRQYle5zOYoRQ1YHXgqAZBp-2jTvOY,239
18
- purepython_aes/aes/modes/block/_base.py,sha256=TH6oekojIZluInw8ZLMFpPNuolYQOcxI0QOgsGIWekc,1119
19
- purepython_aes/aes/modes/block/cbc.py,sha256=JzxXyRu7Q8ZA8_AH7I43Ap5sYnrGyZBZm1i8aU9nLsA,1619
20
- purepython_aes/aes/modes/block/ecb.py,sha256=lL1gyIjq768Xe_V6QdWeG9xn5w2G-uTQqOX_cs9MYH4,668
17
+ purepython_aes/aes/modes/block/__init__.py,sha256=V_hnUcEExOXRL91yPjApl7ms9ADPMOOg7JdqXYro24A,308
18
+ purepython_aes/aes/modes/block/_base.py,sha256=E6tBpKP02bG66BnTJULxFW6pn7S2biBuVztddQ-vCCg,1135
19
+ purepython_aes/aes/modes/block/cbc.py,sha256=GGh8IMa7NEK87UyL1RDM6jewTmNPLP-WWZQo9-asd9w,1627
20
+ purepython_aes/aes/modes/block/ecb.py,sha256=iCLPQRuXZQzPJ3rS3Rw6299--iSgTDXXOSXWjvo6dJk,676
21
+ purepython_aes/aes/modes/block/pcbc.py,sha256=DGWRkNGtIvOiZ6a0qXXWlKCQFT1m01uIs0hvelMEkTA,1674
21
22
  purepython_aes/aes/padding/__init__.py,sha256=X_JqBtvuLm1edPoE7B-DnENBHIgwpemhB7d37ZRDQEY,582
22
23
  purepython_aes/aes/padding/_base.py,sha256=dnuNv8OVOBDxAXSUGEeJu5uy9bSFkY2AKkzrK0If8so,408
23
24
  purepython_aes/aes/padding/ansix923.py,sha256=xWLzlk85vQn1pPZm5x0wJy3aK46bvtK1YHNCLRC0yfA,991
@@ -32,9 +33,9 @@ purepython_aes/const/sbox.py,sha256=RaSYzs2mjUKZjv0fbEwCD3YLLkoz7emJhjEbU-RH09o,
32
33
  purepython_aes/types/__init__.py,sha256=QiuMSx2DaC2lCXoK16RGeEBrfoX4d3jozkx4QV0kFjc,116
33
34
  purepython_aes/types/finite/__init__.py,sha256=2cYu5Zxdp5NKAwYbV-MD2qamSaJxfqRYp0jOt657-YY,110
34
35
  purepython_aes/types/finite/aliases.py,sha256=gKO8ETbMhV5FX6l3enF8wt246FpMPohreTZ3BPDThBM,1982
35
- purepython_aes-0.2.0.dist-info/METADATA,sha256=DIZKs4ZIsGQ8ZljMNgPd0dWUAujarbzQzmWgGJ5erZQ,2112
36
- purepython_aes-0.2.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
37
- purepython_aes-0.2.0.dist-info/scm_file_list.json,sha256=Y5c5xPMffftoqRAymXzMJ2WJ-zhscqId_wxRZb87MQ0,3163
38
- purepython_aes-0.2.0.dist-info/scm_version.json,sha256=IJgEDYtPUbe8ImvyiyQEAgjmVk3WlJEwQ9m3pWC8x3k,160
39
- purepython_aes-0.2.0.dist-info/top_level.txt,sha256=IiTUB8OvHhwNXxf6OXKCfUADF7HcQdCRUMfPJC6TS5k,15
40
- purepython_aes-0.2.0.dist-info/RECORD,,
36
+ purepython_aes-0.3.0.dist-info/METADATA,sha256=XKENPb9b3Lk4glI_ASBHscvOpNOMygjQhN4xzebveI0,2112
37
+ purepython_aes-0.3.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
38
+ purepython_aes-0.3.0.dist-info/scm_file_list.json,sha256=qNYcURIM3xreY52tE_SK5KzeMR1lj2gI8W3yZoTFwPA,3260
39
+ purepython_aes-0.3.0.dist-info/scm_version.json,sha256=tcfnuga8_6ayB7njaJH4xXrur3sxSf7iCqa_AEivSNY,160
40
+ purepython_aes-0.3.0.dist-info/top_level.txt,sha256=IiTUB8OvHhwNXxf6OXKCfUADF7HcQdCRUMfPJC6TS5k,15
41
+ purepython_aes-0.3.0.dist-info/RECORD,,
@@ -35,6 +35,7 @@
35
35
  "src/purepython_aes/aes/modes/block/_base.py",
36
36
  "src/purepython_aes/aes/modes/block/__init__.py",
37
37
  "src/purepython_aes/aes/modes/block/cbc.py",
38
+ "src/purepython_aes/aes/modes/block/pcbc.py",
38
39
  "src/purepython_aes/aes/modes/block/ecb.py",
39
40
  "src/purepython_aes/aes/algorithms/aes128.py",
40
41
  "src/purepython_aes/aes/algorithms/__init__.py",
@@ -65,6 +66,7 @@
65
66
  "tests/unit/aes/modes/test_operations.py",
66
67
  "tests/unit/aes/modes/__init__.py",
67
68
  "tests/unit/aes/modes/block/__init__.py",
69
+ "tests/unit/aes/modes/block/test_pcbc.py",
68
70
  "tests/unit/aes/modes/block/test_ecb.py",
69
71
  "tests/unit/aes/modes/block/test_cbc.py",
70
72
  "tests/unit/aes/algorithms/test_aes256.py",
@@ -1,7 +1,7 @@
1
1
  {
2
- "tag": "0.2.0",
2
+ "tag": "0.3.0",
3
3
  "distance": 0,
4
- "node": "g6499740a96c2f3a034500f56fa2b3813839c4ba5",
4
+ "node": "gd094c7acbd7291759ff4f81bfdab575e037f2200",
5
5
  "dirty": false,
6
6
  "branch": "HEAD",
7
7
  "node_date": "2026-07-13"