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

  1. Serialize PKA operations through a single task or an application-level async Mutex so calls never overlap.
  2. Avoid calling PKA operations (sign/verify/shared_secret) from interrupt context.
  3. Structure protocols so crypto steps complete before starting others (await the result first).
  4. 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

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)