1//! # MD5 Message-Digest Algorithm (MD5)
2//!
3//! ## ⚠️ Security Warning ⚠️
4//!
5//! MD5 is a **cryptographically broken** message digest. It should **not**
6//! be used for data security, for example, to check if data has been
7//! intentionally tampered with.
8//!
9//! However, it is still very useful for purposes of data **integrity**, for
10//! example, to check if data has been **accidentally** tampered with, such as
11//! detecting data corrupted during transmission in a stream or stored in
12//! flash. This is especially important on microcontrollers where the
13//! computational efficiency of MD5 is desired.
14//!
15//! ## Compatibility
16//!
17//! The public API exposed by this module tries to *mimic* the public API
18//! offered by the [MD5][1] crate in an effort to act as a drop-in replacement.
19//! The actual implementation, however, links to whatever is available in the
20//! ROM of the particular ESP32 target. Each chip target may offer a different
21//! underlying MD5 implementation with varying functionality.
22//!
23//! This module offers a least-common-denominator API to stay consistent with
24//! all of them. Usage of this module may help make program binaries smaller
25//! than it would be if you included an MD5 implementation in your project.
26//!
27//! ## Examples
28//!
29//! ### Compute a Full Digest From a Single Buffer
30//!
31//! ```rust, no_run
32#![doc = crate::before_snippet!()]
33//! # use esp_hal::rom::md5;
34//! # let data = "Dummy";
35//! let d: md5::Digest = md5::compute(&data);
36//! println!("{}", d);
37//! # Ok(())
38//! # }
39//! ```
40//!
41//! ### Compute a Digest Over Multiple Buffers
42//! ```rust, no_run
43#![doc = crate::before_snippet!()]
44//! # use esp_hal::rom::md5;
45//! # let data0 = "Dummy";
46//! # let data1 = "Dummy";
47//! #
48//! let mut ctx = md5::Context::new();
49//! ctx.consume(&data0);
50//! ctx.consume(&data1);
51//! let d: md5::Digest = ctx.compute();
52//! println!("{}", d);
53//! # Ok(())
54//! # }
55//! ```
56//!
57//! [1]: <https://crates.io/crates/md5>
5859#[allow(unused)]
60use core::ffi::{c_int, c_uchar, c_void};
61use core::{
62 fmt,
63 mem::MaybeUninit,
64 ops::{Deref, DerefMut},
65};
6667// If there is not exactly one of the MD5 variations defined in the device
68// toml file then `InternalContext` will be either undefined or multiple
69// defined and this module will fail to compile letting you know to fix it
7071#[cfg(rom_md5_bsd)]
72#[derive(Clone)]
73#[repr(C)]
74struct InternalContext {
75 buf: [u32; 4],
76 bits: [u32; 2],
77 _in: [u8; 64],
78}
7980#[cfg(rom_md5_bsd)]
81extern "C" {
82fn esp_rom_md5_init(context: *mut InternalContext);
83fn esp_rom_md5_update(context: *mut InternalContext, buf: *const c_void, len: u32);
84fn esp_rom_md5_final(digest: *mut u8, context: *mut InternalContext);
85}
8687#[cfg(rom_md5_mbedtls)]
88#[derive(Clone)]
89#[repr(C)]
90struct InternalContext {
91 total: [u32; 2],
92 state: [u32; 4],
93 buffer: [c_uchar; 64],
94}
9596#[cfg(rom_md5_mbedtls)]
97extern "C" {
98fn esp_rom_mbedtls_md5_starts_ret(context: *mut InternalContext) -> c_int;
99fn esp_rom_mbedtls_md5_update_ret(
100 context: *mut InternalContext,
101 buf: *const c_void,
102 len: u32,
103 ) -> c_int;
104fn esp_rom_mbedtls_md5_finish_ret(context: *mut InternalContext, digest: *mut u8) -> c_int;
105}
106107/// MD5 context for an ongoing computation
108#[derive(Clone)]
109pub struct Context(InternalContext);
110111impl Context {
112/// Create a new MD5 context
113#[inline]
114pub fn new() -> Self {
115let mut ctx = MaybeUninit::<InternalContext>::uninit();
116unsafe {
117#[cfg(rom_md5_bsd)]
118esp_rom_md5_init(ctx.as_mut_ptr());
119120#[cfg(rom_md5_mbedtls)]
121let _ = esp_rom_mbedtls_md5_starts_ret(ctx.as_mut_ptr());
122123Self(ctx.assume_init())
124 }
125 }
126127/// Feed data to the hasher
128#[inline]
129pub fn consume<T: AsRef<[u8]>>(&mut self, data: T) {
130let data = data.as_ref();
131unsafe {
132#[cfg(rom_md5_bsd)]
133esp_rom_md5_update(
134&mut self.0 as *mut _,
135 data.as_ptr() as *const c_void,
136 data.len() as u32,
137 );
138139#[cfg(rom_md5_mbedtls)]
140let _ = esp_rom_mbedtls_md5_update_ret(
141&mut self.0 as *mut _,
142 data.as_ptr() as *const c_void,
143 data.len() as u32,
144 );
145 }
146 }
147148/// Finalize and return a digest
149#[inline]
150pub fn compute(mut self) -> Digest {
151let mut digest = MaybeUninit::<[u8; 16]>::uninit();
152unsafe {
153#[cfg(rom_md5_bsd)]
154esp_rom_md5_final(digest.as_mut_ptr() as *mut _, &mut self.0 as *mut _);
155156#[cfg(rom_md5_mbedtls)]
157let _ = esp_rom_mbedtls_md5_finish_ret(
158&mut self.0 as *mut _,
159 digest.as_mut_ptr() as *mut _,
160 );
161162 Digest(digest.assume_init())
163 }
164 }
165}
166167impl Default for Context {
168fn default() -> Self {
169Self::new()
170 }
171}
172173/// Compute a full digest from a single buffer
174#[inline]
175pub fn compute<T: AsRef<[u8]>>(data: T) -> Digest {
176let mut ctx = Context::new();
177 ctx.consume(data);
178 ctx.compute()
179}
180181/// 16-byte message digest
182#[derive(Clone, Copy, PartialEq, Eq, Hash)]
183pub struct Digest(pub [u8; 16]);
184185impl fmt::LowerHex for Digest {
186fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
187for &byte in &self.0 {
188write!(f, "{:02x}", byte)?;
189 }
190Ok(())
191 }
192}
193194impl fmt::UpperHex for Digest {
195fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
196for &byte in &self.0 {
197write!(f, "{:02X}", byte)?;
198 }
199Ok(())
200 }
201}
202203impl fmt::Display for Digest {
204#[inline]
205fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
206 <Digest as fmt::LowerHex>::fmt(self, f)
207 }
208}
209210impl fmt::Debug for Digest {
211#[inline]
212fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
213 <Digest as fmt::LowerHex>::fmt(self, f)
214 }
215}
216217impl From<Digest> for [u8; 16] {
218#[inline]
219fn from(digest: Digest) -> Self {
220 digest.0
221}
222}
223224impl From<Context> for Digest {
225#[inline]
226fn from(context: Context) -> Digest {
227 context.compute()
228 }
229}
230231impl Deref for Digest {
232type Target = [u8; 16];
233234#[inline]
235fn deref(&self) -> &Self::Target {
236&self.0
237}
238}
239240impl DerefMut for Digest {
241#[inline]
242fn deref_mut(&mut self) -> &mut Self::Target {
243&mut self.0
244}
245}