Skip to content

Commit

Permalink
Convert PKCS7PaddingContext to Rust
Browse files Browse the repository at this point in the history
  • Loading branch information
alex committed Jun 8, 2024
1 parent f3b0e16 commit b0e09e1
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 31 deletions.
7 changes: 7 additions & 0 deletions src/cryptography/hazmat/bindings/_rust/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,16 @@

import typing

from cryptography.hazmat.primitives import padding

def check_pkcs7_padding(data: bytes) -> bool: ...
def check_ansix923_padding(data: bytes) -> bool: ...

class PKCS7PaddingContext(padding.PaddingContext):
def __init__(self, block_size: int) -> None: ...
def update(self, data: bytes) -> bytes: ...
def finalize(self) -> bytes: ...

class ObjectIdentifier:
def __init__(self, val: str) -> None: ...
@property
Expand Down
31 changes: 5 additions & 26 deletions src/cryptography/hazmat/primitives/padding.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from cryptography import utils
from cryptography.exceptions import AlreadyFinalized
from cryptography.hazmat.bindings._rust import (
PKCS7PaddingContext,
check_ansix923_padding,
check_pkcs7_padding,
)
Expand Down Expand Up @@ -111,37 +112,12 @@ def __init__(self, block_size: int):
self.block_size = block_size

def padder(self) -> PaddingContext:
return _PKCS7PaddingContext(self.block_size)
return PKCS7PaddingContext(self.block_size)

def unpadder(self) -> PaddingContext:
return _PKCS7UnpaddingContext(self.block_size)


class _PKCS7PaddingContext(PaddingContext):
_buffer: bytes | None

def __init__(self, block_size: int):
self.block_size = block_size
# TODO: more copies than necessary, we should use zero-buffer (#193)
self._buffer = b""

def update(self, data: bytes) -> bytes:
self._buffer, result = _byte_padding_update(
self._buffer, data, self.block_size
)
return result

def _padding(self, size: int) -> bytes:
return bytes([size]) * size

def finalize(self) -> bytes:
result = _byte_padding_pad(
self._buffer, self.block_size, self._padding
)
self._buffer = None
return result


class _PKCS7UnpaddingContext(PaddingContext):
_buffer: bytes | None

Expand All @@ -164,6 +140,9 @@ def finalize(self) -> bytes:
return result


PaddingContext.register(PKCS7PaddingContext)


class ANSIX923:
def __init__(self, block_size: int):
_byte_padding_check(block_size)
Expand Down
10 changes: 7 additions & 3 deletions src/rust/src/buf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use pyo3::types::IntoPyDict;
use std::slice;

pub(crate) struct CffiBuf<'p> {
_pyobj: pyo3::Bound<'p, pyo3::PyAny>,
pyobj: pyo3::Bound<'p, pyo3::PyAny>,
_bufobj: pyo3::Bound<'p, pyo3::PyAny>,
buf: &'p [u8],
}
Expand All @@ -34,10 +34,14 @@ fn _extract_buffer_length<'p>(
Ok((bufobj, ptrval))
}

impl CffiBuf<'_> {
impl<'a> CffiBuf<'a> {
pub(crate) fn as_bytes(&self) -> &[u8] {
self.buf
}

pub(crate) fn into_pyobj(self) -> pyo3::Bound<'a, pyo3::PyAny> {
self.pyobj
}
}

impl<'a> pyo3::conversion::FromPyObject<'a> for CffiBuf<'a> {
Expand All @@ -59,7 +63,7 @@ impl<'a> pyo3::conversion::FromPyObject<'a> for CffiBuf<'a> {
};

Ok(CffiBuf {
_pyobj: pyobj.clone(),
pyobj: pyobj.clone(),
_bufobj: bufobj,
buf,
})
Expand Down
1 change: 1 addition & 0 deletions src/rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ fn _rust(py: pyo3::Python<'_>, m: &pyo3::Bound<'_, pyo3::types::PyModule>) -> py
padding::check_ansix923_padding,
m
)?)?;
m.add_class::<padding::PKCS7PaddingContext>()?;
m.add_class::<oid::ObjectIdentifier>()?;

m.add_submodule(&asn1::create_submodule(py)?)?;
Expand Down
49 changes: 49 additions & 0 deletions src/rust/src/padding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
// 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 crate::exceptions;

/// Returns the value of the input with the most-significant-bit copied to all
/// of the bits.
fn duplicate_msb_to_all(a: u8) -> u8 {
Expand Down Expand Up @@ -63,6 +67,51 @@ pub(crate) fn check_ansix923_padding(data: &[u8]) -> bool {
(mismatch & 1) == 0
}

#[pyo3::prelude::pyclass]
pub(crate) struct PKCS7PaddingContext {
block_size: usize,
length_seen: Option<usize>,
}

#[pyo3::prelude::pymethods]
impl PKCS7PaddingContext {
#[new]
fn new(block_size: usize) -> PKCS7PaddingContext {
PKCS7PaddingContext {
block_size: block_size / 8,
length_seen: Some(0),
}
}

fn update<'a>(&mut self, buf: CffiBuf<'a>) -> CryptographyResult<pyo3::Bound<'a, pyo3::PyAny>> {
match self.length_seen.as_mut() {
Some(v) => {
*v += buf.as_bytes().len();
Ok(buf.into_pyobj())
}
None => Err(CryptographyError::from(
exceptions::AlreadyFinalized::new_err("Context was already finalized."),
)),
}
}

fn finalize<'p>(
&mut self,
py: pyo3::Python<'p>,
) -> CryptographyResult<pyo3::Bound<'p, pyo3::types::PyBytes>> {
match self.length_seen.take() {
Some(v) => {
let pad_size = self.block_size - (v % self.block_size);
let pad = vec![pad_size as u8; pad_size];
Ok(pyo3::types::PyBytes::new_bound(py, &pad))
}
None => Err(CryptographyError::from(
exceptions::AlreadyFinalized::new_err("Context was already finalized."),
)),
}
}
}

#[cfg(test)]
mod tests {
use super::constant_time_lt;
Expand Down
4 changes: 2 additions & 2 deletions tests/hazmat/primitives/test_padding.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ def __str__(self):

str(mybytes())
padder = padding.PKCS7(128).padder()
padder.update(mybytes(b"abc"))
data = padder.update(mybytes(b"abc")) + padder.finalize()
unpadder = padding.PKCS7(128).unpadder()
unpadder.update(mybytes(padder.finalize()))
unpadder.update(mybytes(data))
assert unpadder.finalize() == b"abc"

@pytest.mark.parametrize(
Expand Down

0 comments on commit b0e09e1

Please sign in to comment.