Backed out changeset 49b6d8b426ba (bug 1944170) on request for causing Bug 1944433...
[gecko.git] / security / manager / ssl / crypto_hash / src / lib.rs
blob076661419745e66f2e61787583315a9f5eddb32a
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 extern crate base64;
6 extern crate digest;
7 extern crate libc;
8 extern crate md5;
9 extern crate nsstring;
10 extern crate sha1;
11 extern crate sha2;
12 #[macro_use]
13 extern crate xpcom;
15 use base64::Engine;
16 use digest::generic_array::GenericArray;
17 use digest::{Digest, DynDigest};
18 use nserror::{
19     nsresult, NS_ERROR_FAILURE, NS_ERROR_INVALID_ARG, NS_ERROR_NOT_AVAILABLE,
20     NS_ERROR_NOT_INITIALIZED, NS_OK,
22 use nsstring::{nsACString, nsCString};
23 use xpcom::interfaces::{nsICryptoHash, nsIInputStream};
24 use xpcom::xpcom_method;
26 use std::borrow::Borrow;
27 use std::sync::Mutex;
29 enum Algorithm {
30     Md5,
31     Sha1,
32     Sha256,
33     Sha384,
34     Sha512,
37 impl TryFrom<u32> for Algorithm {
38     type Error = nsresult;
40     fn try_from(value: u32) -> Result<Self, Self::Error> {
41         match value {
42             nsICryptoHash::MD5 => Ok(Algorithm::Md5),
43             nsICryptoHash::SHA1 => Ok(Algorithm::Sha1),
44             nsICryptoHash::SHA256 => Ok(Algorithm::Sha256),
45             nsICryptoHash::SHA384 => Ok(Algorithm::Sha384),
46             nsICryptoHash::SHA512 => Ok(Algorithm::Sha512),
47             _ => Err(NS_ERROR_INVALID_ARG),
48         }
49     }
52 impl TryFrom<&nsACString> for Algorithm {
53     type Error = nsresult;
55     fn try_from(value: &nsACString) -> Result<Self, Self::Error> {
56         match value.to_utf8().borrow() {
57             "md5" => Ok(Algorithm::Md5),
58             "sha1" => Ok(Algorithm::Sha1),
59             "sha256" => Ok(Algorithm::Sha256),
60             "sha384" => Ok(Algorithm::Sha384),
61             "sha512" => Ok(Algorithm::Sha512),
62             _ => Err(NS_ERROR_INVALID_ARG),
63         }
64     }
67 #[xpcom(implement(nsICryptoHash), atomic)]
68 struct CryptoHash {
69     digest: Mutex<Option<Box<dyn DynDigest>>>,
72 impl CryptoHash {
73     xpcom_method!(init => Init(algorithm: u32));
74     fn init(&self, algorithm: u32) -> Result<(), nsresult> {
75         let algorithm = algorithm.try_into()?;
76         self.init_with_algorithm(algorithm)
77     }
79     xpcom_method!(init_with_string => InitWithString(algorithm: *const nsACString));
80     fn init_with_string(&self, algorithm: &nsACString) -> Result<(), nsresult> {
81         let algorithm = algorithm.try_into()?;
82         self.init_with_algorithm(algorithm)
83     }
85     fn init_with_algorithm(&self, algorithm: Algorithm) -> Result<(), nsresult> {
86         let digest = match algorithm {
87             Algorithm::Md5 => Box::new(md5::Md5::new()) as Box<dyn DynDigest>,
88             Algorithm::Sha1 => Box::new(sha1::Sha1::new()) as Box<dyn DynDigest>,
89             Algorithm::Sha256 => Box::new(sha2::Sha256::new()) as Box<dyn DynDigest>,
90             Algorithm::Sha384 => Box::new(sha2::Sha384::new()) as Box<dyn DynDigest>,
91             Algorithm::Sha512 => Box::new(sha2::Sha512::new()) as Box<dyn DynDigest>,
92         };
93         let mut guard = self.digest.lock().map_err(|_| NS_ERROR_FAILURE)?;
94         if let Some(_expected_none_digest) = (*guard).replace(digest) {
95             return Err(NS_ERROR_FAILURE);
96         }
97         Ok(())
98     }
100     xpcom_method!(update => Update(data: *const u8, len: u32));
101     fn update(&self, data: *const u8, len: u32) -> Result<(), nsresult> {
102         let mut guard = self.digest.lock().map_err(|_| NS_ERROR_FAILURE)?;
103         let digest = match (*guard).as_mut() {
104             Some(digest) => digest,
105             None => return Err(NS_ERROR_NOT_INITIALIZED),
106         };
107         if len > 0 {
108             // Safety: this is safe as long as xpcom gave us valid arguments.
109             let data = unsafe {
110                 std::slice::from_raw_parts(data, len.try_into().map_err(|_| NS_ERROR_INVALID_ARG)?)
111             };
112             digest.update(data);
113         }
114         Ok(())
115     }
117     xpcom_method!(update_from_stream => UpdateFromStream(stream: *const nsIInputStream, len: u32));
118     fn update_from_stream(&self, stream: &nsIInputStream, len: u32) -> Result<(), nsresult> {
119         let mut guard = self.digest.lock().map_err(|_| NS_ERROR_FAILURE)?;
120         let digest = match (*guard).as_mut() {
121             Some(digest) => digest,
122             None => return Err(NS_ERROR_NOT_INITIALIZED),
123         };
124         let mut available = 0u64;
125         unsafe { stream.Available(&mut available as *mut u64).to_result()? };
126         let mut to_read = if len == u32::MAX {
127             available
128         } else {
129             len as u64
130         };
131         if available == 0 || available < to_read {
132             return Err(NS_ERROR_NOT_AVAILABLE);
133         }
134         let mut buf = vec![0u8; 4096];
135         let buf_len = buf.len() as u64;
136         while to_read > 0 {
137             let chunk_len = if to_read >= buf_len {
138                 buf_len as u32
139             } else {
140                 to_read as u32
141             };
142             let mut read = 0u32;
143             unsafe {
144                 stream
145                     .Read(
146                         buf.as_mut_ptr() as *mut libc::c_char,
147                         chunk_len,
148                         &mut read as *mut u32,
149                     )
150                     .to_result()?
151             };
152             if read > chunk_len {
153                 return Err(NS_ERROR_FAILURE);
154             }
155             digest.update(&buf[0..read.try_into().map_err(|_| NS_ERROR_FAILURE)?]);
156             to_read -= read as u64;
157         }
158         Ok(())
159     }
161     xpcom_method!(finish => Finish(ascii: bool) -> nsACString);
162     fn finish(&self, ascii: bool) -> Result<nsCString, nsresult> {
163         let mut guard = self.digest.lock().map_err(|_| NS_ERROR_FAILURE)?;
164         let digest = match (*guard).take() {
165             Some(digest) => digest,
166             None => return Err(NS_ERROR_NOT_INITIALIZED),
167         };
168         let result = digest.finalize();
169         if ascii {
170             Ok(nsCString::from(
171                 base64::engine::general_purpose::STANDARD.encode(result),
172             ))
173         } else {
174             Ok(nsCString::from(result))
175         }
176     }
179 #[no_mangle]
180 pub extern "C" fn crypto_hash_constructor(
181     iid: *const xpcom::nsIID,
182     result: *mut *mut xpcom::reexports::libc::c_void,
183 ) -> nserror::nsresult {
184     let crypto_hash = CryptoHash::allocate(InitCryptoHash {
185         digest: Mutex::new(None),
186     });
187     unsafe { crypto_hash.QueryInterface(iid, result) }
190 // 32 bytes will be written to `output` so it must point at a buffer
191 // at least that big.
192 #[no_mangle]
193 pub extern "C" fn crypto_hash_sha256(data: *const u8, length: usize, result: *mut u8) {
194     let mut hasher = sha2::Sha256::new();
195     // slice::from_raw_parts doesn't want a null pointer. We'll handle that here
196     // so the caller doesn't have to worry about it.
197     let data = if data.is_null() {
198         &[]
199     } else {
200         unsafe { std::slice::from_raw_parts(data, length) }
201     };
202     // Sha256 implements both Digest and DynDigest so use function call syntax instead of a method
203     // call to disambiguate
204     Digest::update(&mut hasher, data);
205     let result = unsafe { std::slice::from_raw_parts_mut(result, 32) };
206     let result = GenericArray::from_mut_slice(result);
207     Digest::finalize_into(hasher, result);