tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }