summaryrefslogtreecommitdiffstats
path: root/security/manager/ssl/crypto_hash/src
diff options
context:
space:
mode:
Diffstat (limited to 'security/manager/ssl/crypto_hash/src')
-rw-r--r--security/manager/ssl/crypto_hash/src/lib.rs177
1 files changed, 177 insertions, 0 deletions
diff --git a/security/manager/ssl/crypto_hash/src/lib.rs b/security/manager/ssl/crypto_hash/src/lib.rs
new file mode 100644
index 0000000000..9303f3cfd9
--- /dev/null
+++ b/security/manager/ssl/crypto_hash/src/lib.rs
@@ -0,0 +1,177 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+extern crate base64;
+extern crate digest;
+extern crate libc;
+extern crate md5;
+extern crate nsstring;
+extern crate sha1;
+extern crate sha2;
+#[macro_use]
+extern crate xpcom;
+
+use base64::Engine;
+use digest::{Digest, DynDigest};
+use nserror::{
+ nsresult, NS_ERROR_FAILURE, NS_ERROR_INVALID_ARG, NS_ERROR_NOT_AVAILABLE,
+ NS_ERROR_NOT_INITIALIZED, NS_OK,
+};
+use nsstring::{nsACString, nsCString};
+use xpcom::interfaces::{nsICryptoHash, nsIInputStream};
+use xpcom::xpcom_method;
+
+use std::borrow::Borrow;
+use std::sync::Mutex;
+
+enum Algorithm {
+ Md5,
+ Sha1,
+ Sha256,
+ Sha384,
+ Sha512,
+}
+
+impl TryFrom<u32> for Algorithm {
+ type Error = nsresult;
+
+ fn try_from(value: u32) -> Result<Self, Self::Error> {
+ match value {
+ nsICryptoHash::MD5 => Ok(Algorithm::Md5),
+ nsICryptoHash::SHA1 => Ok(Algorithm::Sha1),
+ nsICryptoHash::SHA256 => Ok(Algorithm::Sha256),
+ nsICryptoHash::SHA384 => Ok(Algorithm::Sha384),
+ nsICryptoHash::SHA512 => Ok(Algorithm::Sha512),
+ _ => Err(NS_ERROR_INVALID_ARG),
+ }
+ }
+}
+
+impl TryFrom<&nsACString> for Algorithm {
+ type Error = nsresult;
+
+ fn try_from(value: &nsACString) -> Result<Self, Self::Error> {
+ match value.to_utf8().borrow() {
+ "md5" => Ok(Algorithm::Md5),
+ "sha1" => Ok(Algorithm::Sha1),
+ "sha256" => Ok(Algorithm::Sha256),
+ "sha384" => Ok(Algorithm::Sha384),
+ "sha512" => Ok(Algorithm::Sha512),
+ _ => Err(NS_ERROR_INVALID_ARG),
+ }
+ }
+}
+
+#[xpcom(implement(nsICryptoHash), atomic)]
+struct CryptoHash {
+ digest: Mutex<Option<Box<dyn DynDigest>>>,
+}
+
+impl CryptoHash {
+ xpcom_method!(init => Init(algorithm: u32));
+ fn init(&self, algorithm: u32) -> Result<(), nsresult> {
+ let algorithm = algorithm.try_into()?;
+ self.init_with_algorithm(algorithm)
+ }
+
+ xpcom_method!(init_with_string => InitWithString(algorithm: *const nsACString));
+ fn init_with_string(&self, algorithm: &nsACString) -> Result<(), nsresult> {
+ let algorithm = algorithm.try_into()?;
+ self.init_with_algorithm(algorithm)
+ }
+
+ fn init_with_algorithm(&self, algorithm: Algorithm) -> Result<(), nsresult> {
+ let digest = match algorithm {
+ Algorithm::Md5 => Box::new(md5::Md5::new()) as Box<dyn DynDigest>,
+ Algorithm::Sha1 => Box::new(sha1::Sha1::new()) as Box<dyn DynDigest>,
+ Algorithm::Sha256 => Box::new(sha2::Sha256::new()) as Box<dyn DynDigest>,
+ Algorithm::Sha384 => Box::new(sha2::Sha384::new()) as Box<dyn DynDigest>,
+ Algorithm::Sha512 => Box::new(sha2::Sha512::new()) as Box<dyn DynDigest>,
+ };
+ let mut guard = self.digest.lock().map_err(|_| NS_ERROR_FAILURE)?;
+ if let Some(_expected_none_digest) = (*guard).replace(digest) {
+ return Err(NS_ERROR_FAILURE);
+ }
+ Ok(())
+ }
+
+ xpcom_method!(update => Update(data: *const u8, len: u32));
+ fn update(&self, data: *const u8, len: u32) -> Result<(), nsresult> {
+ let mut guard = self.digest.lock().map_err(|_| NS_ERROR_FAILURE)?;
+ let digest = match (*guard).as_mut() {
+ Some(digest) => digest,
+ None => return Err(NS_ERROR_NOT_INITIALIZED),
+ };
+ // Safety: this is safe as long as xpcom gave us valid arguments.
+ let data = unsafe {
+ std::slice::from_raw_parts(data, len.try_into().map_err(|_| NS_ERROR_INVALID_ARG)?)
+ };
+ digest.update(data);
+ Ok(())
+ }
+
+ xpcom_method!(update_from_stream => UpdateFromStream(stream: *const nsIInputStream, len: u32));
+ fn update_from_stream(&self, stream: &nsIInputStream, len: u32) -> Result<(), nsresult> {
+ let mut guard = self.digest.lock().map_err(|_| NS_ERROR_FAILURE)?;
+ let digest = match (*guard).as_mut() {
+ Some(digest) => digest,
+ None => return Err(NS_ERROR_NOT_INITIALIZED),
+ };
+ let mut available = 0u64;
+ unsafe { stream.Available(&mut available as *mut u64).to_result()? };
+ let mut to_read = if len == u32::MAX { available } else { len as u64 };
+ if available == 0 || available < to_read {
+ return Err(NS_ERROR_NOT_AVAILABLE);
+ }
+ let mut buf = vec![0u8; 4096];
+ let buf_len = buf.len() as u64;
+ while to_read > 0 {
+ let chunk_len = if to_read >= buf_len { buf_len as u32 } else { to_read as u32 };
+ let mut read = 0u32;
+ unsafe {
+ stream
+ .Read(
+ buf.as_mut_ptr() as *mut libc::c_char,
+ chunk_len,
+ &mut read as *mut u32,
+ )
+ .to_result()?
+ };
+ if read > chunk_len {
+ return Err(NS_ERROR_FAILURE);
+ }
+ digest.update(&buf[0..read.try_into().map_err(|_| NS_ERROR_FAILURE)?]);
+ to_read -= read as u64;
+ }
+ Ok(())
+ }
+
+ xpcom_method!(finish => Finish(ascii: bool) -> nsACString);
+ fn finish(&self, ascii: bool) -> Result<nsCString, nsresult> {
+ let mut guard = self.digest.lock().map_err(|_| NS_ERROR_FAILURE)?;
+ let digest = match (*guard).take() {
+ Some(digest) => digest,
+ None => return Err(NS_ERROR_NOT_INITIALIZED),
+ };
+ let result = digest.finalize();
+ if ascii {
+ Ok(nsCString::from(
+ base64::engine::general_purpose::STANDARD.encode(result),
+ ))
+ } else {
+ Ok(nsCString::from(result))
+ }
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn crypto_hash_constructor(
+ iid: *const xpcom::nsIID,
+ result: *mut *mut xpcom::reexports::libc::c_void,
+) -> nserror::nsresult {
+ let crypto_hash = CryptoHash::allocate(InitCryptoHash {
+ digest: Mutex::new(None),
+ });
+ unsafe { crypto_hash.QueryInterface(iid, result) }
+}