Implement full SSD1306 OLED display support: - Splash screen on boot (1.5s) with project name, version, board - Dashboard showing radio state, config, counters, RSSI/SNR - RSSI history sparkline (64-sample ring buffer, 2px-wide bars) - DisplayOn/DisplayOff host commands routed via dedicated channel - Pure render functions against generic DrawTarget (no hardware coupling) - Display on/off control bypasses radio task entirely Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
967 lines
32 KiB
Rust
967 lines
32 KiB
Rust
//! # Elliptic Curve Cryptography (ECC) Accelerator
|
||
//!
|
||
//! ## Overview
|
||
//!
|
||
//! Elliptic Curve Cryptography (ECC) is an approach to public-key cryptography
|
||
//! based on the algebraic structure of elliptic curves. ECC allows smaller
|
||
//! keys compared to RSA cryptography while providing equivalent security.
|
||
//!
|
||
//! ECC Accelerator can complete various calculation based on different
|
||
//! elliptic curves, thus accelerating ECC algorithm and ECC-derived
|
||
//! algorithms (such as ECDSA).
|
||
//!
|
||
//! ## Configuration
|
||
//! ECC Accelerator supports:
|
||
//! - Two different elliptic curves, namely P-192 and P-256 defined in FIPS
|
||
//! 186-3.
|
||
//! - Seven working modes.
|
||
//! - Interrupt upon completion of calculation.
|
||
//!
|
||
//! Inputs of the ECC hardware accelerator must be provided in big-endian
|
||
//! representation. The driver handles the inner representation of the blocks.
|
||
//!
|
||
//! ## Examples
|
||
//! Visit the [ECC] test for an example of using the ECC Accelerator.
|
||
//!
|
||
//! [ECC]: https://github.com/esp-rs/esp-hal/blob/main/hil-test/tests/ecc.rs
|
||
|
||
use core::marker::PhantomData;
|
||
|
||
use crate::{
|
||
interrupt::InterruptHandler,
|
||
peripheral::{Peripheral, PeripheralRef},
|
||
peripherals::{Interrupt, ECC},
|
||
reg_access::{AlignmentHelper, SocDependentEndianess},
|
||
system::{Peripheral as PeripheralEnable, PeripheralClockControl},
|
||
InterruptConfigurable,
|
||
};
|
||
|
||
/// The ECC Accelerator driver instance
|
||
pub struct Ecc<'d, DM: crate::Mode> {
|
||
ecc: PeripheralRef<'d, ECC>,
|
||
alignment_helper: AlignmentHelper<SocDependentEndianess>,
|
||
phantom: PhantomData<DM>,
|
||
}
|
||
|
||
/// ECC interface error
|
||
#[derive(Debug)]
|
||
pub enum Error {
|
||
/// It means the purpose of the selected block does not match the
|
||
/// configured key purpose and the calculation will not proceed.
|
||
SizeMismatchCurve,
|
||
/// It means that the point is not on the curve.
|
||
PointNotOnSelectedCurve,
|
||
}
|
||
|
||
/// Represents supported elliptic curves for cryptographic operations.
|
||
pub enum EllipticCurve {
|
||
/// The P-192 elliptic curve, a 192-bit curve.
|
||
P192 = 0,
|
||
/// The P-256 elliptic curve. a 256-bit curve.
|
||
P256 = 1,
|
||
}
|
||
|
||
#[derive(Clone)]
|
||
/// Represents the operational modes for elliptic curve or modular arithmetic
|
||
/// computations.
|
||
pub enum WorkMode {
|
||
/// Point multiplication mode.
|
||
PointMultiMode = 0,
|
||
#[cfg(esp32c2)]
|
||
/// Division mode.
|
||
DivisionMode = 1,
|
||
/// Point verification mode.
|
||
PointVerif = 2,
|
||
/// Point verification and multiplication mode.
|
||
PointVerifMulti = 3,
|
||
/// Jacobian point multiplication mode.
|
||
JacobianPointMulti = 4,
|
||
#[cfg(esp32h2)]
|
||
/// Point addition mode.
|
||
PointAdd = 5,
|
||
/// Jacobian point verification mode.
|
||
JacobianPointVerif = 6,
|
||
/// Point verification and multiplication in Jacobian coordinates.
|
||
PointVerifJacobianMulti = 7,
|
||
#[cfg(esp32h2)]
|
||
/// Modular addition mode.
|
||
ModAdd = 8,
|
||
#[cfg(esp32h2)]
|
||
/// Modular subtraction mode.
|
||
ModSub = 9,
|
||
#[cfg(esp32h2)]
|
||
/// Modular multiplication mode.
|
||
ModMulti = 10,
|
||
#[cfg(esp32h2)]
|
||
/// Modular division mode.
|
||
ModDiv = 11,
|
||
}
|
||
|
||
impl<'d> Ecc<'d, crate::Blocking> {
|
||
/// Create a new instance in [crate::Blocking] mode.
|
||
pub fn new(ecc: impl Peripheral<P = ECC> + 'd) -> Self {
|
||
crate::into_ref!(ecc);
|
||
|
||
PeripheralClockControl::reset(PeripheralEnable::Ecc);
|
||
PeripheralClockControl::enable(PeripheralEnable::Ecc);
|
||
|
||
Self {
|
||
ecc,
|
||
alignment_helper: AlignmentHelper::default(),
|
||
phantom: PhantomData,
|
||
}
|
||
}
|
||
}
|
||
|
||
impl crate::private::Sealed for Ecc<'_, crate::Blocking> {}
|
||
|
||
impl InterruptConfigurable for Ecc<'_, crate::Blocking> {
|
||
fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
|
||
for core in crate::Cpu::other() {
|
||
crate::interrupt::disable(core, Interrupt::ECC);
|
||
}
|
||
unsafe { crate::interrupt::bind_interrupt(Interrupt::ECC, handler.handler()) };
|
||
unwrap!(crate::interrupt::enable(Interrupt::ECC, handler.priority()));
|
||
}
|
||
}
|
||
|
||
impl<DM: crate::Mode> Ecc<'_, DM> {
|
||
/// Resets the ECC peripheral.
|
||
pub fn reset(&mut self) {
|
||
self.ecc.mult_conf().reset()
|
||
}
|
||
|
||
/// # Base point multiplication
|
||
///
|
||
/// Base Point Multiplication can be represented as:
|
||
/// (Q_x, Q_y) = k * (P_x, P_y)
|
||
///
|
||
/// Output is stored in `x` and `y`.
|
||
///
|
||
/// # Error
|
||
///
|
||
/// This function will return an error if any bitlength value is different
|
||
/// from the bitlength of the prime fields of the curve.
|
||
pub fn affine_point_multiplication(
|
||
&mut self,
|
||
curve: &EllipticCurve,
|
||
k: &[u8],
|
||
x: &mut [u8],
|
||
y: &mut [u8],
|
||
) -> Result<(), Error> {
|
||
let curve = match curve {
|
||
EllipticCurve::P192 => {
|
||
if k.len() != 24 || x.len() != 24 || y.len() != 24 {
|
||
return Err(Error::SizeMismatchCurve);
|
||
}
|
||
false
|
||
}
|
||
EllipticCurve::P256 => {
|
||
if k.len() != 32 || x.len() != 32 || y.len() != 32 {
|
||
return Err(Error::SizeMismatchCurve);
|
||
}
|
||
true
|
||
}
|
||
};
|
||
let mode = WorkMode::PointMultiMode;
|
||
|
||
let mut tmp = [0_u8; 32];
|
||
self.reverse_words(k, &mut tmp);
|
||
self.alignment_helper
|
||
.volatile_write_regset(self.ecc.k_mem(0).as_ptr(), tmp.as_ref(), 8);
|
||
self.reverse_words(x, &mut tmp);
|
||
self.alignment_helper
|
||
.volatile_write_regset(self.ecc.px_mem(0).as_ptr(), tmp.as_ref(), 8);
|
||
self.reverse_words(y, &mut tmp);
|
||
self.alignment_helper
|
||
.volatile_write_regset(self.ecc.py_mem(0).as_ptr(), tmp.as_ref(), 8);
|
||
|
||
self.ecc.mult_conf().write(|w| unsafe {
|
||
w.work_mode()
|
||
.bits(mode as u8)
|
||
.key_length()
|
||
.bit(curve)
|
||
.start()
|
||
.set_bit()
|
||
});
|
||
|
||
// wait for interrupt
|
||
while self.is_busy() {}
|
||
|
||
self.alignment_helper
|
||
.volatile_read_regset(self.ecc.px_mem(0).as_ptr(), &mut tmp, 8);
|
||
self.reverse_words(tmp.as_ref(), x);
|
||
self.alignment_helper
|
||
.volatile_read_regset(self.ecc.py_mem(0).as_ptr(), &mut tmp, 8);
|
||
self.reverse_words(tmp.as_ref(), y);
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// # Finite Field Division
|
||
///
|
||
/// Finite Field Division can be represented as:
|
||
/// Result = P_y * k^{−1} mod p
|
||
///
|
||
/// Output is stored in `y`.
|
||
///
|
||
/// # Error
|
||
///
|
||
/// This function will return an error if any bitlength value is different
|
||
/// from the bitlength of the prime fields of the curve.
|
||
#[cfg(esp32c2)]
|
||
pub fn finite_field_division(
|
||
&mut self,
|
||
curve: &EllipticCurve,
|
||
k: &[u8],
|
||
y: &mut [u8],
|
||
) -> Result<(), Error> {
|
||
let curve = match curve {
|
||
EllipticCurve::P192 => {
|
||
if k.len() != 24 || y.len() != 24 {
|
||
return Err(Error::SizeMismatchCurve);
|
||
}
|
||
false
|
||
}
|
||
EllipticCurve::P256 => {
|
||
if k.len() != 32 || y.len() != 32 {
|
||
return Err(Error::SizeMismatchCurve);
|
||
}
|
||
true
|
||
}
|
||
};
|
||
let mode = WorkMode::DivisionMode;
|
||
|
||
let mut tmp = [0_u8; 32];
|
||
self.reverse_words(k, &mut tmp);
|
||
self.alignment_helper
|
||
.volatile_write_regset(self.ecc.k_mem(0).as_ptr(), tmp.as_ref(), 8);
|
||
self.reverse_words(y, &mut tmp);
|
||
self.alignment_helper
|
||
.volatile_write_regset(self.ecc.py_mem(0).as_ptr(), tmp.as_ref(), 8);
|
||
|
||
self.ecc.mult_conf().write(|w| unsafe {
|
||
w.work_mode()
|
||
.bits(mode as u8)
|
||
.key_length()
|
||
.bit(curve)
|
||
.start()
|
||
.set_bit()
|
||
});
|
||
|
||
// wait for interrupt
|
||
while self.is_busy() {}
|
||
|
||
self.alignment_helper
|
||
.volatile_read_regset(self.ecc.py_mem(0).as_ptr(), &mut tmp, 8);
|
||
self.reverse_words(tmp.as_ref(), y);
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// # Base Point Verification
|
||
///
|
||
/// Base Point Verification can be used to verify if a point (Px, Py) is
|
||
/// on a selected elliptic curve.
|
||
///
|
||
/// # Error
|
||
///
|
||
/// This function will return an error if any bitlength value is different
|
||
/// from the bitlength of the prime fields of the curve.
|
||
///
|
||
/// This function will return an error if the point is not on the selected
|
||
/// elliptic curve.
|
||
pub fn affine_point_verification(
|
||
&mut self,
|
||
curve: &EllipticCurve,
|
||
x: &[u8],
|
||
y: &[u8],
|
||
) -> Result<(), Error> {
|
||
let curve = match curve {
|
||
EllipticCurve::P192 => {
|
||
if x.len() != 24 || y.len() != 24 {
|
||
return Err(Error::SizeMismatchCurve);
|
||
}
|
||
false
|
||
}
|
||
EllipticCurve::P256 => {
|
||
if x.len() != 32 || y.len() != 32 {
|
||
return Err(Error::SizeMismatchCurve);
|
||
}
|
||
true
|
||
}
|
||
};
|
||
let mode = WorkMode::PointVerif;
|
||
|
||
let mut tmp = [0_u8; 32];
|
||
self.reverse_words(x, &mut tmp);
|
||
self.alignment_helper
|
||
.volatile_write_regset(self.ecc.px_mem(0).as_ptr(), tmp.as_ref(), 8);
|
||
self.reverse_words(y, &mut tmp);
|
||
self.alignment_helper
|
||
.volatile_write_regset(self.ecc.py_mem(0).as_ptr(), tmp.as_ref(), 8);
|
||
|
||
self.ecc.mult_conf().write(|w| unsafe {
|
||
w.work_mode()
|
||
.bits(mode as u8)
|
||
.key_length()
|
||
.bit(curve)
|
||
.start()
|
||
.set_bit()
|
||
});
|
||
|
||
// wait for interrupt
|
||
while self.is_busy() {}
|
||
|
||
if !self.ecc.mult_conf().read().verification_result().bit() {
|
||
self.ecc.mult_conf().reset();
|
||
return Err(Error::PointNotOnSelectedCurve);
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// # Base Point Verification + Base Point Multiplication
|
||
///
|
||
/// In this working mode, ECC first verifies if Point (P_x, P_y) is on the
|
||
/// selected elliptic curve or not. If yes, then perform the multiplication:
|
||
/// (Q_x, Q_y) = k * (P_x, P_y)
|
||
///
|
||
/// Output is stored in `x` and `y`.
|
||
///
|
||
/// # Error
|
||
///
|
||
/// This function will return an error if any bitlength value is different
|
||
/// from the bitlength of the prime fields of the curve.
|
||
///
|
||
/// This function will return an error if the point is not on the selected
|
||
/// elliptic curve.
|
||
#[cfg(not(esp32h2))]
|
||
pub fn affine_point_verification_multiplication(
|
||
&mut self,
|
||
curve: &EllipticCurve,
|
||
k: &[u8],
|
||
x: &mut [u8],
|
||
y: &mut [u8],
|
||
) -> Result<(), Error> {
|
||
let curve = match curve {
|
||
EllipticCurve::P192 => {
|
||
if k.len() != 24 || x.len() != 24 || y.len() != 24 {
|
||
return Err(Error::SizeMismatchCurve);
|
||
}
|
||
false
|
||
}
|
||
EllipticCurve::P256 => {
|
||
if k.len() != 32 || x.len() != 32 || y.len() != 32 {
|
||
return Err(Error::SizeMismatchCurve);
|
||
}
|
||
true
|
||
}
|
||
};
|
||
let mode = WorkMode::PointVerifMulti;
|
||
|
||
let mut tmp = [0_u8; 32];
|
||
self.reverse_words(k, &mut tmp);
|
||
self.alignment_helper
|
||
.volatile_write_regset(self.ecc.k_mem(0).as_ptr(), tmp.as_ref(), 8);
|
||
self.reverse_words(x, &mut tmp);
|
||
self.alignment_helper
|
||
.volatile_write_regset(self.ecc.px_mem(0).as_ptr(), tmp.as_ref(), 8);
|
||
self.reverse_words(y, &mut tmp);
|
||
self.alignment_helper
|
||
.volatile_write_regset(self.ecc.py_mem(0).as_ptr(), tmp.as_ref(), 8);
|
||
|
||
self.ecc.mult_conf().write(|w| unsafe {
|
||
w.work_mode()
|
||
.bits(mode as u8)
|
||
.key_length()
|
||
.bit(curve)
|
||
.start()
|
||
.set_bit()
|
||
});
|
||
|
||
// wait for interrupt
|
||
while self.is_busy() {}
|
||
|
||
if !self.ecc.mult_conf().read().verification_result().bit() {
|
||
self.ecc.mult_conf().reset();
|
||
return Err(Error::PointNotOnSelectedCurve);
|
||
}
|
||
|
||
self.alignment_helper
|
||
.volatile_read_regset(self.ecc.px_mem(0).as_ptr(), &mut tmp, 8);
|
||
self.reverse_words(tmp.as_ref(), x);
|
||
self.alignment_helper
|
||
.volatile_read_regset(self.ecc.py_mem(0).as_ptr(), &mut tmp, 8);
|
||
self.reverse_words(tmp.as_ref(), y);
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// # Base Point Verification + Base Point Multiplication
|
||
///
|
||
/// In this working mode, ECC first verifies if Point (P_x, P_y) is on the
|
||
/// selected elliptic curve or not. If yes, then perform the multiplication:
|
||
/// (Q_x, Q_y) = (J_x, J_y, J_z) = k * (P_x, P_y)
|
||
///
|
||
/// The affine point representation output is stored in `px` and `py`.
|
||
/// The Jacobian point representation output is stored in `qx`, `qy`, and
|
||
/// `qz`.
|
||
///
|
||
/// # Error
|
||
///
|
||
/// This function will return an error if any bitlength value is different
|
||
/// from the bitlength of the prime fields of the curve.
|
||
///
|
||
/// This function will return an error if the point is not on the selected
|
||
/// elliptic curve.
|
||
#[allow(clippy::too_many_arguments)]
|
||
#[cfg(esp32h2)]
|
||
pub fn affine_point_verification_multiplication(
|
||
&mut self,
|
||
curve: &EllipticCurve,
|
||
k: &[u8],
|
||
px: &mut [u8],
|
||
py: &mut [u8],
|
||
qx: &mut [u8],
|
||
qy: &mut [u8],
|
||
qz: &mut [u8],
|
||
) -> Result<(), Error> {
|
||
let curve = match curve {
|
||
EllipticCurve::P192 => {
|
||
if k.len() != 24 || px.len() != 24 || py.len() != 24 {
|
||
return Err(Error::SizeMismatchCurve);
|
||
}
|
||
false
|
||
}
|
||
EllipticCurve::P256 => {
|
||
if k.len() != 32 || px.len() != 32 || py.len() != 32 {
|
||
return Err(Error::SizeMismatchCurve);
|
||
}
|
||
true
|
||
}
|
||
};
|
||
let mode = WorkMode::PointVerifMulti;
|
||
|
||
let mut tmp = [0_u8; 32];
|
||
self.reverse_words(k, &mut tmp);
|
||
self.alignment_helper
|
||
.volatile_write_regset(self.ecc.k_mem(0).as_ptr(), tmp.as_ref(), 8);
|
||
self.reverse_words(px, &mut tmp);
|
||
self.alignment_helper
|
||
.volatile_write_regset(self.ecc.px_mem(0).as_ptr(), tmp.as_ref(), 8);
|
||
self.reverse_words(py, &mut tmp);
|
||
self.alignment_helper
|
||
.volatile_write_regset(self.ecc.py_mem(0).as_ptr(), tmp.as_ref(), 8);
|
||
|
||
self.ecc.mult_conf().write(|w| unsafe {
|
||
w.work_mode()
|
||
.bits(mode as u8)
|
||
.key_length()
|
||
.bit(curve)
|
||
.start()
|
||
.set_bit()
|
||
});
|
||
|
||
// wait for interrupt
|
||
while self.is_busy() {}
|
||
|
||
if !self.ecc.mult_conf().read().verification_result().bit() {
|
||
self.ecc.mult_conf().reset();
|
||
return Err(Error::PointNotOnSelectedCurve);
|
||
}
|
||
|
||
self.alignment_helper
|
||
.volatile_read_regset(self.ecc.px_mem(0).as_ptr(), &mut tmp, 8);
|
||
self.reverse_words(tmp.as_ref(), px);
|
||
self.alignment_helper
|
||
.volatile_read_regset(self.ecc.py_mem(0).as_ptr(), &mut tmp, 8);
|
||
self.reverse_words(tmp.as_ref(), py);
|
||
self.alignment_helper
|
||
.volatile_read_regset(self.ecc.qx_mem(0).as_ptr(), &mut tmp, 8);
|
||
self.reverse_words(tmp.as_ref(), qx);
|
||
self.alignment_helper
|
||
.volatile_read_regset(self.ecc.qy_mem(0).as_ptr(), &mut tmp, 8);
|
||
self.reverse_words(tmp.as_ref(), qy);
|
||
self.alignment_helper
|
||
.volatile_read_regset(self.ecc.qz_mem(0).as_ptr(), &mut tmp, 8);
|
||
self.reverse_words(tmp.as_ref(), qz);
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// # Jacobian Point Multiplication
|
||
///
|
||
/// Jacobian Point Multiplication can be represented as:
|
||
/// (Q_x, Q_y, Q_z) = k * (P_x, P_y, 1)
|
||
///
|
||
/// Output is stored in `x`, `y`, and `k`.
|
||
///
|
||
/// # Error
|
||
///
|
||
/// This function will return an error if any bitlength value is different
|
||
/// from the bitlength of the prime fields of the curve.
|
||
pub fn jacobian_point_multiplication(
|
||
&mut self,
|
||
curve: &EllipticCurve,
|
||
k: &mut [u8],
|
||
x: &mut [u8],
|
||
y: &mut [u8],
|
||
) -> Result<(), Error> {
|
||
let curve = match curve {
|
||
EllipticCurve::P192 => {
|
||
if k.len() != 24 || x.len() != 24 || y.len() != 24 {
|
||
return Err(Error::SizeMismatchCurve);
|
||
}
|
||
false
|
||
}
|
||
EllipticCurve::P256 => {
|
||
if k.len() != 32 || x.len() != 32 || y.len() != 32 {
|
||
return Err(Error::SizeMismatchCurve);
|
||
}
|
||
true
|
||
}
|
||
};
|
||
let mode = WorkMode::JacobianPointMulti;
|
||
|
||
let mut tmp = [0_u8; 32];
|
||
self.reverse_words(k, &mut tmp);
|
||
self.alignment_helper
|
||
.volatile_write_regset(self.ecc.k_mem(0).as_ptr(), tmp.as_ref(), 8);
|
||
self.reverse_words(x, &mut tmp);
|
||
self.alignment_helper
|
||
.volatile_write_regset(self.ecc.px_mem(0).as_ptr(), tmp.as_ref(), 8);
|
||
self.reverse_words(y, &mut tmp);
|
||
self.alignment_helper
|
||
.volatile_write_regset(self.ecc.py_mem(0).as_ptr(), tmp.as_ref(), 8);
|
||
|
||
self.ecc.mult_conf().write(|w| unsafe {
|
||
w.work_mode()
|
||
.bits(mode as u8)
|
||
.key_length()
|
||
.bit(curve)
|
||
.start()
|
||
.set_bit()
|
||
});
|
||
|
||
while self.is_busy() {}
|
||
|
||
cfg_if::cfg_if! {
|
||
if #[cfg(not(esp32h2))] {
|
||
self.alignment_helper
|
||
.volatile_read_regset(self.ecc.px_mem(0).as_ptr(), &mut tmp, 8);
|
||
self.reverse_words(tmp.as_ref(), x);
|
||
self.alignment_helper
|
||
.volatile_read_regset(self.ecc.py_mem(0).as_ptr(), &mut tmp, 8);
|
||
self.reverse_words(tmp.as_ref(), y);
|
||
self.alignment_helper
|
||
.volatile_read_regset(self.ecc.k_mem(0).as_ptr(), &mut tmp, 8);
|
||
self.reverse_words(tmp.as_ref(), k);
|
||
} else {
|
||
self.alignment_helper
|
||
.volatile_read_regset(self.ecc.qx_mem(0).as_ptr(), &mut tmp, 8);
|
||
self.reverse_words(tmp.as_ref(), x);
|
||
self.alignment_helper
|
||
.volatile_read_regset(self.ecc.qy_mem(0).as_ptr(), &mut tmp, 8);
|
||
self.reverse_words(tmp.as_ref(), y);
|
||
self.alignment_helper
|
||
.volatile_read_regset(self.ecc.qz_mem(0).as_ptr(), &mut tmp, 8);
|
||
self.reverse_words(tmp.as_ref(), k);
|
||
}
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// # Jacobian Point Verification
|
||
///
|
||
/// Jacobian Point Verification can be used to verify if a point (Q_x, Q_y,
|
||
/// Q_z) is on a selected elliptic curve.
|
||
///
|
||
/// # Error
|
||
///
|
||
/// This function will return an error if any bitlength value is different
|
||
/// from the bitlength of the prime fields of the curve.
|
||
///
|
||
/// This function will return an error if the point is not on the selected
|
||
/// elliptic curve.
|
||
pub fn jacobian_point_verification(
|
||
&mut self,
|
||
curve: &EllipticCurve,
|
||
x: &[u8],
|
||
y: &[u8],
|
||
z: &[u8],
|
||
) -> Result<(), Error> {
|
||
let curve = match curve {
|
||
EllipticCurve::P192 => {
|
||
if x.len() != 24 || y.len() != 24 || z.len() != 24 {
|
||
return Err(Error::SizeMismatchCurve);
|
||
}
|
||
false
|
||
}
|
||
EllipticCurve::P256 => {
|
||
if x.len() != 32 || y.len() != 32 || z.len() != 32 {
|
||
return Err(Error::SizeMismatchCurve);
|
||
}
|
||
true
|
||
}
|
||
};
|
||
let mode = WorkMode::JacobianPointVerif;
|
||
|
||
let mut tmp = [0_u8; 32];
|
||
self.reverse_words(x, &mut tmp);
|
||
|
||
cfg_if::cfg_if! {
|
||
if #[cfg(not(esp32h2))] {
|
||
self.alignment_helper
|
||
.volatile_write_regset(self.ecc.px_mem(0).as_ptr(), tmp.as_ref(), 8);
|
||
self.reverse_words(y, &mut tmp);
|
||
self.alignment_helper
|
||
.volatile_write_regset(self.ecc.py_mem(0).as_ptr(), tmp.as_ref(), 8);
|
||
self.reverse_words(z, &mut tmp);
|
||
self.alignment_helper
|
||
.volatile_write_regset(self.ecc.k_mem(0).as_ptr(), tmp.as_ref(), 8);
|
||
} else {
|
||
self.alignment_helper
|
||
.volatile_write_regset(self.ecc.qx_mem(0).as_ptr(), tmp.as_ref(), 8);
|
||
self.reverse_words(y, &mut tmp);
|
||
self.alignment_helper
|
||
.volatile_write_regset(self.ecc.qy_mem(0).as_ptr(), tmp.as_ref(), 8);
|
||
self.reverse_words(z, &mut tmp);
|
||
self.alignment_helper
|
||
.volatile_write_regset(self.ecc.qz_mem(0).as_ptr(), tmp.as_ref(), 8);
|
||
}
|
||
}
|
||
|
||
self.ecc.mult_conf().write(|w| unsafe {
|
||
w.work_mode()
|
||
.bits(mode as u8)
|
||
.key_length()
|
||
.bit(curve)
|
||
.start()
|
||
.set_bit()
|
||
});
|
||
|
||
// wait for interrupt
|
||
while self.is_busy() {}
|
||
|
||
if !self.ecc.mult_conf().read().verification_result().bit() {
|
||
self.ecc.mult_conf().reset();
|
||
return Err(Error::PointNotOnSelectedCurve);
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// # Base Point Verification + Jacobian Point Multiplication
|
||
///
|
||
/// In this working mode, ECC first verifies if Point (Px, Py) is on the
|
||
/// selected elliptic curve or not. If yes, then perform the multiplication:
|
||
/// (Q_x, Q_y, Q_z) = k * (P_x, P_y, 1)
|
||
///
|
||
/// Output is stored in `x`, `y`, and `k`.
|
||
///
|
||
/// # Error
|
||
///
|
||
/// This function will return an error if any bitlength value is different
|
||
/// from the bitlength of the prime fields of the curve.
|
||
///
|
||
/// This function will return an error if the point is not on the selected
|
||
/// elliptic curve.
|
||
pub fn affine_point_verification_jacobian_multiplication(
|
||
&mut self,
|
||
curve: &EllipticCurve,
|
||
k: &mut [u8],
|
||
x: &mut [u8],
|
||
y: &mut [u8],
|
||
) -> Result<(), Error> {
|
||
let curve = match curve {
|
||
EllipticCurve::P192 => {
|
||
if k.len() != 24 || x.len() != 24 || y.len() != 24 {
|
||
return Err(Error::SizeMismatchCurve);
|
||
}
|
||
false
|
||
}
|
||
EllipticCurve::P256 => {
|
||
if k.len() != 32 || x.len() != 32 || y.len() != 32 {
|
||
return Err(Error::SizeMismatchCurve);
|
||
}
|
||
true
|
||
}
|
||
};
|
||
let mode = WorkMode::PointVerifJacobianMulti;
|
||
|
||
let mut tmp = [0_u8; 32];
|
||
self.reverse_words(k, &mut tmp);
|
||
self.alignment_helper
|
||
.volatile_write_regset(self.ecc.k_mem(0).as_ptr(), tmp.as_ref(), 8);
|
||
self.reverse_words(x, &mut tmp);
|
||
self.alignment_helper
|
||
.volatile_write_regset(self.ecc.px_mem(0).as_ptr(), tmp.as_ref(), 8);
|
||
self.reverse_words(y, &mut tmp);
|
||
self.alignment_helper
|
||
.volatile_write_regset(self.ecc.py_mem(0).as_ptr(), tmp.as_ref(), 8);
|
||
|
||
self.ecc.mult_conf().write(|w| unsafe {
|
||
w.work_mode()
|
||
.bits(mode as u8)
|
||
.key_length()
|
||
.bit(curve)
|
||
.start()
|
||
.set_bit()
|
||
});
|
||
|
||
// wait for interrupt
|
||
while self.is_busy() {}
|
||
|
||
if !self.ecc.mult_conf().read().verification_result().bit() {
|
||
self.ecc.mult_conf().reset();
|
||
return Err(Error::PointNotOnSelectedCurve);
|
||
}
|
||
|
||
if !self.ecc.mult_conf().read().verification_result().bit() {
|
||
self.ecc.mult_conf().reset();
|
||
return Err(Error::PointNotOnSelectedCurve);
|
||
}
|
||
|
||
cfg_if::cfg_if! {
|
||
if #[cfg(not(esp32h2))] {
|
||
self.alignment_helper
|
||
.volatile_read_regset(self.ecc.px_mem(0).as_ptr(), &mut tmp, 8);
|
||
self.reverse_words(tmp.as_ref(), x);
|
||
self.alignment_helper
|
||
.volatile_read_regset(self.ecc.py_mem(0).as_ptr(), &mut tmp, 8);
|
||
self.reverse_words(tmp.as_ref(), y);
|
||
self.alignment_helper
|
||
.volatile_read_regset(self.ecc.k_mem(0).as_ptr(), &mut tmp, 8);
|
||
self.reverse_words(tmp.as_ref(), k);
|
||
} else {
|
||
self.alignment_helper
|
||
.volatile_read_regset(self.ecc.qx_mem(0).as_ptr(), &mut tmp, 8);
|
||
self.reverse_words(tmp.as_ref(), x);
|
||
self.alignment_helper
|
||
.volatile_read_regset(self.ecc.qy_mem(0).as_ptr(), &mut tmp, 8);
|
||
self.reverse_words(tmp.as_ref(), y);
|
||
self.alignment_helper
|
||
.volatile_read_regset(self.ecc.qz_mem(0).as_ptr(), &mut tmp, 8);
|
||
self.reverse_words(tmp.as_ref(), k);
|
||
}
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// # Point Addition
|
||
///
|
||
/// In this working mode, ECC first verifies if Point (Px, Py) is on the
|
||
/// selected elliptic curve or not. If yes, then perform the addition:
|
||
/// (R_x, R_y) = (J_x, J_y, J_z) = (P_x, P_y, 1) + (Q_x, Q_y, Q_z)
|
||
///
|
||
/// This functions requires data in Little Endian.
|
||
/// The affine point representation output is stored in `px` and `py`.
|
||
/// The Jacobian point representation output is stored in `qx`, `qy`, and
|
||
/// `qz`.
|
||
///
|
||
/// # Error
|
||
///
|
||
/// This function will return an error if any bitlength value is different
|
||
/// from the bitlength of the prime fields of the curve.
|
||
///
|
||
/// This function will return an error if the point is not on the selected
|
||
/// elliptic curve.
|
||
#[cfg(esp32h2)]
|
||
pub fn affine_point_addition(
|
||
&mut self,
|
||
curve: &EllipticCurve,
|
||
px: &mut [u8],
|
||
py: &mut [u8],
|
||
qx: &mut [u8],
|
||
qy: &mut [u8],
|
||
qz: &mut [u8],
|
||
) -> Result<(), Error> {
|
||
let curve = match curve {
|
||
EllipticCurve::P192 => {
|
||
if px.len() != 24
|
||
|| py.len() != 24
|
||
|| qx.len() != 24
|
||
|| qy.len() != 24
|
||
|| qz.len() != 24
|
||
{
|
||
return Err(Error::SizeMismatchCurve);
|
||
}
|
||
false
|
||
}
|
||
EllipticCurve::P256 => {
|
||
if px.len() != 32
|
||
|| py.len() != 32
|
||
|| qx.len() != 32
|
||
|| qy.len() != 32
|
||
|| qz.len() != 32
|
||
{
|
||
return Err(Error::SizeMismatchCurve);
|
||
}
|
||
true
|
||
}
|
||
};
|
||
let mode = WorkMode::PointAdd;
|
||
|
||
let mut tmp = [0_u8; 32];
|
||
|
||
tmp[0..px.len()].copy_from_slice(px);
|
||
self.alignment_helper
|
||
.volatile_write_regset(self.ecc.px_mem(0).as_ptr(), &tmp, 8);
|
||
tmp[0..py.len()].copy_from_slice(py);
|
||
self.alignment_helper
|
||
.volatile_write_regset(self.ecc.py_mem(0).as_ptr(), &tmp, 8);
|
||
tmp[0..qx.len()].copy_from_slice(qx);
|
||
self.alignment_helper
|
||
.volatile_write_regset(self.ecc.qx_mem(0).as_ptr(), &tmp, 8);
|
||
tmp[0..qy.len()].copy_from_slice(qy);
|
||
self.alignment_helper
|
||
.volatile_write_regset(self.ecc.qy_mem(0).as_ptr(), &tmp, 8);
|
||
tmp[0..qz.len()].copy_from_slice(qz);
|
||
self.alignment_helper
|
||
.volatile_write_regset(self.ecc.qz_mem(0).as_ptr(), &tmp, 8);
|
||
|
||
self.ecc.mult_conf().write(|w| unsafe {
|
||
w.work_mode()
|
||
.bits(mode as u8)
|
||
.key_length()
|
||
.bit(curve)
|
||
.start()
|
||
.set_bit()
|
||
});
|
||
|
||
// wait for interrupt
|
||
while self.is_busy() {}
|
||
|
||
self.alignment_helper
|
||
.volatile_read_regset(self.ecc.px_mem(0).as_ptr(), &mut tmp, 8);
|
||
let mut tmp_len = px.len();
|
||
px[..].copy_from_slice(&tmp[..tmp_len]);
|
||
self.alignment_helper
|
||
.volatile_read_regset(self.ecc.py_mem(0).as_ptr(), &mut tmp, 8);
|
||
tmp_len = py.len();
|
||
py[..].copy_from_slice(&tmp[..tmp_len]);
|
||
self.alignment_helper
|
||
.volatile_read_regset(self.ecc.qx_mem(0).as_ptr(), &mut tmp, 8);
|
||
tmp_len = qx.len();
|
||
qx[..].copy_from_slice(&tmp[..tmp_len]);
|
||
self.alignment_helper
|
||
.volatile_read_regset(self.ecc.qy_mem(0).as_ptr(), &mut tmp, 8);
|
||
tmp_len = qy.len();
|
||
qy[..].copy_from_slice(&tmp[..tmp_len]);
|
||
self.alignment_helper
|
||
.volatile_read_regset(self.ecc.qz_mem(0).as_ptr(), &mut tmp, 8);
|
||
tmp_len = qz.len();
|
||
qz[..].copy_from_slice(&tmp[..tmp_len]);
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// # Mod Operations (+-*/)
|
||
///
|
||
/// In this working mode, ECC first verifies if Point (A, B) is on the
|
||
/// selected elliptic curve or not. If yes, then perform single mod
|
||
/// operation: R = A (+-*/) B mod N
|
||
///
|
||
/// This functions requires data in Little Endian.
|
||
/// Output is stored in `a` (+-) and in `b` (*/).
|
||
///
|
||
/// # Error
|
||
///
|
||
/// This function will return an error if any bitlength value is different
|
||
/// from the bitlength of the prime fields of the curve.
|
||
///
|
||
/// This function will return an error if the point is not on the selected
|
||
/// elliptic curve.
|
||
#[cfg(esp32h2)]
|
||
pub fn mod_operations(
|
||
&mut self,
|
||
curve: &EllipticCurve,
|
||
a: &mut [u8],
|
||
b: &mut [u8],
|
||
work_mode: WorkMode,
|
||
) -> Result<(), Error> {
|
||
let curve = match curve {
|
||
EllipticCurve::P192 => {
|
||
if a.len() != 24 || b.len() != 24 {
|
||
return Err(Error::SizeMismatchCurve);
|
||
}
|
||
false
|
||
}
|
||
EllipticCurve::P256 => {
|
||
if a.len() != 32 || b.len() != 32 {
|
||
return Err(Error::SizeMismatchCurve);
|
||
}
|
||
true
|
||
}
|
||
};
|
||
|
||
let mut tmp = [0_u8; 32];
|
||
tmp[0..a.len()].copy_from_slice(a);
|
||
self.alignment_helper
|
||
.volatile_write_regset(self.ecc.px_mem(0).as_ptr(), &tmp, 8);
|
||
tmp[0..b.len()].copy_from_slice(b);
|
||
self.alignment_helper
|
||
.volatile_write_regset(self.ecc.py_mem(0).as_ptr(), &tmp, 8);
|
||
|
||
self.ecc.mult_conf().write(|w| unsafe {
|
||
w.work_mode()
|
||
.bits(work_mode.clone() as u8)
|
||
.key_length()
|
||
.bit(curve)
|
||
.start()
|
||
.set_bit()
|
||
});
|
||
|
||
// wait for interrupt
|
||
while self.is_busy() {}
|
||
|
||
match work_mode {
|
||
WorkMode::ModAdd | WorkMode::ModSub => {
|
||
self.alignment_helper.volatile_read_regset(
|
||
self.ecc.px_mem(0).as_ptr(),
|
||
&mut tmp,
|
||
8,
|
||
);
|
||
let tmp_len = a.len();
|
||
a[..].copy_from_slice(&tmp[..tmp_len]);
|
||
}
|
||
WorkMode::ModMulti | WorkMode::ModDiv => {
|
||
self.alignment_helper.volatile_read_regset(
|
||
self.ecc.py_mem(0).as_ptr(),
|
||
&mut tmp,
|
||
8,
|
||
);
|
||
let tmp_len = b.len();
|
||
b[..].copy_from_slice(&tmp[..tmp_len]);
|
||
}
|
||
_ => unreachable!(),
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
fn is_busy(&self) -> bool {
|
||
self.ecc.mult_conf().read().start().bit_is_set()
|
||
}
|
||
|
||
fn reverse_words(&self, src: &[u8], dst: &mut [u8]) {
|
||
let n = core::cmp::min(src.len(), dst.len());
|
||
let nsrc = if src.len() > n {
|
||
src.split_at(n).0
|
||
} else {
|
||
src
|
||
};
|
||
let ndst = if dst.len() > n {
|
||
dst.split_at_mut(n).0
|
||
} else {
|
||
dst
|
||
};
|
||
for (a, b) in nsrc.chunks_exact(4).zip(ndst.rchunks_exact_mut(4)) {
|
||
b.copy_from_slice(&u32::from_be_bytes(a.try_into().unwrap()).to_ne_bytes());
|
||
}
|
||
}
|
||
}
|