Skip to content
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Add initial support for `I2S` in ESP32-H2 (#597)
- Fix rom::crc docs
- Add octal PSRAM support for ESP32-S3 (#610)
- Add MD5 functions from ESP ROM (#618)

### Changed

Expand Down
1 change: 1 addition & 0 deletions esp-hal-common/devices/esp32.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ peripherals = [
# ROM capabilities
"rom_crc_le",
"rom_crc_be",
"rom_md5_bsd",

# Wakeup SOC based on ESP-IDF:
"pm_support_ext0_wakeup",
Expand Down
1 change: 1 addition & 0 deletions esp-hal-common/devices/esp32c2.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ peripherals = [
# ROM capabilities
"rom_crc_le",
"rom_crc_be",
"rom_md5_mbedtls",

# Wakeup SOC based on ESP-IDF:
"pm_support_wifi_wakeup",
Expand Down
1 change: 1 addition & 0 deletions esp-hal-common/devices/esp32c3.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ peripherals = [
# ROM capabilities
"rom_crc_le",
"rom_crc_be",
"rom_md5_bsd",

# Wakeup SOC based on ESP-IDF:
"pm_support_wifi_wakeup",
Expand Down
1 change: 1 addition & 0 deletions esp-hal-common/devices/esp32c6.toml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ peripherals = [
# ROM capabilities
"rom_crc_le",
"rom_crc_be",
"rom_md5_bsd",

# Wakeup SOC based on ESP-IDF:
"pm_support_wifi_wakeup",
Expand Down
1 change: 1 addition & 0 deletions esp-hal-common/devices/esp32h2.toml
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,5 @@ peripherals = [
# ROM capabilities
"rom_crc_le",
"rom_crc_be",
"rom_md5_bsd",
]
1 change: 1 addition & 0 deletions esp-hal-common/devices/esp32s2.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ peripherals = [

# ROM capabilities
"rom_crc_le",
"rom_md5_bsd",

# Wakeup SOC based on ESP-IDF:
"pm_support_ext0_wakeup",
Expand Down
1 change: 1 addition & 0 deletions esp-hal-common/devices/esp32s3.toml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ peripherals = [
# ROM capabilities
"rom_crc_le",
"rom_crc_be",
"rom_md5_bsd",

# Wakeup SOC based on ESP-IDF:
"pm_support_ext0_wakeup",
Expand Down
232 changes: 232 additions & 0 deletions esp-hal-common/src/rom/md5.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
//! MD5 Message-Digest Algorithm
//!
//! # Security Warning
//!
//! MD5 is a **cryptographically broken** message digest. It should **not**
//! be used for data security, for example, to check if data has been
//! intentionally tampered with.
//!
//! However, it is still very useful for purposes of data **integrity**, for
//! example, to check if data has been **accidentally** tampered with, such as
//! detecting data corrupted during transmission in a stream or stored in
//! flash. This is especially important on microcontrollers where the
//! computational efficiency of MD5 is desired.
//!
//! # Compatibility
//!
//! The public API exposed by this module tries to *mimic* the public API
//! offered by the [MD5][1] crate in an effort to act as a drop-in replacement.
//! The actual implementation, however, links to whatever is available in the
//! ROM of the particular ESP32 target. Each chip target may offer a different
//! underlying MD5 implementation with varying functionality.
//!
//! This module offers a least-common-denominator API to stay consistent with
//! all of them. Usage of this module may help make program binaries smaller
//! than it would be if you included an MD5 implementation in your project.
//!
//! # Example
//!
//! To compute a full digest from a single buffer, use the following:
//!
//! ```
//! let d: md5::Digest = md5::compute(&data);
//! writeln!(uart0, "{}", d);
//! ```
//!
//! To compute a digest over multiple buffers:
//!
//! ```
//! let mut ctx = md5::Context::new();
//! ctx.consume(&data0);
//! ctx.consume(&data1);
//! let d: md5::Digest = ctx.compute();
//! writeln!(uart0, "{}", d);
//! ```
//!
//! [1]: <https://crates.io/crates/md5>

#[allow(unused)]
use core::ffi::{c_int, c_uchar, c_void};
use core::{
convert::From,
fmt,
mem::MaybeUninit,
ops::{Deref, DerefMut},
};

// If there is not exactly one of the MD5 variations defined in the device
// toml file then `InternalContext` will be either undefined or multiple
// defined and this module will fail to compile letting you know to fix it

#[cfg(doc)]
struct InternalContext;

#[cfg(rom_md5_bsd)]
#[derive(Clone)]
#[repr(C)]
struct InternalContext {
buf: [u32; 4],
bits: [u32; 2],
_in: [u8; 64],
}

#[cfg(rom_md5_bsd)]
extern "C" {
fn esp_rom_md5_init(context: *mut InternalContext);
fn esp_rom_md5_update(context: *mut InternalContext, buf: *const c_void, len: u32);
fn esp_rom_md5_final(digest: *mut u8, context: *mut InternalContext);
}

#[cfg(rom_md5_mbedtls)]
#[derive(Clone)]
#[repr(C)]
struct InternalContext {
total: [u32; 2],
state: [u32; 4],
buffer: [c_uchar; 64],
}

#[cfg(rom_md5_mbedtls)]
extern "C" {
fn esp_rom_mbedtls_md5_starts_ret(context: *mut InternalContext) -> c_int;
fn esp_rom_mbedtls_md5_update_ret(
context: *mut InternalContext,
buf: *const c_void,
len: u32,
) -> c_int;
fn esp_rom_mbedtls_md5_finish_ret(context: *mut InternalContext, digest: *mut u8) -> c_int;
}

/// MD5 context for an ongoing computation
#[derive(Clone)]
pub struct Context(InternalContext);

impl Context {
/// Create a new MD5 context
#[inline]
pub fn new() -> Self {
let mut ctx = MaybeUninit::<InternalContext>::uninit();
unsafe {
#[cfg(rom_md5_bsd)]
esp_rom_md5_init(ctx.as_mut_ptr());

#[cfg(rom_md5_mbedtls)]
let _ = esp_rom_mbedtls_md5_starts_ret(ctx.as_mut_ptr());

Self(ctx.assume_init())
}
}

/// Feed data to the hasher
#[inline]
pub fn consume<T: AsRef<[u8]>>(&mut self, data: T) {
let data = data.as_ref();
unsafe {
#[cfg(rom_md5_bsd)]
esp_rom_md5_update(
&mut self.0 as *mut _,
data.as_ptr() as *const c_void,
data.len() as u32,
);

#[cfg(rom_md5_mbedtls)]
let _ = esp_rom_mbedtls_md5_update_ret(
&mut self.0 as *mut _,
data.as_ptr() as *const c_void,
data.len() as u32,
);
}
}

/// Finalize and return a digest
#[inline]
pub fn compute(mut self) -> Digest {
let mut digest = MaybeUninit::<[u8; 16]>::uninit();
unsafe {
#[cfg(rom_md5_bsd)]
esp_rom_md5_final(digest.as_mut_ptr() as *mut _, &mut self.0 as *mut _);

#[cfg(rom_md5_mbedtls)]
let _ = esp_rom_mbedtls_md5_finish_ret(
&mut self.0 as *mut _,
digest.as_mut_ptr() as *mut _,
);

Digest(digest.assume_init())
}
}
}

/// Compute a full digest from a single buffer
#[inline]
pub fn compute<T: AsRef<[u8]>>(data: T) -> Digest {
let mut ctx = Context::new();
ctx.consume(data);
ctx.compute()
}

/// 16-byte message digest
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct Digest(pub [u8; 16]);

impl fmt::LowerHex for Digest {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for &byte in &self.0 {
write!(f, "{:02x}", byte)?;
}
Ok(())
}
}

impl fmt::UpperHex for Digest {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for &byte in &self.0 {
write!(f, "{:02X}", byte)?;
}
Ok(())
}
}

impl fmt::Display for Digest {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
<Digest as fmt::LowerHex>::fmt(self, f)
}
}

impl fmt::Debug for Digest {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
<Digest as fmt::LowerHex>::fmt(self, f)
}
}

impl From<Digest> for [u8; 16] {
#[inline]
fn from(digest: Digest) -> Self {
digest.0
}
}

impl From<Context> for Digest {
#[inline]
fn from(context: Context) -> Digest {
context.compute()
}
}

impl Deref for Digest {
type Target = [u8; 16];

#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}

impl DerefMut for Digest {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
1 change: 1 addition & 0 deletions esp-hal-common/src/rom/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
//! read-only memory.

pub mod crc;
pub mod md5;

#[allow(unused)]
extern "C" {
Expand Down
35 changes: 32 additions & 3 deletions esp32-hal/examples/crc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use esp32_hal::{
clock::ClockControl,
peripherals::Peripherals,
prelude::*,
rom::crc,
rom::{crc, md5},
timer::TimerGroup,
Rtc,
Uart,
Expand Down Expand Up @@ -40,6 +40,7 @@ fn main() -> ! {
timer0.start(1u64.secs());

let data = "123456789";
let sentence = "The quick brown fox jumps over a lazy dog";

writeln!(
serial0,
Expand All @@ -66,10 +67,38 @@ fn main() -> ! {
assert_eq!(crc_rohc, 0xd0);
assert_eq!(crc_smbus, 0xf4);

// Hash the sentence one word at a time to *really* test the context
// Use Peekable while iter_intersperse is unstable
let mut md5_ctx = md5::Context::new();
let mut it = sentence.split_whitespace().peekable();
while let Some(word) = it.next() {
md5_ctx.consume(word);
if it.peek().is_some() {
md5_ctx.consume(" ");
}
}
let md5_digest = md5_ctx.compute();

assert_eq!(
md5_digest,
md5::Digest([
0x30, 0xde, 0xd8, 0x07, 0xd6, 0x5e, 0xe0, 0x37, 0x0f, 0xc6, 0xd7, 0x3d, 0x6a, 0xb5,
0x5a, 0x95
])
);

writeln!(
serial0,
"{:08x} {:08x} {:08x} {:08x} {:04x} {:04x} {:02x} {:02x}",
crc_hdlc, crc_bzip2, crc_mpeg2, crc_cksum, crc_kermit, crc_genibus, crc_rohc, crc_smbus
"{:08x} {:08x} {:08x} {:08x} {:04x} {:04x} {:02x} {:02x} {}",
crc_hdlc,
crc_bzip2,
crc_mpeg2,
crc_cksum,
crc_kermit,
crc_genibus,
crc_rohc,
crc_smbus,
md5_digest
)
.unwrap();

Expand Down
4 changes: 4 additions & 0 deletions esp32-hal/ld/rom-functions.x
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,7 @@ PROVIDE (esp_rom_crc8_be = 0x4005d114);
PROVIDE (esp_rom_crc32_le = 0x4005cfec);
PROVIDE (esp_rom_crc16_le = 0x4005d05c);
PROVIDE (esp_rom_crc8_le = 0x4005d0e0);

PROVIDE (esp_rom_md5_init = 0x4005da7c);
PROVIDE (esp_rom_md5_update = 0x4005da9c);
PROVIDE (esp_rom_md5_final = 0x4005db1c);
Loading