Skip to content

Commit 16c7439

Browse files
authored
Add SHA3 private attribute parity to hashlib hash objects (#7134)
1 parent 9e61458 commit 16c7439

2 files changed

Lines changed: 74 additions & 2 deletions

File tree

‎Lib/test/test_hashlib.py‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -485,8 +485,6 @@ def check_sha3(self, name, capacity, rate, suffix):
485485
self.assertEqual(m._rate_bits, rate)
486486
self.assertEqual(m._suffix, suffix)
487487

488-
# TODO: RUSTPYTHON
489-
@unittest.expectedFailure
490488
@requires_sha3
491489
def test_extra_sha3(self):
492490
self.check_sha3('sha3_224', 448, 1152, b'\x06')

‎crates/stdlib/src/hashlib.rs‎

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,28 @@ pub mod _hashlib {
7575
}
7676
}
7777

78+
const KECCAK_WIDTH_BITS: usize = 1600;
79+
80+
fn keccak_suffix(name: &str) -> Option<u8> {
81+
match name {
82+
"sha3_224" | "sha3_256" | "sha3_384" | "sha3_512" => Some(0x06),
83+
"shake_128" | "shake_256" => Some(0x1f),
84+
_ => None,
85+
}
86+
}
87+
88+
fn keccak_rate_bits(name: &str, block_size: usize) -> Option<usize> {
89+
keccak_suffix(name).map(|_| block_size * 8)
90+
}
91+
92+
fn keccak_capacity_bits(name: &str, block_size: usize) -> Option<usize> {
93+
keccak_rate_bits(name, block_size).map(|rate| KECCAK_WIDTH_BITS - rate)
94+
}
95+
96+
fn missing_hash_attribute<T>(vm: &VirtualMachine, class_name: &str, attr: &str) -> PyResult<T> {
97+
Err(vm.new_attribute_error(format!("'{class_name}' object has no attribute '{attr}'")))
98+
}
99+
78100
#[derive(FromArgs)]
79101
#[allow(unused)]
80102
struct XofDigestArgs {
@@ -139,6 +161,32 @@ pub mod _hashlib {
139161
self.ctx.read().block_size()
140162
}
141163

164+
#[pygetset]
165+
fn _capacity_bits(&self, vm: &VirtualMachine) -> PyResult<usize> {
166+
let block_size = self.ctx.read().block_size();
167+
match keccak_capacity_bits(&self.name, block_size) {
168+
Some(capacity) => Ok(capacity),
169+
None => missing_hash_attribute(vm, "HASH", "_capacity_bits"),
170+
}
171+
}
172+
173+
#[pygetset]
174+
fn _rate_bits(&self, vm: &VirtualMachine) -> PyResult<usize> {
175+
let block_size = self.ctx.read().block_size();
176+
match keccak_rate_bits(&self.name, block_size) {
177+
Some(rate) => Ok(rate),
178+
None => missing_hash_attribute(vm, "HASH", "_rate_bits"),
179+
}
180+
}
181+
182+
#[pygetset]
183+
fn _suffix(&self, vm: &VirtualMachine) -> PyResult<PyBytes> {
184+
match keccak_suffix(&self.name) {
185+
Some(suffix) => Ok(vec![suffix].into()),
186+
None => missing_hash_attribute(vm, "HASH", "_suffix"),
187+
}
188+
}
189+
142190
#[pymethod]
143191
fn update(&self, data: ArgBytesLike) {
144192
data.with_ref(|bytes| self.ctx.write().update(bytes));
@@ -212,6 +260,32 @@ pub mod _hashlib {
212260
self.ctx.read().block_size()
213261
}
214262

263+
#[pygetset]
264+
fn _capacity_bits(&self, vm: &VirtualMachine) -> PyResult<usize> {
265+
let block_size = self.ctx.read().block_size();
266+
match keccak_capacity_bits(&self.name, block_size) {
267+
Some(capacity) => Ok(capacity),
268+
None => missing_hash_attribute(vm, "HASHXOF", "_capacity_bits"),
269+
}
270+
}
271+
272+
#[pygetset]
273+
fn _rate_bits(&self, vm: &VirtualMachine) -> PyResult<usize> {
274+
let block_size = self.ctx.read().block_size();
275+
match keccak_rate_bits(&self.name, block_size) {
276+
Some(rate) => Ok(rate),
277+
None => missing_hash_attribute(vm, "HASHXOF", "_rate_bits"),
278+
}
279+
}
280+
281+
#[pygetset]
282+
fn _suffix(&self, vm: &VirtualMachine) -> PyResult<PyBytes> {
283+
match keccak_suffix(&self.name) {
284+
Some(suffix) => Ok(vec![suffix].into()),
285+
None => missing_hash_attribute(vm, "HASHXOF", "_suffix"),
286+
}
287+
}
288+
215289
#[pymethod]
216290
fn update(&self, data: ArgBytesLike) {
217291
data.with_ref(|bytes| self.ctx.write().update(bytes));

0 commit comments

Comments
 (0)