2023-05-15 04:59:50

by FUJITA Tomonori

[permalink] [raw]
Subject: [PATCH 1/2] rust: add synchronous message digest support

From: FUJITA Tomonori <[email protected]>

Adds abstractions for crypto shash.

Signed-off-by: FUJITA Tomonori <[email protected]>
---
rust/bindings/bindings_helper.h | 1 +
rust/helpers.c | 24 +++++++
rust/kernel/crypto.rs | 108 ++++++++++++++++++++++++++++++++
rust/kernel/lib.rs | 2 +
4 files changed, 135 insertions(+)
create mode 100644 rust/kernel/crypto.rs

diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 50e7a76d5455..65683b9aa45d 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -6,6 +6,7 @@
* Sorted alphabetically.
*/

+#include <crypto/hash.h>
#include <linux/slab.h>
#include <linux/refcount.h>
#include <linux/wait.h>
diff --git a/rust/helpers.c b/rust/helpers.c
index 81e80261d597..03c131b1ca38 100644
--- a/rust/helpers.c
+++ b/rust/helpers.c
@@ -18,6 +18,7 @@
* accidentally exposed.
*/

+#include <crypto/hash.h>
#include <linux/bug.h>
#include <linux/build_bug.h>
#include <linux/err.h>
@@ -27,6 +28,29 @@
#include <linux/sched/signal.h>
#include <linux/wait.h>

+void rust_helper_crypto_free_shash(struct crypto_shash *tfm)
+{
+ crypto_free_shash(tfm);
+}
+EXPORT_SYMBOL_GPL(rust_helper_crypto_free_shash);
+
+unsigned int rust_helper_crypto_shash_digestsize(struct crypto_shash *tfm)
+{
+ return crypto_shash_digestsize(tfm);
+}
+EXPORT_SYMBOL_GPL(rust_helper_crypto_shash_digestsize);
+
+unsigned int rust_helper_crypto_shash_descsize(struct crypto_shash *tfm)
+{
+ return crypto_shash_descsize(tfm);
+}
+EXPORT_SYMBOL_GPL(rust_helper_crypto_shash_descsize);
+
+int rust_helper_crypto_shash_init(struct shash_desc *desc) {
+ return crypto_shash_init(desc);
+}
+EXPORT_SYMBOL_GPL(rust_helper_crypto_shash_init);
+
__noreturn void rust_helper_BUG(void)
{
BUG();
diff --git a/rust/kernel/crypto.rs b/rust/kernel/crypto.rs
new file mode 100644
index 000000000000..963487428525
--- /dev/null
+++ b/rust/kernel/crypto.rs
@@ -0,0 +1,108 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Cryptography.
+//!
+//! C headers: [`include/crypto/hash.h`](../../../../include/crypto/hash.h)
+
+use crate::{
+ error::{from_err_ptr, to_result, Result},
+ str::CStr,
+};
+use alloc::alloc::{alloc, dealloc};
+use core::alloc::Layout;
+
+/// Represents `struct crypto_shash *`.
+///
+/// # Invariants
+///
+/// The pointer is valid.
+pub struct Shash(*mut bindings::crypto_shash);
+
+impl Drop for Shash {
+ fn drop(&mut self) {
+ // SAFETY: The type invariant guarantees that the pointer is valid.
+ unsafe { bindings::crypto_free_shash(self.0) }
+ }
+}
+
+impl Shash {
+ /// Creates a [`Shash`] object for a message digest handle.
+ pub fn new(name: &'static CStr, t: u32, mask: u32) -> Result<Shash> {
+ // SAFETY: FFI call.
+ let ptr =
+ unsafe { from_err_ptr(bindings::crypto_alloc_shash(name.as_char_ptr(), t, mask)) }?;
+ Ok(Self(ptr))
+ }
+
+ /// Sets optional key used by the hashing algorithm.
+ pub fn setkey(&mut self, data: &[u8]) -> Result {
+ // SAFETY: The type invariant guarantees that the pointer is valid.
+ to_result(unsafe {
+ bindings::crypto_shash_setkey(self.0, data.as_ptr(), data.len() as u32)
+ })
+ }
+
+ /// Returns the size of the result of the transformation.
+ pub fn digestsize(&self) -> u32 {
+ // SAFETY: The type invariant guarantees that the pointer is valid.
+ unsafe { bindings::crypto_shash_digestsize(self.0) }
+ }
+}
+
+/// Represents `struct shash_desc *`.
+///
+/// # Invariants
+///
+/// The field `ptr` is non-null and valid for the lifetime of the object.
+pub struct ShashDesc<'a> {
+ ptr: *mut bindings::shash_desc,
+ tfm: &'a Shash,
+ size: usize,
+}
+
+impl Drop for ShashDesc<'_> {
+ fn drop(&mut self) {
+ // SAFETY: The type invariant guarantees that the pointer is valid.
+ unsafe {
+ dealloc(
+ self.ptr.cast(),
+ Layout::from_size_align(self.size, 2).unwrap(),
+ );
+ }
+ }
+}
+
+impl<'a> ShashDesc<'a> {
+ /// Creates a [`ShashDesc`] object for a request data structure for message digest.
+ pub fn new(tfm: &'a Shash) -> Result<Self> {
+ // SAFETY: The type invariant guarantees that the pointer is valid.
+ let size = core::mem::size_of::<bindings::shash_desc>()
+ + unsafe { bindings::crypto_shash_descsize(tfm.0) } as usize;
+ let layout = Layout::from_size_align(size, 2)?;
+ let ptr = unsafe { alloc(layout) } as *mut bindings::shash_desc;
+ let mut desc = ShashDesc { ptr, tfm, size };
+ // SAFETY: The `desc.tfm` is non-null and valid for the lifetime of this object.
+ unsafe { (*desc.ptr).tfm = desc.tfm.0 };
+ Ok(desc)
+ }
+
+ /// (Re)initializes message digest.
+ pub fn init(&mut self) -> Result {
+ // SAFETY: The type invariant guarantees that the pointer is valid.
+ to_result(unsafe { bindings::crypto_shash_init(self.ptr) })
+ }
+
+ /// Adds data to message digest for processing.
+ pub fn update(&mut self, data: &[u8]) -> Result {
+ // SAFETY: The type invariant guarantees that the pointer is valid.
+ to_result(unsafe {
+ bindings::crypto_shash_update(self.ptr, data.as_ptr(), data.len() as u32)
+ })
+ }
+
+ /// Calculates message digest.
+ pub fn finalize(&mut self, output: &mut [u8]) -> Result {
+ // SAFETY: The type invariant guarantees that the pointer is valid.
+ to_result(unsafe { bindings::crypto_shash_final(self.ptr, output.as_mut_ptr()) })
+ }
+}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 676995d4e460..753fd62b84f1 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -35,6 +35,8 @@ extern crate self as kernel;
#[cfg(not(testlib))]
mod allocator;
mod build_assert;
+#[cfg(CONFIG_CRYPTO)]
+pub mod crypto;
pub mod error;
pub mod init;
pub mod ioctl;
--
2.34.1



2023-05-15 06:25:55

by Kent Overstreet

[permalink] [raw]
Subject: Re: [PATCH 1/2] rust: add synchronous message digest support

On Mon, May 15, 2023 at 04:34:27AM +0000, FUJITA Tomonori wrote:
> From: FUJITA Tomonori <[email protected]>
>
> Adds abstractions for crypto shash.
>
> Signed-off-by: FUJITA Tomonori <[email protected]>
> ---
> rust/bindings/bindings_helper.h | 1 +
> rust/helpers.c | 24 +++++++
> rust/kernel/crypto.rs | 108 ++++++++++++++++++++++++++++++++
> rust/kernel/lib.rs | 2 +

I think in the long run we're going to need Rust bindings located right
next to the .c files they're wrapping. Certainly modules will.

2023-05-16 05:53:02

by Eric Biggers

[permalink] [raw]
Subject: Re: [PATCH 1/2] rust: add synchronous message digest support

Hi Fujita,

On Mon, May 15, 2023 at 04:34:27AM +0000, FUJITA Tomonori wrote:
> diff --git a/rust/helpers.c b/rust/helpers.c
> index 81e80261d597..03c131b1ca38 100644
> --- a/rust/helpers.c
> +++ b/rust/helpers.c
> @@ -18,6 +18,7 @@
> * accidentally exposed.
> */
>
> +#include <crypto/hash.h>
> #include <linux/bug.h>
> #include <linux/build_bug.h>
> #include <linux/err.h>
> @@ -27,6 +28,29 @@
> #include <linux/sched/signal.h>
> #include <linux/wait.h>
>
> +void rust_helper_crypto_free_shash(struct crypto_shash *tfm)
> +{
> + crypto_free_shash(tfm);
> +}
> +EXPORT_SYMBOL_GPL(rust_helper_crypto_free_shash);

Shouldn't this code be compiled only when the crypto API is available?

> +impl<'a> ShashDesc<'a> {
> + /// Creates a [`ShashDesc`] object for a request data structure for message digest.
> + pub fn new(tfm: &'a Shash) -> Result<Self> {
> + // SAFETY: The type invariant guarantees that the pointer is valid.
> + let size = core::mem::size_of::<bindings::shash_desc>()
> + + unsafe { bindings::crypto_shash_descsize(tfm.0) } as usize;
> + let layout = Layout::from_size_align(size, 2)?;
> + let ptr = unsafe { alloc(layout) } as *mut bindings::shash_desc;
> + let mut desc = ShashDesc { ptr, tfm, size };
> + // SAFETY: The `desc.tfm` is non-null and valid for the lifetime of this object.
> + unsafe { (*desc.ptr).tfm = desc.tfm.0 };
> + Ok(desc)
> + }
> +
> + /// (Re)initializes message digest.
> + pub fn init(&mut self) -> Result {
> + // SAFETY: The type invariant guarantees that the pointer is valid.
> + to_result(unsafe { bindings::crypto_shash_init(self.ptr) })
> + }
> +
> + /// Adds data to message digest for processing.
> + pub fn update(&mut self, data: &[u8]) -> Result {
> + // SAFETY: The type invariant guarantees that the pointer is valid.
> + to_result(unsafe {
> + bindings::crypto_shash_update(self.ptr, data.as_ptr(), data.len() as u32)
> + })
> + }
> +
> + /// Calculates message digest.
> + pub fn finalize(&mut self, output: &mut [u8]) -> Result {
> + // SAFETY: The type invariant guarantees that the pointer is valid.
> + to_result(unsafe { bindings::crypto_shash_final(self.ptr, output.as_mut_ptr()) })
> + }

This doesn't enforce that init() is called before update() or finalize(). I
think that needs to be checked in the Rust code, since the C code doesn't have
defined behavior in that case.

- Eric

2023-05-16 08:35:37

by FUJITA Tomonori

[permalink] [raw]
Subject: Re: [PATCH 1/2] rust: add synchronous message digest support

Hi,

On Mon, 15 May 2023 22:52:19 -0700
Eric Biggers <[email protected]> wrote:

>> +#include <crypto/hash.h>
>> #include <linux/bug.h>
>> #include <linux/build_bug.h>
>> #include <linux/err.h>
>> @@ -27,6 +28,29 @@
>> #include <linux/sched/signal.h>
>> #include <linux/wait.h>
>>
>> +void rust_helper_crypto_free_shash(struct crypto_shash *tfm)
>> +{
>> + crypto_free_shash(tfm);
>> +}
>> +EXPORT_SYMBOL_GPL(rust_helper_crypto_free_shash);
>
> Shouldn't this code be compiled only when the crypto API is available?

Oops, I'll add #ifdef CONFIG_CRYPTO


>> +impl<'a> ShashDesc<'a> {
>> + /// Creates a [`ShashDesc`] object for a request data structure for message digest.
>> + pub fn new(tfm: &'a Shash) -> Result<Self> {
>> + // SAFETY: The type invariant guarantees that the pointer is valid.
>> + let size = core::mem::size_of::<bindings::shash_desc>()
>> + + unsafe { bindings::crypto_shash_descsize(tfm.0) } as usize;
>> + let layout = Layout::from_size_align(size, 2)?;
>> + let ptr = unsafe { alloc(layout) } as *mut bindings::shash_desc;
>> + let mut desc = ShashDesc { ptr, tfm, size };
>> + // SAFETY: The `desc.tfm` is non-null and valid for the lifetime of this object.
>> + unsafe { (*desc.ptr).tfm = desc.tfm.0 };
>> + Ok(desc)
>> + }
>> +
>> + /// (Re)initializes message digest.
>> + pub fn init(&mut self) -> Result {
>> + // SAFETY: The type invariant guarantees that the pointer is valid.
>> + to_result(unsafe { bindings::crypto_shash_init(self.ptr) })
>> + }
>> +
>> + /// Adds data to message digest for processing.
>> + pub fn update(&mut self, data: &[u8]) -> Result {
>> + // SAFETY: The type invariant guarantees that the pointer is valid.
>> + to_result(unsafe {
>> + bindings::crypto_shash_update(self.ptr, data.as_ptr(), data.len() as u32)
>> + })
>> + }
>> +
>> + /// Calculates message digest.
>> + pub fn finalize(&mut self, output: &mut [u8]) -> Result {
>> + // SAFETY: The type invariant guarantees that the pointer is valid.
>> + to_result(unsafe { bindings::crypto_shash_final(self.ptr, output.as_mut_ptr()) })
>> + }
>
> This doesn't enforce that init() is called before update() or finalize(). I
> think that needs to be checked in the Rust code, since the C code doesn't have
> defined behavior in that case.

Surely, Rust side should handle the case.

If the new() function internally calls init() before returning, it
works? The new() returns an initialized ShaDesc object.


Thanks for reviewing!