From: Mike Lothian <mike@fireburn.co.uk>
To: linux-crypto@vger.kernel.org
Cc: "Mike Lothian" <mike@fireburn.co.uk>,
"Herbert Xu" <herbert@gondor.apana.org.au>,
"David S. Miller" <davem@davemloft.net>,
"Eric Biggers" <ebiggers@kernel.org>,
"Jason A. Donenfeld" <Jason@zx2c4.com>,
"Ard Biesheuvel" <ardb@kernel.org>,
"Miguel Ojeda" <ojeda@kernel.org>,
"Boqun Feng" <boqun@kernel.org>, "Gary Guo" <gary@garyguo.net>,
"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
"Benno Lossin" <lossin@kernel.org>,
"Andreas Hindborg" <a.hindborg@kernel.org>,
"Alice Ryhl" <aliceryhl@google.com>,
"Trevor Gross" <tmgross@umich.edu>,
"Danilo Krummrich" <dakr@kernel.org>,
"Daniel Almeida" <daniel.almeida@collabora.com>,
"Tamir Duberstein" <tamird@kernel.org>,
"Alexandre Courbot" <acourbot@nvidia.com>,
"Onur Özkan" <work@onurozkan.dev>,
"Lyude Paul" <lyude@redhat.com>,
"Greg Kroah-Hartman" <gregkh@linuxfoundation.org>,
"Asahi Lina" <lina+kernel@asahilina.net>,
"Burak Emir" <bqe@google.com>, "Lorenzo Stoakes" <ljs@kernel.org>,
"Joel Fernandes" <joelagnelf@nvidia.com>,
"Yury Norov" <ynorov@nvidia.com>,
"David Gow" <david@davidgow.net>,
linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org
Subject: [PATCH v3 2/2] rust: crypto: add synchronous RSA akcipher support
Date: Wed, 26 Aug 2026 17:29:49 +0100 [thread overview]
Message-ID: <20260826163004.3365-3-mike@fireburn.co.uk> (raw)
In-Reply-To: <20260826163004.3365-1-mike@fireburn.co.uk>
Add an RAII Rust wrapper for RSA public keys backed by the kernel
crypto_akcipher implementation. Encode modulus and exponent components
in the PKCS#1 DER form accepted by the existing RSA transform and
expose synchronous public-key encryption.
Provide RSAES-OAEP with SHA-256 for consumers which need that standard
encoding, while keeping the OAEP seed explicit for use with the kernel
CSPRNG and deterministic known-answer tests. Wipe encoded messages,
expanded AES keys, and Secret byte strings with memzero_explicit().
Gate the AES, SHA-256, and akcipher bindings with dedicated Rust
Kconfig symbols. Cover the DER encoder and OAEP path with a published
Wycheproof vector.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Mike Lothian <mike@fireburn.co.uk>
---
crypto/Kconfig | 10 ++
lib/crypto/Kconfig | 19 ++
rust/bindings/bindings_helper.h | 1 +
rust/helpers/crypto.c | 27 +++
rust/kernel/Kconfig.test | 14 ++
rust/kernel/crypto.rs | 72 +++++++-
rust/kernel/crypto/akcipher.rs | 298 ++++++++++++++++++++++++++++++++
7 files changed, 435 insertions(+), 6 deletions(-)
create mode 100644 rust/kernel/crypto/akcipher.rs
diff --git a/crypto/Kconfig b/crypto/Kconfig
index f1e372195273..9b7cfdbef7c4 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -116,6 +116,16 @@ config CRYPTO_AKCIPHER
select CRYPTO_AKCIPHER2
select CRYPTO_ALGAPI
+config RUST_CRYPTO_AKCIPHER
+ bool
+ depends on RUST
+ select CRYPTO_AKCIPHER
+ help
+ Enable safe Rust abstractions for the public-key cipher API. The
+ crypto API core is built into the kernel because Rust abstractions
+ are part of the built-in kernel crate; individual algorithms may
+ remain modules.
+
config CRYPTO_KPP2
tristate
select CRYPTO_ALGAPI2
diff --git a/lib/crypto/Kconfig b/lib/crypto/Kconfig
index 591c1c2a7fb3..9d47fce62f54 100644
--- a/lib/crypto/Kconfig
+++ b/lib/crypto/Kconfig
@@ -36,6 +36,16 @@ config CRYPTO_LIB_AES_CBC_MACS
this if your module uses any of the functions from
<crypto/aes-cbc-macs.h>.
+config RUST_CRYPTO_LIB_AES
+ bool
+ depends on RUST
+ select CRYPTO_LIB_AES
+ select CRYPTO_LIB_AES_CBC_MACS
+ help
+ Enable the Rust bindings for the synchronous AES library functions.
+ The selected C libraries are built into the kernel because Rust
+ abstractions are part of the built-in kernel crate.
+
config CRYPTO_LIB_AESGCM
tristate
select CRYPTO_LIB_AES
@@ -216,6 +226,15 @@ config CRYPTO_LIB_SHA256
Select this if your module uses any of these functions from
<crypto/sha2.h>.
+config RUST_CRYPTO_LIB_SHA256
+ bool
+ depends on RUST
+ select CRYPTO_LIB_SHA256
+ help
+ Enable the Rust bindings for the synchronous SHA-256 and HMAC-SHA256
+ library functions. The selected C library is built into the kernel
+ because Rust abstractions are part of the built-in kernel crate.
+
config CRYPTO_LIB_SHA256_ARCH
bool
depends on CRYPTO_LIB_SHA256 && !UML
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 8d7489b8cce8..bb46317898c1 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/aes.h>
#include <crypto/sha2.h>
diff --git a/rust/helpers/crypto.c b/rust/helpers/crypto.c
index a18780231ce0..f0cdf7fa8a76 100644
--- a/rust/helpers/crypto.c
+++ b/rust/helpers/crypto.c
@@ -1,9 +1,30 @@
// SPDX-License-Identifier: GPL-2.0
+#include <crypto/akcipher.h>
#include <crypto/aes.h>
#include <crypto/aes-cbc-macs.h>
#include <linux/string.h>
+__rust_helper void rust_helper_memzero_explicit(void *s, size_t count)
+{
+ memzero_explicit(s, count);
+}
+
+#ifdef CONFIG_RUST_CRYPTO_AKCIPHER
+__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 key_len)
+{
+ return crypto_akcipher_set_pub_key(tfm, key, key_len);
+}
+#endif
+
+#ifdef CONFIG_RUST_CRYPTO_LIB_AES
/*
* 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
@@ -19,6 +40,11 @@ rust_helper_aes_enckey_encrypt_block(const struct aes_enckey *key, u8 *out,
aes_encrypt(key, out, in);
}
+__rust_helper void rust_helper_aes_enckey_zero(struct aes_enckey *key)
+{
+ memzero_explicit(key, sizeof(*key));
+}
+
/*
* 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
@@ -35,3 +61,4 @@ rust_helper_aes_cmac(const u8 *key, const u8 *data, size_t data_len, u8 *out)
aes_cmac(&cmac_key, data, data_len, out);
memzero_explicit(&cmac_key, sizeof(cmac_key));
}
+#endif
diff --git a/rust/kernel/Kconfig.test b/rust/kernel/Kconfig.test
index e6a5c7a795f0..32e1a0c17d08 100644
--- a/rust/kernel/Kconfig.test
+++ b/rust/kernel/Kconfig.test
@@ -83,4 +83,18 @@ config RUST_BITFIELD_KUNIT_TEST
If unsure, say N.
+config RUST_CRYPTO_KUNIT_TEST
+ bool "KUnit tests for Rust crypto APIs" if !KUNIT_ALL_TESTS
+ default KUNIT_ALL_TESTS
+ select CRYPTO_RSA
+ select RUST_CRYPTO_AKCIPHER
+ select RUST_CRYPTO_LIB_SHA256
+ help
+ This option enables KUnit tests for the safe Rust crypto APIs,
+ including public-key encoding and encryption. The tests include
+ published known-answer vectors and are intended for development and
+ testing rather than regular kernel use cases.
+
+ If unsure, say N.
+
endif
diff --git a/rust/kernel/crypto.rs b/rust/kernel/crypto.rs
index 5f7c301b2bb8..0cf38541c8d1 100644
--- a/rust/kernel/crypto.rs
+++ b/rust/kernel/crypto.rs
@@ -9,18 +9,75 @@
//! 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),
+//! Public-key ciphers are available through [`akcipher`] when
+//! `CONFIG_RUST_CRYPTO_AKCIPHER` is enabled.
+//!
+//! C headers: [`include/crypto/akcipher.h`](srctree/include/crypto/akcipher.h),
+//! [`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::*};
+use core::ops::{Deref, DerefMut};
+
+use crate::bindings;
+#[cfg(any(
+ CONFIG_RUST_CRYPTO_AKCIPHER,
+ CONFIG_RUST_CRYPTO_LIB_AES,
+ CONFIG_RUST_CRYPTO_LIB_SHA256
+))]
+use crate::{error::to_result, prelude::*};
+
+#[cfg(CONFIG_RUST_CRYPTO_AKCIPHER)]
+pub mod akcipher;
/// 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;
+/// A fixed-size byte string which is wiped when dropped.
+///
+/// This is intended for cryptographic keys and other sensitive intermediate
+/// values. Borrowing the contained bytes can still create copies which this
+/// type cannot track; callers should avoid copying them unnecessarily.
+pub struct Secret<const N: usize>([u8; N]);
+
+impl<const N: usize> Secret<N> {
+ /// Wraps bytes which should be wiped when their owner is dropped.
+ pub const fn new(bytes: [u8; N]) -> Self {
+ Self(bytes)
+ }
+
+ /// Creates an all-zero byte string.
+ pub const fn zeroed() -> Self {
+ Self([0; N])
+ }
+}
+
+impl<const N: usize> Deref for Secret<N> {
+ type Target = [u8; N];
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+impl<const N: usize> DerefMut for Secret<N> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.0
+ }
+}
+
+impl<const N: usize> Drop for Secret<N> {
+ fn drop(&mut self) {
+ // SAFETY: `self.0` is valid for exactly `N` writable bytes. The helper
+ // uses `memzero_explicit()`, so the wipe is not optimised away.
+ unsafe { bindings::memzero_explicit(self.0.as_mut_ptr().cast(), N) };
+ }
+}
+
/// Returns the SHA-256 digest of `data`.
+#[cfg(CONFIG_RUST_CRYPTO_LIB_SHA256)]
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
@@ -30,6 +87,7 @@
}
/// Returns `HMAC-SHA256(key, data)`.
+#[cfg(CONFIG_RUST_CRYPTO_LIB_SHA256)]
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`
@@ -51,6 +109,7 @@
/// prepared and wiped internally; the call is infallible.
///
/// [`include/crypto/aes-cbc-macs.h`]: srctree/include/crypto/aes-cbc-macs.h
+#[cfg(CONFIG_RUST_CRYPTO_LIB_AES)]
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()`
@@ -77,8 +136,10 @@
/// let _ct = cipher.encrypt_block(&[0u8; 16]);
/// # Ok::<(), Error>(())
/// ```
+#[cfg(CONFIG_RUST_CRYPTO_LIB_AES)]
pub struct Aes128(bindings::aes_enckey);
+#[cfg(CONFIG_RUST_CRYPTO_LIB_AES)]
impl Aes128 {
/// Expands an AES-128 key from 16 raw key bytes.
pub fn new(key: &[u8; AES128_BLOCK_SIZE]) -> Result<Self> {
@@ -105,11 +166,10 @@ impl Aes128 {
}
}
+#[cfg(CONFIG_RUST_CRYPTO_LIB_AES)]
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()) };
+ // SAFETY: `self.0` is a valid, owned `aes_enckey`.
+ unsafe { bindings::aes_enckey_zero(&mut self.0) };
}
}
diff --git a/rust/kernel/crypto/akcipher.rs b/rust/kernel/crypto/akcipher.rs
new file mode 100644
index 000000000000..c5fc2ca0cfbe
--- /dev/null
+++ b/rust/kernel/crypto/akcipher.rs
@@ -0,0 +1,298 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Safe wrappers for the kernel public-key cipher API.
+//!
+//! C header: [`include/crypto/akcipher.h`](srctree/include/crypto/akcipher.h)
+
+use core::ptr::NonNull;
+
+use crate::{
+ alloc::{Flags, KVec},
+ bindings, c_str,
+ crypto::{sha256, SHA256_DIGEST_SIZE},
+ error::{from_err_ptr, to_result},
+ prelude::*,
+};
+
+/// A configured RSA public key.
+///
+/// The key is backed by the existing kernel `"rsa"` akcipher implementation.
+/// Creating it converts unsigned big-endian modulus and exponent components
+/// into the PKCS#1 DER form consumed by the crypto API.
+pub struct RsaPublicKey {
+ tfm: NonNull<bindings::crypto_akcipher>,
+ size: usize,
+}
+
+impl RsaPublicKey {
+ /// Create an RSA public key from unsigned big-endian components.
+ pub fn new(modulus: &[u8], exponent: &[u8], flags: Flags) -> Result<Self> {
+ let modulus = trim_unsigned(modulus).ok_or(EINVAL)?;
+ let exponent = trim_unsigned(exponent).ok_or(EINVAL)?;
+ let der = encode_rsa_public_key(modulus, exponent, flags)?;
+
+ // SAFETY: The name is NUL-terminated and remains live for the call.
+ let tfm = from_err_ptr(unsafe {
+ bindings::crypto_alloc_akcipher(c_str!("rsa").as_char_ptr(), 0, 0)
+ })?;
+ let tfm = NonNull::new(tfm).ok_or(ENOMEM)?;
+
+ // SAFETY: `tfm` is a live akcipher transform and `der` contains
+ // `der.len()` initialized bytes.
+ let result = to_result(unsafe {
+ bindings::crypto_akcipher_set_pub_key(
+ tfm.as_ptr(),
+ der.as_ptr().cast(),
+ der.len().try_into()?,
+ )
+ });
+ if let Err(err) = result {
+ // SAFETY: `tfm` was returned by `crypto_alloc_akcipher()` and has
+ // not been freed.
+ unsafe { bindings::crypto_free_akcipher(tfm.as_ptr()) };
+ return Err(err);
+ }
+
+ Ok(Self {
+ tfm,
+ size: modulus.len(),
+ })
+ }
+
+ /// Return the RSA modulus size in bytes.
+ pub fn size(&self) -> usize {
+ self.size
+ }
+
+ /// Apply the raw RSA public-key operation to one already-encoded message.
+ ///
+ /// Both buffers must have the modulus size. The output is fixed-width,
+ /// unsigned, and big-endian. Prefer a padded scheme such as
+ /// [`oaep_sha256_encrypt`](Self::oaep_sha256_encrypt).
+ pub fn encrypt(&mut self, encoded: &[u8], out: &mut [u8]) -> Result {
+ if encoded.len() != self.size || out.len() != self.size {
+ return Err(EINVAL);
+ }
+
+ out.fill(0);
+ // SAFETY: `self.tfm` remains live and exclusively borrowed for the
+ // synchronous operation; both buffers are valid for their lengths.
+ to_result(unsafe {
+ bindings::crypto_akcipher_sync_encrypt(
+ self.tfm.as_ptr(),
+ encoded.as_ptr().cast(),
+ encoded.len().try_into()?,
+ out.as_mut_ptr().cast(),
+ out.len().try_into()?,
+ )
+ })
+ }
+
+ /// Encrypt a message using RSAES-OAEP with SHA-256 and an empty label.
+ ///
+ /// `seed` is a caller-provided random OAEP seed. It is explicit so callers
+ /// can use the kernel CSPRNG while tests can use published deterministic
+ /// vectors.
+ #[cfg(CONFIG_RUST_CRYPTO_LIB_SHA256)]
+ pub fn oaep_sha256_encrypt(
+ &mut self,
+ message: &[u8],
+ seed: &[u8; SHA256_DIGEST_SIZE],
+ out: &mut [u8],
+ flags: Flags,
+ ) -> Result {
+ let overhead = 2 * SHA256_DIGEST_SIZE + 2;
+ if out.len() != self.size || self.size < overhead || message.len() > self.size - overhead {
+ return Err(EINVAL);
+ }
+
+ let mut encoded = KVec::from_elem(0u8, self.size, flags)?;
+ let result = (|| {
+ encoded[1..1 + SHA256_DIGEST_SIZE].copy_from_slice(seed);
+ let db = &mut encoded[1 + SHA256_DIGEST_SIZE..];
+ db[..SHA256_DIGEST_SIZE].copy_from_slice(&sha256(&[]));
+ let separator = db.len() - message.len() - 1;
+ db[separator] = 1;
+ db[separator + 1..].copy_from_slice(message);
+
+ mgf1_sha256_xor(seed, db, flags)?;
+ let (seed_block, masked_db) = encoded[1..].split_at_mut(SHA256_DIGEST_SIZE);
+ mgf1_sha256_xor(masked_db, seed_block, flags)?;
+
+ self.encrypt(&encoded, out)
+ })();
+ encoded.fill(0);
+ result
+ }
+}
+
+impl Drop for RsaPublicKey {
+ fn drop(&mut self) {
+ // SAFETY: `self.tfm` was returned by `crypto_alloc_akcipher()` and is
+ // owned by this object.
+ unsafe { bindings::crypto_free_akcipher(self.tfm.as_ptr()) };
+ }
+}
+
+#[cfg(CONFIG_RUST_CRYPTO_LIB_SHA256)]
+fn mgf1_sha256_xor(seed: &[u8], output: &mut [u8], flags: Flags) -> Result {
+ let mut input = KVec::with_capacity(seed.len().checked_add(4).ok_or(EOVERFLOW)?, flags)?;
+ let result = (|| {
+ let mut counter = 0u32;
+ for chunk in output.chunks_mut(SHA256_DIGEST_SIZE) {
+ input.clear();
+ input.extend_from_slice(seed, flags)?;
+ input.extend_from_slice(&counter.to_be_bytes(), flags)?;
+ let digest = sha256(&input);
+ for (byte, mask) in chunk.iter_mut().zip(digest) {
+ *byte ^= mask;
+ }
+ counter = counter.checked_add(1).ok_or(EOVERFLOW)?;
+ }
+ Ok(())
+ })();
+ input.fill(0);
+ result
+}
+
+fn trim_unsigned(value: &[u8]) -> Option<&[u8]> {
+ let value = value
+ .iter()
+ .position(|byte| *byte != 0)
+ .map(|i| &value[i..])?;
+ Some(value)
+}
+
+fn der_length_size(length: usize) -> Result<usize> {
+ if length < 128 {
+ return Ok(1);
+ }
+
+ let bytes = (usize::BITS - length.leading_zeros()).div_ceil(8) as usize;
+ if bytes > 126 {
+ return Err(EOVERFLOW);
+ }
+ Ok(1 + bytes)
+}
+
+fn push_der_length(out: &mut KVec<u8>, length: usize, flags: Flags) -> Result {
+ if length < 128 {
+ out.push(length as u8, flags)?;
+ return Ok(());
+ }
+
+ let bytes = der_length_size(length)? - 1;
+ out.push(0x80 | bytes as u8, flags)?;
+ for shift in (0..bytes).rev() {
+ out.push((length >> (shift * 8)) as u8, flags)?;
+ }
+ Ok(())
+}
+
+fn der_integer_size(value: &[u8]) -> Result<usize> {
+ let leading_zero = usize::from(value[0] & 0x80 != 0);
+ 1usize
+ .checked_add(der_length_size(value.len() + leading_zero)?)
+ .and_then(|size| size.checked_add(value.len() + leading_zero))
+ .ok_or(EOVERFLOW)
+}
+
+fn push_der_integer(out: &mut KVec<u8>, value: &[u8], flags: Flags) -> Result {
+ let leading_zero = value[0] & 0x80 != 0;
+ out.push(0x02, flags)?;
+ push_der_length(out, value.len() + usize::from(leading_zero), flags)?;
+ if leading_zero {
+ out.push(0, flags)?;
+ }
+ out.extend_from_slice(value, flags)?;
+ Ok(())
+}
+
+fn encode_rsa_public_key(modulus: &[u8], exponent: &[u8], flags: Flags) -> Result<KVec<u8>> {
+ let content_len = der_integer_size(modulus)?
+ .checked_add(der_integer_size(exponent)?)
+ .ok_or(EOVERFLOW)?;
+ let total_len = 1usize
+ .checked_add(der_length_size(content_len)?)
+ .and_then(|size| size.checked_add(content_len))
+ .ok_or(EOVERFLOW)?;
+ let mut der = KVec::with_capacity(total_len, flags)?;
+ der.push(0x30, flags)?;
+ push_der_length(&mut der, content_len, flags)?;
+ push_der_integer(&mut der, modulus, flags)?;
+ push_der_integer(&mut der, exponent, flags)?;
+ Ok(der)
+}
+
+#[cfg(CONFIG_RUST_CRYPTO_KUNIT_TEST)]
+#[crate::macros::kunit_tests(rust_kernel_crypto_akcipher)]
+mod tests {
+ use super::*;
+ use crate::alloc::flags::GFP_KERNEL;
+
+ // Wycheproof rsa_oaep_2048_sha256_mgf1sha256_test.json, test case 3.
+ const OAEP_MODULUS: [u8; 256] = [
+ 0xa2, 0xb4, 0x51, 0xa0, 0x7d, 0x0a, 0xa5, 0xf9, 0x6e, 0x45, 0x56, 0x71, 0x51, 0x35, 0x50,
+ 0x51, 0x4a, 0x8a, 0x5b, 0x46, 0x2e, 0xbe, 0xf7, 0x17, 0x09, 0x4f, 0xa1, 0xfe, 0xe8, 0x22,
+ 0x24, 0xe6, 0x37, 0xf9, 0x74, 0x6d, 0x3f, 0x7c, 0xaf, 0xd3, 0x18, 0x78, 0xd8, 0x03, 0x25,
+ 0xb6, 0xef, 0x5a, 0x17, 0x00, 0xf6, 0x59, 0x03, 0xb4, 0x69, 0x42, 0x9e, 0x89, 0xd6, 0xea,
+ 0xc8, 0x84, 0x50, 0x97, 0xb5, 0xab, 0x39, 0x31, 0x89, 0xdb, 0x92, 0x51, 0x2e, 0xd8, 0xa7,
+ 0x71, 0x1a, 0x12, 0x53, 0xfa, 0xcd, 0x20, 0xf7, 0x9c, 0x15, 0xe8, 0x24, 0x7f, 0x3d, 0x3e,
+ 0x42, 0xe4, 0x6e, 0x48, 0xc9, 0x8e, 0x25, 0x4a, 0x2f, 0xe9, 0x76, 0x53, 0x13, 0xa0, 0x3e,
+ 0xff, 0x8f, 0x17, 0xe1, 0xa0, 0x29, 0x39, 0x7a, 0x1f, 0xa2, 0x6a, 0x8d, 0xce, 0x26, 0xf4,
+ 0x90, 0xed, 0x81, 0x29, 0x96, 0x15, 0xd9, 0x81, 0x4c, 0x22, 0xda, 0x61, 0x04, 0x28, 0xe0,
+ 0x9c, 0x7d, 0x96, 0x58, 0x59, 0x42, 0x66, 0xf5, 0xc0, 0x21, 0xd0, 0xfc, 0xec, 0xa0, 0x8d,
+ 0x94, 0x5a, 0x12, 0xbe, 0x82, 0xde, 0x4d, 0x1e, 0xce, 0x6b, 0x4c, 0x03, 0x14, 0x5b, 0x5d,
+ 0x34, 0x95, 0xd4, 0xed, 0x54, 0x11, 0xeb, 0x87, 0x8d, 0xaf, 0x05, 0xfd, 0x7a, 0xfc, 0x3e,
+ 0x09, 0xad, 0xa0, 0xf1, 0x12, 0x64, 0x22, 0xf5, 0x90, 0x97, 0x5a, 0x19, 0x69, 0x81, 0x6f,
+ 0x48, 0x69, 0x8b, 0xcb, 0xba, 0x1b, 0x4d, 0x9c, 0xae, 0x79, 0xd4, 0x60, 0xd8, 0xf9, 0xf8,
+ 0x5e, 0x79, 0x75, 0x00, 0x5d, 0x9b, 0xc2, 0x2c, 0x4e, 0x5a, 0xc0, 0xf7, 0xc1, 0xa4, 0x5d,
+ 0x12, 0x56, 0x9a, 0x62, 0x80, 0x7d, 0x3b, 0x9a, 0x02, 0xe5, 0xa5, 0x30, 0xe7, 0x73, 0x06,
+ 0x6f, 0x45, 0x3d, 0x1f, 0x5b, 0x4c, 0x2e, 0x9c, 0xf7, 0x82, 0x02, 0x83, 0xf7, 0x42, 0xb9,
+ 0xd5,
+ ];
+ const OAEP_SEED: [u8; 32] = [
+ 0x70, 0x97, 0x14, 0xb0, 0x48, 0xc3, 0x69, 0x73, 0x22, 0x69, 0xa3, 0xd8, 0xf9, 0x23, 0x02,
+ 0x50, 0x87, 0x70, 0xa4, 0x43, 0x68, 0x01, 0x4b, 0x3a, 0x5c, 0xb1, 0x85, 0xc0, 0xc9, 0x1d,
+ 0x97, 0x2c,
+ ];
+ const OAEP_CIPHERTEXT: [u8; 256] = [
+ 0x5e, 0xab, 0x3f, 0x07, 0x41, 0xe6, 0x39, 0x86, 0xed, 0x64, 0x7d, 0x53, 0xe1, 0xcd, 0x71,
+ 0xdf, 0x04, 0x19, 0x86, 0x90, 0x08, 0x03, 0xd0, 0xf9, 0x9c, 0x68, 0x35, 0x5d, 0x24, 0x9a,
+ 0x15, 0xa4, 0x7d, 0xc5, 0xb4, 0xf7, 0x0a, 0x19, 0x14, 0x77, 0x65, 0x42, 0x99, 0xe5, 0xa2,
+ 0x73, 0x1f, 0x3b, 0x4e, 0xec, 0x76, 0xde, 0xa1, 0x82, 0x62, 0xfc, 0x69, 0x6a, 0xc7, 0x94,
+ 0xe5, 0xf6, 0x6c, 0xbf, 0xcd, 0xda, 0xc4, 0x47, 0x2c, 0x57, 0x8e, 0x24, 0x6c, 0x26, 0x70,
+ 0x75, 0x98, 0x05, 0x55, 0x84, 0x54, 0x0b, 0x83, 0x98, 0x36, 0xb1, 0x40, 0x4c, 0x56, 0x11,
+ 0xae, 0x55, 0x8a, 0x98, 0x4c, 0xee, 0x8f, 0xd0, 0x36, 0xce, 0xa9, 0x24, 0xe0, 0xbe, 0x24,
+ 0x74, 0xa9, 0x40, 0xf6, 0x1e, 0x0a, 0xcc, 0x14, 0xfc, 0xae, 0x95, 0xeb, 0xdc, 0x59, 0x94,
+ 0x2a, 0x9c, 0xe9, 0xaf, 0x9a, 0x9c, 0x81, 0x99, 0x9f, 0x7f, 0x68, 0x15, 0xf0, 0x57, 0xff,
+ 0xdc, 0x25, 0x33, 0xcb, 0x15, 0xd6, 0x39, 0x1d, 0x1e, 0x2d, 0x95, 0xf1, 0x6f, 0x9c, 0x04,
+ 0x20, 0x9c, 0x88, 0x9a, 0x4c, 0x35, 0x9c, 0x7d, 0x29, 0x26, 0xd2, 0x8a, 0x66, 0xe2, 0xb0,
+ 0x30, 0xa4, 0x16, 0xb9, 0x28, 0xd2, 0x82, 0x56, 0x27, 0x99, 0x8e, 0x51, 0x91, 0xfb, 0x49,
+ 0x83, 0xa6, 0xe6, 0x50, 0x24, 0x26, 0x2d, 0x94, 0xfc, 0x09, 0x18, 0x7a, 0x2d, 0x78, 0x16,
+ 0x21, 0x22, 0x43, 0x32, 0x51, 0xd1, 0xbf, 0xcc, 0x8e, 0x50, 0x7d, 0x06, 0xeb, 0xa2, 0xd2,
+ 0x29, 0xc1, 0x00, 0x31, 0x26, 0x1d, 0xa3, 0x2a, 0xb8, 0xcc, 0xd1, 0x5f, 0x1c, 0x5f, 0x9f,
+ 0xbf, 0x07, 0xed, 0x15, 0x84, 0x83, 0xd7, 0x36, 0xa1, 0x10, 0xaf, 0x4b, 0x44, 0xd6, 0xa4,
+ 0xda, 0x60, 0xd6, 0xcb, 0x51, 0x9b, 0x44, 0x54, 0x21, 0x3c, 0xf9, 0xf0, 0xdc, 0x56, 0x0f,
+ 0x2b,
+ ];
+
+ #[test]
+ fn rsa_der_encoding() -> Result {
+ let der = encode_rsa_public_key(&[0x80, 0x01], &[0x01, 0x00, 0x01], GFP_KERNEL)?;
+ assert_eq!(
+ der.as_slice(),
+ &[0x30, 0x0a, 0x02, 0x03, 0x00, 0x80, 0x01, 0x02, 0x03, 0x01, 0x00, 0x01]
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn rsa_oaep_sha256_wycheproof() -> Result {
+ let mut key = RsaPublicKey::new(&OAEP_MODULUS, &[0x01, 0x00, 0x01], GFP_KERNEL)?;
+ let mut out = [0u8; 256];
+ key.oaep_sha256_encrypt(b"Test", &OAEP_SEED, &mut out, GFP_KERNEL)?;
+ assert_eq!(out, OAEP_CIPHERTEXT);
+ Ok(())
+ }
+}
next prev parent reply other threads:[~2026-08-26 16:30 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-26 16:29 [PATCH v3 0/2] rust: crypto: AES, CMAC, SHA-256, HMAC and RSA bindings Mike Lothian
2026-08-26 16:29 ` [PATCH v3 1/2] rust: crypto: add AES-128, AES-CMAC, SHA-256, and HMAC bindings Mike Lothian
2026-08-26 22:09 ` Eric Biggers
2026-08-26 16:29 ` Mike Lothian [this message]
2026-08-27 3:03 ` [PATCH v3 2/2] rust: crypto: add synchronous RSA akcipher support Eric Biggers
2026-08-27 14:46 ` Miguel Ojeda
2026-08-27 18:29 ` Eric Biggers
2026-08-27 23:00 ` Miguel Ojeda
2026-08-27 14:59 ` Miguel Ojeda
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260826163004.3365-3-mike@fireburn.co.uk \
--to=mike@fireburn.co.uk \
--cc=Jason@zx2c4.com \
--cc=a.hindborg@kernel.org \
--cc=acourbot@nvidia.com \
--cc=aliceryhl@google.com \
--cc=ardb@kernel.org \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun@kernel.org \
--cc=bqe@google.com \
--cc=dakr@kernel.org \
--cc=daniel.almeida@collabora.com \
--cc=davem@davemloft.net \
--cc=david@davidgow.net \
--cc=ebiggers@kernel.org \
--cc=gary@garyguo.net \
--cc=gregkh@linuxfoundation.org \
--cc=herbert@gondor.apana.org.au \
--cc=joelagnelf@nvidia.com \
--cc=lina+kernel@asahilina.net \
--cc=linux-crypto@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=ljs@kernel.org \
--cc=lossin@kernel.org \
--cc=lyude@redhat.com \
--cc=ojeda@kernel.org \
--cc=rust-for-linux@vger.kernel.org \
--cc=tamird@kernel.org \
--cc=tmgross@umich.edu \
--cc=work@onurozkan.dev \
--cc=ynorov@nvidia.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox