Skip to content

Commit 55b90b4

Browse files
Fix IndexError in ChaCha20 quarter-round by correcting 2D array indexing and documenting the algorithm
1 parent be3c717 commit 55b90b4

File tree

1 file changed

+67
-22
lines changed

1 file changed

+67
-22
lines changed

pydatastructs/crypto/ChaCha20.py

Lines changed: 67 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -40,28 +40,73 @@ def __repr__(self):
4040
"""Returns a string representation of the object for debugging."""
4141
return f"<ChaCha20(key={self.key[:4].hex()}..., nonce={self.nonce.hex()}, counter={self.counter})>"
4242

43-
def _quarter_round(self, state: List[int], a: int, b: int, c: int, d: int):
44-
state[a] = (state[a] + state[b]) % (2**32)
45-
state[d] ^= state[a]
46-
state[d] = ((state[d] << 16) | (state[d] >> 16)) % (2**32)
47-
state[c] = (state[c] + state[d]) % (2**32)
48-
state[b] ^= state[c]
49-
state[b] = ((state[b] << 12) | (state[b] >> 20)) % (2**32)
50-
state[a] = (state[a] + state[b]) % (2**32)
51-
state[d] ^= state[a]
52-
state[d] = ((state[d] << 8) | (state[d] >> 24)) % (2**32)
53-
state[c] = (state[c] + state[d]) % (2**32)
54-
state[b] ^= state[c]
55-
state[b] = ((state[b] << 7) | (state[b] >> 25)) % (2**32)
56-
def _double_round(self, state: List[int]):
57-
self._quarter_round(state, 0, 4, 8, 12)
58-
self._quarter_round(state, 1, 5, 9, 13)
59-
self._quarter_round(state, 2, 6, 10, 14)
60-
self._quarter_round(state, 3, 7, 11, 15)
61-
self._quarter_round(state, 0, 5, 10, 15)
62-
self._quarter_round(state, 1, 6, 11, 12)
63-
self._quarter_round(state, 2, 7, 8, 13)
64-
self._quarter_round(state, 3, 4, 9, 14)
43+
44+
def _quarter_round(self, state: np.ndarray, a: tuple, b: tuple, c: tuple, d: tuple):
45+
46+
"""
47+
Performs the ChaCha20 quarter-round operation on the 4x4 state matrix.
48+
49+
The quarter-round consists of four operations (Add, XOR, and Rotate) performed on
50+
four elements of the state. It is a core component of the ChaCha20 algorithm, ensuring
51+
diffusion of bits for cryptographic security.
52+
53+
Parameters:
54+
-----------
55+
state : np.ndarray
56+
A 4x4 matrix (NumPy array) representing the ChaCha20 state.
57+
58+
a, b, c, d : tuple
59+
Each tuple represents the (row, column) indices of four elements in the state matrix
60+
to be processed in the quarter-round.
61+
62+
Operations:
63+
-----------
64+
- Add: Adds two values modulo 2^32.
65+
- XOR: Performs a bitwise XOR operation.
66+
- Rotate: Rotates bits (circular shift) to the left.
67+
68+
Formula for the quarter-round (performed four times):
69+
-----------------------------------------------------
70+
1. a += b; d ^= a; d <<<= 16
71+
2. c += d; b ^= c; b <<<= 12
72+
3. a += b; d ^= a; d <<<= 8
73+
4. c += d; b ^= c; b <<<= 7
74+
75+
"""
76+
ax, ay = a
77+
bx, by = b
78+
cx, cy = c
79+
dx, dy = d
80+
81+
state[ax, ay] = (state[ax, ay] + state[bx, by]) % (2**32)
82+
state[dx, dy] ^= state[ax, ay]
83+
state[dx, dy] = ((state[dx, dy] << 16) | (state[dx, dy] >> 16)) % (2**32)
84+
85+
state[cx, cy] = (state[cx, cy] + state[dx, dy]) % (2**32)
86+
state[bx, by] ^= state[cx, cy]
87+
state[bx, by] = ((state[bx, by] << 12) | (state[bx, by] >> 20)) % (2**32)
88+
89+
state[ax, ay] = (state[ax, ay] + state[bx, by]) % (2**32)
90+
state[dx, dy] ^= state[ax, ay]
91+
state[dx, dy] = ((state[dx, dy] << 8) | (state[dx, dy] >> 24)) % (2**32)
92+
93+
state[cx, cy] = (state[cx, cy] + state[dx, dy]) % (2**32)
94+
state[bx, by] ^= state[cx, cy]
95+
state[bx, by] = ((state[bx, by] << 7) | (state[bx, by] >> 25)) % (2**32)
96+
97+
def _double_round(self, state: np.ndarray):
98+
99+
self._quarter_round(state, (0, 0), (1, 0), (2, 0), (3, 0))
100+
self._quarter_round(state, (0, 1), (1, 1), (2, 1), (3, 1))
101+
self._quarter_round(state, (0, 2), (1, 2), (2, 2), (3, 2))
102+
self._quarter_round(state, (0, 3), (1, 3), (2, 3), (3, 3))
103+
104+
self._quarter_round(state, (0, 0), (1, 1), (2, 2), (3, 3))
105+
self._quarter_round(state, (0, 1), (1, 2), (2, 3), (3, 0))
106+
self._quarter_round(state, (0, 2), (1, 3), (2, 0), (3, 1))
107+
self._quarter_round(state, (0, 3), (1, 0), (2, 1), (3, 2))
108+
109+
65110
def _chacha20_block(self, counter: int) -> bytes:
66111
"""
67112
Generates a 64-byte keystream block from 16-word (512-bit) state

0 commit comments

Comments
 (0)