embassy-rs/embassy · error
the PKA is in use
Error message
the PKA is in use
What it means
The PKA driver wraps the hardware public-key accelerator in a global Mutex (DRIVER). with_pka uses try_lock().expect("the PKA is in use"), so any attempt to run a PKA operation while another context already holds the accelerator panics rather than blocking. Like the AES driver, this enforces single-owner access to the peripheral.
Solutions
- Serialize PKA operations through a single task or an application-level async Mutex so calls never overlap.
- Avoid calling PKA operations (sign/verify/shared_secret) from interrupt context.
- Structure protocols so crypto steps complete before starting others (await the result first).
- Keep the driver's guard scope minimal; do not hold BlockingPka across long waits.
Example fix
// before
let sig = pka.sign(&key, &hash).await;
spawn(async move { pka.verify(...).await }); // may overlap -> panic
// after
let sig = pka.sign(&key, &hash).await;
pka.verify(&key, &hash, &sig).await; // sequential on same task Defensive patterns
Strategy: try-catch
Validate before calling
// Serialize PKA usage at the application level:
static PKA_BUSY: AtomicBool = AtomicBool::new(false);
fn pka_free() -> bool { !PKA_BUSY.load(Ordering::Acquire) } Type guard
fn can_use_pka() -> bool {
!PKA_BUSY.load(core::sync::atomic::Ordering::Acquire)
} Try / catch
// Prevent instead of catch (embedded panics abort):
static PKA: Mutex<CriticalSectionRawMutex, Pka<'static>> = Mutex::new(Pka::new());
async fn with_pka_app<T>(f: impl FnOnce(&Pka<'static>) -> T) -> T {
let p = PKA.lock().await;
f(&p)
} Prevention
- Run all PKA operations (sign/verify/shared_secret) from one task or through an app-level async Mutex.
- Never invoke PKA operations from ISR context.
- Await each crypto operation to completion before starting the next.
When it happens
Trigger: Calling public_key, shared_secret, sign, or verify while another task/ISR is executing a PKA operation; e.g. performing an ECDH key exchange and an ECDSA signature concurrently, or calling sign from an interrupt while a task verifies.
Common situations: TLS/embedded protocols spawning concurrent crypto tasks; calling PKA operations from both an ISR and the main loop; reentrant helper libraries that both use the PKA driver.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
AI-assisted analysis of embassy-rs/embassy@463a07b963 (2026-09-10).
Data as JSON: /api/errors/47dbe48076a93a38.
Report an issue: GitHub.
Appendix: source
Thrown at embassy-stm32/src/pka/driver.rs:54
use super::EccProjectivePoint;
use super::{EccPoint, EcdsaCurveParams, EcdsaPublicKey, EcdsaSignature, Error, Pka};
use crate::mode::Blocking;
use crate::suspend::ResumablePeripheral;
foreach_peripheral!(
(pka, $inst:ident) => {
type BlockingPka = Pka<'static, crate::peripherals::$inst, Blocking>;
static DRIVER: Mutex<CriticalSectionRawMutex, ResumablePeripheral<BlockingPka>> =
Mutex::new(ResumablePeripheral::new_suspended(unsafe { crate::peripherals::$inst::steal() }));
};
);
/// Runs `f` with the engine enabled and initialized.
fn with_pka<R>(f: impl FnOnce(&mut BlockingPka) -> R) -> R {
#[cfg(feature = "embassy-crypto-rng")]
crate::rng::driver::ensure_running();
let mut driver = DRIVER.try_lock().expect("the PKA is in use");
let mut pka = driver.borrow();
#[cfg(any(pka_v1a, pka_n6))]
assert!(
!pka.is_limited(),
"the PKA of this chip only verifies ECDSA signatures (limited mode)"
);
f(&mut pka)
}
/// How many nonces ECDSA signing tries before giving up on the hardware.
const SIGN_ATTEMPTS: usize = 8;
/// Montgomery constants of an odd big-endian modulus of exactly `L` limbs.
const fn monty<const L: usize>(n_be: &[u8]) -> FixedMontyParams<L> {
FixedMontyParams::new_vartime(Uint::from_be_slice(n_be).to_odd().expect_copied("odd modulus"))
}
/// Big-endian bytes to an integer, `N == L * Limb::BYTES`.View on GitHub (pinned to 463a07b963)