mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-02 19:39:49 +09:00
Merge pull request #7159 from youknowone/hashlib
_hashlib.HMAC and Update hmac from v3.14.3
This commit is contained in:
1
Lib/hmac.py
vendored
1
Lib/hmac.py
vendored
@@ -159,6 +159,7 @@ class HMAC:
|
||||
# Call __new__ directly to avoid the expensive __init__.
|
||||
other = self.__class__.__new__(self.__class__)
|
||||
other.digest_size = self.digest_size
|
||||
other.block_size = self.block_size
|
||||
if self._hmac:
|
||||
other._hmac = self._hmac.copy()
|
||||
other._inner = other._outer = None
|
||||
|
||||
48
Lib/test/test_hmac.py
vendored
48
Lib/test/test_hmac.py
vendored
@@ -1066,22 +1066,6 @@ class OpenSSLConstructorTestCase(ThroughOpenSSLAPIMixin,
|
||||
):
|
||||
self.hmac_digest(b'key', b'msg', value)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_constructor(self):
|
||||
return super().test_constructor()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_constructor_missing_digestmod(self):
|
||||
return super().test_constructor_missing_digestmod()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_constructor_unknown_digestmod(self):
|
||||
return super().test_constructor_unknown_digestmod()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: module '_hashlib' has no attribute 'HMAC'. Did you mean: 'exc_type'?
|
||||
def test_internal_types(self):
|
||||
return super().test_internal_types()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: module '_hashlib' has no attribute 'hmac_digest'
|
||||
def test_digest(self):
|
||||
return super().test_digest()
|
||||
@@ -1137,6 +1121,15 @@ class SanityTestCaseMixin(CreatorMixin):
|
||||
self.assertEqual(h.digest_size, self.digest_size)
|
||||
self.assertEqual(h.block_size, self.block_size)
|
||||
|
||||
def test_copy(self):
|
||||
# Test a generic copy() and the attributes it exposes.
|
||||
# See https://github.com/python/cpython/issues/142451.
|
||||
h1 = self.hmac_new(b"my secret key", digestmod=self.digestname)
|
||||
h2 = h1.copy()
|
||||
self.assertEqual(h1.name, h2.name)
|
||||
self.assertEqual(h1.digest_size, h2.digest_size)
|
||||
self.assertEqual(h1.block_size, h2.block_size)
|
||||
|
||||
def test_repr(self):
|
||||
# HMAC object representation may differ across implementations
|
||||
raise NotImplementedError
|
||||
@@ -1160,7 +1153,6 @@ class PySanityTestCase(ThroughObjectMixin, PyModuleMixin, SanityTestCaseMixin,
|
||||
|
||||
|
||||
@hashlib_helper.requires_openssl_hashdigest('sha256')
|
||||
@unittest.skip("TODO: RUSTPYTHON; AttributeError: module '_hashlib' has no attribute 'HMAC'")
|
||||
class OpenSSLSanityTestCase(ThroughOpenSSLAPIMixin, SanityTestCaseMixin,
|
||||
unittest.TestCase):
|
||||
|
||||
@@ -1257,18 +1249,6 @@ class OpenSSLUpdateTestCase(UpdateTestCaseMixin, unittest.TestCase):
|
||||
def gil_minsize(self):
|
||||
return _hashlib._GIL_MINSIZE
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_update(self):
|
||||
return super().test_update()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: module '_hashlib' has no attribute '_GIL_MINSIZE'
|
||||
def test_update_large(self):
|
||||
return super().test_update_large()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; TypeError: a bytes-like object is required, not 'NoneType'
|
||||
def test_update_exceptions(self):
|
||||
return super().test_update_exceptions()
|
||||
|
||||
|
||||
class BuiltinUpdateTestCase(BuiltinModuleMixin,
|
||||
UpdateTestCaseMixin, unittest.TestCase):
|
||||
@@ -1320,7 +1300,6 @@ class PythonCopyTestCase(CopyBaseTestCase, unittest.TestCase):
|
||||
self.assertNotEqual(id(h1._inner), id(h2._inner))
|
||||
self.assertNotEqual(id(h1._outer), id(h2._outer))
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; TypeError: a bytes-like object is required, not 'NoneType'
|
||||
def test_equality(self):
|
||||
# Testing if the copy has the same digests.
|
||||
h1 = hmac.HMAC(b"key", digestmod="sha256")
|
||||
@@ -1329,7 +1308,6 @@ class PythonCopyTestCase(CopyBaseTestCase, unittest.TestCase):
|
||||
self.assertEqual(h1.digest(), h2.digest())
|
||||
self.assertEqual(h1.hexdigest(), h2.hexdigest())
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; TypeError: a bytes-like object is required, not 'NoneType'
|
||||
def test_equality_new(self):
|
||||
# Testing if the copy has the same digests with hmac.new().
|
||||
h1 = hmac.new(b"key", digestmod="sha256")
|
||||
@@ -1375,14 +1353,6 @@ class OpenSSLCopyTestCase(ExtensionCopyTestCase, unittest.TestCase):
|
||||
def init(self, h):
|
||||
h._init_openssl_hmac(b"key", b"msg", digestmod="sha256")
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; _hashlib.UnsupportedDigestmodError: unsupported hash type
|
||||
def test_attributes(self):
|
||||
return super().test_attributes()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; _hashlib.UnsupportedDigestmodError: unsupported hash type
|
||||
def test_realcopy(self):
|
||||
return super().test_realcopy()
|
||||
|
||||
|
||||
@hashlib_helper.requires_builtin_hmac()
|
||||
class BuiltinCopyTestCase(ExtensionCopyTestCase, unittest.TestCase):
|
||||
|
||||
@@ -19,7 +19,7 @@ pub mod _hashlib {
|
||||
types::{Constructor, Representable},
|
||||
};
|
||||
use blake2::{Blake2b512, Blake2s256};
|
||||
use digest::{DynDigest, core_api::BlockSizeUser};
|
||||
use digest::{DynDigest, OutputSizeUser, core_api::BlockSizeUser};
|
||||
use digest::{ExtendableOutput, Update};
|
||||
use dyn_clone::{DynClone, clone_trait_object};
|
||||
use hmac::Mac;
|
||||
@@ -258,6 +258,105 @@ pub mod _hashlib {
|
||||
)
|
||||
}
|
||||
|
||||
// Object-safe HMAC trait for type-erased dispatch
|
||||
trait DynHmac: Send + Sync {
|
||||
fn dyn_update(&mut self, data: &[u8]);
|
||||
fn dyn_finalize(&self) -> Vec<u8>;
|
||||
fn dyn_clone(&self) -> Box<dyn DynHmac>;
|
||||
}
|
||||
|
||||
struct TypedHmac<D>(D);
|
||||
|
||||
impl<D> DynHmac for TypedHmac<D>
|
||||
where
|
||||
D: Mac + Clone + Send + Sync + 'static,
|
||||
{
|
||||
fn dyn_update(&mut self, data: &[u8]) {
|
||||
Mac::update(&mut self.0, data);
|
||||
}
|
||||
|
||||
fn dyn_finalize(&self) -> Vec<u8> {
|
||||
self.0.clone().finalize().into_bytes().to_vec()
|
||||
}
|
||||
|
||||
fn dyn_clone(&self) -> Box<dyn DynHmac> {
|
||||
Box::new(TypedHmac(self.0.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
#[pyattr]
|
||||
#[pyclass(module = "_hashlib", name = "HMAC")]
|
||||
#[derive(PyPayload)]
|
||||
pub struct PyHmac {
|
||||
algo_name: String,
|
||||
digest_size: usize,
|
||||
block_size: usize,
|
||||
ctx: PyRwLock<Box<dyn DynHmac>>,
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for PyHmac {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "HMAC {}", self.algo_name)
|
||||
}
|
||||
}
|
||||
|
||||
#[pyclass(with(Representable), flags(IMMUTABLETYPE))]
|
||||
impl PyHmac {
|
||||
#[pyslot]
|
||||
fn slot_new(_cls: PyTypeRef, _args: FuncArgs, vm: &VirtualMachine) -> PyResult {
|
||||
Err(vm.new_type_error("cannot create '_hashlib.HMAC' instances".to_owned()))
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn name(&self) -> String {
|
||||
format!("hmac-{}", self.algo_name)
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn digest_size(&self) -> usize {
|
||||
self.digest_size
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn block_size(&self) -> usize {
|
||||
self.block_size
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn update(&self, msg: ArgBytesLike) {
|
||||
msg.with_ref(|bytes| self.ctx.write().dyn_update(bytes));
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn digest(&self) -> PyBytes {
|
||||
self.ctx.read().dyn_finalize().into()
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn hexdigest(&self) -> String {
|
||||
hex::encode(self.ctx.read().dyn_finalize())
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn copy(&self) -> Self {
|
||||
Self {
|
||||
algo_name: self.algo_name.clone(),
|
||||
digest_size: self.digest_size,
|
||||
block_size: self.block_size,
|
||||
ctx: PyRwLock::new(self.ctx.read().dyn_clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Representable for PyHmac {
|
||||
fn repr_str(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
|
||||
Ok(format!(
|
||||
"<{} HMAC object @ {:#x}>",
|
||||
zelf.algo_name, zelf as *const _ as usize
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[pyattr]
|
||||
#[pyclass(module = "_hashlib", name = "HASH")]
|
||||
#[derive(PyPayload)]
|
||||
@@ -646,18 +745,50 @@ pub mod _hashlib {
|
||||
#[pyarg(positional)]
|
||||
key: ArgBytesLike,
|
||||
#[pyarg(any, optional)]
|
||||
msg: OptionalArg<ArgBytesLike>,
|
||||
msg: OptionalArg<Option<ArgBytesLike>>,
|
||||
#[pyarg(named, optional)]
|
||||
digestmod: OptionalArg<PyObjectRef>,
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn hmac_new(args: NewHMACHashArgs, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
|
||||
let _ = args;
|
||||
Err(vm.new_exception_msg(
|
||||
UnsupportedDigestmodError::static_type().to_owned(),
|
||||
"unsupported hash type".to_owned(),
|
||||
))
|
||||
fn hmac_new(args: NewHMACHashArgs, vm: &VirtualMachine) -> PyResult<PyHmac> {
|
||||
let digestmod = args.digestmod.into_option().ok_or_else(|| {
|
||||
vm.new_type_error("Missing required parameter 'digestmod'.".to_owned())
|
||||
})?;
|
||||
let name = resolve_digestmod(&digestmod, vm)?;
|
||||
|
||||
let key_buf = args.key.borrow_buf();
|
||||
let msg_data = args.msg.flatten();
|
||||
|
||||
macro_rules! make_hmac {
|
||||
($hash_ty:ty) => {{
|
||||
let mut mac = <hmac::Hmac<$hash_ty> as Mac>::new_from_slice(&key_buf)
|
||||
.map_err(|_| vm.new_value_error("invalid key length".to_owned()))?;
|
||||
if let Some(ref m) = msg_data {
|
||||
m.with_ref(|bytes| Mac::update(&mut mac, bytes));
|
||||
}
|
||||
Ok(PyHmac {
|
||||
algo_name: name,
|
||||
digest_size: <$hash_ty as OutputSizeUser>::output_size(),
|
||||
block_size: <$hash_ty as BlockSizeUser>::block_size(),
|
||||
ctx: PyRwLock::new(Box::new(TypedHmac(mac))),
|
||||
})
|
||||
}};
|
||||
}
|
||||
|
||||
match name.as_str() {
|
||||
"md5" => make_hmac!(Md5),
|
||||
"sha1" => make_hmac!(Sha1),
|
||||
"sha224" => make_hmac!(Sha224),
|
||||
"sha256" => make_hmac!(Sha256),
|
||||
"sha384" => make_hmac!(Sha384),
|
||||
"sha512" => make_hmac!(Sha512),
|
||||
"sha3_224" => make_hmac!(Sha3_224),
|
||||
"sha3_256" => make_hmac!(Sha3_256),
|
||||
"sha3_384" => make_hmac!(Sha3_384),
|
||||
"sha3_512" => make_hmac!(Sha3_512),
|
||||
_ => Err(unsupported_hash(&name, vm)),
|
||||
}
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
|
||||
Reference in New Issue
Block a user