mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [RFC PATCH 0/2] rust: crypto: library AES-128 / SHA-256 / HMAC + RSA
@ 2026-06-17 15:01 Mike Lothian
  2026-06-17 15:01 ` [RFC PATCH 1/2] rust: crypto: add library AES-128 / SHA-256 / HMAC-SHA256 bindings Mike Lothian
                   ` (3 more replies)
  0 siblings, 4 replies; 11+ messages in thread
From: Mike Lothian @ 2026-06-17 15:01 UTC (permalink / raw)
  To: rust-for-linux
  Cc: Mike Lothian, linux-crypto, Eric Biggers, Herbert Xu,
	David S. Miller, Ard Biesheuvel, Miguel Ojeda, Boqun Feng,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Danilo Krummrich, linux-kernel

This RFC series adds a small, reusable kernel::crypto module so in-kernel
Rust code can hash, encrypt a single AES block, and do RSA public-key
encryption:

  1/2  sha256(), hmac_sha256(), Aes128 (single-block ECB)
  2/2  Akcipher + rsa_pubkey_encrypt() over crypto_akcipher

Patch 1 binds the library crypto (lib/crypto) functions directly
(SHA-256 / HMAC-SHA256) and uses one rust_helper_ shim for aes_encrypt()
(its transparent union is unbindable). It runs synchronously in the
calling context with no allocation and is the independently-mergeable,
self-contained contribution.

Patch 2 adds crypto::Akcipher, a thin wrapper over the asynchronous
public-key API (crypto_akcipher) driven synchronously, and a
crypto::rsa_pubkey_encrypt() convenience built on it: it DER-encodes the
RSAPublicKey the "rsa" transform expects, runs one encrypt, and leaves
padding to the caller. The request/scatterlist/completion plumbing (all
static-inline or on-stack) plus a kmalloc bounce for the DMA data path
live in one rust_helper_ shim; crypto_free_akcipher() and
crypto_akcipher_set_pub_key() are exposed through 1:1 shims. Going
through crypto_akcipher rather than the MPI math library means it
composes with any registered RSA implementation, including hardware
offload. It is kept a separate patch so the public-key surface can be
reviewed (or deferred) on its own without touching patch 1.

Both were factored out of an out-of-tree in-kernel Rust DisplayLink DL3
dock driver (which needs SHA/HMAC/AES for HDCP 2.2 and RSA for the AKE),
but the module is generic. Compile-tested in-tree against drm-next.

Mike Lothian (2):
  rust: crypto: add library AES-128 / SHA-256 / HMAC-SHA256 bindings
  rust: crypto: add RSA public-key encryption via crypto_akcipher

 rust/bindings/bindings_helper.h |   3 +
 rust/helpers/crypto.c           |  95 +++++++++++++++++++++++++++
 rust/helpers/helpers.c          |   1 +
 rust/kernel/crypto.rs           | 255 ++++++++++++++++++++++++++++++++++
 rust/kernel/lib.rs              |   1 +
 5 files changed, 355 insertions(+)

--
2.54.0

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [RFC PATCH 1/2] rust: crypto: add library AES-128 / SHA-256 / HMAC-SHA256 bindings
  2026-06-17 15:01 [RFC PATCH 0/2] rust: crypto: library AES-128 / SHA-256 / HMAC + RSA Mike Lothian
@ 2026-06-17 15:01 ` Mike Lothian
  2026-06-17 17:18   ` Eric Biggers
  2026-06-17 15:01 ` [RFC PATCH 2/2] rust: crypto: add RSA public-key encryption via crypto_akcipher Mike Lothian
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 11+ messages in thread
From: Mike Lothian @ 2026-06-17 15:01 UTC (permalink / raw)
  To: rust-for-linux
  Cc: Mike Lothian, linux-crypto, Eric Biggers, Miguel Ojeda,
	Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich,
	Daniel Almeida, Greg Kroah-Hartman, Yury Norov (NVIDIA),
	Asahi Lina, Lorenzo Stoakes, Joel Fernandes, Alexandre Courbot,
	FUJITA Tomonori, Krishna Ketan Rai, linux-kernel

Add a small `kernel::crypto` module exposing the kernel's synchronous
library crypto (lib/crypto) to Rust: SHA-256, HMAC-SHA256 and single-block
AES-128 ECB. These are one-shot, allocation-free and run in the calling
context, suitable for in-kernel Rust users that need a hash or a block
cipher without the full asynchronous crypto API.

SHA-256 (`sha256()`) and HMAC-SHA256 (`hmac_sha256_usingrawkey()`) are
plain exported functions and are bound directly via bindgen, so their
header `<crypto/sha2.h>` is added to bindings_helper.h. AES single-block
encryption goes through a `rust_helper_` shim because `aes_encrypt()` takes
a transparent union (`aes_encrypt_arg`) that bindgen cannot express; the
shim also zeroes the expanded key schedule before returning.

The Rust API: `crypto::sha256(&[u8]) -> [u8; 32]`,
`crypto::hmac_sha256(key, data) -> [u8; 32]`, and the `crypto::Aes128`
type, created with `Aes128::new(key)` and used via
`encrypt_block(&[u8; 16]) -> Result<[u8; 16]>`.

Signed-off-by: Mike Lothian <mike@fireburn.co.uk>
Assisted-by: Claude:claude-opus-4-8 [Claude-Code]
---
 rust/bindings/bindings_helper.h |  2 +
 rust/helpers/crypto.c           | 25 +++++++++++
 rust/helpers/helpers.c          |  1 +
 rust/kernel/crypto.rs           | 77 +++++++++++++++++++++++++++++++++
 rust/kernel/lib.rs              |  1 +
 5 files changed, 106 insertions(+)
 create mode 100644 rust/helpers/crypto.c
 create mode 100644 rust/kernel/crypto.rs

diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 1124785e210b..14671e1825bb 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -28,6 +28,8 @@
  */
 #include <linux/hrtimer_types.h>
 
+#include <crypto/sha2.h>
+
 #include <linux/acpi.h>
 #include <linux/gpu_buddy.h>
 #include <drm/drm_device.h>
diff --git a/rust/helpers/crypto.c b/rust/helpers/crypto.c
new file mode 100644
index 000000000000..dc9614f6fc8e
--- /dev/null
+++ b/rust/helpers/crypto.c
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <crypto/aes.h>
+#include <linux/string.h>
+
+/*
+ * AES-128 single-block ECB encryption: out = AES(key, in).
+ *
+ * A helper because aes_encrypt() takes a transparent union (aes_encrypt_arg)
+ * that bindgen cannot express. SHA-256 and HMAC-SHA256 are plain extern
+ * functions and are bound directly.
+ */
+__rust_helper int
+rust_helper_aes128_encrypt_block(const u8 *key, const u8 *in, u8 *out)
+{
+	struct aes_enckey enckey;
+	int ret;
+
+	ret = aes_prepareenckey(&enckey, key, AES_KEYSIZE_128);
+	if (ret)
+		return ret;
+	aes_encrypt(&enckey, out, in);
+	memzero_explicit(&enckey, sizeof(enckey));
+	return 0;
+}
diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
index 4488a87223b9..45e67929251e 100644
--- a/rust/helpers/helpers.c
+++ b/rust/helpers/helpers.c
@@ -55,6 +55,7 @@
 #include "cpufreq.c"
 #include "cpumask.c"
 #include "cred.c"
+#include "crypto.c"
 #include "device.c"
 #include "dma.c"
 #include "dma-resv.c"
diff --git a/rust/kernel/crypto.rs b/rust/kernel/crypto.rs
new file mode 100644
index 000000000000..c8f2cb994cfd
--- /dev/null
+++ b/rust/kernel/crypto.rs
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Safe wrappers over the kernel's synchronous library crypto.
+//!
+//! Exposes the one-shot `lib/crypto` primitives — AES-128 single-block ECB,
+//! SHA-256 and HMAC-SHA256 — for use from Rust. They run synchronously in the
+//! calling context with no allocation; the hashes are infallible.
+//!
+//! C headers: [`include/crypto/aes.h`](srctree/include/crypto/aes.h),
+//! [`include/crypto/sha2.h`](srctree/include/crypto/sha2.h).
+
+use crate::{bindings, error::to_result, prelude::*};
+
+/// Size of a SHA-256 / HMAC-SHA256 digest, in bytes.
+pub const SHA256_DIGEST_SIZE: usize = 32;
+/// AES-128 block and key size, in bytes.
+pub const AES128_BLOCK_SIZE: usize = 16;
+
+/// Returns the SHA-256 digest of `data`.
+pub fn sha256(data: &[u8]) -> [u8; SHA256_DIGEST_SIZE] {
+    let mut out = [0u8; SHA256_DIGEST_SIZE];
+    // SAFETY: `data` is valid for `data.len()` reads and `out` is a valid
+    // `SHA256_DIGEST_SIZE`-byte output buffer, as `sha256()` requires.
+    unsafe { bindings::sha256(data.as_ptr(), data.len(), out.as_mut_ptr()) };
+    out
+}
+
+/// Returns `HMAC-SHA256(key, data)`.
+pub fn hmac_sha256(key: &[u8], data: &[u8]) -> [u8; SHA256_DIGEST_SIZE] {
+    let mut out = [0u8; SHA256_DIGEST_SIZE];
+    // SAFETY: `key` and `data` are valid for their respective lengths and `out`
+    // is a valid `SHA256_DIGEST_SIZE`-byte output buffer, as required.
+    unsafe {
+        bindings::hmac_sha256_usingrawkey(
+            key.as_ptr(),
+            key.len(),
+            data.as_ptr(),
+            data.len(),
+            out.as_mut_ptr(),
+        )
+    };
+    out
+}
+
+/// An AES-128 key usable for single-block ECB encryption.
+///
+/// # Examples
+///
+/// ```
+/// use kernel::crypto::Aes128;
+/// let cipher = Aes128::new([0u8; 16]);
+/// let _ct = cipher.encrypt_block(&[0u8; 16])?;
+/// # Ok::<(), Error>(())
+/// ```
+pub struct Aes128([u8; AES128_BLOCK_SIZE]);
+
+impl Aes128 {
+    /// Creates an AES-128 key from 16 raw key bytes.
+    pub fn new(key: [u8; AES128_BLOCK_SIZE]) -> Self {
+        Self(key)
+    }
+
+    /// Encrypts one 16-byte block: returns `AES-128-ECB(key, block)`.
+    pub fn encrypt_block(
+        &self,
+        block: &[u8; AES128_BLOCK_SIZE],
+    ) -> Result<[u8; AES128_BLOCK_SIZE]> {
+        let mut out = [0u8; AES128_BLOCK_SIZE];
+        // SAFETY: `self.0`, `block` and `out` are all valid 16-byte buffers, as
+        // the helper requires.
+        let ret = unsafe {
+            bindings::aes128_encrypt_block(self.0.as_ptr(), block.as_ptr(), out.as_mut_ptr())
+        };
+        to_result(ret)?;
+        Ok(out)
+    }
+}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index b72b2fbe046d..3448fa3a0e9e 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -58,6 +58,7 @@
 pub mod cpufreq;
 pub mod cpumask;
 pub mod cred;
+pub mod crypto;
 pub mod debugfs;
 pub mod device;
 pub mod device_id;
-- 
2.54.0


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [RFC PATCH 2/2] rust: crypto: add RSA public-key encryption via crypto_akcipher
  2026-06-17 15:01 [RFC PATCH 0/2] rust: crypto: library AES-128 / SHA-256 / HMAC + RSA Mike Lothian
  2026-06-17 15:01 ` [RFC PATCH 1/2] rust: crypto: add library AES-128 / SHA-256 / HMAC-SHA256 bindings Mike Lothian
@ 2026-06-17 15:01 ` Mike Lothian
  2026-06-17 17:52   ` Eric Biggers
  2026-06-17 15:13 ` [RFC PATCH 0/2] rust: crypto: library AES-128 / SHA-256 / HMAC + RSA Miguel Ojeda
  2026-07-03  3:00 ` [RFC PATCH v2 0/3] " Mike Lothian
  3 siblings, 1 reply; 11+ messages in thread
From: Mike Lothian @ 2026-06-17 15:01 UTC (permalink / raw)
  To: rust-for-linux
  Cc: Mike Lothian, linux-crypto, Eric Biggers, Miguel Ojeda,
	Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich,
	Daniel Almeida, Greg Kroah-Hartman, Yury Norov (NVIDIA),
	Asahi Lina, Lorenzo Stoakes, Joel Fernandes, Alexandre Courbot,
	FUJITA Tomonori, Krishna Ketan Rai, linux-kernel

Add a Rust binding for the asynchronous public-key cipher API
(crypto_akcipher), driven synchronously, and a crypto::rsa_pubkey_encrypt()
convenience built on it.

crypto::Akcipher wraps a tfm allocated with crypto_alloc_akcipher(): new()
selects the algorithm by name, set_pub_key() installs the key in the
algorithm's wire format, and encrypt() runs one public-key operation and
blocks until it completes. crypto::rsa_pubkey_encrypt() DER-encodes the
RSAPublicKey { modulus, publicExponent } that the "rsa" transform's
set_pub_key expects and computes out = (input ^ e) mod n; the caller
applies any padding (PKCS#1 v1.5, EME-OAEP, ...) to the input first, and
out is zeroed on any error so it never retains data from a partial
computation.

The request object, scatterlists and the completion wait are static-inline
or on-stack state that cannot be expressed from Rust, and the akcipher
data path needs DMA-capable (kmalloc, not vmap-stack) buffers, so the
canonical synchronous encrypt sequence and a kmalloc bounce live in a
single rust_helper_ shim; crypto_free_akcipher() and
crypto_akcipher_set_pub_key() are exposed through 1:1 shims as they too
are static inlines.

This goes through the crypto_akcipher subsystem rather than the raw MPI
math library, so it composes with any registered akcipher implementation
(including hardware offload) and does not open-code modular exponentiation.

Signed-off-by: Mike Lothian <mike@fireburn.co.uk>
Assisted-by: Claude:claude-opus-4-8 [Claude-Code]
---
 rust/bindings/bindings_helper.h |   1 +
 rust/helpers/crypto.c           |  70 +++++++++++++
 rust/kernel/crypto.rs           | 180 +++++++++++++++++++++++++++++++-
 3 files changed, 250 insertions(+), 1 deletion(-)

diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 14671e1825bb..e6add9754acd 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -28,6 +28,7 @@
  */
 #include <linux/hrtimer_types.h>
 
+#include <crypto/akcipher.h>
 #include <crypto/sha2.h>
 
 #include <linux/acpi.h>
diff --git a/rust/helpers/crypto.c b/rust/helpers/crypto.c
index dc9614f6fc8e..6681e9155c84 100644
--- a/rust/helpers/crypto.c
+++ b/rust/helpers/crypto.c
@@ -1,6 +1,10 @@
 // SPDX-License-Identifier: GPL-2.0
 
 #include <crypto/aes.h>
+#include <crypto/akcipher.h>
+#include <linux/crypto.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
 #include <linux/string.h>
 
 /*
@@ -23,3 +27,69 @@ rust_helper_aes128_encrypt_block(const u8 *key, const u8 *in, u8 *out)
 	memzero_explicit(&enckey, sizeof(enckey));
 	return 0;
 }
+
+/*
+ * crypto_free_akcipher() and crypto_akcipher_set_pub_key() are static inlines,
+ * so they are exposed to Rust through these 1:1 shims (the same convention as
+ * the other rust_helper_ wrappers).
+ */
+__rust_helper void
+rust_helper_crypto_free_akcipher(struct crypto_akcipher *tfm)
+{
+	crypto_free_akcipher(tfm);
+}
+
+__rust_helper int
+rust_helper_crypto_akcipher_set_pub_key(struct crypto_akcipher *tfm,
+					const void *key, unsigned int keylen)
+{
+	return crypto_akcipher_set_pub_key(tfm, key, keylen);
+}
+
+/*
+ * One-shot synchronous public-key encrypt over the akcipher API: encrypts
+ * @src_len bytes at @src into @dst (capacity @dst_len) and returns the
+ * ciphertext length or -errno.
+ *
+ * The request object, the scatterlists and the completion wait are all
+ * static-inline or on-stack state that cannot be expressed from Rust, so the
+ * canonical synchronous akcipher sequence lives here. The akcipher data path
+ * also needs DMA-capable (kmalloc, not vmap-stack) buffers, so @src/@dst are
+ * bounced through kmalloc'd copies; @dst is overwritten only on success and
+ * the bounce buffers are wiped on free.
+ */
+__rust_helper int
+rust_helper_akcipher_encrypt_oneshot(struct crypto_akcipher *tfm,
+				     const u8 *src, unsigned int src_len,
+				     u8 *dst, unsigned int dst_len)
+{
+	struct akcipher_request *req;
+	struct scatterlist src_sg, dst_sg;
+	DECLARE_CRYPTO_WAIT(wait);
+	u8 *in, *out;
+	int ret = -ENOMEM;
+
+	req = akcipher_request_alloc(tfm, GFP_KERNEL);
+	in = kmemdup(src, src_len, GFP_KERNEL);
+	out = kzalloc(dst_len, GFP_KERNEL);
+	if (!req || !in || !out)
+		goto out;
+
+	sg_init_one(&src_sg, in, src_len);
+	sg_init_one(&dst_sg, out, dst_len);
+	akcipher_request_set_crypt(req, &src_sg, &dst_sg, src_len, dst_len);
+	akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
+				      crypto_req_done, &wait);
+
+	ret = crypto_wait_req(crypto_akcipher_encrypt(req), &wait);
+	if (ret == 0) {
+		memcpy(dst, out, dst_len);
+		ret = req->dst_len;
+	}
+out:
+	kfree_sensitive(in);
+	kfree_sensitive(out);
+	if (req)
+		akcipher_request_free(req);
+	return ret;
+}
diff --git a/rust/kernel/crypto.rs b/rust/kernel/crypto.rs
index c8f2cb994cfd..0824f6ec81df 100644
--- a/rust/kernel/crypto.rs
+++ b/rust/kernel/crypto.rs
@@ -6,10 +6,21 @@
 //! SHA-256 and HMAC-SHA256 — for use from Rust. They run synchronously in the
 //! calling context with no allocation; the hashes are infallible.
 //!
+//! Also exposes the asynchronous public-key API ([`Akcipher`]) over
+//! `crypto_akcipher`, driven synchronously, and a convenience RSA public-key
+//! primitive built on it for callers that do their own padding (see
+//! [`rsa_pubkey_encrypt`]).
+//!
 //! C headers: [`include/crypto/aes.h`](srctree/include/crypto/aes.h),
+//! [`include/crypto/akcipher.h`](srctree/include/crypto/akcipher.h),
 //! [`include/crypto/sha2.h`](srctree/include/crypto/sha2.h).
 
-use crate::{bindings, error::to_result, prelude::*};
+use crate::{
+    bindings,
+    error::{from_err_ptr, to_result},
+    prelude::*,
+};
+use core::ptr::NonNull;
 
 /// Size of a SHA-256 / HMAC-SHA256 digest, in bytes.
 pub const SHA256_DIGEST_SIZE: usize = 32;
@@ -75,3 +86,170 @@ pub fn encrypt_block(
         Ok(out)
     }
 }
+
+/// An asynchronous public-key cipher transform (`struct crypto_akcipher`),
+/// driven synchronously.
+///
+/// Wraps a tfm allocated with `crypto_alloc_akcipher()`; the underlying
+/// algorithm (e.g. `"rsa"`) is selected by name in [`Akcipher::new`]. After a
+/// key is installed with [`set_pub_key`](Akcipher::set_pub_key), [`encrypt`]
+/// performs one public-key operation, blocking until the request completes.
+///
+/// [`encrypt`]: Akcipher::encrypt
+///
+/// # Examples
+///
+/// ```
+/// use kernel::crypto::Akcipher;
+/// // `der` is a DER-encoded RSAPublicKey; `pt` is already padded to key size.
+/// # fn f(der: &[u8], pt: &[u8]) -> Result {
+/// let mut tfm = Akcipher::new(c"rsa")?;
+/// tfm.set_pub_key(der)?;
+/// let mut ct = [0u8; 256];
+/// let n = tfm.encrypt(pt, &mut ct)?;
+/// # let _ = n; Ok(())
+/// # }
+/// ```
+pub struct Akcipher(NonNull<bindings::crypto_akcipher>);
+
+// SAFETY: a tfm is a self-contained kernel object with no thread affinity; the
+// synchronous request path takes its own per-call state, so the handle may be
+// moved and used from any thread.
+unsafe impl Send for Akcipher {}
+
+impl Akcipher {
+    /// Allocates a transform for the named akcipher algorithm (e.g. `c"rsa"`).
+    pub fn new(alg_name: &core::ffi::CStr) -> Result<Self> {
+        // SAFETY: `alg_name` is a valid NUL-terminated C string; the call
+        // returns a valid tfm pointer or an `ERR_PTR`.
+        let tfm = from_err_ptr(unsafe {
+            bindings::crypto_alloc_akcipher(alg_name.as_ptr().cast(), 0, 0)
+        })?;
+        Ok(Self(NonNull::new(tfm).ok_or(ENOMEM)?))
+    }
+
+    /// Installs the public key, encoded in the algorithm's expected wire format
+    /// (for `"rsa"`, a DER-encoded `RSAPublicKey`).
+    pub fn set_pub_key(&mut self, key: &[u8]) -> Result {
+        // SAFETY: `self.0` is a live tfm; `key` is valid for `key.len()` reads.
+        to_result(unsafe {
+            bindings::crypto_akcipher_set_pub_key(
+                self.0.as_ptr(),
+                key.as_ptr().cast(),
+                key.len() as u32,
+            )
+        })
+    }
+
+    /// Encrypts `src` into `dst`, returning the ciphertext length. Blocks until
+    /// the operation completes. `dst` must be at least the algorithm's maximum
+    /// output size (the key/modulus size for RSA).
+    pub fn encrypt(&self, src: &[u8], dst: &mut [u8]) -> Result<usize> {
+        // SAFETY: `self.0` is a live tfm; `src`/`dst` are valid for their
+        // lengths. The helper bounces them through kmalloc'd buffers, runs one
+        // synchronous akcipher encrypt, and returns the length or a negative
+        // errno.
+        let ret = unsafe {
+            bindings::akcipher_encrypt_oneshot(
+                self.0.as_ptr(),
+                src.as_ptr(),
+                src.len() as u32,
+                dst.as_mut_ptr(),
+                dst.len() as u32,
+            )
+        };
+        to_result(ret)?;
+        Ok(ret as usize)
+    }
+}
+
+impl Drop for Akcipher {
+    fn drop(&mut self) {
+        // SAFETY: `self.0` was allocated by `crypto_alloc_akcipher()` and is
+        // freed exactly once here.
+        unsafe { bindings::crypto_free_akcipher(self.0.as_ptr()) };
+    }
+}
+
+/// Appends a DER definite length to `out`.
+fn der_len(out: &mut KVec<u8>, len: usize) -> Result {
+    if len < 0x80 {
+        out.push(len as u8, GFP_KERNEL)?;
+        return Ok(());
+    }
+    let mut bytes = [0u8; core::mem::size_of::<usize>()];
+    let mut n = 0;
+    let mut rest = len;
+    while rest > 0 {
+        bytes[n] = rest as u8;
+        rest >>= 8;
+        n += 1;
+    }
+    out.push(0x80 | n as u8, GFP_KERNEL)?;
+    for i in (0..n).rev() {
+        out.push(bytes[i], GFP_KERNEL)?;
+    }
+    Ok(())
+}
+
+/// Appends a DER `INTEGER` carrying the unsigned big-endian magnitude `bytes`.
+fn der_integer(out: &mut KVec<u8>, bytes: &[u8]) -> Result {
+    // Canonicalise: drop leading zero octets, keeping at least one.
+    let mut mag = bytes;
+    while mag.len() > 1 && mag[0] == 0 {
+        mag = &mag[1..];
+    }
+    // A leading zero is needed to keep the value positive if the top bit is set
+    // (or to represent zero when the magnitude is empty).
+    let pad = mag.is_empty() || mag[0] & 0x80 != 0;
+    out.push(0x02, GFP_KERNEL)?;
+    der_len(out, mag.len() + pad as usize)?;
+    if pad {
+        out.push(0x00, GFP_KERNEL)?;
+    }
+    out.extend_from_slice(mag, GFP_KERNEL)?;
+    Ok(())
+}
+
+/// DER-encodes `RSAPublicKey ::= SEQUENCE { modulus INTEGER, publicExponent
+/// INTEGER }` from big-endian `modulus`/`exponent`, as `rsa`'s `set_pub_key`
+/// expects.
+fn der_rsa_pubkey(modulus: &[u8], exponent: &[u8]) -> Result<KVec<u8>> {
+    let mut body = KVec::new();
+    der_integer(&mut body, modulus)?;
+    der_integer(&mut body, exponent)?;
+    let mut der = KVec::new();
+    der.push(0x30, GFP_KERNEL)?;
+    der_len(&mut der, body.len())?;
+    der.extend_from_slice(&body, GFP_KERNEL)?;
+    Ok(der)
+}
+
+/// Computes the RSA public-key operation `out = (input ^ exponent) mod modulus`
+/// through the `crypto_akcipher` `"rsa"` transform.
+///
+/// All buffers are unsigned big-endian. `out` is written fixed-width to exactly
+/// `out.len()` bytes (left zero-padded by the cipher); pass `out.len()` equal to
+/// the modulus size (e.g. 128 for RSA-1024). This is the bare primitive: the
+/// caller applies any padding (PKCS#1 v1.5, EME-OAEP, …) to `input` first.
+///
+/// `input` interpreted as an integer must be less than `modulus`, as RSA
+/// requires; otherwise an error is returned. On any error `out` is zeroed, so
+/// it never retains data from a partial computation.
+pub fn rsa_pubkey_encrypt(
+    modulus: &[u8],
+    exponent: &[u8],
+    input: &[u8],
+    out: &mut [u8],
+) -> Result {
+    let der = der_rsa_pubkey(modulus, exponent)?;
+    let mut tfm = Akcipher::new(c"rsa")?;
+    tfm.set_pub_key(&der)?;
+    match tfm.encrypt(input, out) {
+        Ok(_) => Ok(()),
+        Err(e) => {
+            out.fill(0);
+            Err(e)
+        }
+    }
+}
-- 
2.54.0


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RFC PATCH 0/2] rust: crypto: library AES-128 / SHA-256 / HMAC + RSA
  2026-06-17 15:01 [RFC PATCH 0/2] rust: crypto: library AES-128 / SHA-256 / HMAC + RSA Mike Lothian
  2026-06-17 15:01 ` [RFC PATCH 1/2] rust: crypto: add library AES-128 / SHA-256 / HMAC-SHA256 bindings Mike Lothian
  2026-06-17 15:01 ` [RFC PATCH 2/2] rust: crypto: add RSA public-key encryption via crypto_akcipher Mike Lothian
@ 2026-06-17 15:13 ` Miguel Ojeda
  2026-06-17 15:19   ` Mike Lothian
  2026-07-03  3:00 ` [RFC PATCH v2 0/3] " Mike Lothian
  3 siblings, 1 reply; 11+ messages in thread
From: Miguel Ojeda @ 2026-06-17 15:13 UTC (permalink / raw)
  To: Mike Lothian
  Cc: rust-for-linux, linux-crypto, Eric Biggers, Herbert Xu,
	David S. Miller, Ard Biesheuvel, Miguel Ojeda, Boqun Feng,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Danilo Krummrich, linux-kernel

On Wed, Jun 17, 2026 at 5:01 PM Mike Lothian <mike@fireburn.co.uk> wrote:
>
> Both were factored out of an out-of-tree in-kernel Rust DisplayLink DL3
> dock driver (which needs SHA/HMAC/AES for HDCP 2.2 and RSA for the AKE),
> but the module is generic. Compile-tested in-tree against drm-next.

Same question as in the other patch series you just sent: is this only
expected for an out-of-tree driver? Maintainers generally cannot take
dead code, unless there is a good justification behind it -- is the
driver expected to land upstream? Do you have a link to the code?

Thanks!

Cheers,
Miguel

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RFC PATCH 0/2] rust: crypto: library AES-128 / SHA-256 / HMAC + RSA
  2026-06-17 15:13 ` [RFC PATCH 0/2] rust: crypto: library AES-128 / SHA-256 / HMAC + RSA Miguel Ojeda
@ 2026-06-17 15:19   ` Mike Lothian
  0 siblings, 0 replies; 11+ messages in thread
From: Mike Lothian @ 2026-06-17 15:19 UTC (permalink / raw)
  To: Miguel Ojeda
  Cc: rust-for-linux, linux-crypto, Eric Biggers, Herbert Xu,
	David S. Miller, Ard Biesheuvel, Miguel Ojeda, Boqun Feng,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Danilo Krummrich, linux-kernel

On Wed, 17 Jun 2026 at 16:13, Miguel Ojeda
<miguel.ojeda.sandonis@gmail.com> wrote:
>
> On Wed, Jun 17, 2026 at 5:01 PM Mike Lothian <mike@fireburn.co.uk> wrote:
> >
> > Both were factored out of an out-of-tree in-kernel Rust DisplayLink DL3
> > dock driver (which needs SHA/HMAC/AES for HDCP 2.2 and RSA for the AKE),
> > but the module is generic. Compile-tested in-tree against drm-next.
>
> Same question as in the other patch series you just sent: is this only
> expected for an out-of-tree driver? Maintainers generally cannot take
> dead code, unless there is a good justification behind it -- is the
> driver expected to land upstream? Do you have a link to the code?
>
> Thanks!
>
> Cheers,
> Miguel

Hi

I've just posted it
https://lore.kernel.org/r/20260617151249.2937-1-mike@fireburn.co.uk

I'd like it upstream if I get it working

Chers

Mike

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RFC PATCH 1/2] rust: crypto: add library AES-128 / SHA-256 / HMAC-SHA256 bindings
  2026-06-17 15:01 ` [RFC PATCH 1/2] rust: crypto: add library AES-128 / SHA-256 / HMAC-SHA256 bindings Mike Lothian
@ 2026-06-17 17:18   ` Eric Biggers
  0 siblings, 0 replies; 11+ messages in thread
From: Eric Biggers @ 2026-06-17 17:18 UTC (permalink / raw)
  To: Mike Lothian
  Cc: rust-for-linux, linux-crypto, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Danilo Krummrich, Daniel Almeida,
	Greg Kroah-Hartman, Yury Norov (NVIDIA),
	Asahi Lina, Lorenzo Stoakes, Joel Fernandes, Alexandre Courbot,
	FUJITA Tomonori, Krishna Ketan Rai, linux-kernel

On Wed, Jun 17, 2026 at 04:01:32PM +0100, Mike Lothian wrote:
> +/*
> + * AES-128 single-block ECB encryption: out = AES(key, in).
> + *
> + * A helper because aes_encrypt() takes a transparent union (aes_encrypt_arg)
> + * that bindgen cannot express. SHA-256 and HMAC-SHA256 are plain extern
> + * functions and are bound directly.
> + */
> +__rust_helper int
> +rust_helper_aes128_encrypt_block(const u8 *key, const u8 *in, u8 *out)
> +{
> +	struct aes_enckey enckey;
> +	int ret;
> +
> +	ret = aes_prepareenckey(&enckey, key, AES_KEYSIZE_128);
> +	if (ret)
> +		return ret;
> +	aes_encrypt(&enckey, out, in);
> +	memzero_explicit(&enckey, sizeof(enckey));
> +	return 0;
> +}

This is kind of an anti-pattern, both in expanding the key for every
block and also exposing bare AES instead of AES modes of operation.
It's true that lib/crypto/ is missing a lot of AES modes (I'm working on
that), but AES-CMAC is there already which is one of the two you need.

- Eric

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RFC PATCH 2/2] rust: crypto: add RSA public-key encryption via crypto_akcipher
  2026-06-17 15:01 ` [RFC PATCH 2/2] rust: crypto: add RSA public-key encryption via crypto_akcipher Mike Lothian
@ 2026-06-17 17:52   ` Eric Biggers
  0 siblings, 0 replies; 11+ messages in thread
From: Eric Biggers @ 2026-06-17 17:52 UTC (permalink / raw)
  To: Mike Lothian
  Cc: rust-for-linux, linux-crypto, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Danilo Krummrich, Daniel Almeida,
	Greg Kroah-Hartman, Yury Norov (NVIDIA),
	Asahi Lina, Lorenzo Stoakes, Joel Fernandes, Alexandre Courbot,
	FUJITA Tomonori, Krishna Ketan Rai, linux-kernel

On Wed, Jun 17, 2026 at 04:01:33PM +0100, Mike Lothian wrote:
> Add a Rust binding for the asynchronous public-key cipher API
> (crypto_akcipher), driven synchronously, and a crypto::rsa_pubkey_encrypt()
> convenience built on it.

Could you keep the crypto_akcipher support private to the file and just
expose rsa_pubkey_encrypt()?  I don't want the use of crypto_akcipher to
be growing significantly.  It's a very bad API, as I think is clear by
all the workarounds you had to implement to use it.  I'd like to replace
it with lib/crypto/ APIs for RSA eventually.

- Eric

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [RFC PATCH v2 0/3] rust: crypto: library AES-128 / SHA-256 / HMAC + RSA
  2026-06-17 15:01 [RFC PATCH 0/2] rust: crypto: library AES-128 / SHA-256 / HMAC + RSA Mike Lothian
                   ` (2 preceding siblings ...)
  2026-06-17 15:13 ` [RFC PATCH 0/2] rust: crypto: library AES-128 / SHA-256 / HMAC + RSA Miguel Ojeda
@ 2026-07-03  3:00 ` Mike Lothian
  2026-07-03  3:00   ` [RFC PATCH v2 1/3] rust: crypto: add library AES-128 / SHA-256 / HMAC-SHA256 bindings Mike Lothian
                     ` (2 more replies)
  3 siblings, 3 replies; 11+ messages in thread
From: Mike Lothian @ 2026-07-03  3:00 UTC (permalink / raw)
  To: rust-for-linux
  Cc: linux-crypto, Eric Biggers, Herbert Xu, David S. Miller,
	Ard Biesheuvel, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Danilo Krummrich, linux-kernel, Mike Lothian

This is v2 of the crypto bindings, rebased onto current drm-next --
no functional change from the version prepared but never sent after
v1's review (see "Changes since v1" below for what that review fixed).
It adds a small, reusable kernel::crypto module so in-kernel Rust code
can hash, MAC, encrypt a single AES block, and do RSA public-key
encryption:

  1/3  sha256(), hmac_sha256(), Aes128 (key-prepared-once block cipher)
  2/3  aes_cmac() over the in-tree AES-CMAC library
  3/3  rsa_pubkey_encrypt() over a new lib/crypto RSA primitive

Patch 1 binds the library crypto (lib/crypto) functions directly
(SHA-256 / HMAC-SHA256) and uses one rust_helper_ shim for aes_encrypt()
(its transparent union is unbindable). It runs synchronously in the
calling context with no allocation and is the independently-mergeable,
self-contained contribution.

Patch 2 adds crypto::aes_cmac() over the in-tree AES-CMAC library
(<crypto/aes-cbc-macs.h>) -- the one mode of operation the consumer needs
that lib/crypto already ships -- rather than building CMAC out of bare
AES. The 128-bit key is prepared once and wiped after use.

Patch 3 adds an RSA public-key primitive. Rather than bind crypto_akcipher
(which Eric flagged as a very bad API not to grow), it adds a small
lib/crypto entry point -- lib/crypto/rsa.c, rsa_pubkey_encrypt(), the bare
RSAEP primitive c = m^e mod n [RFC8017 sec 5.1.1] over the MPI library --
and binds that directly: no akcipher tfm, DER key encoding, scatterlists or
async completion. The caller applies its own padding (RSAES-OAEP, ...). It
is gated by a new bool CONFIG_CRYPTO_LIB_RSA (selects MPILIB); because the
Rust wrapper is exported from the built-in kernel crate the symbol must be
in vmlinux, so the option is a bool and the wrapper is
#[cfg(CONFIG_CRYPTO_LIB_RSA)]-gated -- a kernel that does not configure it
gains no dependency. A from-scratch constant-time/allocation-free lib/crypto
RSA (Eric's longer-term direction) is left as future work.

All three were factored out of an in-kernel Rust DisplayLink DL3 dock
driver (which needs SHA/HMAC/AES-CMAC for HDCP 2.2 and RSA for the AKE),
posted alongside as drm/vino ("[RFC PATCH v2 00/6] drm/vino: DisplayLink
DL3 dock driver"); the intent is to land both upstream. The module is
generic. Compile-tested in-tree against current drm-next. Source:
  https://github.com/FireBurn/vino-scripts
  https://github.com/FireBurn/linux/tree/vino
  https://gitlab.freedesktop.org/FireBurn/linux/-/tree/vino

Changes since v1 (Eric Biggers):
 - Drop the bare single-block ECB helper that re-expanded the key per block;
   Aes128 now prepares the key schedule once and reuses it for a keystream.
 - Add aes_cmac() over the in-tree AES-CMAC library (<crypto/aes-cbc-macs.h>)
   instead of building CMAC out of bare AES (now patch 2/3); the vino driver
   drops its hand-rolled CMAC for it.
 - RSA no longer binds crypto_akcipher. v1 exposed an Akcipher transform;
   following Eric's guidance to drop that API, v2 adds a small lib/crypto RSA
   primitive instead (lib/crypto/rsa.c, now patch 3/3) and binds it directly.
   A full constant-time lib/crypto RSA is left as future work.
 - Rebased onto current drm-next; no other functional change.

Mike Lothian (3):
  rust: crypto: add library AES-128 / SHA-256 / HMAC-SHA256 bindings
  rust: crypto: use the in-tree AES-CMAC library
  rust: crypto: add an RSA public-key primitive in lib/crypto

 include/crypto/rsa.h            |  15 +++
 lib/crypto/Kconfig              |   9 ++
 lib/crypto/Makefile             |   3 +
 lib/crypto/rsa.c                | 102 +++++++++++++++++++
 rust/bindings/bindings_helper.h |   4 +
 rust/helpers/crypto.c           |  37 +++++++
 rust/helpers/helpers.c          |   1 +
 rust/kernel/crypto.rs           | 169 ++++++++++++++++++++++++++++++++
 rust/kernel/lib.rs              |   1 +
 9 files changed, 341 insertions(+)
 create mode 100644 include/crypto/rsa.h
 create mode 100644 lib/crypto/rsa.c
 create mode 100644 rust/helpers/crypto.c
 create mode 100644 rust/kernel/crypto.rs

--
2.55.0

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [RFC PATCH v2 1/3] rust: crypto: add library AES-128 / SHA-256 / HMAC-SHA256 bindings
  2026-07-03  3:00 ` [RFC PATCH v2 0/3] " Mike Lothian
@ 2026-07-03  3:00   ` Mike Lothian
  2026-07-03  3:00   ` [RFC PATCH v2 2/3] rust: crypto: use the in-tree AES-CMAC library Mike Lothian
  2026-07-03  3:00   ` [RFC PATCH v2 3/3] rust: crypto: add an RSA public-key primitive in lib/crypto Mike Lothian
  2 siblings, 0 replies; 11+ messages in thread
From: Mike Lothian @ 2026-07-03  3:00 UTC (permalink / raw)
  To: rust-for-linux
  Cc: linux-crypto, Eric Biggers, Herbert Xu, David S. Miller,
	Ard Biesheuvel, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Danilo Krummrich, linux-kernel, Mike Lothian

Add a small `kernel::crypto` module exposing the kernel's synchronous
library crypto (lib/crypto) to Rust: SHA-256, HMAC-SHA256 and single-block
AES-128 ECB. These are one-shot, allocation-free and run in the calling
context, suitable for in-kernel Rust users that need a hash or a block
cipher without the full asynchronous crypto API.

SHA-256 (`sha256()`) and HMAC-SHA256 (`hmac_sha256_usingrawkey()`) are
plain exported functions and are bound directly via bindgen, so their
header `<crypto/sha2.h>` is added to bindings_helper.h. AES single-block
encryption goes through a `rust_helper_` shim because `aes_encrypt()` takes
a transparent union (`aes_encrypt_arg`) that bindgen cannot express; the
shim also zeroes the expanded key schedule before returning.

The Rust API: `crypto::sha256(&[u8]) -> [u8; 32]`,
`crypto::hmac_sha256(key, data) -> [u8; 32]`, and the `crypto::Aes128`
type, created with `Aes128::new(key)` and used via
`encrypt_block(&[u8; 16]) -> Result<[u8; 16]>`.

Signed-off-by: Mike Lothian <mike@fireburn.co.uk>
Assisted-by: Claude:claude-opus-4-8 [Claude-Code]
---
 rust/bindings/bindings_helper.h |  2 +
 rust/helpers/crypto.c           | 25 +++++++++++
 rust/helpers/helpers.c          |  1 +
 rust/kernel/crypto.rs           | 77 +++++++++++++++++++++++++++++++++
 rust/kernel/lib.rs              |  1 +
 5 files changed, 106 insertions(+)
 create mode 100644 rust/helpers/crypto.c
 create mode 100644 rust/kernel/crypto.rs

diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 1124785e210b..14671e1825bb 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -28,6 +28,8 @@
  */
 #include <linux/hrtimer_types.h>
 
+#include <crypto/sha2.h>
+
 #include <linux/acpi.h>
 #include <linux/gpu_buddy.h>
 #include <drm/drm_device.h>
diff --git a/rust/helpers/crypto.c b/rust/helpers/crypto.c
new file mode 100644
index 000000000000..dc9614f6fc8e
--- /dev/null
+++ b/rust/helpers/crypto.c
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <crypto/aes.h>
+#include <linux/string.h>
+
+/*
+ * AES-128 single-block ECB encryption: out = AES(key, in).
+ *
+ * A helper because aes_encrypt() takes a transparent union (aes_encrypt_arg)
+ * that bindgen cannot express. SHA-256 and HMAC-SHA256 are plain extern
+ * functions and are bound directly.
+ */
+__rust_helper int
+rust_helper_aes128_encrypt_block(const u8 *key, const u8 *in, u8 *out)
+{
+	struct aes_enckey enckey;
+	int ret;
+
+	ret = aes_prepareenckey(&enckey, key, AES_KEYSIZE_128);
+	if (ret)
+		return ret;
+	aes_encrypt(&enckey, out, in);
+	memzero_explicit(&enckey, sizeof(enckey));
+	return 0;
+}
diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
index 998e31052e66..4f8a1d6d129c 100644
--- a/rust/helpers/helpers.c
+++ b/rust/helpers/helpers.c
@@ -56,6 +56,7 @@
 #include "cpufreq.c"
 #include "cpumask.c"
 #include "cred.c"
+#include "crypto.c"
 #include "device.c"
 #include "dma.c"
 #include "dma-resv.c"
diff --git a/rust/kernel/crypto.rs b/rust/kernel/crypto.rs
new file mode 100644
index 000000000000..c8f2cb994cfd
--- /dev/null
+++ b/rust/kernel/crypto.rs
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Safe wrappers over the kernel's synchronous library crypto.
+//!
+//! Exposes the one-shot `lib/crypto` primitives — AES-128 single-block ECB,
+//! SHA-256 and HMAC-SHA256 — for use from Rust. They run synchronously in the
+//! calling context with no allocation; the hashes are infallible.
+//!
+//! C headers: [`include/crypto/aes.h`](srctree/include/crypto/aes.h),
+//! [`include/crypto/sha2.h`](srctree/include/crypto/sha2.h).
+
+use crate::{bindings, error::to_result, prelude::*};
+
+/// Size of a SHA-256 / HMAC-SHA256 digest, in bytes.
+pub const SHA256_DIGEST_SIZE: usize = 32;
+/// AES-128 block and key size, in bytes.
+pub const AES128_BLOCK_SIZE: usize = 16;
+
+/// Returns the SHA-256 digest of `data`.
+pub fn sha256(data: &[u8]) -> [u8; SHA256_DIGEST_SIZE] {
+    let mut out = [0u8; SHA256_DIGEST_SIZE];
+    // SAFETY: `data` is valid for `data.len()` reads and `out` is a valid
+    // `SHA256_DIGEST_SIZE`-byte output buffer, as `sha256()` requires.
+    unsafe { bindings::sha256(data.as_ptr(), data.len(), out.as_mut_ptr()) };
+    out
+}
+
+/// Returns `HMAC-SHA256(key, data)`.
+pub fn hmac_sha256(key: &[u8], data: &[u8]) -> [u8; SHA256_DIGEST_SIZE] {
+    let mut out = [0u8; SHA256_DIGEST_SIZE];
+    // SAFETY: `key` and `data` are valid for their respective lengths and `out`
+    // is a valid `SHA256_DIGEST_SIZE`-byte output buffer, as required.
+    unsafe {
+        bindings::hmac_sha256_usingrawkey(
+            key.as_ptr(),
+            key.len(),
+            data.as_ptr(),
+            data.len(),
+            out.as_mut_ptr(),
+        )
+    };
+    out
+}
+
+/// An AES-128 key usable for single-block ECB encryption.
+///
+/// # Examples
+///
+/// ```
+/// use kernel::crypto::Aes128;
+/// let cipher = Aes128::new([0u8; 16]);
+/// let _ct = cipher.encrypt_block(&[0u8; 16])?;
+/// # Ok::<(), Error>(())
+/// ```
+pub struct Aes128([u8; AES128_BLOCK_SIZE]);
+
+impl Aes128 {
+    /// Creates an AES-128 key from 16 raw key bytes.
+    pub fn new(key: [u8; AES128_BLOCK_SIZE]) -> Self {
+        Self(key)
+    }
+
+    /// Encrypts one 16-byte block: returns `AES-128-ECB(key, block)`.
+    pub fn encrypt_block(
+        &self,
+        block: &[u8; AES128_BLOCK_SIZE],
+    ) -> Result<[u8; AES128_BLOCK_SIZE]> {
+        let mut out = [0u8; AES128_BLOCK_SIZE];
+        // SAFETY: `self.0`, `block` and `out` are all valid 16-byte buffers, as
+        // the helper requires.
+        let ret = unsafe {
+            bindings::aes128_encrypt_block(self.0.as_ptr(), block.as_ptr(), out.as_mut_ptr())
+        };
+        to_result(ret)?;
+        Ok(out)
+    }
+}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 9512af7156df..7fcbf3e7d7af 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -59,6 +59,7 @@
 pub mod cpufreq;
 pub mod cpumask;
 pub mod cred;
+pub mod crypto;
 pub mod debugfs;
 pub mod device;
 pub mod device_id;
-- 
2.55.0


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [RFC PATCH v2 2/3] rust: crypto: use the in-tree AES-CMAC library
  2026-07-03  3:00 ` [RFC PATCH v2 0/3] " Mike Lothian
  2026-07-03  3:00   ` [RFC PATCH v2 1/3] rust: crypto: add library AES-128 / SHA-256 / HMAC-SHA256 bindings Mike Lothian
@ 2026-07-03  3:00   ` Mike Lothian
  2026-07-03  3:00   ` [RFC PATCH v2 3/3] rust: crypto: add an RSA public-key primitive in lib/crypto Mike Lothian
  2 siblings, 0 replies; 11+ messages in thread
From: Mike Lothian @ 2026-07-03  3:00 UTC (permalink / raw)
  To: rust-for-linux
  Cc: linux-crypto, Eric Biggers, Herbert Xu, David S. Miller,
	Ard Biesheuvel, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Danilo Krummrich, linux-kernel, Mike Lothian

Address the v1 RFC review (Eric Biggers):

 - Drop the bare single-block ECB helper (`aes128_encrypt_block`), which
   re-expanded the AES key on every block and exposed bare ECB instead of a
   mode of operation. `Aes128::new()` now prepares the key schedule once (via
   `aes_prepareenckey()`) and `encrypt_block()` reuses it, so a keystream loop
   (e.g. AES-CTR, which `lib/crypto` does not yet provide) no longer re-expands
   the key per block. This stays a low-level building block for the modes the
   library is missing.

 - Add `crypto::aes_cmac()` over the in-tree AES-CMAC library
   (<crypto/aes-cbc-macs.h>) instead of building CMAC out of bare AES, so the
   one mode of operation vino needs that the library already ships comes from
   the library.

Signed-off-by: Mike Lothian <mike@fireburn.co.uk>
Assisted-by: Claude:claude-opus-4-8 [Claude-Code]
---
 rust/bindings/bindings_helper.h |  1 +
 rust/helpers/crypto.c           | 40 ++++++++++------
 rust/kernel/crypto.rs           | 85 ++++++++++++++++++++++++++-------
 3 files changed, 94 insertions(+), 32 deletions(-)

diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 14671e1825bb..60effaf3af16 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -28,6 +28,7 @@
  */
 #include <linux/hrtimer_types.h>
 
+#include <crypto/aes.h>
 #include <crypto/sha2.h>
 
 #include <linux/acpi.h>
diff --git a/rust/helpers/crypto.c b/rust/helpers/crypto.c
index dc9614f6fc8e..a18780231ce0 100644
--- a/rust/helpers/crypto.c
+++ b/rust/helpers/crypto.c
@@ -1,25 +1,37 @@
 // SPDX-License-Identifier: GPL-2.0
 
 #include <crypto/aes.h>
+#include <crypto/aes-cbc-macs.h>
 #include <linux/string.h>
 
 /*
- * AES-128 single-block ECB encryption: out = AES(key, in).
- *
- * A helper because aes_encrypt() takes a transparent union (aes_encrypt_arg)
- * that bindgen cannot express. SHA-256 and HMAC-SHA256 are plain extern
+ * aes_encrypt() takes a transparent union (aes_encrypt_arg) that bindgen cannot
+ * express, so the single-block encrypt step is wrapped here. The key schedule
+ * is prepared once (aes_prepareenckey() is a plain extern bound directly) and
+ * the resulting struct aes_enckey is reused across blocks by the caller, so the
+ * key is not re-expanded per block. SHA-256 and HMAC-SHA256 are plain extern
  * functions and are bound directly.
  */
-__rust_helper int
-rust_helper_aes128_encrypt_block(const u8 *key, const u8 *in, u8 *out)
+__rust_helper void
+rust_helper_aes_enckey_encrypt_block(const struct aes_enckey *key, u8 *out,
+				     const u8 *in)
 {
-	struct aes_enckey enckey;
-	int ret;
+	aes_encrypt(key, out, in);
+}
+
+/*
+ * AES-CMAC one-shot over the in-tree library (crypto/aes-cbc-macs.h): prepares
+ * the 128-bit key, MACs @data and writes the 16-byte tag to @out. A helper
+ * because both aes_cmac_preparekey()'s struct and the aes_cmac() one-shot are
+ * not expressible from Rust directly. The key length is fixed at 128 bits, so
+ * aes_cmac_preparekey() cannot fail; the prepared key is wiped before return.
+ */
+__rust_helper void
+rust_helper_aes_cmac(const u8 *key, const u8 *data, size_t data_len, u8 *out)
+{
+	struct aes_cmac_key cmac_key;
 
-	ret = aes_prepareenckey(&enckey, key, AES_KEYSIZE_128);
-	if (ret)
-		return ret;
-	aes_encrypt(&enckey, out, in);
-	memzero_explicit(&enckey, sizeof(enckey));
-	return 0;
+	aes_cmac_preparekey(&cmac_key, key, AES_KEYSIZE_128);
+	aes_cmac(&cmac_key, data, data_len, out);
+	memzero_explicit(&cmac_key, sizeof(cmac_key));
 }
diff --git a/rust/kernel/crypto.rs b/rust/kernel/crypto.rs
index c8f2cb994cfd..7d96c1c710a4 100644
--- a/rust/kernel/crypto.rs
+++ b/rust/kernel/crypto.rs
@@ -2,11 +2,15 @@
 
 //! Safe wrappers over the kernel's synchronous library crypto.
 //!
-//! Exposes the one-shot `lib/crypto` primitives — AES-128 single-block ECB,
-//! SHA-256 and HMAC-SHA256 — for use from Rust. They run synchronously in the
-//! calling context with no allocation; the hashes are infallible.
+//! Exposes the one-shot `lib/crypto` primitives — AES-128 (an [`Aes128`] key
+//! prepared once for single-block encryption, the building block for modes the
+//! library does not yet provide such as AES-CTR), the in-tree AES-CMAC
+//! ([`aes_cmac`]), SHA-256 and HMAC-SHA256 — for use from Rust. They run
+//! synchronously in the calling context with no allocation; the hashes and the
+//! MAC are infallible.
 //!
 //! C headers: [`include/crypto/aes.h`](srctree/include/crypto/aes.h),
+//! [`include/crypto/aes-cbc-macs.h`](srctree/include/crypto/aes-cbc-macs.h),
 //! [`include/crypto/sha2.h`](srctree/include/crypto/sha2.h).
 
 use crate::{bindings, error::to_result, prelude::*};
@@ -42,36 +46,81 @@
     out
 }
 
-/// An AES-128 key usable for single-block ECB encryption.
+/// Returns `AES-CMAC-128(key, data)` (RFC 4493), computed by the in-tree
+/// AES-CMAC library ([`include/crypto/aes-cbc-macs.h`]). The 128-bit key is
+/// prepared and wiped internally; the call is infallible.
+///
+/// [`include/crypto/aes-cbc-macs.h`]: srctree/include/crypto/aes-cbc-macs.h
+pub fn aes_cmac(
+    key: &[u8; AES128_BLOCK_SIZE],
+    data: &[u8],
+) -> [u8; AES128_BLOCK_SIZE] {
+    let mut out = [0u8; AES128_BLOCK_SIZE];
+    // SAFETY: `key` is a valid 16-byte key, `data` is valid for `data.len()`
+    // reads, and `out` is a valid `AES128_BLOCK_SIZE`-byte output buffer, as the
+    // helper requires.
+    unsafe {
+        bindings::aes_cmac(key.as_ptr(), data.as_ptr(), data.len(), out.as_mut_ptr())
+    };
+    out
+}
+
+/// An AES-128 key, expanded once for single-block encryption.
+///
+/// The key schedule is computed in [`Aes128::new`] and reused across every
+/// [`encrypt_block`](Aes128::encrypt_block) call, so encrypting a stream of
+/// blocks (e.g. an AES-CTR keystream) does not re-expand the key per block. This
+/// is a low-level building block: prefer a full mode of operation where the
+/// library provides one (see [`aes_cmac`]); the bare block cipher is here only
+/// for modes `lib/crypto` does not yet expose, such as AES-CTR.
 ///
 /// # Examples
 ///
 /// ```
 /// use kernel::crypto::Aes128;
-/// let cipher = Aes128::new([0u8; 16]);
-/// let _ct = cipher.encrypt_block(&[0u8; 16])?;
+/// let cipher = Aes128::new(&[0u8; 16])?;
+/// let _ct = cipher.encrypt_block(&[0u8; 16]);
 /// # Ok::<(), Error>(())
 /// ```
-pub struct Aes128([u8; AES128_BLOCK_SIZE]);
+pub struct Aes128(bindings::aes_enckey);
 
 impl Aes128 {
-    /// Creates an AES-128 key from 16 raw key bytes.
-    pub fn new(key: [u8; AES128_BLOCK_SIZE]) -> Self {
-        Self(key)
+    /// Expands an AES-128 key from 16 raw key bytes.
+    pub fn new(key: &[u8; AES128_BLOCK_SIZE]) -> Result<Self> {
+        // SAFETY: `aes_enckey` is a plain-old-data key schedule (integer arrays
+        // in a union of integer arrays); an all-zero bit pattern is a valid,
+        // inert initial value, fully overwritten by `aes_prepareenckey()` below.
+        let mut enckey: bindings::aes_enckey = unsafe { core::mem::zeroed() };
+        // SAFETY: `enckey` is a valid, owned `aes_enckey`; `key` is a valid
+        // 16-byte buffer; `AES128_BLOCK_SIZE` (16) is a supported key length.
+        let ret = unsafe {
+            bindings::aes_prepareenckey(&mut enckey, key.as_ptr(), AES128_BLOCK_SIZE)
+        };
+        to_result(ret)?;
+        Ok(Self(enckey))
     }
 
-    /// Encrypts one 16-byte block: returns `AES-128-ECB(key, block)`.
+    /// Encrypts one 16-byte block with the prepared key: returns
+    /// `AES-128-ECB(key, block)`.
     pub fn encrypt_block(
         &self,
         block: &[u8; AES128_BLOCK_SIZE],
-    ) -> Result<[u8; AES128_BLOCK_SIZE]> {
+    ) -> [u8; AES128_BLOCK_SIZE] {
         let mut out = [0u8; AES128_BLOCK_SIZE];
-        // SAFETY: `self.0`, `block` and `out` are all valid 16-byte buffers, as
-        // the helper requires.
-        let ret = unsafe {
-            bindings::aes128_encrypt_block(self.0.as_ptr(), block.as_ptr(), out.as_mut_ptr())
+        // SAFETY: `self.0` is a prepared encryption key; `block` and `out` are
+        // valid 16-byte buffers, as the helper requires.
+        unsafe {
+            bindings::aes_enckey_encrypt_block(&self.0, out.as_mut_ptr(), block.as_ptr())
         };
-        to_result(ret)?;
-        Ok(out)
+        out
+    }
+}
+
+impl Drop for Aes128 {
+    fn drop(&mut self) {
+        // SAFETY: `self.0` is a valid, owned `aes_enckey`; overwriting it with
+        // an all-zero `aes_enckey` clears the expanded key schedule.
+        // `write_volatile` keeps the store from being optimised away.
+        unsafe { core::ptr::write_volatile(&mut self.0, core::mem::zeroed()) };
     }
 }
-- 
2.55.0


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [RFC PATCH v2 3/3] rust: crypto: add an RSA public-key primitive in lib/crypto
  2026-07-03  3:00 ` [RFC PATCH v2 0/3] " Mike Lothian
  2026-07-03  3:00   ` [RFC PATCH v2 1/3] rust: crypto: add library AES-128 / SHA-256 / HMAC-SHA256 bindings Mike Lothian
  2026-07-03  3:00   ` [RFC PATCH v2 2/3] rust: crypto: use the in-tree AES-CMAC library Mike Lothian
@ 2026-07-03  3:00   ` Mike Lothian
  2 siblings, 0 replies; 11+ messages in thread
From: Mike Lothian @ 2026-07-03  3:00 UTC (permalink / raw)
  To: rust-for-linux
  Cc: linux-crypto, Eric Biggers, Herbert Xu, David S. Miller,
	Ard Biesheuvel, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Danilo Krummrich, linux-kernel, Mike Lothian

Address the v1 RFC review (Eric Biggers): rather than binding
crypto_akcipher ("a very bad API"), add the RSA public-key operation as a
small lib/crypto primitive and bind that.

New lib/crypto/rsa.c exports

  int rsa_pubkey_encrypt(const u8 *n, size_t n_len, const u8 *e, size_t
			 e_len, const u8 *in, size_t in_len, u8 *out,
			 size_t out_len);

the bare RSAEP primitive c = m^e mod n [RFC8017 sec 5.1.1], computed with
the MPI big-integer library (the same arithmetic crypto/rsa.c uses) but
without the akcipher tfm, DER key encoding, scatterlists or async
completion. It rejects m >= n (which mpi_powm() would otherwise silently
reduce, yielding an undecryptable ciphertext), writes the result
fixed-width big-endian left zero-padded to out_len, and zeroes out on
every error path. Callers apply their own padding (RSAES-OAEP, ...).
Declared in include/crypto/rsa.h, gated by a new CONFIG_CRYPTO_LIB_RSA
(selects MPILIB).

crypto::rsa_pubkey_encrypt() wraps it directly. Because the wrapper is
compiled into and exported from the built-in kernel crate, the C symbol
must live in vmlinux: CONFIG_CRYPTO_LIB_RSA is therefore a bool (a select
forces it built-in) and the wrapper is #[cfg(CONFIG_CRYPTO_LIB_RSA)]-gated
so no vmlinux dependency is introduced for kernels that do not configure
it. A consumer (e.g. the vino driver) selects it.

A from-scratch constant-time/allocation-free RSA is out of scope; this is
the lib/crypto entry point that lets a Rust caller avoid crypto_akcipher.

Signed-off-by: Mike Lothian <mike@fireburn.co.uk>
Assisted-by: Claude:claude-opus-4-8 [Claude-Code]
---
 include/crypto/rsa.h            |  15 +++++
 lib/crypto/Kconfig              |   9 +++
 lib/crypto/Makefile             |   3 +
 lib/crypto/rsa.c                | 102 ++++++++++++++++++++++++++++++++
 rust/bindings/bindings_helper.h |   1 +
 rust/kernel/crypto.rs           |  43 ++++++++++++++
 6 files changed, 173 insertions(+)
 create mode 100644 include/crypto/rsa.h
 create mode 100644 lib/crypto/rsa.c

diff --git a/include/crypto/rsa.h b/include/crypto/rsa.h
new file mode 100644
index 000000000000..c11d7b5cae9a
--- /dev/null
+++ b/include/crypto/rsa.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * RSA public-key primitive (RSAEP), library interface.
+ *
+ * Copyright (c) 2026 Mike Lothian <mike@fireburn.co.uk>
+ */
+#ifndef _CRYPTO_RSA_H
+#define _CRYPTO_RSA_H
+
+#include <linux/types.h>
+
+int rsa_pubkey_encrypt(const u8 *n, size_t n_len, const u8 *e, size_t e_len,
+		       const u8 *in, size_t in_len, u8 *out, size_t out_len);
+
+#endif /* _CRYPTO_RSA_H */
diff --git a/lib/crypto/Kconfig b/lib/crypto/Kconfig
index 591c1c2a7fb3..04655c104e65 100644
--- a/lib/crypto/Kconfig
+++ b/lib/crypto/Kconfig
@@ -45,6 +45,15 @@ config CRYPTO_LIB_AESGCM
 config CRYPTO_LIB_ARC4
 	tristate
 
+config CRYPTO_LIB_RSA
+	bool
+	select MPILIB
+	help
+	  The RSA public-key primitive (RSAEP, out = in^e mod n), built on the
+	  MPI big-integer library. The bare primitive only; callers apply their
+	  own padding. Bool rather than tristate: the in-kernel consumer is the
+	  built-in Rust crypto bindings, so it must live in vmlinux.
+
 config CRYPTO_LIB_GF128MUL
 	tristate
 
diff --git a/lib/crypto/Makefile b/lib/crypto/Makefile
index f1e9bf89785f..486557df59e3 100644
--- a/lib/crypto/Makefile
+++ b/lib/crypto/Makefile
@@ -69,6 +69,9 @@ libaesgcm-y					:= aesgcm.o
 obj-$(CONFIG_CRYPTO_LIB_ARC4)			+= libarc4.o
 libarc4-y					:= arc4.o
 
+obj-$(CONFIG_CRYPTO_LIB_RSA)			+= librsa.o
+librsa-y					:= rsa.o
+
 obj-$(CONFIG_CRYPTO_LIB_GF128MUL)		+= gf128mul.o
 
 ################################################################################
diff --git a/lib/crypto/rsa.c b/lib/crypto/rsa.c
new file mode 100644
index 000000000000..21c1d6c9ea26
--- /dev/null
+++ b/lib/crypto/rsa.c
@@ -0,0 +1,102 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * RSA public-key primitive (RSAEP) [RFC8017 sec 5.1.1].
+ *
+ * A minimal synchronous library entry point for the RSA public-key operation,
+ * built on the MPI big-integer library (the same arithmetic the crypto_akcipher
+ * "rsa" transform uses), but without the akcipher tfm allocation, DER key
+ * encoding, scatterlists or asynchronous completion machinery. It performs only
+ * the bare RSAEP primitive; callers apply any encoding/padding (RSAES-OAEP,
+ * RSAES-PKCS1-v1_5, ...) themselves.
+ *
+ * Copyright (c) 2026 Mike Lothian <mike@fireburn.co.uk>
+ */
+
+#include <crypto/rsa.h>
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/mpi.h>
+#include <linux/module.h>
+#include <linux/string.h>
+
+/**
+ * rsa_pubkey_encrypt() - RSA public-key operation, out = (in ^ e) mod n
+ * @n: modulus, unsigned big-endian
+ * @n_len: length of @n, in bytes
+ * @e: public exponent, unsigned big-endian
+ * @e_len: length of @e, in bytes
+ * @in: input, unsigned big-endian; already padded by the caller
+ * @in_len: length of @in, in bytes
+ * @out: output buffer; receives the result big-endian, left zero-padded to
+ *	 exactly @out_len bytes
+ * @out_len: size of @out; pass the modulus length (e.g. 128 for RSA-1024)
+ *
+ * Computes the bare RSAEP primitive c = m^e mod n, where m is @in interpreted
+ * as a big-endian integer. This is the raw public-key operation [RFC8017 sec
+ * 5.1.1]; the caller is responsible for any message encoding/padding.
+ *
+ * @in interpreted as an integer must be numerically less than @n, as RSA
+ * requires: a larger value would be silently reduced mod n by the modular
+ * exponentiation and yield a ciphertext the peer cannot decrypt, so it is
+ * rejected. On any error path @out is zeroed, so it never retains data from a
+ * partial computation.
+ *
+ * Return: 0 on success; -EINVAL on a malformed key or out-of-range input;
+ * -EOVERFLOW if the result does not fit in @out_len; -ENOMEM on allocation
+ * failure.
+ */
+int rsa_pubkey_encrypt(const u8 *n, size_t n_len, const u8 *e, size_t e_len,
+		       const u8 *in, size_t in_len, u8 *out, size_t out_len)
+{
+	MPI mn, me, mbase, mres;
+	unsigned int nbytes;
+	int ret = -ENOMEM;
+
+	if (!n_len || !e_len || !in_len || !out_len)
+		return -EINVAL;
+
+	mn = mpi_read_raw_data(n, n_len);
+	me = mpi_read_raw_data(e, e_len);
+	mbase = mpi_read_raw_data(in, in_len);
+	mres = mpi_alloc(0);
+	if (!mn || !me || !mbase || !mres)
+		goto out;
+
+	/*
+	 * RSA requires 0 <= m < n; reject m >= n rather than let mpi_powm()
+	 * silently reduce it and produce an undecryptable ciphertext.
+	 */
+	if (mpi_cmp(mbase, mn) >= 0) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ret = mpi_powm(mres, mbase, me, mn);
+	if (ret)
+		goto out;
+
+	/*
+	 * mpi_read_buffer() writes the minimal big-endian form left-aligned
+	 * (and returns -EOVERFLOW without writing if @out_len is too small);
+	 * right-align it into the fixed-width output and zero-pad the front.
+	 */
+	ret = mpi_read_buffer(mres, out, out_len, &nbytes, NULL);
+	if (ret)
+		goto out;
+	if (nbytes < out_len) {
+		memmove(out + (out_len - nbytes), out, nbytes);
+		memset(out, 0, out_len - nbytes);
+	}
+out:
+	if (ret)
+		memzero_explicit(out, out_len);
+	mpi_free(mn);
+	mpi_free(me);
+	mpi_free(mbase);
+	mpi_free(mres);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(rsa_pubkey_encrypt);
+
+MODULE_DESCRIPTION("RSA public-key primitive (RSAEP)");
+MODULE_LICENSE("GPL");
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 60effaf3af16..7bb04d68f9d2 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -29,6 +29,7 @@
 #include <linux/hrtimer_types.h>
 
 #include <crypto/aes.h>
+#include <crypto/rsa.h>
 #include <crypto/sha2.h>
 
 #include <linux/acpi.h>
diff --git a/rust/kernel/crypto.rs b/rust/kernel/crypto.rs
index 7d96c1c710a4..b6836e771cfa 100644
--- a/rust/kernel/crypto.rs
+++ b/rust/kernel/crypto.rs
@@ -9,8 +9,12 @@
 //! synchronously in the calling context with no allocation; the hashes and the
 //! MAC are infallible.
 //!
+//! Also exposes the `lib/crypto` RSA public-key primitive
+//! ([`rsa_pubkey_encrypt`]) for callers that do their own padding.
+//!
 //! C headers: [`include/crypto/aes.h`](srctree/include/crypto/aes.h),
 //! [`include/crypto/aes-cbc-macs.h`](srctree/include/crypto/aes-cbc-macs.h),
+//! [`include/crypto/rsa.h`](srctree/include/crypto/rsa.h),
 //! [`include/crypto/sha2.h`](srctree/include/crypto/sha2.h).
 
 use crate::{bindings, error::to_result, prelude::*};
@@ -124,3 +128,42 @@ fn drop(&mut self) {
         unsafe { core::ptr::write_volatile(&mut self.0, core::mem::zeroed()) };
     }
 }
+
+/// Computes the RSA public-key primitive `out = (input ^ exponent) mod modulus`
+/// using the in-tree `lib/crypto` RSA library (`rsa_pubkey_encrypt()` in
+/// [`lib/crypto/rsa.c`](srctree/lib/crypto/rsa.c)).
+///
+/// All buffers are unsigned big-endian. `out` is written fixed-width to exactly
+/// `out.len()` bytes (left zero-padded); pass `out.len()` equal to the modulus
+/// size (e.g. 128 for RSA-1024). This is the bare primitive: the caller applies
+/// any padding (PKCS#1 v1.5, EME-OAEP, …) to `input` first.
+///
+/// `input` interpreted as an integer must be less than `modulus`, as RSA
+/// requires; otherwise an error is returned. On any error `out` is zeroed by the
+/// library, so it never retains data from a partial computation.
+///
+/// Only available when `CONFIG_CRYPTO_LIB_RSA` is enabled (a consumer selects
+/// it); the backing library is then built into the kernel.
+#[cfg(CONFIG_CRYPTO_LIB_RSA)]
+pub fn rsa_pubkey_encrypt(
+    modulus: &[u8],
+    exponent: &[u8],
+    input: &[u8],
+    out: &mut [u8],
+) -> Result {
+    // SAFETY: each slice is valid for its stated length; the library function
+    // reads `modulus`/`exponent`/`input` and writes exactly `out.len()` bytes
+    // into `out` (left zero-padded), zeroing it on any error.
+    to_result(unsafe {
+        bindings::rsa_pubkey_encrypt(
+            modulus.as_ptr(),
+            modulus.len(),
+            exponent.as_ptr(),
+            exponent.len(),
+            input.as_ptr(),
+            input.len(),
+            out.as_mut_ptr(),
+            out.len(),
+        )
+    })
+}
-- 
2.55.0


^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2026-07-03  3:01 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-06-17 15:01 [RFC PATCH 0/2] rust: crypto: library AES-128 / SHA-256 / HMAC + RSA Mike Lothian
2026-06-17 15:01 ` [RFC PATCH 1/2] rust: crypto: add library AES-128 / SHA-256 / HMAC-SHA256 bindings Mike Lothian
2026-06-17 17:18   ` Eric Biggers
2026-06-17 15:01 ` [RFC PATCH 2/2] rust: crypto: add RSA public-key encryption via crypto_akcipher Mike Lothian
2026-06-17 17:52   ` Eric Biggers
2026-06-17 15:13 ` [RFC PATCH 0/2] rust: crypto: library AES-128 / SHA-256 / HMAC + RSA Miguel Ojeda
2026-06-17 15:19   ` Mike Lothian
2026-07-03  3:00 ` [RFC PATCH v2 0/3] " Mike Lothian
2026-07-03  3:00   ` [RFC PATCH v2 1/3] rust: crypto: add library AES-128 / SHA-256 / HMAC-SHA256 bindings Mike Lothian
2026-07-03  3:00   ` [RFC PATCH v2 2/3] rust: crypto: use the in-tree AES-CMAC library Mike Lothian
2026-07-03  3:00   ` [RFC PATCH v2 3/3] rust: crypto: add an RSA public-key primitive in lib/crypto Mike Lothian

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

Powered by JetHome