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/. */
16 use digest::generic_array::GenericArray;
17 use digest::{Digest, DynDigest};
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;
37 impl TryFrom<u32> for Algorithm {
38 type Error = nsresult;
40 fn try_from(value: u32) -> Result<Self, Self::Error> {
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),
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),
67 #[xpcom(implement(nsICryptoHash), atomic)]
69 digest: Mutex<Option<Box<dyn DynDigest>>>,
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)
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)
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>,
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);
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),
108 // Safety: this is safe as long as xpcom gave us valid arguments.
110 std::slice::from_raw_parts(data, len.try_into().map_err(|_| NS_ERROR_INVALID_ARG)?)
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),
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 {
131 if available == 0 || available < to_read {
132 return Err(NS_ERROR_NOT_AVAILABLE);
134 let mut buf = vec![0u8; 4096];
135 let buf_len = buf.len() as u64;
137 let chunk_len = if to_read >= buf_len {
146 buf.as_mut_ptr() as *mut libc::c_char,
148 &mut read as *mut u32,
152 if read > chunk_len {
153 return Err(NS_ERROR_FAILURE);
155 digest.update(&buf[0..read.try_into().map_err(|_| NS_ERROR_FAILURE)?]);
156 to_read -= read as u64;
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),
168 let result = digest.finalize();
171 base64::engine::general_purpose::STANDARD.encode(result),
174 Ok(nsCString::from(result))
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),
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.
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() {
200 unsafe { std::slice::from_raw_parts(data, length) }
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);