lib.rs (7219B)
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/. */ 4 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; 14 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, 21 }; 22 use nsstring::{nsACString, nsCString}; 23 use xpcom::interfaces::{nsICryptoHash, nsIInputStream}; 24 use xpcom::xpcom_method; 25 26 use std::borrow::Borrow; 27 use std::sync::Mutex; 28 29 enum Algorithm { 30 Md5, 31 Sha1, 32 Sha256, 33 Sha384, 34 Sha512, 35 } 36 37 impl TryFrom<u32> for Algorithm { 38 type Error = nsresult; 39 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 } 50 } 51 52 impl TryFrom<&nsACString> for Algorithm { 53 type Error = nsresult; 54 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 } 65 } 66 67 #[xpcom(implement(nsICryptoHash), atomic)] 68 struct CryptoHash { 69 digest: Mutex<Option<Box<dyn DynDigest>>>, 70 } 71 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 } 78 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 } 84 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 } 99 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 } 116 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 } 160 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 } 177 } 178 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) } 188 } 189 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); 208 }