feat: add display UI with splash screen, dashboard, and RSSI sparkline

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>
This commit is contained in:
swaitsandClaude Opus 4.6 committed 2026-03-29 20:42:54 -06:00
1 parent 7064110293
commit 3102fc747a
17624 files changed
+4564928 -38

No files matched your search

+966
View File
@@ -0,0 +1,966 @@
//! # 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());
}
}
}