Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions src/cryptography/hazmat/backends/openssl/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
_EllipticCurvePrivateKey,
_EllipticCurvePublicKey,
)
from cryptography.hazmat.backends.openssl.hashes import _HashContext
from cryptography.hazmat.backends.openssl.hmac import _HMACContext
from cryptography.hazmat.backends.openssl.poly1305 import (
_POLY1305_KEY_SIZE,
Expand Down Expand Up @@ -274,11 +273,6 @@ def hmac_supported(self, algorithm: hashes.HashAlgorithm) -> bool:

return self.hash_supported(algorithm)

def create_hash_ctx(
self, algorithm: hashes.HashAlgorithm
) -> hashes.HashContext:
return _HashContext(self, algorithm)

def cipher_supported(self, cipher: CipherAlgorithm, mode: Mode) -> bool:
if self._fips_enabled:
# FIPS mode requires AES. TripleDES is disallowed/deprecated in
Expand Down
88 changes: 0 additions & 88 deletions src/cryptography/hazmat/backends/openssl/hashes.py

This file was deleted.

2 changes: 2 additions & 0 deletions src/cryptography/hazmat/bindings/_rust/openssl/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ import typing
from cryptography.hazmat.bindings._rust.openssl import (
ed448,
ed25519,
hashes,
x448,
x25519,
)

__all__ = [
"openssl_version",
"raise_openssl_error",
"hashes",
"ed448",
"ed25519",
"x448",
Expand Down
17 changes: 17 additions & 0 deletions src/cryptography/hazmat/bindings/_rust/openssl/hashes.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.

import typing

from cryptography.hazmat.primitives import hashes

class Hash(hashes.HashContext):
def __init__(
self, algorithm: hashes.HashAlgorithm, backend: typing.Any = None
) -> None: ...
@property
def algorithm(self) -> hashes.HashAlgorithm: ...
def update(self, data: bytes) -> None: ...
def finalize(self) -> bytes: ...
def copy(self) -> Hash: ...
76 changes: 29 additions & 47 deletions src/cryptography/hazmat/primitives/hashes.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,31 @@
import abc
import typing

from cryptography import utils
from cryptography.exceptions import AlreadyFinalized
from cryptography.hazmat.bindings._rust import openssl as rust_openssl

__all__ = [
"HashAlgorithm",
"HashContext",
"Hash",
"ExtendableOutputFunction",
"SHA1",
"SHA512_224",
"SHA512_256",
"SHA224",
"SHA256",
"SHA384",
"SHA512",
"SHA3_224",
"SHA3_256",
"SHA3_384",
"SHA3_512",
"SHAKE128",
"SHAKE256",
"MD5",
"BLAKE2b",
"BLAKE2s",
"SM3",
]


class HashAlgorithm(metaclass=abc.ABCMeta):
Expand Down Expand Up @@ -62,57 +85,16 @@ def copy(self) -> HashContext:
"""


Hash = rust_openssl.hashes.Hash
HashContext.register(Hash)


class ExtendableOutputFunction(metaclass=abc.ABCMeta):
"""
An interface for extendable output functions.
"""


class Hash(HashContext):
_ctx: typing.Optional[HashContext]

def __init__(
self,
algorithm: HashAlgorithm,
backend: typing.Any = None,
ctx: typing.Optional[HashContext] = None,
) -> None:
if not isinstance(algorithm, HashAlgorithm):
raise TypeError("Expected instance of hashes.HashAlgorithm.")
self._algorithm = algorithm

if ctx is None:
from cryptography.hazmat.backends.openssl.backend import (
backend as ossl,
)

self._ctx = ossl.create_hash_ctx(self.algorithm)
else:
self._ctx = ctx

@property
def algorithm(self) -> HashAlgorithm:
return self._algorithm

def update(self, data: bytes) -> None:
if self._ctx is None:
raise AlreadyFinalized("Context was already finalized.")
utils._check_byteslike("data", data)
self._ctx.update(data)

def copy(self) -> Hash:
if self._ctx is None:
raise AlreadyFinalized("Context was already finalized.")
return Hash(self.algorithm, ctx=self._ctx.copy())

def finalize(self) -> bytes:
if self._ctx is None:
raise AlreadyFinalized("Context was already finalized.")
digest = self._ctx.finalize()
self._ctx = None
return digest


class SHA1(HashAlgorithm):
name = "sha1"
digest_size = 20
Expand Down
154 changes: 154 additions & 0 deletions src/rust/src/backend/hashes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
// This file is dual licensed under the terms of the Apache License, Version
// 2.0, and the BSD License. See the LICENSE file in the root of this repository
// for complete details.

use crate::buf::CffiBuf;
use crate::error::{CryptographyError, CryptographyResult};
use std::borrow::Cow;

#[pyo3::prelude::pyclass(module = "cryptography.hazmat.bindings._rust.openssl.hashes")]
struct Hash {
#[pyo3(get)]
algorithm: pyo3::Py<pyo3::PyAny>,
ctx: Option<openssl::hash::Hasher>,
}

impl Hash {
fn get_ctx(&self, py: pyo3::Python<'_>) -> CryptographyResult<&openssl::hash::Hasher> {
if let Some(ctx) = self.ctx.as_ref() {
return Ok(ctx);
};
Err(CryptographyError::from(pyo3::PyErr::from_value(
py.import(pyo3::intern!(py, "cryptography.exceptions"))?
.call_method1(
pyo3::intern!(py, "AlreadyFinalized"),
("Context was already finalized.",),
)?,
)))
}

fn get_mut_ctx(
&mut self,
py: pyo3::Python<'_>,
) -> CryptographyResult<&mut openssl::hash::Hasher> {
if let Some(ctx) = self.ctx.as_mut() {
return Ok(ctx);
}
Err(CryptographyError::from(pyo3::PyErr::from_value(
py.import(pyo3::intern!(py, "cryptography.exceptions"))?
.call_method1(
pyo3::intern!(py, "AlreadyFinalized"),
("Context was already finalized.",),
)?,
)))
}
}

#[pyo3::pymethods]
impl Hash {
#[new]
#[pyo3(signature = (algorithm, backend=None))]
fn new(
py: pyo3::Python<'_>,
algorithm: &pyo3::PyAny,
backend: Option<&pyo3::PyAny>,
) -> CryptographyResult<Hash> {
let _ = backend;
let hash_algorithm_class = py
.import(pyo3::intern!(py, "cryptography.hazmat.primitives.hashes"))?
.getattr(pyo3::intern!(py, "HashAlgorithm"))?;
if !algorithm.is_instance(hash_algorithm_class)? {
return Err(CryptographyError::from(
pyo3::exceptions::PyTypeError::new_err(
"Expected instance of hashes.HashAlgorithm.",
),
));
}

let name = algorithm
.getattr(pyo3::intern!(py, "name"))?
.extract::<&str>()?;
let openssl_name = if name == "blake2b" || name == "blake2s" {
let digest_size = algorithm
.getattr(pyo3::intern!(py, "digest_size"))?
.extract::<usize>()?;
Cow::Owned(format!("{}{}", name, digest_size * 8))
} else {
Cow::Borrowed(name)
};

let md = match openssl::hash::MessageDigest::from_name(&openssl_name) {
Some(md) => md,
None => {
let exceptions_module = py.import(pyo3::intern!(py, "cryptography.exceptions"))?;
let reason = exceptions_module
.getattr(pyo3::intern!(py, "_Reasons"))?
.getattr(pyo3::intern!(py, "UNSUPPORTED_HASH"))?;
return Err(CryptographyError::from(pyo3::PyErr::from_value(
exceptions_module.call_method1(
pyo3::intern!(py, "UnsupportedAlgorithm"),
(
format!("{} is not a supported hash on this backend", name),
reason,
),
)?,
)));
}
};
let ctx = openssl::hash::Hasher::new(md)?;

Ok(Hash {
algorithm: algorithm.into(),
ctx: Some(ctx),
})
}

fn update(&mut self, py: pyo3::Python<'_>, data: CffiBuf<'_>) -> CryptographyResult<()> {
self.get_mut_ctx(py)?.update(data.as_bytes())?;
Ok(())
}

fn finalize<'p>(
&mut self,
py: pyo3::Python<'p>,
) -> CryptographyResult<&'p pyo3::types::PyBytes> {
#[cfg(not(any(CRYPTOGRAPHY_IS_LIBRESSL, CRYPTOGRAPHY_IS_BORINGSSL)))]
{
let xof_class = py
.import(pyo3::intern!(py, "cryptography.hazmat.primitives.hashes"))?
.getattr(pyo3::intern!(py, "ExtendableOutputFunction"))?;
let algorithm = self.algorithm.clone_ref(py);
let algorithm = algorithm.as_ref(py);
if algorithm.is_instance(xof_class)? {
let ctx = self.get_mut_ctx(py)?;
let digest_size = algorithm
.getattr(pyo3::intern!(py, "digest_size"))?
.extract::<usize>()?;
let result = pyo3::types::PyBytes::new_with(py, digest_size, |b| {
ctx.finish_xof(b).unwrap();
Ok(())
})?;
self.ctx = None;
return Ok(result);
}
}

let data = self.get_mut_ctx(py)?.finish()?;
self.ctx = None;
Ok(pyo3::types::PyBytes::new(py, &data))
}

fn copy(&self, py: pyo3::Python<'_>) -> CryptographyResult<Hash> {
Ok(Hash {
algorithm: self.algorithm.clone_ref(py),
ctx: Some(self.get_ctx(py)?.clone()),
})
}
}

pub(crate) fn create_module(py: pyo3::Python<'_>) -> pyo3::PyResult<&pyo3::prelude::PyModule> {
let m = pyo3::prelude::PyModule::new(py, "hashes")?;
m.add_class::<Hash>()?;

Ok(m)
}
Loading