blob: 7577f083e2a754b4da713181da0b326582d782c8 [file] [log] [blame]
//! Digest Transform support
use core_foundation::base::{CFIndex, TCFType};
use core_foundation::data::CFData;
use core_foundation::error::CFError;
use core_foundation::string::CFString;
use core_foundation_sys::base::CFTypeRef;
use core_foundation_sys::data::CFDataRef;
use core_foundation_sys::string::CFStringRef;
use security_framework_sys::digest_transform::*;
use security_framework_sys::transform::*;
use std::ptr;
use crate::os::macos::transform::SecTransform;
#[derive(Debug, Copy, Clone)]
/// A type of digest.
pub struct DigestType(CFStringRef);
#[allow(missing_docs)]
impl DigestType {
#[inline(always)]
#[must_use]
pub fn hmac_md5() -> Self {
unsafe { Self(kSecDigestHMACMD5) }
}
#[inline(always)]
#[must_use]
pub fn hmac_sha1() -> Self {
unsafe { Self(kSecDigestHMACSHA1) }
}
#[inline(always)]
#[must_use]
pub fn hmac_sha2() -> Self {
unsafe { Self(kSecDigestHMACSHA2) }
}
#[inline(always)]
#[must_use]
pub fn md2() -> Self {
unsafe { Self(kSecDigestMD2) }
}
#[inline(always)]
#[must_use]
pub fn md4() -> Self {
unsafe { Self(kSecDigestMD4) }
}
#[inline(always)]
#[must_use]
pub fn md5() -> Self {
unsafe { Self(kSecDigestMD5) }
}
#[inline(always)]
#[must_use]
pub fn sha1() -> Self {
unsafe { Self(kSecDigestSHA1) }
}
#[inline(always)]
#[must_use]
pub fn sha2() -> Self {
unsafe { Self(kSecDigestSHA2) }
}
#[inline(always)]
fn to_type(self) -> CFTypeRef {
self.0 as CFTypeRef
}
}
/// A builder for digest transform operations.
pub struct Builder {
digest_type: Option<DigestType>,
digest_length: Option<CFIndex>,
hmac_key: Option<CFData>,
}
impl Default for Builder {
#[inline(always)]
fn default() -> Self {
Self::new()
}
}
impl Builder {
/// Returns a new builder with default settings.
#[inline(always)]
#[must_use]
pub fn new() -> Self {
Self {
digest_type: None,
digest_length: None,
hmac_key: None,
}
}
/// Sets the type of digest to perform.
///
/// If not set, an appropriate digest will be selected for you.
#[inline]
pub fn type_(&mut self, digest_type: DigestType) -> &mut Self {
self.digest_type = Some(digest_type);
self
}
/// Sets the output length of the digest.
///
/// If not set, an appropriate length will be selected for you. Some digest
/// types only support specific output lengths.
#[inline]
pub fn length(&mut self, digest_length: CFIndex) -> &mut Self {
self.digest_length = Some(digest_length);
self
}
/// Sets the key used for HMAC digests.
///
/// Only applies to `HmacMd5`, `HmacSha1`, and `HmacSha2` digests.
#[inline]
pub fn hmac_key(&mut self, hmac_key: CFData) -> &mut Self {
self.hmac_key = Some(hmac_key);
self
}
/// Computes the digest of the data.
pub fn execute(&self, data: &CFData) -> Result<CFData, CFError> {
unsafe {
let digest_type = match self.digest_type {
Some(ref digest_type) => digest_type.to_type(),
None => ptr::null(),
};
let digest_length = self.digest_length.unwrap_or(0);
let mut error = ptr::null_mut();
let transform = SecDigestTransformCreate(digest_type, digest_length, &mut error);
if transform.is_null() {
return Err(CFError::wrap_under_create_rule(error));
}
let mut transform = SecTransform::wrap_under_create_rule(transform);
if let Some(ref hmac_key) = self.hmac_key {
let key = CFString::wrap_under_get_rule(kSecDigestHMACKeyAttribute);
transform.set_attribute(&key, hmac_key)?;
}
let key = CFString::wrap_under_get_rule(kSecTransformInputAttributeName);
transform.set_attribute(&key, data)?;
let result = transform.execute()?;
Ok(CFData::wrap_under_get_rule(
result.as_CFTypeRef() as CFDataRef
))
}
}
}
#[cfg(test)]
mod test {
use super::*;
use core_foundation::data::CFData;
use hex;
#[test]
fn md5() {
let data = CFData::from_buffer("The quick brown fox jumps over the lazy dog".as_bytes());
let hash = Builder::new()
.type_(DigestType::md5())
.execute(&data)
.unwrap();
assert_eq!(
hex::encode(hash.bytes()),
"9e107d9d372bb6826bd81d3542a419d6"
);
}
#[test]
fn hmac_sha1() {
let data = CFData::from_buffer("The quick brown fox jumps over the lazy dog".as_bytes());
let key = CFData::from_buffer(b"key");
let hash = Builder::new()
.type_(DigestType::hmac_sha1())
.hmac_key(key)
.execute(&data)
.unwrap();
assert_eq!(
hex::encode(hash.bytes()),
"de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9"
);
}
}