1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
//! Digest (hash) algorithms backed by wolfCrypt's EVP_MD API.
//!
//! Each type implements the RustCrypto [`digest`](digest_trait) 0.10 traits
//! (`OutputSizeUser`, `BlockSizeUser`, `Update`, `FixedOutput`,
//! `FixedOutputReset`, `Reset`, `HashMarker`) so they satisfy the
//! blanket `Digest` impl automatically.
//!
//! Callers should `use digest_trait::Digest` (re-exported as
//! `wolfcrypt::digest::digest_trait`) for the full API:
//! `new()`, `update()`, `finalize()`, `finalize_reset()`, `reset()`.
use core::ffi::c_void;
use generic_array::GenericArray;
use typenum::*;
// Re-export the trait crate types we use in our public API.
pub use digest_trait;
/// Internal macro that stamps out a complete digest wrapper for one algorithm.
///
/// The generated struct holds a heap-allocated `EVP_MD_CTX` and delegates
/// all hashing to wolfCrypt through the OpenSSL-compat EVP layer.
macro_rules! impl_digest {
(
$name:ident,
$evp_fn:path,
$output_size:ty,
$block_size:ty,
$cfg_gate:meta
) => {
#[$cfg_gate]
pub struct $name {
ctx: *mut wolfcrypt_rs::EVP_MD_CTX,
}
// SAFETY: EVP_MD_CTX is heap-allocated and only accessed through
// &self / &mut self. wolfCrypt's EVP layer is thread-safe when a
// context is used from a single thread, which Rust's ownership
// rules enforce.
#[$cfg_gate]
unsafe impl Send for $name {}
#[$cfg_gate]
impl $name {
/// Return the algorithm descriptor pointer for this hash.
#[inline]
fn evp_md() -> *const wolfcrypt_rs::EVP_MD {
// SAFETY: EVP_sha* functions return a static const pointer.
unsafe { $evp_fn() }
}
}
// ------------------------------------------------------------------
// core / RustCrypto trait impls
// ------------------------------------------------------------------
#[$cfg_gate]
impl Default for $name {
fn default() -> Self {
// SAFETY: EVP_MD_CTX_new returns a heap-allocated context
// or NULL on OOM.
let ctx = unsafe { wolfcrypt_rs::EVP_MD_CTX_new() };
assert!(!ctx.is_null(), "EVP_MD_CTX_new returned NULL");
// SAFETY: ctx is non-null and freshly allocated.
unsafe {
let rc = wolfcrypt_rs::EVP_DigestInit_ex(
ctx,
Self::evp_md(),
core::ptr::null_mut(),
);
assert_eq!(rc, 1, "EVP_DigestInit_ex failed (OOM or invalid algorithm)");
}
Self { ctx }
}
}
#[$cfg_gate]
impl Clone for $name {
fn clone(&self) -> Self {
// SAFETY: Allocate a fresh context and deep-copy state.
let new_ctx = unsafe { wolfcrypt_rs::EVP_MD_CTX_new() };
assert!(!new_ctx.is_null(), "EVP_MD_CTX_new returned NULL");
// SAFETY: both contexts are valid, non-overlapping.
unsafe {
let rc = wolfcrypt_rs::EVP_MD_CTX_copy(new_ctx, self.ctx);
assert_eq!(rc, 1, "EVP_MD_CTX_copy failed (OOM)");
}
Self { ctx: new_ctx }
}
}
#[$cfg_gate]
impl Drop for $name {
fn drop(&mut self) {
// SAFETY: self.ctx was allocated via EVP_MD_CTX_new and
// is only freed once here.
unsafe {
wolfcrypt_rs::EVP_MD_CTX_free(self.ctx);
}
}
}
#[$cfg_gate]
impl digest_trait::OutputSizeUser for $name {
type OutputSize = $output_size;
}
#[$cfg_gate]
impl digest_trait::core_api::BlockSizeUser for $name {
type BlockSize = $block_size;
}
#[$cfg_gate]
impl digest_trait::Update for $name {
fn update(&mut self, data: &[u8]) {
// SAFETY: self.ctx is valid. data pointer and length are
// guaranteed correct by the slice reference.
unsafe {
let rc = wolfcrypt_rs::EVP_DigestUpdate(
self.ctx,
data.as_ptr() as *const c_void,
data.len(),
);
assert_eq!(rc, 1, "EVP_DigestUpdate failed (context not initialized)");
}
}
}
#[$cfg_gate]
impl digest_trait::FixedOutput for $name {
fn finalize_into(self, out: &mut GenericArray<u8, Self::OutputSize>) {
let mut len: u32 = 0;
// SAFETY: out is exactly OutputSize bytes. self.ctx is
// valid. After this call, Drop will free the context.
unsafe {
let rc = wolfcrypt_rs::EVP_DigestFinal(
self.ctx,
out.as_mut_ptr(),
&mut len,
);
assert_eq!(rc, 1, "EVP_DigestFinal failed (context not initialized)");
}
// Drop runs after this and frees self.ctx.
}
}
#[$cfg_gate]
impl digest_trait::FixedOutputReset for $name {
fn finalize_into_reset(
&mut self,
out: &mut GenericArray<u8, Self::OutputSize>,
) {
let mut len: u32 = 0;
// SAFETY: ctx is valid, out has correct size.
unsafe {
let rc = wolfcrypt_rs::EVP_DigestFinal(
self.ctx,
out.as_mut_ptr(),
&mut len,
);
assert_eq!(rc, 1, "EVP_DigestFinal failed (context not initialized)");
}
// Re-initialise for reuse.
// SAFETY: ctx is still allocated (DigestFinal does not free it).
unsafe {
let rc = wolfcrypt_rs::EVP_DigestInit_ex(
self.ctx,
Self::evp_md(),
core::ptr::null_mut(),
);
assert_eq!(rc, 1, "EVP_DigestInit_ex failed after finalize_into_reset (OOM or invalid algorithm)");
}
}
}
#[$cfg_gate]
impl digest_trait::Reset for $name {
fn reset(&mut self) {
// SAFETY: ctx is valid. cleanup + re-init is the
// documented way to reset an EVP_MD_CTX.
unsafe {
wolfcrypt_rs::EVP_MD_CTX_cleanup(self.ctx);
let rc = wolfcrypt_rs::EVP_DigestInit_ex(
self.ctx,
Self::evp_md(),
core::ptr::null_mut(),
);
assert_eq!(rc, 1, "EVP_DigestInit_ex failed in reset (OOM or invalid algorithm)");
}
}
}
#[$cfg_gate]
impl digest_trait::HashMarker for $name {}
};
}
// ======================================================================
// Stamp out all nine digest types
// ======================================================================
impl_digest!(Sha1, wolfcrypt_rs::EVP_sha1, U20, U64, cfg(wolfssl_openssl_extra));
impl_digest!(Sha224, wolfcrypt_rs::EVP_sha224, U28, U64, cfg(all(wolfssl_openssl_extra, wolfssl_sha224)));
impl_digest!(Sha256, wolfcrypt_rs::EVP_sha256, U32, U64, cfg(wolfssl_openssl_extra));
impl_digest!(Sha384, wolfcrypt_rs::EVP_sha384, U48, U128, cfg(all(wolfssl_openssl_extra, wolfssl_sha384)));
impl_digest!(Sha512, wolfcrypt_rs::EVP_sha512, U64, U128, cfg(all(wolfssl_openssl_extra, wolfssl_sha512)));
impl_digest!(Sha512_256, wolfcrypt_rs::EVP_sha512_256, U32, U128, cfg(all(wolfssl_openssl_extra, wolfssl_sha512)));
impl_digest!(Sha3_256, wolfcrypt_rs::EVP_sha3_256, U32, U136, cfg(all(wolfssl_openssl_extra, wolfssl_sha3)));
impl_digest!(Sha3_384, wolfcrypt_rs::EVP_sha3_384, U48, U104, cfg(all(wolfssl_openssl_extra, wolfssl_sha3)));
impl_digest!(Sha3_512, wolfcrypt_rs::EVP_sha3_512, U64, U72, cfg(all(wolfssl_openssl_extra, wolfssl_sha3)));