use core::marker::PhantomData;
use embassy_hal_internal::{Peri, PeripheralType};
use embassy_sync::waitqueue::AtomicWaker;
use crate::interrupt::typelevel::Interrupt;
use crate::{interrupt, pac, peripherals, rcc};
static PKA_WAKER: AtomicWaker = AtomicWaker::new();
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum PkaMode {
ModularExp = 0x00,
MontgomeryParam = 0x01,
ModularExpFast = 0x02,
ModularExpProtect = 0x03,
RsaCrtExp = 0x07,
ModularInv = 0x08,
ArithmeticAdd = 0x09,
ArithmeticSub = 0x0A,
ArithmeticMul = 0x0B,
Comparison = 0x0C,
ModularRed = 0x0D,
ModularAdd = 0x0E,
ModularSub = 0x0F,
MontgomeryMul = 0x10,
EccMul = 0x20,
EccCompleteAdd = 0x23,
EcdsaSign = 0x24,
EcdsaVerify = 0x26,
DoubleBaseLadder = 0x27,
PointCheck = 0x28,
EccProjectiveToAffine = 0x2F,
}
mod offsets {
pub mod montgomery_param {
pub const IN_MOD_NB_BITS: usize = 0x08;
pub const IN_MODULUS: usize = 0xC88;
pub const OUT_PARAMETER: usize = 0x220;
}
pub mod modular_exp {
pub const IN_EXP_NB_BITS: usize = 0x00;
pub const IN_OP_NB_BITS: usize = 0x08;
pub const IN_MONTGOMERY_PARAM: usize = 0x220;
pub const IN_EXPONENT_BASE: usize = 0x868;
pub const IN_EXPONENT: usize = 0xA78;
pub const IN_MODULUS: usize = 0xC88;
pub const OUT_RESULT: usize = 0x438;
#[allow(dead_code)]
pub const OUT_ERROR: usize = 0xE98;
}
pub mod modular_exp_protect {
pub const IN_EXP_NB_BITS: usize = 0x00;
pub const IN_OP_NB_BITS: usize = 0x08;
pub const IN_EXPONENT_BASE: usize = 0x12C8; pub const IN_EXPONENT: usize = 0x10B8; pub const IN_MODULUS: usize = 0x438; pub const IN_PHI: usize = 0x868; pub const OUT_RESULT: usize = 0x438;
}
pub mod rsa_crt {
pub const IN_MOD_NB_BITS: usize = 0x08;
pub const IN_DP_CRT: usize = 0x330;
pub const IN_DQ_CRT: usize = 0xA78;
pub const IN_QINV_CRT: usize = 0x548;
pub const IN_PRIME_P: usize = 0x760;
pub const IN_PRIME_Q: usize = 0xC88;
pub const IN_EXPONENT_BASE: usize = 0xEA0;
pub const OUT_RESULT: usize = 0x438;
}
pub mod ecc_mul {
pub const IN_EXP_NB_BITS: usize = 0x00;
pub const IN_OP_NB_BITS: usize = 0x08;
pub const IN_A_COEFF_SIGN: usize = 0x10;
pub const IN_A_COEFF: usize = 0x18;
pub const IN_B_COEFF: usize = 0x120;
pub const IN_MOD_GF: usize = 0xC88;
pub const IN_K: usize = 0xEA0;
pub const IN_INITIAL_POINT_X: usize = 0x178;
pub const IN_INITIAL_POINT_Y: usize = 0x70;
pub const IN_N_PRIME_ORDER: usize = 0xB88;
pub const OUT_RESULT_X: usize = 0x178;
pub const OUT_RESULT_Y: usize = 0x1D0;
pub const OUT_ERROR: usize = 0x280;
}
pub mod ecdsa_sign {
pub const IN_ORDER_NB_BITS: usize = 0x00;
pub const IN_MOD_NB_BITS: usize = 0x08;
pub const IN_A_COEFF_SIGN: usize = 0x10;
pub const IN_A_COEFF: usize = 0x18;
pub const IN_B_COEFF: usize = 0x120;
pub const IN_MOD_GF: usize = 0xC88;
pub const IN_K: usize = 0xEA0;
pub const IN_INITIAL_POINT_X: usize = 0x178;
pub const IN_INITIAL_POINT_Y: usize = 0x70;
pub const IN_HASH_E: usize = 0xBE8;
pub const IN_PRIVATE_KEY_D: usize = 0xB28;
pub const IN_ORDER_N: usize = 0xB88;
pub const OUT_ERROR: usize = 0xBE0;
pub const OUT_SIGNATURE_R: usize = 0x330;
pub const OUT_SIGNATURE_S: usize = 0x388;
#[allow(dead_code)]
pub const OUT_FINAL_POINT_X: usize = 0x1000;
#[allow(dead_code)]
pub const OUT_FINAL_POINT_Y: usize = 0x1058;
}
pub mod ecdsa_verif {
pub const IN_ORDER_NB_BITS: usize = 0x08;
pub const IN_MOD_NB_BITS: usize = 0xC8;
pub const IN_A_COEFF_SIGN: usize = 0x68;
pub const IN_A_COEFF: usize = 0x70;
pub const IN_MOD_GF: usize = 0xD0;
pub const IN_INITIAL_POINT_X: usize = 0x278;
pub const IN_INITIAL_POINT_Y: usize = 0x2D0;
pub const IN_PUBLIC_KEY_POINT_X: usize = 0xEF8;
pub const IN_PUBLIC_KEY_POINT_Y: usize = 0xF50;
pub const IN_SIGNATURE_R: usize = 0xCE0;
pub const IN_SIGNATURE_S: usize = 0x868;
pub const IN_HASH_E: usize = 0xFA8;
pub const IN_ORDER_N: usize = 0xC88;
pub const OUT_RESULT: usize = 0x1D0;
}
pub mod point_check {
pub const IN_MOD_NB_BITS: usize = 0x08;
pub const IN_A_COEFF_SIGN: usize = 0x10;
pub const IN_A_COEFF: usize = 0x18;
pub const IN_B_COEFF: usize = 0x120;
pub const IN_MOD_GF: usize = 0x70;
pub const IN_INITIAL_POINT_X: usize = 0x178;
pub const IN_INITIAL_POINT_Y: usize = 0x1D0;
#[allow(dead_code)]
pub const IN_MONTGOMERY_PARAM: usize = 0xC8;
pub const OUT_ERROR: usize = 0x280;
}
pub mod modular_inv {
pub const IN_NB_BITS: usize = 0x08;
pub const IN_OP1: usize = 0x650;
pub const IN_OP2_MOD: usize = 0x868;
pub const OUT_RESULT: usize = 0xA78;
}
pub mod arithmetic {
pub const IN_NB_BITS: usize = 0x08;
pub const IN_OP1: usize = 0x650;
pub const IN_OP2: usize = 0x868;
pub const IN_OP3_MOD: usize = 0xC88;
pub const OUT_RESULT: usize = 0xA78;
}
pub mod modular_red {
pub const IN_OP_LENGTH: usize = 0x00;
pub const IN_MOD_LENGTH: usize = 0x08;
pub const IN_OPERAND: usize = 0x650;
pub const IN_MODULUS: usize = 0x868;
pub const OUT_RESULT: usize = 0xA78;
}
pub mod ecc_complete_add {
pub const IN_MOD_NB_BITS: usize = 0x08;
pub const IN_A_COEFF_SIGN: usize = 0x10;
pub const IN_A_COEFF: usize = 0x18;
pub const IN_MOD_P: usize = 0x70;
pub const IN_POINT1_X: usize = 0x228;
pub const IN_POINT1_Y: usize = 0x280;
pub const IN_POINT1_Z: usize = 0x2D8;
pub const IN_POINT2_X: usize = 0x330;
pub const IN_POINT2_Y: usize = 0x388;
pub const IN_POINT2_Z: usize = 0x3E0;
pub const OUT_RESULT_X: usize = 0x960;
pub const OUT_RESULT_Y: usize = 0x9B8;
pub const OUT_RESULT_Z: usize = 0xA10;
}
pub mod double_base_ladder {
pub const IN_PRIME_ORDER_NB_BITS: usize = 0x00;
pub const IN_MOD_NB_BITS: usize = 0x08;
pub const IN_A_COEFF_SIGN: usize = 0x10;
pub const IN_A_COEFF: usize = 0x18;
pub const IN_MOD_P: usize = 0x70;
pub const IN_K: usize = 0x120;
pub const IN_M: usize = 0x178;
pub const IN_POINT1_X: usize = 0x228;
pub const IN_POINT1_Y: usize = 0x280;
pub const IN_POINT1_Z: usize = 0x2D8;
pub const IN_POINT2_X: usize = 0x330;
pub const IN_POINT2_Y: usize = 0x388;
pub const IN_POINT2_Z: usize = 0x3E0;
pub const OUT_RESULT_X: usize = 0x178;
pub const OUT_RESULT_Y: usize = 0x1D0;
pub const OUT_ERROR: usize = 0x120;
}
pub mod projective_to_affine {
pub const IN_MOD_NB_BITS: usize = 0x08;
pub const IN_MOD_P: usize = 0x70;
pub const IN_POINT_X: usize = 0x960;
pub const IN_POINT_Y: usize = 0x9B8;
pub const IN_POINT_Z: usize = 0xA10;
pub const IN_MONTGOMERY_PARAM: usize = 0xC8;
pub const OUT_RESULT_X: usize = 0x178;
pub const OUT_RESULT_Y: usize = 0x1D0;
pub const OUT_ERROR: usize = 0x280;
}
}
pub struct InterruptHandler<T: Instance> {
_phantom: PhantomData<T>,
}
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
unsafe fn on_interrupt() {
let sr = T::regs().sr().read();
if sr.procendf() {
T::regs().clrfr().write(|w| w.set_procendfc(true));
PKA_WAKER.wake();
}
if sr.ramerrf() || sr.addrerrf() || sr.operrf() {
T::regs().clrfr().write(|w| {
w.set_ramerrfc(true);
w.set_addrerrfc(true);
w.set_operrfc(true);
});
PKA_WAKER.wake();
}
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {
RamError,
AddressError,
OperationError,
InvalidSize,
Timeout,
PointNotOnCurve,
}
#[derive(Clone)]
pub struct EcdsaCurveParams {
pub p_modulus: &'static [u8],
pub a_coefficient: &'static [u8],
pub a_coefficient_sign: u32,
pub b_coefficient: &'static [u8],
pub generator_x: &'static [u8],
pub generator_y: &'static [u8],
pub order: &'static [u8],
}
impl EcdsaCurveParams {
pub const fn nist_p256() -> Self {
Self {
p_modulus: &P256_P,
a_coefficient: &P256_A,
a_coefficient_sign: 1, b_coefficient: &P256_B,
generator_x: &P256_GX,
generator_y: &P256_GY,
order: &P256_N,
}
}
}
const P256_P: [u8; 32] = [
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
];
const P256_A: [u8; 32] = [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
];
const P256_B: [u8; 32] = [
0x5A, 0xC6, 0x35, 0xD8, 0xAA, 0x3A, 0x93, 0xE7, 0xB3, 0xEB, 0xBD, 0x55, 0x76, 0x98, 0x86, 0xBC, 0x65, 0x1D, 0x06,
0xB0, 0xCC, 0x53, 0xB0, 0xF6, 0x3B, 0xCE, 0x3C, 0x3E, 0x27, 0xD2, 0x60, 0x4B,
];
const P256_GX: [u8; 32] = [
0x6B, 0x17, 0xD1, 0xF2, 0xE1, 0x2C, 0x42, 0x47, 0xF8, 0xBC, 0xE6, 0xE5, 0x63, 0xA4, 0x40, 0xF2, 0x77, 0x03, 0x7D,
0x81, 0x2D, 0xEB, 0x33, 0xA0, 0xF4, 0xA1, 0x39, 0x45, 0xD8, 0x98, 0xC2, 0x96,
];
const P256_GY: [u8; 32] = [
0x4F, 0xE3, 0x42, 0xE2, 0xFE, 0x1A, 0x7F, 0x9B, 0x8E, 0xE7, 0xEB, 0x4A, 0x7C, 0x0F, 0x9E, 0x16, 0x2B, 0xCE, 0x33,
0x57, 0x6B, 0x31, 0x5E, 0xCE, 0xCB, 0xB6, 0x40, 0x68, 0x37, 0xBF, 0x51, 0xF5,
];
const P256_N: [u8; 32] = [
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBC, 0xE6, 0xFA,
0xAD, 0xA7, 0x17, 0x9E, 0x84, 0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, 0x25, 0x51,
];
pub struct EcdsaPublicKey<'a> {
pub x: &'a [u8],
pub y: &'a [u8],
}
pub struct EcdsaSignature<'a> {
pub r: &'a [u8],
pub s: &'a [u8],
}
pub struct EccPoint {
pub x: [u8; 66], pub y: [u8; 66],
pub size: usize,
}
impl EccPoint {
pub fn new(size: usize) -> Self {
Self {
x: [0u8; 66],
y: [0u8; 66],
size,
}
}
}
pub struct RsaParams<'a> {
pub modulus: &'a [u8],
pub exponent: &'a [u8],
}
pub struct RsaCrtParams<'a> {
pub prime_p: &'a [u8],
pub prime_q: &'a [u8],
pub dp: &'a [u8],
pub dq: &'a [u8],
pub qinv: &'a [u8],
}
pub struct EccProjectivePoint {
pub x: [u8; 66], pub y: [u8; 66],
pub z: [u8; 66],
pub size: usize,
}
impl EccProjectivePoint {
pub fn new(size: usize) -> Self {
Self {
x: [0u8; 66],
y: [0u8; 66],
z: [0u8; 66],
size,
}
}
pub fn from_affine(x: &[u8], y: &[u8]) -> Self {
let size = x.len();
let mut point = Self::new(size);
point.x[..size].copy_from_slice(x);
point.y[..size].copy_from_slice(y);
point.z[size - 1] = 1;
point
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ComparisonResult {
Less,
Equal,
Greater,
}
pub struct ModExpProtectParams<'a> {
pub base: &'a [u8],
pub exponent: &'a [u8],
pub modulus: &'a [u8],
pub phi: &'a [u8],
}
pub struct Pka<'d, T: Instance> {
_peripheral: Peri<'d, T>,
}
impl<'d, T: Instance> Pka<'d, T> {
const RAM_ERASE_TIMEOUT: u32 = 100_000;
pub fn new_blocking(
peripheral: Peri<'d, T>,
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
) -> Self {
rcc::enable_and_reset::<T>();
T::Interrupt::unpend();
unsafe { T::Interrupt::enable() };
Self {
_peripheral: peripheral,
}
}
pub fn ecdsa_verify(
&mut self,
curve: &EcdsaCurveParams,
public_key: &EcdsaPublicKey,
signature: &EcdsaSignature,
message_hash: &[u8],
) -> Result<bool, Error> {
let modulus_size = curve.p_modulus.len();
let order_size = curve.order.len();
if curve.a_coefficient.len() != modulus_size
|| curve.generator_x.len() != modulus_size
|| curve.generator_y.len() != modulus_size
|| public_key.x.len() != modulus_size
|| public_key.y.len() != modulus_size
|| signature.r.len() != order_size
|| signature.s.len() != order_size
|| message_hash.len() > order_size
{
return Err(Error::InvalidSize);
}
self.init_pka()?;
let order_nb_bits = Self::get_opt_bit_size(order_size, curve.order[0]);
let mod_nb_bits = Self::get_opt_bit_size(modulus_size, curve.p_modulus[0]);
self.write_ram_word(offsets::ecdsa_verif::IN_ORDER_NB_BITS, order_nb_bits);
self.write_ram_word(offsets::ecdsa_verif::IN_MOD_NB_BITS, mod_nb_bits);
self.write_ram_word(offsets::ecdsa_verif::IN_A_COEFF_SIGN, curve.a_coefficient_sign);
self.write_operand(offsets::ecdsa_verif::IN_A_COEFF, curve.a_coefficient);
self.write_operand(offsets::ecdsa_verif::IN_MOD_GF, curve.p_modulus);
self.write_operand(offsets::ecdsa_verif::IN_INITIAL_POINT_X, curve.generator_x);
self.write_operand(offsets::ecdsa_verif::IN_INITIAL_POINT_Y, curve.generator_y);
self.write_operand(offsets::ecdsa_verif::IN_PUBLIC_KEY_POINT_X, public_key.x);
self.write_operand(offsets::ecdsa_verif::IN_PUBLIC_KEY_POINT_Y, public_key.y);
self.write_operand(offsets::ecdsa_verif::IN_SIGNATURE_R, signature.r);
self.write_operand(offsets::ecdsa_verif::IN_SIGNATURE_S, signature.s);
self.write_operand(offsets::ecdsa_verif::IN_HASH_E, message_hash);
self.write_operand(offsets::ecdsa_verif::IN_ORDER_N, curve.order);
self.set_mode(PkaMode::EcdsaVerify);
self.start_and_wait()?;
let result = self.read_ram_word(offsets::ecdsa_verif::OUT_RESULT);
self.disable_pka();
Ok(result == 0xD60D)
}
pub fn ecdsa_sign(
&mut self,
curve: &EcdsaCurveParams,
private_key: &[u8],
k: &[u8],
message_hash: &[u8],
signature_r: &mut [u8],
signature_s: &mut [u8],
) -> Result<(), Error> {
let modulus_size = curve.p_modulus.len();
let order_size = curve.order.len();
if private_key.len() != order_size
|| k.len() != order_size
|| message_hash.len() > order_size
|| signature_r.len() < order_size
|| signature_s.len() < order_size
{
return Err(Error::InvalidSize);
}
self.init_pka()?;
self.set_mode(PkaMode::EcdsaSign);
let order_nb_bits = Self::get_opt_bit_size(order_size, curve.order[0]);
let mod_nb_bits = Self::get_opt_bit_size(modulus_size, curve.p_modulus[0]);
self.write_ram_word(offsets::ecdsa_sign::IN_ORDER_NB_BITS, order_nb_bits);
self.write_ram_word(offsets::ecdsa_sign::IN_MOD_NB_BITS, mod_nb_bits);
self.write_ram_word(offsets::ecdsa_sign::IN_A_COEFF_SIGN, curve.a_coefficient_sign);
self.write_operand(offsets::ecdsa_sign::IN_A_COEFF, curve.a_coefficient);
self.write_operand(offsets::ecdsa_sign::IN_B_COEFF, curve.b_coefficient);
self.write_operand(offsets::ecdsa_sign::IN_MOD_GF, curve.p_modulus);
self.write_operand(offsets::ecdsa_sign::IN_INITIAL_POINT_X, curve.generator_x);
self.write_operand(offsets::ecdsa_sign::IN_INITIAL_POINT_Y, curve.generator_y);
self.write_operand(offsets::ecdsa_sign::IN_ORDER_N, curve.order);
self.write_operand(offsets::ecdsa_sign::IN_PRIVATE_KEY_D, private_key);
self.write_operand(offsets::ecdsa_sign::IN_K, k);
self.write_operand(offsets::ecdsa_sign::IN_HASH_E, message_hash);
self.start_and_wait()?;
let result = self.read_ram_word(offsets::ecdsa_sign::OUT_ERROR);
if result != 0xD60D {
self.disable_pka();
return Err(Error::OperationError);
}
self.read_operand(offsets::ecdsa_sign::OUT_SIGNATURE_R, &mut signature_r[..order_size]);
self.read_operand(offsets::ecdsa_sign::OUT_SIGNATURE_S, &mut signature_s[..order_size]);
self.disable_pka();
Ok(())
}
pub fn ecc_mul(
&mut self,
curve: &EcdsaCurveParams,
k: &[u8],
point_x: &[u8],
point_y: &[u8],
result: &mut EccPoint,
) -> Result<(), Error> {
let modulus_size = curve.p_modulus.len();
let order_size = curve.order.len();
if k.len() != order_size
|| point_x.len() != modulus_size
|| point_y.len() != modulus_size
|| result.size != modulus_size
{
return Err(Error::InvalidSize);
}
self.init_pka()?;
let exp_nb_bits = Self::get_opt_bit_size(k.len(), curve.order[0]);
let mod_nb_bits = Self::get_opt_bit_size(modulus_size, curve.p_modulus[0]);
self.write_ram_word(offsets::ecc_mul::IN_EXP_NB_BITS, exp_nb_bits);
self.write_ram_word(offsets::ecc_mul::IN_OP_NB_BITS, mod_nb_bits);
self.write_ram_word(offsets::ecc_mul::IN_A_COEFF_SIGN, curve.a_coefficient_sign);
self.write_operand(offsets::ecc_mul::IN_A_COEFF, curve.a_coefficient);
self.write_operand(offsets::ecc_mul::IN_B_COEFF, curve.b_coefficient);
self.write_operand(offsets::ecc_mul::IN_MOD_GF, curve.p_modulus);
self.write_operand(offsets::ecc_mul::IN_N_PRIME_ORDER, curve.order);
self.write_operand(offsets::ecc_mul::IN_K, k);
self.write_operand(offsets::ecc_mul::IN_INITIAL_POINT_X, point_x);
self.write_operand(offsets::ecc_mul::IN_INITIAL_POINT_Y, point_y);
self.set_mode(PkaMode::EccMul);
self.start_and_wait()?;
let status = self.read_ram_word(offsets::ecc_mul::OUT_ERROR);
if status != 0xD60D {
self.disable_pka();
return Err(Error::OperationError);
}
self.read_operand(offsets::ecc_mul::OUT_RESULT_X, &mut result.x[..modulus_size]);
self.read_operand(offsets::ecc_mul::OUT_RESULT_Y, &mut result.y[..modulus_size]);
self.disable_pka();
Ok(())
}
pub fn point_check(&mut self, curve: &EcdsaCurveParams, point_x: &[u8], point_y: &[u8]) -> Result<bool, Error> {
let modulus_size = curve.p_modulus.len();
if point_x.len() != modulus_size || point_y.len() != modulus_size {
return Err(Error::InvalidSize);
}
self.init_pka()?;
let mod_nb_bits = Self::get_opt_bit_size(modulus_size, curve.p_modulus[0]);
self.write_ram_word(offsets::point_check::IN_MOD_NB_BITS, mod_nb_bits);
self.write_ram_word(offsets::point_check::IN_A_COEFF_SIGN, curve.a_coefficient_sign);
self.write_operand(offsets::point_check::IN_A_COEFF, curve.a_coefficient);
self.write_operand(offsets::point_check::IN_B_COEFF, curve.b_coefficient);
self.write_operand(offsets::point_check::IN_MOD_GF, curve.p_modulus);
self.write_operand(offsets::point_check::IN_INITIAL_POINT_X, point_x);
self.write_operand(offsets::point_check::IN_INITIAL_POINT_Y, point_y);
self.set_mode(PkaMode::PointCheck);
self.start_and_wait()?;
let result = self.read_ram_word(offsets::point_check::OUT_ERROR);
self.disable_pka();
Ok(result == 0xD60D)
}
pub fn modular_exp(
&mut self,
base: &[u8],
exponent: &[u8],
modulus: &[u8],
result: &mut [u8],
) -> Result<(), Error> {
let mod_size = modulus.len();
let exp_size = exponent.len();
if base.len() > mod_size || result.len() < mod_size {
return Err(Error::InvalidSize);
}
self.init_pka()?;
self.set_mode(PkaMode::ModularExp);
let exp_nb_bits = (exp_size * 8) as u32;
let mod_nb_bits = (mod_size * 8) as u32;
self.write_ram_word(offsets::modular_exp::IN_EXP_NB_BITS, exp_nb_bits);
self.write_ram_word(offsets::modular_exp::IN_OP_NB_BITS, mod_nb_bits);
self.write_operand(offsets::modular_exp::IN_EXPONENT_BASE, base);
self.write_operand(offsets::modular_exp::IN_EXPONENT, exponent);
self.write_operand(offsets::modular_exp::IN_MODULUS, modulus);
self.start_and_wait()?;
self.read_operand(offsets::modular_exp::OUT_RESULT, &mut result[..mod_size]);
Ok(())
}
pub fn rsa_crt_exp(&mut self, ciphertext: &[u8], params: &RsaCrtParams, result: &mut [u8]) -> Result<(), Error> {
let p_size = params.prime_p.len();
let q_size = params.prime_q.len();
let mod_size = p_size + q_size;
if ciphertext.len() > mod_size
|| params.dp.len() != p_size
|| params.dq.len() != q_size
|| params.qinv.len() != p_size
|| result.len() < mod_size
{
return Err(Error::InvalidSize);
}
self.init_pka()?;
self.set_mode(PkaMode::RsaCrtExp);
let mod_nb_bits = (mod_size * 8) as u32;
self.write_ram_word(offsets::rsa_crt::IN_MOD_NB_BITS, mod_nb_bits);
self.write_operand(offsets::rsa_crt::IN_PRIME_P, params.prime_p);
self.write_operand(offsets::rsa_crt::IN_PRIME_Q, params.prime_q);
self.write_operand(offsets::rsa_crt::IN_DP_CRT, params.dp);
self.write_operand(offsets::rsa_crt::IN_DQ_CRT, params.dq);
self.write_operand(offsets::rsa_crt::IN_QINV_CRT, params.qinv);
self.write_operand(offsets::rsa_crt::IN_EXPONENT_BASE, ciphertext);
self.start_and_wait()?;
self.read_operand(offsets::rsa_crt::OUT_RESULT, &mut result[..mod_size]);
Ok(())
}
pub fn modular_inv(&mut self, a: &[u8], modulus: &[u8], result: &mut [u8]) -> Result<(), Error> {
let size = modulus.len();
if a.len() != size || result.len() < size {
return Err(Error::InvalidSize);
}
self.init_pka()?;
self.set_mode(PkaMode::ModularInv);
let nb_bits = Self::get_opt_bit_size(size, modulus[0]);
self.write_ram_word(offsets::modular_inv::IN_NB_BITS, nb_bits);
self.write_operand(offsets::modular_inv::IN_OP1, a);
self.write_operand(offsets::modular_inv::IN_OP2_MOD, modulus);
self.start_and_wait()?;
self.read_operand(offsets::modular_inv::OUT_RESULT, &mut result[..size]);
Ok(())
}
pub fn modular_add(&mut self, a: &[u8], b: &[u8], modulus: &[u8], result: &mut [u8]) -> Result<(), Error> {
self.arithmetic_op(PkaMode::ModularAdd, a, b, Some(modulus), result)
}
pub fn modular_sub(&mut self, a: &[u8], b: &[u8], modulus: &[u8], result: &mut [u8]) -> Result<(), Error> {
self.arithmetic_op(PkaMode::ModularSub, a, b, Some(modulus), result)
}
pub fn arithmetic_mul(&mut self, a: &[u8], b: &[u8], result: &mut [u8]) -> Result<(), Error> {
self.arithmetic_op(PkaMode::ArithmeticMul, a, b, None, result)
}
fn arithmetic_op(
&mut self,
mode: PkaMode,
a: &[u8],
b: &[u8],
modulus: Option<&[u8]>,
result: &mut [u8],
) -> Result<(), Error> {
let size = a.len();
if b.len() != size {
return Err(Error::InvalidSize);
}
self.init_pka()?;
self.set_mode(mode);
let nb_bits = (size * 8) as u32;
self.write_ram_word(offsets::arithmetic::IN_NB_BITS, nb_bits);
self.write_operand(offsets::arithmetic::IN_OP1, a);
self.write_operand(offsets::arithmetic::IN_OP2, b);
if let Some(m) = modulus {
self.write_operand(offsets::arithmetic::IN_OP3_MOD, m);
}
self.start_and_wait()?;
let result_size = if mode == PkaMode::ArithmeticMul { size * 2 } else { size };
self.read_operand(offsets::arithmetic::OUT_RESULT, &mut result[..result_size]);
Ok(())
}
pub fn montgomery_param(&mut self, modulus: &[u8], result: &mut [u32]) -> Result<(), Error> {
let size = modulus.len();
let word_count = (size + 3) / 4;
if result.len() < word_count {
return Err(Error::InvalidSize);
}
self.init_pka()?;
self.set_mode(PkaMode::MontgomeryParam);
let mut bytes_to_skip = 0;
while bytes_to_skip < size && modulus[bytes_to_skip] == 0 {
bytes_to_skip += 1;
}
let new_size = size - bytes_to_skip;
let first_nonzero = if bytes_to_skip < size {
modulus[bytes_to_skip]
} else {
0
};
let nb_bits = Self::get_opt_bit_size(new_size, first_nonzero);
self.write_ram_word(offsets::montgomery_param::IN_MOD_NB_BITS, nb_bits);
self.write_operand(offsets::montgomery_param::IN_MODULUS, modulus);
self.start_and_wait()?;
for i in 0..word_count {
result[i] = self.read_ram_word(offsets::montgomery_param::OUT_PARAMETER + i * 4);
}
Ok(())
}
pub fn modular_exp_fast(
&mut self,
base: &[u8],
exponent: &[u8],
modulus: &[u8],
montgomery_param: &[u32],
result: &mut [u8],
) -> Result<(), Error> {
let mod_size = modulus.len();
let exp_size = exponent.len();
if base.len() > mod_size || result.len() < mod_size {
return Err(Error::InvalidSize);
}
self.init_pka()?;
self.set_mode(PkaMode::ModularExpFast);
let exp_nb_bits = (exp_size * 8) as u32;
let mod_nb_bits = (mod_size * 8) as u32;
self.write_ram_word(offsets::modular_exp::IN_EXP_NB_BITS, exp_nb_bits);
self.write_ram_word(offsets::modular_exp::IN_OP_NB_BITS, mod_nb_bits);
for (i, &word) in montgomery_param.iter().enumerate() {
self.write_ram_word(offsets::modular_exp::IN_MONTGOMERY_PARAM + i * 4, word);
}
self.write_operand(offsets::modular_exp::IN_EXPONENT_BASE, base);
self.write_operand(offsets::modular_exp::IN_EXPONENT, exponent);
self.write_operand(offsets::modular_exp::IN_MODULUS, modulus);
self.start_and_wait()?;
self.read_operand(offsets::modular_exp::OUT_RESULT, &mut result[..mod_size]);
Ok(())
}
pub fn modular_exp_protect(&mut self, params: &ModExpProtectParams, result: &mut [u8]) -> Result<(), Error> {
let mod_size = params.modulus.len();
let exp_size = params.exponent.len();
if params.base.len() > mod_size || params.phi.len() != mod_size || result.len() < mod_size {
return Err(Error::InvalidSize);
}
self.init_pka()?;
self.set_mode(PkaMode::ModularExpProtect);
let exp_nb_bits = (exp_size * 8) as u32;
let mod_nb_bits = (mod_size * 8) as u32;
self.write_ram_word(offsets::modular_exp_protect::IN_EXP_NB_BITS, exp_nb_bits);
self.write_ram_word(offsets::modular_exp_protect::IN_OP_NB_BITS, mod_nb_bits);
self.write_operand(offsets::modular_exp_protect::IN_EXPONENT_BASE, params.base);
self.write_operand(offsets::modular_exp_protect::IN_EXPONENT, params.exponent);
self.write_operand(offsets::modular_exp_protect::IN_MODULUS, params.modulus);
self.write_operand(offsets::modular_exp_protect::IN_PHI, params.phi);
self.start_and_wait()?;
self.read_operand(offsets::modular_exp_protect::OUT_RESULT, &mut result[..mod_size]);
Ok(())
}
pub fn montgomery_mul(&mut self, a: &[u8], b: &[u8], modulus: &[u8], result: &mut [u8]) -> Result<(), Error> {
self.arithmetic_op(PkaMode::MontgomeryMul, a, b, Some(modulus), result)
}
pub fn arithmetic_add(&mut self, a: &[u8], b: &[u8], result: &mut [u8]) -> Result<(), Error> {
let size = a.len();
if b.len() != size || result.len() < size {
return Err(Error::InvalidSize);
}
self.init_pka()?;
self.set_mode(PkaMode::ArithmeticAdd);
let nb_bits = Self::get_opt_bit_size(size, a[0].max(b[0]));
self.write_ram_word(offsets::arithmetic::IN_NB_BITS, nb_bits);
self.write_operand(offsets::arithmetic::IN_OP1, a);
self.write_operand(offsets::arithmetic::IN_OP2, b);
self.start_and_wait()?;
self.read_operand(offsets::arithmetic::OUT_RESULT, &mut result[..size]);
self.disable_pka();
Ok(())
}
pub fn arithmetic_sub(&mut self, a: &[u8], b: &[u8], result: &mut [u8]) -> Result<(), Error> {
let size = a.len();
if b.len() != size || result.len() < size {
return Err(Error::InvalidSize);
}
self.init_pka()?;
self.set_mode(PkaMode::ArithmeticSub);
let nb_bits = Self::get_opt_bit_size(size, a[0].max(b[0]));
self.write_ram_word(offsets::arithmetic::IN_NB_BITS, nb_bits);
self.write_operand(offsets::arithmetic::IN_OP1, a);
self.write_operand(offsets::arithmetic::IN_OP2, b);
self.start_and_wait()?;
self.read_operand(offsets::arithmetic::OUT_RESULT, &mut result[..size]);
Ok(())
}
pub fn comparison(&mut self, a: &[u8], b: &[u8]) -> Result<ComparisonResult, Error> {
let size = a.len();
if b.len() != size {
return Err(Error::InvalidSize);
}
self.init_pka()?;
self.set_mode(PkaMode::Comparison);
let nb_bits = (size * 8) as u32;
self.write_ram_word(offsets::arithmetic::IN_NB_BITS, nb_bits);
self.write_operand(offsets::arithmetic::IN_OP1, a);
self.write_operand(offsets::arithmetic::IN_OP2, b);
self.start_and_wait()?;
let result = self.read_ram_word(offsets::arithmetic::OUT_RESULT);
match result {
0xED2C => Ok(ComparisonResult::Equal), 0x7AF8 => Ok(ComparisonResult::Greater), 0x916A => Ok(ComparisonResult::Less), _ => Err(Error::OperationError),
}
}
pub fn modular_red(&mut self, a: &[u8], modulus: &[u8], result: &mut [u8]) -> Result<(), Error> {
let op_size = a.len();
let mod_size = modulus.len();
if result.len() < mod_size {
return Err(Error::InvalidSize);
}
self.init_pka()?;
self.set_mode(PkaMode::ModularRed);
let op_nb_bits = (op_size * 8) as u32;
let mod_nb_bits = (mod_size * 8) as u32;
self.write_ram_word(offsets::modular_red::IN_OP_LENGTH, op_nb_bits);
self.write_ram_word(offsets::modular_red::IN_MOD_LENGTH, mod_nb_bits);
self.write_operand(offsets::modular_red::IN_OPERAND, a);
self.write_operand(offsets::modular_red::IN_MODULUS, modulus);
self.start_and_wait()?;
self.read_operand(offsets::modular_red::OUT_RESULT, &mut result[..mod_size]);
Ok(())
}
pub fn ecc_complete_add(
&mut self,
curve: &EcdsaCurveParams,
p: &EccProjectivePoint,
q: &EccProjectivePoint,
result: &mut EccProjectivePoint,
) -> Result<(), Error> {
let modulus_size = curve.p_modulus.len();
if p.size != modulus_size || q.size != modulus_size || result.size != modulus_size {
return Err(Error::InvalidSize);
}
self.init_pka()?;
let mod_nb_bits = Self::get_opt_bit_size(modulus_size, curve.p_modulus[0]);
self.write_ram_word(offsets::ecc_complete_add::IN_MOD_NB_BITS, mod_nb_bits);
self.write_ram_word(offsets::ecc_complete_add::IN_A_COEFF_SIGN, curve.a_coefficient_sign);
self.write_operand(offsets::ecc_complete_add::IN_A_COEFF, curve.a_coefficient);
self.write_operand(offsets::ecc_complete_add::IN_MOD_P, curve.p_modulus);
self.write_operand(offsets::ecc_complete_add::IN_POINT1_X, &p.x[..modulus_size]);
self.write_operand(offsets::ecc_complete_add::IN_POINT1_Y, &p.y[..modulus_size]);
self.write_operand(offsets::ecc_complete_add::IN_POINT1_Z, &p.z[..modulus_size]);
self.write_operand(offsets::ecc_complete_add::IN_POINT2_X, &q.x[..modulus_size]);
self.write_operand(offsets::ecc_complete_add::IN_POINT2_Y, &q.y[..modulus_size]);
self.write_operand(offsets::ecc_complete_add::IN_POINT2_Z, &q.z[..modulus_size]);
self.set_mode(PkaMode::EccCompleteAdd);
self.start_and_wait()?;
self.read_operand(offsets::ecc_complete_add::OUT_RESULT_X, &mut result.x[..modulus_size]);
self.read_operand(offsets::ecc_complete_add::OUT_RESULT_Y, &mut result.y[..modulus_size]);
self.read_operand(offsets::ecc_complete_add::OUT_RESULT_Z, &mut result.z[..modulus_size]);
self.disable_pka();
Ok(())
}
pub fn double_base_ladder(
&mut self,
curve: &EcdsaCurveParams,
k: &[u8],
p: &EccProjectivePoint,
m: &[u8],
q: &EccProjectivePoint,
result: &mut EccPoint,
) -> Result<(), Error> {
let modulus_size = curve.p_modulus.len();
let order_size = curve.order.len();
if k.len() != order_size
|| m.len() != order_size
|| p.size != modulus_size
|| q.size != modulus_size
|| result.size != modulus_size
{
return Err(Error::InvalidSize);
}
self.init_pka()?;
let order_nb_bits = Self::get_opt_bit_size(order_size, curve.order[0]);
let mod_nb_bits = Self::get_opt_bit_size(modulus_size, curve.p_modulus[0]);
self.write_ram_word(offsets::double_base_ladder::IN_PRIME_ORDER_NB_BITS, order_nb_bits);
self.write_ram_word(offsets::double_base_ladder::IN_MOD_NB_BITS, mod_nb_bits);
self.write_ram_word(offsets::double_base_ladder::IN_A_COEFF_SIGN, curve.a_coefficient_sign);
self.write_operand(offsets::double_base_ladder::IN_A_COEFF, curve.a_coefficient);
self.write_operand(offsets::double_base_ladder::IN_MOD_P, curve.p_modulus);
self.write_operand(offsets::double_base_ladder::IN_K, k);
self.write_operand(offsets::double_base_ladder::IN_M, m);
self.write_operand(offsets::double_base_ladder::IN_POINT1_X, &p.x[..modulus_size]);
self.write_operand(offsets::double_base_ladder::IN_POINT1_Y, &p.y[..modulus_size]);
self.write_operand(offsets::double_base_ladder::IN_POINT1_Z, &p.z[..modulus_size]);
self.write_operand(offsets::double_base_ladder::IN_POINT2_X, &q.x[..modulus_size]);
self.write_operand(offsets::double_base_ladder::IN_POINT2_Y, &q.y[..modulus_size]);
self.write_operand(offsets::double_base_ladder::IN_POINT2_Z, &q.z[..modulus_size]);
self.set_mode(PkaMode::DoubleBaseLadder);
self.start_and_wait()?;
let status = self.read_ram_word(offsets::double_base_ladder::OUT_ERROR);
if status != 0xD60D {
self.disable_pka();
return Err(Error::OperationError);
}
self.read_operand(offsets::double_base_ladder::OUT_RESULT_X, &mut result.x[..modulus_size]);
self.read_operand(offsets::double_base_ladder::OUT_RESULT_Y, &mut result.y[..modulus_size]);
self.disable_pka();
Ok(())
}
pub fn projective_to_affine(
&mut self,
modulus: &[u8],
montgomery_param: &[u32],
point: &EccProjectivePoint,
result: &mut EccPoint,
) -> Result<(), Error> {
let modulus_size = modulus.len();
if point.size != modulus_size || result.size != modulus_size {
return Err(Error::InvalidSize);
}
self.init_pka()?;
let mod_nb_bits = Self::get_opt_bit_size(modulus_size, modulus[0]);
self.write_ram_word(offsets::projective_to_affine::IN_MOD_NB_BITS, mod_nb_bits);
self.write_operand(offsets::projective_to_affine::IN_MOD_P, modulus);
for (i, &word) in montgomery_param.iter().enumerate() {
self.write_ram_word(offsets::projective_to_affine::IN_MONTGOMERY_PARAM + i * 4, word);
}
self.write_operand(offsets::projective_to_affine::IN_POINT_X, &point.x[..modulus_size]);
self.write_operand(offsets::projective_to_affine::IN_POINT_Y, &point.y[..modulus_size]);
self.write_operand(offsets::projective_to_affine::IN_POINT_Z, &point.z[..modulus_size]);
self.set_mode(PkaMode::EccProjectiveToAffine);
self.start_and_wait()?;
let status = self.read_ram_word(offsets::projective_to_affine::OUT_ERROR);
if status != 0xD60D {
self.disable_pka();
return Err(Error::OperationError);
}
self.read_operand(
offsets::projective_to_affine::OUT_RESULT_X,
&mut result.x[..modulus_size],
);
self.read_operand(
offsets::projective_to_affine::OUT_RESULT_Y,
&mut result.y[..modulus_size],
);
self.disable_pka();
Ok(())
}
fn init_pka(&mut self) -> Result<(), Error> {
let p = T::regs();
let sr_ptr = p.sr().as_ptr() as *const u32;
let sr_raw = unsafe { sr_ptr.read_volatile() };
let cr_raw = p.cr().read().0;
if (cr_raw & 0x01) != 0 && (sr_raw & 0x01) != 0 {
return Ok(());
}
if (cr_raw & 0x01) == 0 {
#[cfg(rng_wba6)]
{
use crate::pac::rcc::vals::Rngsel;
let rcc = crate::pac::RCC;
let was_rng_enabled = rcc.ahb2enr().read().rngen();
if !was_rng_enabled {
rcc.ccipr2().modify(|w| w.set_rngsel(Rngsel::HSI));
rcc.ahb2enr().modify(|w| w.set_rngen(true));
let rng = crate::pac::RNG;
rng.cr().modify(|w| w.set_rngen(true));
cortex_m::asm::delay(10000); }
}
let mut timeout: u32 = 0;
loop {
p.cr().write(|w| w.set_en(true));
if p.cr().read().en() {
break;
}
timeout += 1;
if timeout > Self::RAM_ERASE_TIMEOUT {
return Err(Error::Timeout);
}
}
}
let mut timeout: u32 = 0;
loop {
let sr_raw = unsafe { sr_ptr.read_volatile() };
if sr_raw & 0x01 != 0 {
break;
}
timeout += 1;
if timeout > 1_000_000 {
return Err(Error::Timeout);
}
}
p.clrfr().write(|w| {
w.set_procendfc(true);
w.set_ramerrfc(true);
w.set_addrerrfc(true);
w.set_operrfc(true);
});
Ok(())
}
fn set_mode(&mut self, mode: PkaMode) {
let p = T::regs();
p.cr().modify(|w| {
w.set_mode(mode as u8);
w.set_procendie(false);
w.set_ramerrie(false);
w.set_addrerrie(false);
w.set_operrie(false);
});
}
fn start_and_wait(&mut self) -> Result<(), Error> {
let p = T::regs();
p.cr().modify(|w| w.set_start(true));
let mut timeout: u32 = 0;
loop {
let sr = p.sr().read();
if sr.ramerrf() {
p.clrfr().write(|w| w.set_ramerrfc(true));
return Err(Error::RamError);
}
if sr.addrerrf() {
p.clrfr().write(|w| w.set_addrerrfc(true));
return Err(Error::AddressError);
}
if sr.operrf() {
p.clrfr().write(|w| w.set_operrfc(true));
return Err(Error::OperationError);
}
if sr.procendf() {
p.clrfr().write(|w| w.set_procendfc(true));
break;
}
timeout += 1;
if timeout > 10_000_000 {
return Err(Error::Timeout);
}
}
Ok(())
}
fn disable_pka(&mut self) {
T::regs().cr().modify(|w| w.set_en(false));
}
fn get_opt_bit_size(byte_count: usize, msb: u8) -> u32 {
let position = if msb == 0 { 0 } else { 8 - msb.leading_zeros() };
((byte_count as u32 - 1) * 8) + position
}
fn write_operand(&mut self, offset: usize, data: &[u8]) {
let n = data.len();
let word_count = (n + 3) / 4;
for index in 0..(n / 4) {
let i = n - (index * 4);
let word = (data[i - 1] as u32)
| ((data[i - 2] as u32) << 8)
| ((data[i - 3] as u32) << 16)
| ((data[i - 4] as u32) << 24);
self.write_ram_word(offset + index * 4, word);
}
let remainder = n % 4;
if remainder > 0 {
let index = n / 4;
let word = match remainder {
1 => data[0] as u32,
2 => (data[1] as u32) | ((data[0] as u32) << 8),
3 => (data[2] as u32) | ((data[1] as u32) << 8) | ((data[0] as u32) << 16),
_ => 0,
};
self.write_ram_word(offset + index * 4, word);
}
self.write_ram_word(offset + word_count * 4, 0);
self.write_ram_word(offset + (word_count + 1) * 4, 0);
}
fn read_operand(&self, offset: usize, data: &mut [u8]) {
let n = data.len();
for index in 0..(n / 4) {
let word = self.read_ram_word(offset + index * 4);
let i = n - (index * 4);
data[i - 1] = (word & 0xFF) as u8;
data[i - 2] = ((word >> 8) & 0xFF) as u8;
data[i - 3] = ((word >> 16) & 0xFF) as u8;
data[i - 4] = ((word >> 24) & 0xFF) as u8;
}
let remainder = n % 4;
if remainder > 0 {
let index = n / 4;
let word = self.read_ram_word(offset + index * 4);
match remainder {
1 => data[0] = (word & 0xFF) as u8,
2 => {
data[1] = (word & 0xFF) as u8;
data[0] = ((word >> 8) & 0xFF) as u8;
}
3 => {
data[2] = (word & 0xFF) as u8;
data[1] = ((word >> 8) & 0xFF) as u8;
data[0] = ((word >> 16) & 0xFF) as u8;
}
_ => {}
}
}
}
fn write_ram_word(&mut self, offset: usize, value: u32) {
let p = T::regs();
let word_index = offset / 4;
unsafe {
let ram_ptr = p.ram(word_index).as_ptr() as *mut u32;
ram_ptr.write_volatile(value);
}
}
fn read_ram_word(&self, offset: usize) -> u32 {
let p = T::regs();
let word_index = offset / 4;
unsafe {
let ram_ptr = p.ram(word_index).as_ptr() as *const u32;
ram_ptr.read_volatile()
}
}
}
trait SealedInstance {
fn regs() -> pac::pka::Pka;
}
#[allow(private_bounds)]
pub trait Instance: SealedInstance + PeripheralType + crate::rcc::RccPeripheral + 'static + Send {
type Interrupt: interrupt::typelevel::Interrupt;
}
foreach_interrupt!(
($inst:ident, pka, PKA, GLOBAL, $irq:ident) => {
impl Instance for peripherals::$inst {
type Interrupt = crate::interrupt::typelevel::$irq;
}
impl SealedInstance for peripherals::$inst {
fn regs() -> crate::pac::pka::Pka {
crate::pac::$inst
}
}
};
);