2023-07-10 10:31:46

by FUJITA Tomonori

[permalink] [raw]
Subject: [PATCH v2 0/3] Rust abstractions for Crypto API

This patchset adds minimum Rust abstractions for Crypto API; message
digest and random number generator.

I'm trying to upstream network and crypto abstractions separately so
v2 has only crypto stuff.

Changes since v1 [1]:
- ShashDesc::new() returns the initialized object.
- checks the length of buffer.
- fix a compile error without CONFIG_CRYPTO.
- adds RNG support.
- drops network code.
- updates the CRYPTO API entry.

[1] https://lore.kernel.org/netdev/010101881db036fb-2fb6981d-e0ef-4ad1-83c3-54d64b6d93b3-000000@us-west-2.amazonses.com/T/

FUJITA Tomonori (3):
rust: crypto abstractions for synchronous message digest API
rust: crypto abstractions for random number generator API
MAINTAINERS: add Rust crypto abstractions files to the CRYPTO API
entry

MAINTAINERS | 2 +
rust/bindings/bindings_helper.h | 2 +
rust/helpers.c | 38 ++++++++++
rust/kernel/crypto.rs | 6 ++
rust/kernel/crypto/hash.rs | 128 ++++++++++++++++++++++++++++++++
rust/kernel/crypto/rng.rs | 101 +++++++++++++++++++++++++
rust/kernel/lib.rs | 2 +
7 files changed, 279 insertions(+)
create mode 100644 rust/kernel/crypto.rs
create mode 100644 rust/kernel/crypto/hash.rs
create mode 100644 rust/kernel/crypto/rng.rs

--
2.34.1



2023-07-10 10:31:56

by FUJITA Tomonori

[permalink] [raw]
Subject: [PATCH v2 1/3] rust: crypto abstractions for synchronous message digest API

This patch adds basic abstractions for synchronous message digest API,
wrapping crypto_shash and shash_desc structures.

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

diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 3e601ce2548d..2f198c6d5de5 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/errname.h>
#include <linux/slab.h>
#include <linux/refcount.h>
diff --git a/rust/helpers.c b/rust/helpers.c
index bb594da56137..7966902ed8eb 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>
@@ -28,6 +29,31 @@
#include <linux/sched/signal.h>
#include <linux/wait.h>

+#ifdef CONFIG_CRYPTO
+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);
+#endif
+
__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..f80dd7bd3381
--- /dev/null
+++ b/rust/kernel/crypto.rs
@@ -0,0 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Cryptography.
+
+pub mod hash;
diff --git a/rust/kernel/crypto/hash.rs b/rust/kernel/crypto/hash.rs
new file mode 100644
index 000000000000..cdbc8e70e8f5
--- /dev/null
+++ b/rust/kernel/crypto/hash.rs
@@ -0,0 +1,128 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Cryptographic Hash operations.
+//!
+//! C headers: [`include/crypto/hash.h`](../../../../include/crypto/hash.h)
+
+use crate::{
+ error::{
+ code::{EINVAL, ENOMEM},
+ from_err_ptr, to_result, Result,
+ },
+ str::CStr,
+};
+use alloc::alloc::{alloc, dealloc};
+use core::alloc::Layout;
+
+/// Corresponds to the kernel's `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: &CStr, t: u32, mask: u32) -> Result<Shash> {
+ // SAFETY: There are no safety requirements for this FFI call.
+ let ptr =
+ unsafe { from_err_ptr(bindings::crypto_alloc_shash(name.as_char_ptr(), t, mask)) }?;
+ // INVARIANT: `ptr` is valid and non-null since `crypto_alloc_shash`
+ // returned a valid pointer which was null-checked.
+ 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) }
+ }
+}
+
+/// Corresponds to the kernel's `struct shash_desc`.
+///
+/// # Invariants
+///
+/// The field `ptr` is valid.
+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 `tfm.0` 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)?;
+ // SAFETY: It's safe because layout has non-zero size.
+ let ptr = unsafe { alloc(layout) } as *mut bindings::shash_desc;
+ if ptr.is_null() {
+ return Err(ENOMEM);
+ }
+ // INVARIANT: `ptr` is valid and non-null since `alloc`
+ // returned a valid pointer which was null-checked.
+ let mut desc = ShashDesc { ptr, tfm, size };
+ // SAFETY: `desc.ptr` is valid and non-null since `alloc`
+ // returned a valid pointer which was null-checked.
+ // Additionally, The type invariant guarantees that `tfm.0` is valid.
+ unsafe { (*desc.ptr).tfm = desc.tfm.0 };
+ desc.reset()?;
+ Ok(desc)
+ }
+
+ /// Re-initializes message digest.
+ pub fn reset(&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 {
+ if data.len() > u32::MAX as usize {
+ return Err(EINVAL);
+ }
+ // 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 {
+ if self.tfm.digestsize() as usize > output.len() {
+ return Err(EINVAL);
+ }
+ // 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 85b261209977..3cb8bd8a17d9 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -31,6 +31,8 @@
#[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-07-10 10:32:01

by FUJITA Tomonori

[permalink] [raw]
Subject: [PATCH v2 2/3] rust: crypto abstractions for random number generator API

This patch adds basic abstractions for random number generator API,
wrapping crypto_rng structure.

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

diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 2f198c6d5de5..089ac38c6461 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -7,6 +7,7 @@
*/

#include <crypto/hash.h>
+#include <crypto/rng.h>
#include <linux/errname.h>
#include <linux/slab.h>
#include <linux/refcount.h>
diff --git a/rust/helpers.c b/rust/helpers.c
index 7966902ed8eb..e4dcd611738f 100644
--- a/rust/helpers.c
+++ b/rust/helpers.c
@@ -19,6 +19,7 @@
*/

#include <crypto/hash.h>
+#include <crypto/rng.h>
#include <linux/bug.h>
#include <linux/build_bug.h>
#include <linux/err.h>
@@ -52,6 +53,17 @@ int rust_helper_crypto_shash_init(struct shash_desc *desc) {
return crypto_shash_init(desc);
}
EXPORT_SYMBOL_GPL(rust_helper_crypto_shash_init);
+
+void rust_helper_crypto_free_rng(struct crypto_rng *tfm) {
+ crypto_free_rng(tfm);
+}
+EXPORT_SYMBOL_GPL(rust_helper_crypto_free_rng);
+
+int rust_helper_crypto_rng_generate(struct crypto_rng *tfm, const u8 *src,
+ unsigned int slen, u8 *dst, unsigned int dlen) {
+ return crypto_rng_generate(tfm, src, slen, dst, dlen);
+}
+EXPORT_SYMBOL_GPL(rust_helper_crypto_rng_generate);
#endif

__noreturn void rust_helper_BUG(void)
diff --git a/rust/kernel/crypto.rs b/rust/kernel/crypto.rs
index f80dd7bd3381..a1995e6c85d4 100644
--- a/rust/kernel/crypto.rs
+++ b/rust/kernel/crypto.rs
@@ -3,3 +3,4 @@
//! Cryptography.

pub mod hash;
+pub mod rng;
diff --git a/rust/kernel/crypto/rng.rs b/rust/kernel/crypto/rng.rs
new file mode 100644
index 000000000000..683f5ee464ce
--- /dev/null
+++ b/rust/kernel/crypto/rng.rs
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Random number generator.
+//!
+//! C headers: [`include/crypto/rng.h`](../../../../include/crypto/rng.h)
+
+use crate::{
+ error::{code::EINVAL, from_err_ptr, to_result, Result},
+ str::CStr,
+};
+
+/// Type of Random number generator.
+///
+/// # Invariants
+///
+/// The pointer is valid.
+enum RngType {
+ /// Uses `crypto_default_rng`
+ // We don't need to keep an pointer for the default but simpler.
+ Default(*mut bindings::crypto_rng),
+
+ /// Allocated via `crypto_alloc_rng.
+ Allocated(*mut bindings::crypto_rng),
+}
+
+/// Corresponds to the kernel's `struct crypto_rng`.
+pub struct Rng(RngType);
+
+impl Drop for Rng {
+ fn drop(&mut self) {
+ match self.0 {
+ RngType::Default(_) => {
+ // SAFETY: it's safe because `crypto_get_default_rng()` was called during
+ // the initialization.
+ unsafe {
+ bindings::crypto_put_default_rng();
+ }
+ }
+ RngType::Allocated(ptr) => {
+ // SAFETY: The type invariants of `RngType` guarantees that the pointer is valid.
+ unsafe { bindings::crypto_free_rng(ptr) };
+ }
+ }
+ }
+}
+
+impl Rng {
+ /// Creates a [`Rng`] instance.
+ pub fn new(name: &CStr, t: u32, mask: u32) -> Result<Self> {
+ // SAFETY: There are no safety requirements for this FFI call.
+ let ptr = unsafe { from_err_ptr(bindings::crypto_alloc_rng(name.as_char_ptr(), t, mask)) }?;
+ // INVARIANT: `ptr` is valid and non-null since `crypto_alloc_rng`
+ // returned a valid pointer which was null-checked.
+ Ok(Self(RngType::Allocated(ptr)))
+ }
+
+ /// Creates a [`Rng`] instance with a default algorithm.
+ pub fn new_with_default() -> Result<Self> {
+ // SAFETY: There are no safety requirements for this FFI call.
+ to_result(unsafe { bindings::crypto_get_default_rng() })?;
+ // INVARIANT: The C API guarantees that `crypto_default_rng` is valid until
+ // `crypto_put_default_rng` is called.
+ Ok(Self(RngType::Default(unsafe {
+ bindings::crypto_default_rng
+ })))
+ }
+
+ /// Get a random number.
+ pub fn generate(&mut self, src: &[u8], dst: &mut [u8]) -> Result {
+ if src.len() > u32::MAX as usize || dst.len() > u32::MAX as usize {
+ return Err(EINVAL);
+ }
+ let ptr = match self.0 {
+ RngType::Default(ptr) => ptr,
+ RngType::Allocated(ptr) => ptr,
+ };
+ // SAFETY: The type invariants of `RngType' guarantees that the pointer is valid.
+ to_result(unsafe {
+ bindings::crypto_rng_generate(
+ ptr,
+ src.as_ptr(),
+ src.len() as u32,
+ dst.as_mut_ptr(),
+ dst.len() as u32,
+ )
+ })
+ }
+
+ /// Re-initializes the [`Rng`] instance.
+ pub fn reset(&mut self, seed: &[u8]) -> Result {
+ if seed.len() > u32::MAX as usize {
+ return Err(EINVAL);
+ }
+ let ptr = match self.0 {
+ RngType::Default(ptr) => ptr,
+ RngType::Allocated(ptr) => ptr,
+ };
+ // SAFETY: The type invariants of `RngType' guarantees that the pointer is valid.
+ to_result(unsafe { bindings::crypto_rng_reset(ptr, seed.as_ptr(), seed.len() as u32) })
+ }
+}
--
2.34.1


2023-07-14 16:53:50

by Benno Lossin

[permalink] [raw]
Subject: Re: [PATCH v2 1/3] rust: crypto abstractions for synchronous message digest API

> This patch adds basic abstractions for synchronous message digest API,
> wrapping crypto_shash and shash_desc structures.
>
> Signed-off-by: FUJITA Tomonori <[email protected]>
> ---
> rust/bindings/bindings_helper.h | 1 +
> rust/helpers.c | 26 +++++++
> rust/kernel/crypto.rs | 5 ++
> rust/kernel/crypto/hash.rs | 128 ++++++++++++++++++++++++++++++++
> rust/kernel/lib.rs | 2 +
> 5 files changed, 162 insertions(+)
> create mode 100644 rust/kernel/crypto.rs
> create mode 100644 rust/kernel/crypto/hash.rs
>
> diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
> index 3e601ce2548d..2f198c6d5de5 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/errname.h>
> #include <linux/slab.h>
> #include <linux/refcount.h>
> diff --git a/rust/helpers.c b/rust/helpers.c
> index bb594da56137..7966902ed8eb 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>
> @@ -28,6 +29,31 @@
> #include <linux/sched/signal.h>
> #include <linux/wait.h>
>
> +#ifdef CONFIG_CRYPTO
> +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);
> +#endif
> +
> __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..f80dd7bd3381
> --- /dev/null
> +++ b/rust/kernel/crypto.rs
> @@ -0,0 +1,5 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! Cryptography.
> +
> +pub mod hash;
> diff --git a/rust/kernel/crypto/hash.rs b/rust/kernel/crypto/hash.rs
> new file mode 100644
> index 000000000000..cdbc8e70e8f5
> --- /dev/null
> +++ b/rust/kernel/crypto/hash.rs
> @@ -0,0 +1,128 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! Cryptographic Hash operations.
> +//!
> +//! C headers: [`include/crypto/hash.h`](../../../../include/crypto/hash.h)
> +
> +use crate::{
> + error::{
> + code::{EINVAL, ENOMEM},
> + from_err_ptr, to_result, Result,
> + },
> + str::CStr,
> +};
> +use alloc::alloc::{alloc, dealloc};
> +use core::alloc::Layout;
> +
> +/// Corresponds to the kernel's `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: &CStr, t: u32, mask: u32) -> Result<Shash> {
> + // SAFETY: There are no safety requirements for this FFI call.
> + let ptr =
> + unsafe { from_err_ptr(bindings::crypto_alloc_shash(name.as_char_ptr(), t, mask)) }?;
> + // INVARIANT: `ptr` is valid and non-null since `crypto_alloc_shash`
> + // returned a valid pointer which was null-checked.
> + 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) }
> + }
> +}
> +
> +/// Corresponds to the kernel's `struct shash_desc`.
> +///
> +/// # Invariants
> +///
> +/// The field `ptr` is valid.
> +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 `tfm.0` 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)?;

I still do not like this arbitrary `2` constant as the alignment. Why is
this correct? It should be explained in the code. Otherwise use a
different way to compute the layout via `Layout::new()` and/or
`Layout::repeat`/`Layout::extend` etc.

--
Cheers,
Benno

> + // SAFETY: It's safe because layout has non-zero size.
> + let ptr = unsafe { alloc(layout) } as *mut bindings::shash_desc;
> + if ptr.is_null() {
> + return Err(ENOMEM);
> + }
> + // INVARIANT: `ptr` is valid and non-null since `alloc`
> + // returned a valid pointer which was null-checked.
> + let mut desc = ShashDesc { ptr, tfm, size };
> + // SAFETY: `desc.ptr` is valid and non-null since `alloc`
> + // returned a valid pointer which was null-checked.
> + // Additionally, The type invariant guarantees that `tfm.0` is valid.
> + unsafe { (*desc.ptr).tfm = desc.tfm.0 };
> + desc.reset()?;
> + Ok(desc)
> + }
> +
> + /// Re-initializes message digest.
> + pub fn reset(&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 {
> + if data.len() > u32::MAX as usize {
> + return Err(EINVAL);
> + }
> + // 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 {
> + if self.tfm.digestsize() as usize > output.len() {
> + return Err(EINVAL);
> + }
> + // 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 85b261209977..3cb8bd8a17d9 100644
> --- a/rust/kernel/lib.rs
> +++ b/rust/kernel/lib.rs
> @@ -31,6 +31,8 @@
> #[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-07-14 17:04:00

by Benno Lossin

[permalink] [raw]
Subject: Re: [PATCH v2 2/3] rust: crypto abstractions for random number generator API

> This patch adds basic abstractions for random number generator API,
> wrapping crypto_rng structure.
>
> Signed-off-by: FUJITA Tomonori <[email protected]>
> ---
> rust/bindings/bindings_helper.h | 1 +
> rust/helpers.c | 12 ++++
> rust/kernel/crypto.rs | 1 +
> rust/kernel/crypto/rng.rs | 101 ++++++++++++++++++++++++++++++++
> 4 files changed, 115 insertions(+)
> create mode 100644 rust/kernel/crypto/rng.rs
>
> diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
> index 2f198c6d5de5..089ac38c6461 100644
> --- a/rust/bindings/bindings_helper.h
> +++ b/rust/bindings/bindings_helper.h
> @@ -7,6 +7,7 @@
> */
>
> #include <crypto/hash.h>
> +#include <crypto/rng.h>
> #include <linux/errname.h>
> #include <linux/slab.h>
> #include <linux/refcount.h>
> diff --git a/rust/helpers.c b/rust/helpers.c
> index 7966902ed8eb..e4dcd611738f 100644
> --- a/rust/helpers.c
> +++ b/rust/helpers.c
> @@ -19,6 +19,7 @@
> */
>
> #include <crypto/hash.h>
> +#include <crypto/rng.h>
> #include <linux/bug.h>
> #include <linux/build_bug.h>
> #include <linux/err.h>
> @@ -52,6 +53,17 @@ int rust_helper_crypto_shash_init(struct shash_desc *desc) {
> return crypto_shash_init(desc);
> }
> EXPORT_SYMBOL_GPL(rust_helper_crypto_shash_init);
> +
> +void rust_helper_crypto_free_rng(struct crypto_rng *tfm) {
> + crypto_free_rng(tfm);
> +}
> +EXPORT_SYMBOL_GPL(rust_helper_crypto_free_rng);
> +
> +int rust_helper_crypto_rng_generate(struct crypto_rng *tfm, const u8 *src,
> + unsigned int slen, u8 *dst, unsigned int dlen) {
> + return crypto_rng_generate(tfm, src, slen, dst, dlen);
> +}
> +EXPORT_SYMBOL_GPL(rust_helper_crypto_rng_generate);
> #endif
>
> __noreturn void rust_helper_BUG(void)
> diff --git a/rust/kernel/crypto.rs b/rust/kernel/crypto.rs
> index f80dd7bd3381..a1995e6c85d4 100644
> --- a/rust/kernel/crypto.rs
> +++ b/rust/kernel/crypto.rs
> @@ -3,3 +3,4 @@
> //! Cryptography.
>
> pub mod hash;
> +pub mod rng;
> diff --git a/rust/kernel/crypto/rng.rs b/rust/kernel/crypto/rng.rs
> new file mode 100644
> index 000000000000..683f5ee464ce
> --- /dev/null
> +++ b/rust/kernel/crypto/rng.rs
> @@ -0,0 +1,101 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! Random number generator.
> +//!
> +//! C headers: [`include/crypto/rng.h`](../../../../include/crypto/rng.h)
> +
> +use crate::{
> + error::{code::EINVAL, from_err_ptr, to_result, Result},
> + str::CStr,
> +};
> +
> +/// Type of Random number generator.
> +///
> +/// # Invariants
> +///
> +/// The pointer is valid.
> +enum RngType {
> + /// Uses `crypto_default_rng`
> + // We don't need to keep an pointer for the default but simpler.
> + Default(*mut bindings::crypto_rng),
> +
> + /// Allocated via `crypto_alloc_rng.
> + Allocated(*mut bindings::crypto_rng),
> +}

You could also do this:
```
enum RngType {
Default,
Allocated(NonNull<bindings::crypto_rng>),
}
```

Assuming that `crypto_alloc_rng` never returns null.
Then this definition will the same size as a plain pointer.

> +
> +/// Corresponds to the kernel's `struct crypto_rng`.
> +pub struct Rng(RngType);
> +
> +impl Drop for Rng {
> + fn drop(&mut self) {
> + match self.0 {
> + RngType::Default(_) => {
> + // SAFETY: it's safe because `crypto_get_default_rng()` was called during
> + // the initialization.
> + unsafe {
> + bindings::crypto_put_default_rng();
> + }
> + }
> + RngType::Allocated(ptr) => {
> + // SAFETY: The type invariants of `RngType` guarantees that the pointer is valid.
> + unsafe { bindings::crypto_free_rng(ptr) };
> + }
> + }
> + }
> +}
> +
> +impl Rng {
> + /// Creates a [`Rng`] instance.
> + pub fn new(name: &CStr, t: u32, mask: u32) -> Result<Self> {
> + // SAFETY: There are no safety requirements for this FFI call.
> + let ptr = unsafe { from_err_ptr(bindings::crypto_alloc_rng(name.as_char_ptr(), t, mask)) }?;
> + // INVARIANT: `ptr` is valid and non-null since `crypto_alloc_rng`
> + // returned a valid pointer which was null-checked.
> + Ok(Self(RngType::Allocated(ptr)))
> + }
> +
> + /// Creates a [`Rng`] instance with a default algorithm.
> + pub fn new_with_default() -> Result<Self> {
> + // SAFETY: There are no safety requirements for this FFI call.
> + to_result(unsafe { bindings::crypto_get_default_rng() })?;
> + // INVARIANT: The C API guarantees that `crypto_default_rng` is valid until
> + // `crypto_put_default_rng` is called.
> + Ok(Self(RngType::Default(unsafe {
> + bindings::crypto_default_rng

You are accessing a `mut static`, this is `unsafe` (hence the need
for an `unsafe` block) and needs a safety comment, why is it safe to
access this mutable static? What synchronizes the access (is it not needed)?

> + })))
> + }
> +
> + /// Get a random number.
> + pub fn generate(&mut self, src: &[u8], dst: &mut [u8]) -> Result {
> + if src.len() > u32::MAX as usize || dst.len() > u32::MAX as usize {
> + return Err(EINVAL);
> + }
> + let ptr = match self.0 {
> + RngType::Default(ptr) => ptr,
> + RngType::Allocated(ptr) => ptr,
> + };
> + // SAFETY: The type invariants of `RngType' guarantees that the pointer is valid.
> + to_result(unsafe {
> + bindings::crypto_rng_generate(
> + ptr,
> + src.as_ptr(),
> + src.len() as u32,
> + dst.as_mut_ptr(),
> + dst.len() as u32,
> + )
> + })
> + }
> +
> + /// Re-initializes the [`Rng`] instance.
> + pub fn reset(&mut self, seed: &[u8]) -> Result {
> + if seed.len() > u32::MAX as usize {
> + return Err(EINVAL);
> + }
> + let ptr = match self.0 {
> + RngType::Default(ptr) => ptr,
> + RngType::Allocated(ptr) => ptr,
> + };
> + // SAFETY: The type invariants of `RngType' guarantees that the pointer is valid.
> + to_result(unsafe { bindings::crypto_rng_reset(ptr, seed.as_ptr(), seed.len() as u32) })

If I read this correctly, then if I have to threads each with a `Default`
crypto rng, then one could be reset as the other is concurrently
generating some random numbers, right? This *should* be fine, but does
it make sense to do it? Or should there only be one thread accessing
the default crypto rng?

--
Cheers,
Benno

> + }
> +}
> --
> 2.34.1
>