mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
Fix _hashlib.compare_digest to reject non-ASCII strings (#7280)
Add non-ASCII string check to _hashlib.compare_digest, matching the behavior of _operator._compare_digest. When both arguments are strings, non-ASCII characters now correctly raise TypeError. Also replace the non-constant-time == comparison with constant_time_eq for proper timing-attack resistance, and return PyResult<bool> instead of PyResult<PyObjectRef>.
This commit is contained in:
1 parent
828f960110
commit
44b1ca01fa
4 files changed
+18
-21
No files matched your search
Generated
+1
@@ -3238,6 +3238,7 @@ dependencies = [
|
||||
"bzip2",
|
||||
"cfg-if",
|
||||
"chrono",
|
||||
"constant_time_eq",
|
||||
"crc32fast",
|
||||
"crossbeam-utils",
|
||||
"csv-core",
|
||||
|
||||
Vendored
-8
@@ -1484,19 +1484,11 @@ class HMACCompareDigestTestCase(CompareDigestMixin, unittest.TestCase):
|
||||
else:
|
||||
self.assertIs(self.compare_digest, operator_compare_digest)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: TypeError not raised by compare_digest
|
||||
def test_exceptions(self):
|
||||
return super().test_exceptions()
|
||||
|
||||
|
||||
@hashlib_helper.requires_hashlib()
|
||||
class OpenSSLCompareDigestTestCase(CompareDigestMixin, unittest.TestCase):
|
||||
compare_digest = openssl_compare_digest
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: TypeError not raised by compare_digest
|
||||
def test_exceptions(self):
|
||||
return super().test_exceptions()
|
||||
|
||||
|
||||
class OperatorCompareDigestTestCase(CompareDigestMixin, unittest.TestCase):
|
||||
compare_digest = operator_compare_digest
|
||||
|
||||
@@ -69,6 +69,7 @@ sha3 = "0.10.1"
|
||||
blake2 = "0.10.4"
|
||||
hmac = "0.12"
|
||||
pbkdf2 = { version = "0.12", features = ["hmac"] }
|
||||
constant_time_eq = { workspace = true }
|
||||
|
||||
## unicode stuff
|
||||
unicode_names2 = { workspace = true }
|
||||
|
||||
@@ -14,7 +14,6 @@ pub mod _hashlib {
|
||||
PyBaseExceptionRef, PyBytes, PyFrozenSet, PyStr, PyTypeRef, PyUtf8StrRef, PyValueError,
|
||||
},
|
||||
class::StaticType,
|
||||
convert::ToPyObject,
|
||||
function::{ArgBytesLike, ArgStrOrBytesLike, FuncArgs, OptionalArg},
|
||||
types::{Constructor, Representable},
|
||||
};
|
||||
@@ -724,22 +723,26 @@ pub mod _hashlib {
|
||||
a: ArgStrOrBytesLike,
|
||||
b: ArgStrOrBytesLike,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<PyObjectRef> {
|
||||
const fn is_str(arg: &ArgStrOrBytesLike) -> bool {
|
||||
matches!(arg, ArgStrOrBytesLike::Str(_))
|
||||
}
|
||||
) -> PyResult<bool> {
|
||||
use constant_time_eq::constant_time_eq;
|
||||
|
||||
if is_str(&a) != is_str(&b) {
|
||||
return Err(vm.new_type_error(format!(
|
||||
match (&a, &b) {
|
||||
(ArgStrOrBytesLike::Str(a), ArgStrOrBytesLike::Str(b)) => {
|
||||
if !a.isascii() || !b.isascii() {
|
||||
return Err(vm.new_type_error(
|
||||
"comparing strings with non-ASCII characters is not supported",
|
||||
));
|
||||
}
|
||||
Ok(constant_time_eq(a.as_bytes(), b.as_bytes()))
|
||||
}
|
||||
(ArgStrOrBytesLike::Buf(a), ArgStrOrBytesLike::Buf(b)) => {
|
||||
Ok(a.with_ref(|a| b.with_ref(|b| constant_time_eq(a, b))))
|
||||
}
|
||||
_ => Err(vm.new_type_error(format!(
|
||||
"a bytes-like object is required, not '{}'",
|
||||
b.as_object().class().name()
|
||||
)));
|
||||
))),
|
||||
}
|
||||
|
||||
let a_hash = a.borrow_bytes().to_vec();
|
||||
let b_hash = b.borrow_bytes().to_vec();
|
||||
|
||||
Ok((a_hash == b_hash).to_pyobject(vm))
|
||||
}
|
||||
|
||||
#[derive(FromArgs, Debug)]
|
||||
|
||||
Reference in new issue
Block a user