Skip to content

Commit

Permalink
Bug 1834862 - implement nsICryptoHash in rust r=jschanck,supply-chain…
Browse files Browse the repository at this point in the history
…-reviewers, a=dmeehan

Differential Revision: https://phabricator.services.mozilla.com/D179727
  • Loading branch information
mozkeeler committed Jun 7, 2023
1 parent 168ecf5 commit be07b1d
Show file tree
Hide file tree
Showing 25 changed files with 1,062 additions and 270 deletions.
25 changes: 25 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions security/manager/ssl/components.conf
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ Classes = [
{
'cid': '{36a1d3b3-d886-4317-96ff-87b0005cfef7}',
'contract_ids': ['@mozilla.org/security/hash;1'],
'type': 'nsCryptoHash',
'legacy_constructor': 'mozilla::psm::NSSConstructor<nsCryptoHash>',
'headers': ['/security/manager/ssl/crypto_hash/crypto_hash.h'],
'legacy_constructor': 'crypto_hash_constructor',
},
{
'cid': '{45a5fe2f-c350-4b86-962d-02d5aaaa955a}',
Expand Down
15 changes: 15 additions & 0 deletions security/manager/ssl/crypto_hash/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "crypto_hash"
version = "0.1.0"
edition = "2021"

[dependencies]
base64 = "0.21"
digest = "0.10.2"
libc = "0.2"
md-5 = "0.10.2"
nserror = { path = "../../../../xpcom/rust/nserror" }
nsstring = { path = "../../../../xpcom/rust/nsstring" }
sha1 = "0.10.2"
sha2 = "0.10.2"
xpcom = { path = "../../../../xpcom/rust/xpcom" }
46 changes: 46 additions & 0 deletions security/manager/ssl/crypto_hash/crypto_hash.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/* 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/. */

#include "nsICryptoHash.h"

extern "C" {
nsresult crypto_hash_constructor(REFNSIID iid, void** result);
};

nsresult NS_NewCryptoHash(uint32_t aHashType, nsICryptoHash** aOutHasher) {
MOZ_ASSERT(aOutHasher);

nsCOMPtr<nsICryptoHash> hasher;
nsresult rv =
crypto_hash_constructor(NS_ICRYPTOHASH_IID, getter_AddRefs(hasher));
if (NS_FAILED(rv)) {
return rv;
}
rv = hasher->Init(aHashType);
if (NS_FAILED(rv)) {
return rv;
}
hasher.forget(aOutHasher);

return NS_OK;
}

nsresult NS_NewCryptoHash(const nsACString& aHashType,
nsICryptoHash** aOutHasher) {
MOZ_ASSERT(aOutHasher);

nsCOMPtr<nsICryptoHash> hasher;
nsresult rv =
crypto_hash_constructor(NS_ICRYPTOHASH_IID, getter_AddRefs(hasher));
if (NS_FAILED(rv)) {
return rv;
}
rv = hasher->InitWithString(aHashType);
if (NS_FAILED(rv)) {
return rv;
}
hasher.forget(aOutHasher);

return NS_OK;
}
177 changes: 177 additions & 0 deletions security/manager/ssl/crypto_hash/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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) }
}
1 change: 0 additions & 1 deletion security/manager/ssl/moz.build
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,6 @@ UNIFIED_SOURCES += [
"nsCertOverrideService.cpp",
"nsCertTree.cpp",
"nsClientAuthRemember.cpp",
"nsCryptoHash.cpp",
"nsNSSCallbacks.cpp",
"nsNSSCertHelper.cpp",
"nsNSSCertificate.cpp",
Expand Down
Loading

0 comments on commit be07b1d

Please sign in to comment.