refactor(core/rust/crypto): unify init_ctx macros
What changed, and why it matters
This commit is a code cleanup: it removes duplicate helper macros from several cryptography modules and replaces them with a single shared macro. There is no change to user-facing behavior, no bug fix, and no security patch. It is purely an internal refactoring to make the code easier to maintain.
No security action needed. Treat as normal refactoring; standard code review and CI testing are sufficient.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change unifies the init_ctx! macro used to allocate pinned stack memory for cryptographic contexts. Previously each of aesgcm.rs, hmac.rs, sha256.rs, and sha512.rs defined its own identical or near-identical macro. The commit moves the macro into memory.rs, generalizes it to accept the context type as a parameter, and updates call sites. The generated code remains the same: stack-allocated Memory<T>, pinned with Pin::new_unchecked, and passed to the type’s constructor. No functional or security behavior changes.
Changed components
core/embed/rust/src/crypto/memory.rscore/embed/rust/src/crypto/aesgcm.rscore/embed/rust/src/crypto/hmac.rscore/embed/rust/src/crypto/merkle.rscore/embed/rust/src/crypto/sha256.rscore/embed/rust/src/crypto/sha512.rsInspect captured patch +55 / −77
diff --git a/core/embed/rust/src/crypto/aesgcm.rs b/core/embed/rust/src/crypto/aesgcm.rs
index e82c2e91..1b4b2863 100644
--- a/core/embed/rust/src/crypto/aesgcm.rs
+++ b/core/embed/rust/src/crypto/aesgcm.rs
@@ -169,22 +169,9 @@ impl Drop for AesGcm<'_> {
}
}
-#[allow(unused_macros)]
-macro_rules! init_ctx {
- ($name:ident, $key:expr, $iv:expr) => {
- // assign the backing memory to $name...
- let mut $name = crate::crypto::aesgcm::AesGcm::memory();
- // ... then make it inaccessible by overwriting the binding, and pin it
- #[allow(unused_mut)]
- let mut $name = unsafe {
- crate::crypto::aesgcm::AesGcm::new(core::pin::Pin::new_unchecked(&mut $name), $key, $iv)
- };
- };
-}
-
#[cfg(test)]
mod test {
- use super::*;
+ use super::{super::memory::init_ctx, *};
struct Vector {
key: &'static str,
@@ -295,9 +282,9 @@ mod test {
for v in AES_GCM_VECTORS {
let (key, iv, aad, plaintext, ciphertext) = v.decoded();
- init_ctx!(ctx_enc, &key, &iv);
+ init_ctx!(AesGcm, ctx_enc, &key, &iv);
let mut ctx_enc = ctx_enc.unwrap();
- init_ctx!(ctx_dec, &key, &iv);
+ init_ctx!(AesGcm, ctx_dec, &key, &iv);
let mut ctx_dec = ctx_dec.unwrap();
if !plaintext.is_empty() {
@@ -323,7 +310,7 @@ mod test {
#[test]
fn test_state() {
- init_ctx!(ctx, &[0u8; 16], b"1");
+ init_ctx!(AesGcm, ctx, &[0u8; 16], b"1");
let mut ctx = ctx.unwrap();
// ok: empty string tag
@@ -403,7 +390,7 @@ mod test {
let (key, iv, aad, pt, ct) = v.decoded();
// Test encryption.
- init_ctx!(ctx, &key, &iv);
+ init_ctx!(AesGcm, ctx, &key, &iv);
let mut ctx = ctx.unwrap();
if !aad.is_empty() {
ctx.auth(&aad).unwrap();
@@ -434,7 +421,7 @@ mod test {
let (key, iv, aad, pt, ct) = v.decoded();
// Test encryption.
- init_ctx!(ctx, &key, &iv);
+ init_ctx!(AesGcm, ctx, &key, &iv);
let mut ctx = ctx.unwrap();
if !aad.is_empty() {
ctx.auth(&aad).unwrap();
@@ -469,7 +456,7 @@ mod test {
let chunk_len = pt.len() / 3;
let mut buffer = vec![0; pt.len()];
- init_ctx!(ctx, &key, &iv);
+ init_ctx!(AesGcm, ctx, &key, &iv);
let mut ctx = ctx.unwrap();
ctx.decrypt(&ct[..chunk_len], &mut buffer[..chunk_len])
.unwrap();
@@ -500,7 +487,7 @@ mod test {
let chunk_len = pt.len() / 3;
let mut buffer = ct;
- init_ctx!(ctx, &key, &iv);
+ init_ctx!(AesGcm, ctx, &key, &iv);
let mut ctx = ctx.unwrap();
ctx.decrypt_in_place(&mut buffer[..chunk_len]).unwrap();
ctx.auth(aad.get(..7).unwrap_or(&[])).unwrap();
diff --git a/core/embed/rust/src/crypto/hmac.rs b/core/embed/rust/src/crypto/hmac.rs
index ffdae50b..b47ee61d 100644
--- a/core/embed/rust/src/crypto/hmac.rs
+++ b/core/embed/rust/src/crypto/hmac.rs
@@ -2,7 +2,10 @@ use core::pin::Pin;
use zeroize::Zeroize as _;
-use super::{ffi, memory::Memory};
+use super::{
+ ffi,
+ memory::{init_ctx, Memory},
+};
pub const DIGEST_SIZE: usize = ffi::SHA256_DIGEST_LENGTH as usize;
pub type Digest = [u8; DIGEST_SIZE];
@@ -40,22 +43,8 @@ impl Drop for HmacSha256<'_> {
}
}
-macro_rules! init_ctx {
- ($name:ident, $key:expr) => {
- // assign the backing memory to $name...
- let mut $name = crate::crypto::hmac::HmacSha256::memory();
- // ... then make it inaccessible by overwriting the binding, and pin it
- #[allow(unused_mut)]
- let mut $name = unsafe {
- crate::crypto::hmac::HmacSha256::new(core::pin::Pin::new_unchecked(&mut $name), $key)
- };
- };
-}
-
-pub(crate) use init_ctx;
-
pub fn digest_into(key: &[u8], data: &[u8], out: &mut Digest) {
- init_ctx!(ctx, key);
+ init_ctx!(HmacSha256, ctx, key);
ctx.update(data);
ctx.finalize_into(out);
}
@@ -128,7 +117,7 @@ mod test {
let mut out = [0u8; DIGEST_SIZE];
let mut out_hex = [0u8; DIGEST_SIZE * 2];
- init_ctx!(ctx, b"");
+ init_ctx!(HmacSha256, ctx, b"");
ctx.finalize_into(&mut out);
hexlify(&out, &mut out_hex);
@@ -148,7 +137,7 @@ mod test {
// case 3
let key =
b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa";
- init_ctx!(ctx, key);
+ init_ctx!(HmacSha256, ctx, key);
for _ in 0..50 {
ctx.update(b"\xdd");
}
@@ -161,7 +150,7 @@ mod test {
// case 4
let key = b"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19";
- init_ctx!(ctx, key);
+ init_ctx!(HmacSha256, ctx, key);
for _ in 0..50 {
ctx.update(b"\xcd");
}
diff --git a/core/embed/rust/src/crypto/memory.rs b/core/embed/rust/src/crypto/memory.rs
index a6f151b8..2849395c 100644
--- a/core/embed/rust/src/crypto/memory.rs
+++ b/core/embed/rust/src/crypto/memory.rs
@@ -2,6 +2,11 @@ use core::{marker::PhantomPinned, mem::MaybeUninit, pin::Pin};
use zeroize::{zeroize_flat_type, Zeroize};
+/// Wrapper for a memory used as a context by C functions. Its purpose is to be
+/// !Unpin, thus prevent moves when accessed through a Pin. We want to avoid
+/// moves as they can leave cryptographic data in memory.
+///
+/// T needs to be a plain struct that is valid when zeroed.
pub struct Memory<T> {
inner: T,
_phantom: PhantomPinned,
@@ -55,3 +60,22 @@ impl<T> Zeroize for Pin<&mut Memory<T>> {
}
}
}
+
+/// Initializes backing memory on the stack and passes it to a constructor.
+/// The macro is basically a specialized version of `core::pin::pin!` for use
+/// with Memory<T>.
+#[allow(unused_macros)]
+macro_rules! init_ctx {
+ ($type:ty, $name:ident $(, $arg:expr)*) => {
+ // assign the backing memory to $name...
+ let mut $name = <$type>::memory();
+ // ... then make it inaccessible by overwriting the binding, and pin it
+ // SAFETY: The value is pinned: it is the local above which cannot be named outside this macro.
+ #[allow(unused_mut)]
+ let mut $name = unsafe {
+ <$type>::new(core::pin::Pin::new_unchecked(&mut $name), $($arg),*)
+ };
+ };
+}
+
+pub(crate) use init_ctx;
diff --git a/core/embed/rust/src/crypto/merkle.rs b/core/embed/rust/src/crypto/merkle.rs
index 551cfc2d..6c03d766 100644
--- a/core/embed/rust/src/crypto/merkle.rs
+++ b/core/embed/rust/src/crypto/merkle.rs
@@ -1,4 +1,4 @@
-use super::sha256;
+use super::{memory::init_ctx, sha256};
/// Calculate a Merkle root based on a leaf element and a proof of inclusion.
///
@@ -7,7 +7,7 @@ pub fn merkle_root(elem: &[u8], proof: &[sha256::Digest]) -> sha256::Digest {
let mut out = sha256::Digest::default();
// hash the leaf element
- sha256::init_ctx!(ctx);
+ init_ctx!(sha256::Sha256, ctx);
ctx.update(&[0x00]);
ctx.update(elem);
ctx.finalize_into(&mut out);
@@ -19,7 +19,7 @@ pub fn merkle_root(elem: &[u8], proof: &[sha256::Digest]) -> sha256::Digest {
} else {
(proof_elem, &out)
};
- sha256::init_ctx!(ctx);
+ init_ctx!(sha256::Sha256, ctx);
ctx.update(&[0x01]);
ctx.update(min);
ctx.update(max);
diff --git a/core/embed/rust/src/crypto/sha256.rs b/core/embed/rust/src/crypto/sha256.rs
index 36037e96..8b7bb695 100644
--- a/core/embed/rust/src/crypto/sha256.rs
+++ b/core/embed/rust/src/crypto/sha256.rs
@@ -2,7 +2,10 @@ use core::pin::Pin;
use zeroize::Zeroize as _;
-use super::{ffi, memory::Memory};
+use super::{
+ ffi,
+ memory::{init_ctx, Memory},
+};
pub const DIGEST_SIZE: usize = ffi::SHA256_DIGEST_LENGTH as usize;
pub type Digest = [u8; DIGEST_SIZE];
@@ -40,22 +43,8 @@ impl Drop for Sha256<'_> {
}
}
-macro_rules! init_ctx {
- ($name:ident) => {
- // assign the backing memory to $name...
- let mut $name = crate::crypto::sha256::Sha256::memory();
- // ... then make it inaccessible by overwriting the binding, and pin it
- #[allow(unused_mut)]
- let mut $name = unsafe {
- crate::crypto::sha256::Sha256::new(core::pin::Pin::new_unchecked(&mut $name))
- };
- };
-}
-
-pub(crate) use init_ctx;
-
pub fn digest_into(data: &[u8], out: &mut Digest) {
- init_ctx!(ctx);
+ init_ctx!(Sha256, ctx);
ctx.update(data);
ctx.finalize_into(out);
}
@@ -94,7 +83,7 @@ mod test {
let mut out = Digest::default();
let mut out_hex = [0u8; DIGEST_SIZE * 2];
- init_ctx!(ctx);
+ init_ctx!(Sha256, ctx);
ctx.finalize_into(&mut out);
hexlify(&out, &mut out_hex);
diff --git a/core/embed/rust/src/crypto/sha512.rs b/core/embed/rust/src/crypto/sha512.rs
index b7d773cd..999ce017 100644
--- a/core/embed/rust/src/crypto/sha512.rs
+++ b/core/embed/rust/src/crypto/sha512.rs
@@ -1,6 +1,9 @@
use core::pin::Pin;
-use super::{ffi, memory::Memory};
+use super::{
+ ffi,
+ memory::{init_ctx, Memory},
+};
use zeroize::Zeroize as _;
@@ -41,22 +44,8 @@ impl Drop for Sha512<'_> {
}
}
-macro_rules! init_ctx {
- ($name:ident) => {
- // assign the backing memory to $name...
- let mut $name = crate::crypto::sha512::Sha512::memory();
- // ... then make it inaccessible by overwriting the binding, and pin it
- #[allow(unused_mut)]
- let mut $name = unsafe {
- crate::crypto::sha512::Sha512::new(core::pin::Pin::new_unchecked(&mut $name))
- };
- };
-}
-
-pub(crate) use init_ctx;
-
pub fn digest_into(data: &[u8], out: &mut Digest) {
- init_ctx!(ctx);
+ init_ctx!(Sha512, ctx);
ctx.update(data);
ctx.finalize_into(out);
}
@@ -103,7 +92,7 @@ mod test {
let mut out = [0u8; DIGEST_SIZE];
let mut out_hex = [0u8; DIGEST_SIZE * 2];
- init_ctx!(ctx);
+ init_ctx!(Sha512, ctx);
ctx.finalize_into(&mut out);
hexlify(&out, &mut out_hex);
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.