esp_hal/rom/
md5.rs

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>
58
59#[allow(unused)]
60use core::ffi::{c_int, c_uchar, c_void};
61use core::{
62    fmt,
63    mem::MaybeUninit,
64    ops::{Deref, DerefMut},
65};
66
67// 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
70
71#[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}
79
80#[cfg(rom_md5_bsd)]
81extern "C" {
82    fn esp_rom_md5_init(context: *mut InternalContext);
83    fn esp_rom_md5_update(context: *mut InternalContext, buf: *const c_void, len: u32);
84    fn esp_rom_md5_final(digest: *mut u8, context: *mut InternalContext);
85}
86
87#[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}
95
96#[cfg(rom_md5_mbedtls)]
97extern "C" {
98    fn esp_rom_mbedtls_md5_starts_ret(context: *mut InternalContext) -> c_int;
99    fn esp_rom_mbedtls_md5_update_ret(
100        context: *mut InternalContext,
101        buf: *const c_void,
102        len: u32,
103    ) -> c_int;
104    fn esp_rom_mbedtls_md5_finish_ret(context: *mut InternalContext, digest: *mut u8) -> c_int;
105}
106
107/// MD5 context for an ongoing computation
108#[derive(Clone)]
109pub struct Context(InternalContext);
110
111impl Context {
112    /// Create a new MD5 context
113    #[inline]
114    pub fn new() -> Self {
115        let mut ctx = MaybeUninit::<InternalContext>::uninit();
116        unsafe {
117            #[cfg(rom_md5_bsd)]
118            esp_rom_md5_init(ctx.as_mut_ptr());
119
120            #[cfg(rom_md5_mbedtls)]
121            let _ = esp_rom_mbedtls_md5_starts_ret(ctx.as_mut_ptr());
122
123            Self(ctx.assume_init())
124        }
125    }
126
127    /// Feed data to the hasher
128    #[inline]
129    pub fn consume<T: AsRef<[u8]>>(&mut self, data: T) {
130        let data = data.as_ref();
131        unsafe {
132            #[cfg(rom_md5_bsd)]
133            esp_rom_md5_update(
134                &mut self.0 as *mut _,
135                data.as_ptr() as *const c_void,
136                data.len() as u32,
137            );
138
139            #[cfg(rom_md5_mbedtls)]
140            let _ = 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    }
147
148    /// Finalize and return a digest
149    #[inline]
150    pub fn compute(mut self) -> Digest {
151        let mut digest = MaybeUninit::<[u8; 16]>::uninit();
152        unsafe {
153            #[cfg(rom_md5_bsd)]
154            esp_rom_md5_final(digest.as_mut_ptr() as *mut _, &mut self.0 as *mut _);
155
156            #[cfg(rom_md5_mbedtls)]
157            let _ = esp_rom_mbedtls_md5_finish_ret(
158                &mut self.0 as *mut _,
159                digest.as_mut_ptr() as *mut _,
160            );
161
162            Digest(digest.assume_init())
163        }
164    }
165}
166
167impl Default for Context {
168    fn default() -> Self {
169        Self::new()
170    }
171}
172
173/// Compute a full digest from a single buffer
174#[inline]
175pub fn compute<T: AsRef<[u8]>>(data: T) -> Digest {
176    let mut ctx = Context::new();
177    ctx.consume(data);
178    ctx.compute()
179}
180
181/// 16-byte message digest
182#[derive(Clone, Copy, PartialEq, Eq, Hash)]
183pub struct Digest(pub [u8; 16]);
184
185impl fmt::LowerHex for Digest {
186    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
187        for &byte in &self.0 {
188            write!(f, "{:02x}", byte)?;
189        }
190        Ok(())
191    }
192}
193
194impl fmt::UpperHex for Digest {
195    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
196        for &byte in &self.0 {
197            write!(f, "{:02X}", byte)?;
198        }
199        Ok(())
200    }
201}
202
203impl fmt::Display for Digest {
204    #[inline]
205    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
206        <Digest as fmt::LowerHex>::fmt(self, f)
207    }
208}
209
210impl fmt::Debug for Digest {
211    #[inline]
212    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
213        <Digest as fmt::LowerHex>::fmt(self, f)
214    }
215}
216
217impl From<Digest> for [u8; 16] {
218    #[inline]
219    fn from(digest: Digest) -> Self {
220        digest.0
221    }
222}
223
224impl From<Context> for Digest {
225    #[inline]
226    fn from(context: Context) -> Digest {
227        context.compute()
228    }
229}
230
231impl Deref for Digest {
232    type Target = [u8; 16];
233
234    #[inline]
235    fn deref(&self) -> &Self::Target {
236        &self.0
237    }
238}
239
240impl DerefMut for Digest {
241    #[inline]
242    fn deref_mut(&mut self) -> &mut Self::Target {
243        &mut self.0
244    }
245}