Skip to content

Commit

Permalink
Rollback to pyo3 = { version = "0.16.6", features = ["abi3"] }
Browse files Browse the repository at this point in the history
In order to avoid `ImportError: PyO3 modules compiled for CPython 3.8 or
older may only be initialized once per interpreter process` introduced
since pyo3 >= 0.17.0, which only allow each `#[pymodule]` to be
initialized once, this PR temporarily rollback to pyo3 = 0.16.6.

It also remove the use of `PyUserWarning` which introduced since pyo3 >
0.18.0.

See PyO3/pyo3@78ba70d
See PyO3/pyo3@1d20f2a

Fixes #694

Signed-off-by: Wong Hoi Sing Edison <[email protected]>
  • Loading branch information
hswong3i committed May 4, 2024
1 parent 35e5a6f commit ec38f43
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 70 deletions.
57 changes: 16 additions & 41 deletions src/_bcrypt/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/_bcrypt/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ edition = "2018"
publish = false

[dependencies]
pyo3 = { version = "0.21", features = ["abi3"] }
pyo3 = { version = "0.16.6", features = ["abi3"] }
bcrypt = "0.15"
bcrypt-pbkdf = "0.10.0"
base64 = "0.22.1"
Expand Down
30 changes: 7 additions & 23 deletions src/_bcrypt/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
#![deny(rust_2018_idioms)]

use base64::Engine;
use pyo3::prelude::PyBytesMethods;
use pyo3::PyTypeInfo;
use std::convert::TryInto;
use std::io::Write;
Expand All @@ -29,7 +28,7 @@ fn gensalt<'p>(
py: pyo3::Python<'p>,
rounds: Option<u16>,
prefix: Option<&[u8]>,
) -> pyo3::PyResult<pyo3::Bound<'p, pyo3::types::PyBytes>> {
) -> pyo3::PyResult<&'p pyo3::types::PyBytes> {
let rounds = rounds.unwrap_or(12);
let prefix = prefix.unwrap_or(b"2b");

Expand All @@ -48,7 +47,7 @@ fn gensalt<'p>(

let encoded_salt = BASE64_ENGINE.encode(salt);

pyo3::types::PyBytes::new_bound_with(
pyo3::types::PyBytes::new_with(
py,
1 + prefix.len() + 1 + 2 + 1 + encoded_salt.len(),
|mut b| {
Expand All @@ -69,7 +68,7 @@ fn hashpw<'p>(
py: pyo3::Python<'p>,
password: &[u8],
salt: &[u8],
) -> pyo3::PyResult<pyo3::Bound<'p, pyo3::types::PyBytes>> {
) -> pyo3::PyResult<&'p pyo3::types::PyBytes> {
// bcrypt originally suffered from a wraparound bug:
// http://www.openwall.com/lists/oss-security/2012/01/02/4
// This bug was corrected in the OpenBSD source by truncating inputs to 72
Expand Down Expand Up @@ -113,7 +112,7 @@ fn hashpw<'p>(
let hashed = py
.allow_threads(|| bcrypt::hash_with_salt(password, cost, raw_salt))
.map_err(|_| pyo3::exceptions::PyValueError::new_err("Invalid salt"))?;
Ok(pyo3::types::PyBytes::new_bound(
Ok(pyo3::types::PyBytes::new(
py,
hashed.format_for_version(version).as_bytes(),
))
Expand All @@ -135,7 +134,7 @@ fn kdf<'p>(
desired_key_bytes: usize,
rounds: u32,
ignore_few_rounds: Option<bool>,
) -> pyo3::PyResult<pyo3::Bound<'p, pyo3::types::PyBytes>> {
) -> pyo3::PyResult<&'p pyo3::types::PyBytes> {
let ignore_few_rounds = ignore_few_rounds.unwrap_or(false);

if password.is_empty() || salt.is_empty() {
Expand All @@ -156,19 +155,7 @@ fn kdf<'p>(
));
}

if rounds < 50 && !ignore_few_rounds {
// They probably think bcrypt.kdf()'s rounds parameter is logarithmic,
// expecting this value to be slow enough (it probably would be if this
// were bcrypt). Emit a warning.
pyo3::PyErr::warn_bound(
py,
&pyo3::exceptions::PyUserWarning::type_object_bound(py),
&format!("Warning: bcrypt.kdf() called with only {rounds} round(s). This few is not secure: the parameter is linear, like PBKDF2."),
3
)?;
}

pyo3::types::PyBytes::new_bound_with(py, desired_key_bytes, |output| {
pyo3::types::PyBytes::new_with(py, desired_key_bytes, |output| {
py.allow_threads(|| {
bcrypt_pbkdf::bcrypt_pbkdf(password, salt, rounds, output).unwrap();
});
Expand All @@ -177,10 +164,7 @@ fn kdf<'p>(
}

#[pyo3::prelude::pymodule]
fn _bcrypt(
_py: pyo3::Python<'_>,
m: &pyo3::Bound<'_, pyo3::types::PyModule>,
) -> pyo3::PyResult<()> {
fn _bcrypt(_py: pyo3::Python<'_>, m: &pyo3::types::PyModule) -> pyo3::PyResult<()> {
m.add_function(pyo3::wrap_pyfunction!(gensalt, m)?)?;
m.add_function(pyo3::wrap_pyfunction!(hashpw, m)?)?;
m.add_function(pyo3::wrap_pyfunction!(checkpw, m)?)?;
Expand Down
5 changes: 0 additions & 5 deletions tests/test_bcrypt.py
Original file line number Diff line number Diff line change
Expand Up @@ -462,11 +462,6 @@ def test_kdf_no_warn_rounds():
bcrypt.kdf(b"password", b"salt", 10, 10, True)


def test_kdf_warn_rounds():
with pytest.warns(UserWarning):
bcrypt.kdf(b"password", b"salt", 10, 10)


@pytest.mark.parametrize(
("password", "salt", "desired_key_bytes", "rounds", "error"),
[
Expand Down

0 comments on commit ec38f43

Please sign in to comment.