├── .gitignore ├── Cargo.toml ├── .github └── workflows │ └── build.yml ├── benches └── lib.rs ├── README.md ├── LICENSE.md └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /Cargo.lock 2 | /target 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "md5" 3 | version = "0.8.0" 4 | edition = "2021" 5 | license = "Apache-2.0/MIT" 6 | authors = [ 7 | "Ivan Ukhov ", 8 | "Kamal Ahmad ", 9 | "Konstantin Stepanov ", 10 | "Lukas Kalbertodt ", 11 | "Nathan Musoke ", 12 | "Scott Mabin ", 13 | "Tony Arcieri ", 14 | "Wim de With ", 15 | "Yosef Dinerstein ", 16 | ] 17 | description = "The package provides the MD5 hash function." 18 | documentation = "https://docs.rs/md5" 19 | homepage = "https://github.com/stainless-steel/md5" 20 | repository = "https://github.com/stainless-steel/md5" 21 | readme = "README.md" 22 | categories = ["algorithms", "cryptography"] 23 | keywords = ["checksum", "digest", "hash", "md5"] 24 | 25 | [features] 26 | default = ["std"] 27 | std = [] 28 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | workflow_dispatch: 11 | 12 | jobs: 13 | check: 14 | runs-on: macos-latest 15 | steps: 16 | - uses: actions/checkout@v4 17 | - run: rustup toolchain install stable --profile=minimal --component clippy --component rustfmt 18 | - run: cargo clippy -- -D warnings 19 | - run: cargo fmt --all -- --check 20 | 21 | bench: 22 | strategy: 23 | matrix: 24 | os: [macos-latest, ubuntu-latest] 25 | runs-on: ${{ matrix.os }} 26 | steps: 27 | - uses: actions/checkout@v4 28 | - run: rustup toolchain install nightly --profile=minimal 29 | - run: cargo +nightly bench 30 | 31 | test: 32 | strategy: 33 | matrix: 34 | os: [macos-latest, ubuntu-latest] 35 | runs-on: ${{ matrix.os }} 36 | steps: 37 | - uses: actions/checkout@v4 38 | - run: rustup toolchain install stable --profile=minimal 39 | - run: cargo test 40 | -------------------------------------------------------------------------------- /benches/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | extern crate test; 4 | 5 | use md5::Context; 6 | 7 | macro_rules! implement( 8 | ($($size:literal => ($compute:ident, $context:ident),)*) => ($( 9 | #[bench] 10 | fn $compute(bencher: &mut test::Bencher) { 11 | compute($size, bencher); 12 | } 13 | 14 | #[bench] 15 | fn $context(bencher: &mut test::Bencher) { 16 | context($size, bencher); 17 | } 18 | )*); 19 | ); 20 | 21 | implement! { 22 | 1000 => (compute_0001000, context_0001000), 23 | 10000 => (compute_0010000, context_0010000), 24 | 100000 => (compute_0100000, context_0100000), 25 | 1000000 => (compute_1000000, context_1000000), 26 | } 27 | 28 | fn compute(size: usize, bencher: &mut test::Bencher) { 29 | let data = vec![0xffu8; size]; 30 | bencher.iter(|| { 31 | test::black_box(md5::compute(&data)); 32 | }); 33 | } 34 | 35 | fn context(size: usize, bencher: &mut test::Bencher) { 36 | let data = vec![0xffu8; size]; 37 | bencher.iter(|| { 38 | let mut context = Context::new(); 39 | context.consume(&data); 40 | test::black_box(context.finalize()); 41 | }); 42 | } 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MD5 [![Package][package-img]][package-url] [![Documentation][documentation-img]][documentation-url] [![Build][build-img]][build-url] 2 | 3 | The package provides the [MD5] hash function. 4 | 5 | ## Example 6 | 7 | ```rust 8 | let digest = md5::compute(b"abcdefghijklmnopqrstuvwxyz"); 9 | assert_eq!(format!("{:x}", digest), "c3fcd3d76192e4007dfb496cca67e13b"); 10 | ``` 11 | 12 | ## Security Warning 13 | 14 | The package is provided for the purposes of interoperability with protocols and 15 | systems that mandate the use of MD5. However, MD5 should be considered 16 | [cryptographically broken and unsuitable for further use][VU836068]. Collision 17 | attacks against MD5 are both practical and trivial, and [theoretical attacks 18 | against MD5 have been found][ACM1724151]. 19 | 20 | [RFC6151] advises no new protocols to be designed with any MD5-based 21 | constructions, including HMAC-MD5. 22 | 23 | ## Contribution 24 | 25 | Your contribution is highly appreciated. Do not hesitate to open an issue or a 26 | pull request. Note that any contribution submitted for inclusion in the project 27 | will be licensed according to the terms given in [LICENSE.md](LICENSE.md). 28 | 29 | [ACM1724151]: https://dl.acm.org/citation.cfm?id=1724151 30 | [MD5]: https://en.wikipedia.org/wiki/MD5 31 | [RFC6151]: https://tools.ietf.org/html/rfc6151 32 | [VU836068]: https://www.kb.cert.org/vuls/id/836068 33 | 34 | [build-img]: https://github.com/stainless-steel/md5/workflows/build/badge.svg 35 | [build-url]: https://github.com/stainless-steel/md5/actions/workflows/build.yml 36 | [documentation-img]: https://docs.rs/md5/badge.svg 37 | [documentation-url]: https://docs.rs/md5 38 | [package-img]: https://img.shields.io/crates/v/md5.svg 39 | [package-url]: https://crates.io/crates/md5 40 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # License 2 | 3 | The project is dual licensed under the terms of the Apache License, Version 2.0, 4 | and the MIT License. You may obtain copies of the two licenses at 5 | 6 | * https://www.apache.org/licenses/LICENSE-2.0 and 7 | * https://opensource.org/licenses/MIT, respectively. 8 | 9 | The following two notices apply to every file of the project. 10 | 11 | ## The Apache License 12 | 13 | ``` 14 | Copyright 2015–2025 The md5 Developers 15 | 16 | Licensed under the Apache License, Version 2.0 (the “License”); you may not use 17 | this file except in compliance with the License. You may obtain a copy of the 18 | License at 19 | 20 | http://www.apache.org/licenses/LICENSE-2.0 21 | 22 | Unless required by applicable law or agreed to in writing, software distributed 23 | under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR 24 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 25 | specific language governing permissions and limitations under the License. 26 | ``` 27 | 28 | ## The MIT License 29 | 30 | ``` 31 | Copyright 2015–2025 The md5 Developers 32 | 33 | Permission is hereby granted, free of charge, to any person obtaining a copy of 34 | this software and associated documentation files (the “Software”), to deal in 35 | the Software without restriction, including without limitation the rights to 36 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 37 | the Software, and to permit persons to whom the Software is furnished to do so, 38 | subject to the following conditions: 39 | 40 | The above copyright notice and this permission notice shall be included in all 41 | copies or substantial portions of the Software. 42 | 43 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 44 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 45 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 46 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 47 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 48 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 49 | ``` 50 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! The [MD5] hash function. 2 | //! 3 | //! ## Example 4 | //! 5 | //! ``` 6 | //! let digest = md5::compute(b"abcdefghijklmnopqrstuvwxyz"); 7 | //! assert_eq!(format!("{:x}", digest), "c3fcd3d76192e4007dfb496cca67e13b"); 8 | //! ``` 9 | //! 10 | //! ## Security Warning 11 | //! 12 | //! The package is provided for the purposes of interoperability with protocols 13 | //! and systems that mandate the use of MD5. However, MD5 should be considered 14 | //! [cryptographically broken and unsuitable for further use][VU836068]. 15 | //! Collision attacks against MD5 are both practical and trivial, and 16 | //! [theoretical attacks against MD5 have been found][ACM1724151]. 17 | //! 18 | //! [RFC6151] advises no new protocols to be designed with any MD5-based 19 | //! constructions, including HMAC-MD5. 20 | //! 21 | //! [MD5]: https://en.wikipedia.org/wiki/MD5 22 | //! 23 | //! [ACM1724151]: https://dl.acm.org/citation.cfm?id=1724151 24 | //! [RFC6151]: https://tools.ietf.org/html/rfc6151 25 | //! [VU836068]: https://www.kb.cert.org/vuls/id/836068 26 | 27 | // The implementation is based on: 28 | // https://www.ietf.org/rfc/rfc1321.txt 29 | 30 | #![cfg_attr(not(feature = "std"), no_std)] 31 | 32 | #[cfg(feature = "std")] 33 | use std as core; 34 | 35 | /// A digest. 36 | #[derive(Clone, Copy, Eq, Hash, PartialEq)] 37 | pub struct Digest(pub [u8; 16]); 38 | 39 | impl core::convert::From for [u8; 16] { 40 | #[inline] 41 | fn from(digest: Digest) -> Self { 42 | digest.0 43 | } 44 | } 45 | 46 | impl core::fmt::Debug for Digest { 47 | #[inline] 48 | fn fmt(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result { 49 | core::fmt::LowerHex::fmt(self, formatter) 50 | } 51 | } 52 | 53 | impl core::ops::Deref for Digest { 54 | type Target = [u8; 16]; 55 | 56 | #[inline] 57 | fn deref(&self) -> &Self::Target { 58 | &self.0 59 | } 60 | } 61 | 62 | impl core::ops::DerefMut for Digest { 63 | #[inline] 64 | fn deref_mut(&mut self) -> &mut Self::Target { 65 | &mut self.0 66 | } 67 | } 68 | 69 | macro_rules! implement { 70 | ($kind:ident, $format:expr) => { 71 | impl core::fmt::$kind for Digest { 72 | fn fmt(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result { 73 | for value in &self.0 { 74 | write!(formatter, $format, value)?; 75 | } 76 | Ok(()) 77 | } 78 | } 79 | }; 80 | } 81 | 82 | implement!(LowerHex, "{:02x}"); 83 | implement!(UpperHex, "{:02X}"); 84 | 85 | /// A context. 86 | #[derive(Clone)] 87 | pub struct Context { 88 | buffer: [u8; 64], 89 | count: u64, 90 | state: [u32; 4], 91 | } 92 | 93 | const PADDING: [u8; 64] = [ 94 | 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 95 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 96 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 97 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 98 | ]; 99 | 100 | impl Context { 101 | /// Create a context for computing a digest. 102 | #[inline] 103 | pub fn new() -> Context { 104 | Context { 105 | buffer: [0; 64], 106 | count: 0, 107 | state: [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476], 108 | } 109 | } 110 | 111 | /// Consume data. 112 | #[inline] 113 | pub fn consume>(&mut self, data: T) { 114 | consume(self, data.as_ref()); 115 | } 116 | 117 | /// Finalize and return the digest. 118 | #[rustfmt::skip] 119 | #[allow(clippy::double_parens, clippy::needless_range_loop)] 120 | pub fn finalize(mut self) -> Digest { 121 | let mut input = [0u32; 16]; 122 | let k = ((self.count >> 3) & 0x3f) as usize; 123 | input[14] = self.count as u32; 124 | input[15] = (self.count >> 32) as u32; 125 | consume( 126 | &mut self, 127 | &PADDING[..(if k < 56 { 56 - k } else { 120 - k })], 128 | ); 129 | let mut j = 0; 130 | for i in 0..14 { 131 | input[i] = ((self.buffer[j + 3] as u32) << 24) | 132 | ((self.buffer[j + 2] as u32) << 16) | 133 | ((self.buffer[j + 1] as u32) << 8) | 134 | ((self.buffer[j ] as u32) ); 135 | j += 4; 136 | } 137 | transform(&mut self.state, &input); 138 | let mut digest = [0u8; 16]; 139 | let mut j = 0; 140 | for i in 0..4 { 141 | digest[j ] = ((self.state[i] ) & 0xff) as u8; 142 | digest[j + 1] = ((self.state[i] >> 8) & 0xff) as u8; 143 | digest[j + 2] = ((self.state[i] >> 16) & 0xff) as u8; 144 | digest[j + 3] = ((self.state[i] >> 24) & 0xff) as u8; 145 | j += 4; 146 | } 147 | Digest(digest) 148 | } 149 | 150 | /// Finalize and return the digest. 151 | #[deprecated(since = "0.8.0", note = "Use `finalize`.")] 152 | #[inline] 153 | pub fn compute(self) -> Digest { 154 | self.finalize() 155 | } 156 | } 157 | 158 | impl Default for Context { 159 | #[inline] 160 | fn default() -> Self { 161 | Self::new() 162 | } 163 | } 164 | 165 | impl core::convert::From for Digest { 166 | #[inline] 167 | fn from(context: Context) -> Digest { 168 | context.finalize() 169 | } 170 | } 171 | 172 | #[cfg(feature = "std")] 173 | impl core::io::Write for Context { 174 | #[inline] 175 | fn write(&mut self, data: &[u8]) -> core::io::Result { 176 | self.consume(data); 177 | Ok(data.len()) 178 | } 179 | 180 | #[inline] 181 | fn flush(&mut self) -> core::io::Result<()> { 182 | Ok(()) 183 | } 184 | } 185 | 186 | /// Compute the digest of data. 187 | #[inline] 188 | pub fn compute>(data: T) -> Digest { 189 | let mut context = Context::new(); 190 | context.consume(data); 191 | context.finalize() 192 | } 193 | 194 | #[rustfmt::skip] 195 | #[allow(clippy::double_parens, clippy::needless_range_loop)] 196 | fn consume( 197 | Context { 198 | buffer, 199 | count, 200 | state, 201 | }: &mut Context, 202 | data: &[u8], 203 | ) { 204 | let mut input = [0u32; 16]; 205 | let mut k = ((*count >> 3) & 0x3f) as usize; 206 | *count = count.wrapping_add((data.len() as u64) << 3); 207 | for &value in data { 208 | buffer[k] = value; 209 | k += 1; 210 | if k == 0x40 { 211 | let mut j = 0; 212 | for i in 0..16 { 213 | input[i] = ((buffer[j + 3] as u32) << 24) | 214 | ((buffer[j + 2] as u32) << 16) | 215 | ((buffer[j + 1] as u32) << 8) | 216 | ((buffer[j ] as u32) ); 217 | j += 4; 218 | } 219 | transform(state, &input); 220 | k = 0; 221 | } 222 | } 223 | } 224 | 225 | #[rustfmt::skip] 226 | fn transform(state: &mut [u32; 4], input: &[u32; 16]) { 227 | let (mut a, mut b, mut c, mut d) = (state[0], state[1], state[2], state[3]); 228 | macro_rules! add( 229 | ($a:expr, $b:expr) => ($a.wrapping_add($b)); 230 | ); 231 | macro_rules! rotate( 232 | ($x:expr, $n:expr) => ($x.rotate_left($n)); 233 | ); 234 | { 235 | macro_rules! F( 236 | ($x:expr, $y:expr, $z:expr) => (($x & $y) | (!$x & $z)); 237 | ); 238 | macro_rules! T( 239 | ($a:expr, $b:expr, $c:expr, $d:expr, $x:expr, $s:expr, $ac:expr) => ({ 240 | $a = add!(add!(add!($a, F!($b, $c, $d)), $x), $ac); 241 | $a = rotate!($a, $s); 242 | $a = add!($a, $b); 243 | }); 244 | ); 245 | const S1: u32 = 7; 246 | const S2: u32 = 12; 247 | const S3: u32 = 17; 248 | const S4: u32 = 22; 249 | T!(a, b, c, d, input[ 0], S1, 3614090360); 250 | T!(d, a, b, c, input[ 1], S2, 3905402710); 251 | T!(c, d, a, b, input[ 2], S3, 606105819); 252 | T!(b, c, d, a, input[ 3], S4, 3250441966); 253 | T!(a, b, c, d, input[ 4], S1, 4118548399); 254 | T!(d, a, b, c, input[ 5], S2, 1200080426); 255 | T!(c, d, a, b, input[ 6], S3, 2821735955); 256 | T!(b, c, d, a, input[ 7], S4, 4249261313); 257 | T!(a, b, c, d, input[ 8], S1, 1770035416); 258 | T!(d, a, b, c, input[ 9], S2, 2336552879); 259 | T!(c, d, a, b, input[10], S3, 4294925233); 260 | T!(b, c, d, a, input[11], S4, 2304563134); 261 | T!(a, b, c, d, input[12], S1, 1804603682); 262 | T!(d, a, b, c, input[13], S2, 4254626195); 263 | T!(c, d, a, b, input[14], S3, 2792965006); 264 | T!(b, c, d, a, input[15], S4, 1236535329); 265 | } 266 | { 267 | macro_rules! F( 268 | ($x:expr, $y:expr, $z:expr) => (($x & $z) | ($y & !$z)); 269 | ); 270 | macro_rules! T( 271 | ($a:expr, $b:expr, $c:expr, $d:expr, $x:expr, $s:expr, $ac:expr) => ({ 272 | $a = add!(add!(add!($a, F!($b, $c, $d)), $x), $ac); 273 | $a = rotate!($a, $s); 274 | $a = add!($a, $b); 275 | }); 276 | ); 277 | const S1: u32 = 5; 278 | const S2: u32 = 9; 279 | const S3: u32 = 14; 280 | const S4: u32 = 20; 281 | T!(a, b, c, d, input[ 1], S1, 4129170786); 282 | T!(d, a, b, c, input[ 6], S2, 3225465664); 283 | T!(c, d, a, b, input[11], S3, 643717713); 284 | T!(b, c, d, a, input[ 0], S4, 3921069994); 285 | T!(a, b, c, d, input[ 5], S1, 3593408605); 286 | T!(d, a, b, c, input[10], S2, 38016083); 287 | T!(c, d, a, b, input[15], S3, 3634488961); 288 | T!(b, c, d, a, input[ 4], S4, 3889429448); 289 | T!(a, b, c, d, input[ 9], S1, 568446438); 290 | T!(d, a, b, c, input[14], S2, 3275163606); 291 | T!(c, d, a, b, input[ 3], S3, 4107603335); 292 | T!(b, c, d, a, input[ 8], S4, 1163531501); 293 | T!(a, b, c, d, input[13], S1, 2850285829); 294 | T!(d, a, b, c, input[ 2], S2, 4243563512); 295 | T!(c, d, a, b, input[ 7], S3, 1735328473); 296 | T!(b, c, d, a, input[12], S4, 2368359562); 297 | } 298 | { 299 | macro_rules! F( 300 | ($x:expr, $y:expr, $z:expr) => ($x ^ $y ^ $z); 301 | ); 302 | macro_rules! T( 303 | ($a:expr, $b:expr, $c:expr, $d:expr, $x:expr, $s:expr, $ac:expr) => ({ 304 | $a = add!(add!(add!($a, F!($b, $c, $d)), $x), $ac); 305 | $a = rotate!($a, $s); 306 | $a = add!($a, $b); 307 | }); 308 | ); 309 | const S1: u32 = 4; 310 | const S2: u32 = 11; 311 | const S3: u32 = 16; 312 | const S4: u32 = 23; 313 | T!(a, b, c, d, input[ 5], S1, 4294588738); 314 | T!(d, a, b, c, input[ 8], S2, 2272392833); 315 | T!(c, d, a, b, input[11], S3, 1839030562); 316 | T!(b, c, d, a, input[14], S4, 4259657740); 317 | T!(a, b, c, d, input[ 1], S1, 2763975236); 318 | T!(d, a, b, c, input[ 4], S2, 1272893353); 319 | T!(c, d, a, b, input[ 7], S3, 4139469664); 320 | T!(b, c, d, a, input[10], S4, 3200236656); 321 | T!(a, b, c, d, input[13], S1, 681279174); 322 | T!(d, a, b, c, input[ 0], S2, 3936430074); 323 | T!(c, d, a, b, input[ 3], S3, 3572445317); 324 | T!(b, c, d, a, input[ 6], S4, 76029189); 325 | T!(a, b, c, d, input[ 9], S1, 3654602809); 326 | T!(d, a, b, c, input[12], S2, 3873151461); 327 | T!(c, d, a, b, input[15], S3, 530742520); 328 | T!(b, c, d, a, input[ 2], S4, 3299628645); 329 | } 330 | { 331 | macro_rules! F( 332 | ($x:expr, $y:expr, $z:expr) => ($y ^ ($x | !$z)); 333 | ); 334 | macro_rules! T( 335 | ($a:expr, $b:expr, $c:expr, $d:expr, $x:expr, $s:expr, $ac:expr) => ({ 336 | $a = add!(add!(add!($a, F!($b, $c, $d)), $x), $ac); 337 | $a = rotate!($a, $s); 338 | $a = add!($a, $b); 339 | }); 340 | ); 341 | const S1: u32 = 6; 342 | const S2: u32 = 10; 343 | const S3: u32 = 15; 344 | const S4: u32 = 21; 345 | T!(a, b, c, d, input[ 0], S1, 4096336452); 346 | T!(d, a, b, c, input[ 7], S2, 1126891415); 347 | T!(c, d, a, b, input[14], S3, 2878612391); 348 | T!(b, c, d, a, input[ 5], S4, 4237533241); 349 | T!(a, b, c, d, input[12], S1, 1700485571); 350 | T!(d, a, b, c, input[ 3], S2, 2399980690); 351 | T!(c, d, a, b, input[10], S3, 4293915773); 352 | T!(b, c, d, a, input[ 1], S4, 2240044497); 353 | T!(a, b, c, d, input[ 8], S1, 1873313359); 354 | T!(d, a, b, c, input[15], S2, 4264355552); 355 | T!(c, d, a, b, input[ 6], S3, 2734768916); 356 | T!(b, c, d, a, input[13], S4, 1309151649); 357 | T!(a, b, c, d, input[ 4], S1, 4149444226); 358 | T!(d, a, b, c, input[11], S2, 3174756917); 359 | T!(c, d, a, b, input[ 2], S3, 718787259); 360 | T!(b, c, d, a, input[ 9], S4, 3951481745); 361 | } 362 | state[0] = add!(state[0], a); 363 | state[1] = add!(state[1], b); 364 | state[2] = add!(state[2], c); 365 | state[3] = add!(state[3], d); 366 | } 367 | 368 | #[cfg(test)] 369 | mod tests { 370 | use std::io::prelude::Write; 371 | 372 | use super::Context; 373 | 374 | #[test] 375 | fn compute() { 376 | let inputs = [ 377 | "", 378 | "a", 379 | "abc", 380 | "message digest", 381 | "abcdefghijklmnopqrstuvwxyz", 382 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 383 | "0123456789012345678901234567890123456789012345678901234567890123", 384 | "12345678901234567890123456789012345678901234567890123456789012345678901234567890", 385 | ]; 386 | let outputs = [ 387 | "d41d8cd98f00b204e9800998ecf8427e", 388 | "0cc175b9c0f1b6a831c399e269772661", 389 | "900150983cd24fb0d6963f7d28e17f72", 390 | "f96b697d7cb7938d525a2f31aaf161d0", 391 | "c3fcd3d76192e4007dfb496cca67e13b", 392 | "d174ab98d277d9f5a5611c2c9f419d9f", 393 | "7f7bfd348709deeaace19e3f535f8c54", 394 | "57edf4a22be3c955ac49da2e2107b67a", 395 | ]; 396 | for (input, &output) in inputs.iter().zip(outputs.iter()) { 397 | let digest = super::compute(input); 398 | assert_eq!(format!("{digest:x}"), output); 399 | 400 | let mut context = Context::new(); 401 | context.consume(input); 402 | let digest = context.finalize(); 403 | assert_eq!(format!("{digest:x}"), output); 404 | } 405 | } 406 | 407 | #[test] 408 | fn index() { 409 | let mut digest = super::compute(b"abc"); 410 | assert_eq!(digest[0], 0x90); 411 | assert_eq!(&digest[0], &0x90); 412 | assert_eq!(&mut digest[0], &mut 0x90); 413 | } 414 | 415 | #[test] 416 | fn write_29() { 417 | let data = vec![0; 8 * 1024 * 1024]; 418 | let mut context = Context::new(); 419 | for _ in 0..64 { 420 | context.write(&data).unwrap(); 421 | } 422 | assert_eq!( 423 | format!("{:x}", context.finalize()), 424 | "aa559b4e3523a6c931f08f4df52d58f2", 425 | ); 426 | } 427 | 428 | #[test] 429 | fn write_32() { 430 | let data = vec![0; std::u32::MAX as usize + 1]; 431 | let mut context = Context::new(); 432 | context.write(&data).unwrap(); 433 | assert_eq!( 434 | format!("{:x}", context.finalize()), 435 | "c9a5a6878d97b48cc965c1e41859f034", 436 | ); 437 | } 438 | } 439 | --------------------------------------------------------------------------------