Message ID | 010101881db037b4-c8c941a9-c482-4759-9c07-b8bf645d96ed-000000@us-west-2.amazonses.com |
---|---|
State | New |
Headers | show |
Series | rust: networking and crypto abstractions | expand |
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
Hi, On Mon, 15 May 2023 22:52:19 -0700 Eric Biggers <ebiggers@kernel.org> 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!
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;