apple_security_framework/os/macos/
digest_transform.rs

1//! Digest Transform support
2
3use std::ptr;
4
5use core_foundation::{
6    base::{CFIndex, TCFType},
7    data::CFData,
8    error::CFError,
9    string::CFString,
10};
11use core_foundation_sys::{base::CFTypeRef, data::CFDataRef, string::CFStringRef};
12use security_framework_sys::{digest_transform::*, transform::*};
13
14use crate::os::macos::transform::SecTransform;
15
16#[derive(Debug, Copy, Clone)]
17/// A type of digest.
18pub struct DigestType(CFStringRef);
19
20#[allow(missing_docs)]
21impl DigestType {
22    #[inline(always)]
23    #[must_use]
24    pub fn hmac_md5() -> Self {
25        unsafe { Self(kSecDigestHMACMD5) }
26    }
27
28    #[inline(always)]
29    #[must_use]
30    pub fn hmac_sha1() -> Self {
31        unsafe { Self(kSecDigestHMACSHA1) }
32    }
33
34    #[inline(always)]
35    #[must_use]
36    pub fn hmac_sha2() -> Self {
37        unsafe { Self(kSecDigestHMACSHA2) }
38    }
39
40    #[inline(always)]
41    #[must_use]
42    pub fn md2() -> Self {
43        unsafe { Self(kSecDigestMD2) }
44    }
45
46    #[inline(always)]
47    #[must_use]
48    pub fn md4() -> Self {
49        unsafe { Self(kSecDigestMD4) }
50    }
51
52    #[inline(always)]
53    #[must_use]
54    pub fn md5() -> Self {
55        unsafe { Self(kSecDigestMD5) }
56    }
57
58    #[inline(always)]
59    #[must_use]
60    pub fn sha1() -> Self {
61        unsafe { Self(kSecDigestSHA1) }
62    }
63
64    #[inline(always)]
65    #[must_use]
66    pub fn sha2() -> Self {
67        unsafe { Self(kSecDigestSHA2) }
68    }
69
70    #[inline(always)]
71    fn to_type(self) -> CFTypeRef {
72        self.0 as CFTypeRef
73    }
74}
75
76/// A builder for digest transform operations.
77pub struct Builder {
78    digest_type: Option<DigestType>,
79    digest_length: Option<CFIndex>,
80    hmac_key: Option<CFData>,
81}
82
83impl Default for Builder {
84    #[inline(always)]
85    fn default() -> Self {
86        Self::new()
87    }
88}
89
90impl Builder {
91    /// Returns a new builder with default settings.
92    #[inline(always)]
93    #[must_use]
94    pub fn new() -> Self {
95        Self {
96            digest_type: None,
97            digest_length: None,
98            hmac_key: None,
99        }
100    }
101
102    /// Sets the type of digest to perform.
103    ///
104    /// If not set, an appropriate digest will be selected for you.
105    #[inline]
106    pub fn type_(&mut self, digest_type: DigestType) -> &mut Self {
107        self.digest_type = Some(digest_type);
108        self
109    }
110
111    /// Sets the output length of the digest.
112    ///
113    /// If not set, an appropriate length will be selected for you. Some digest
114    /// types only support specific output lengths.
115    #[inline]
116    pub fn length(&mut self, digest_length: CFIndex) -> &mut Self {
117        self.digest_length = Some(digest_length);
118        self
119    }
120
121    /// Sets the key used for HMAC digests.
122    ///
123    /// Only applies to `HmacMd5`, `HmacSha1`, and `HmacSha2` digests.
124    #[inline]
125    pub fn hmac_key(&mut self, hmac_key: CFData) -> &mut Self {
126        self.hmac_key = Some(hmac_key);
127        self
128    }
129
130    /// Computes the digest of the data.
131    pub fn execute(&self, data: &CFData) -> Result<CFData, CFError> {
132        unsafe {
133            let digest_type = match self.digest_type {
134                Some(ref digest_type) => digest_type.to_type(),
135                None => ptr::null(),
136            };
137
138            let digest_length = self.digest_length.unwrap_or(0);
139
140            let mut error = ptr::null_mut();
141            let transform = SecDigestTransformCreate(digest_type, digest_length, &mut error);
142            if transform.is_null() {
143                return Err(CFError::wrap_under_create_rule(error));
144            }
145            let mut transform = SecTransform::wrap_under_create_rule(transform);
146
147            if let Some(ref hmac_key) = self.hmac_key {
148                let key = CFString::wrap_under_get_rule(kSecDigestHMACKeyAttribute);
149                transform.set_attribute(&key, hmac_key)?;
150            }
151
152            let key = CFString::wrap_under_get_rule(kSecTransformInputAttributeName);
153            transform.set_attribute(&key, data)?;
154
155            let result = transform.execute()?;
156            Ok(CFData::wrap_under_get_rule(
157                result.as_CFTypeRef() as CFDataRef
158            ))
159        }
160    }
161}
162
163#[cfg(test)]
164mod test {
165    use core_foundation::data::CFData;
166    use hex;
167
168    use super::*;
169
170    #[test]
171    fn md5() {
172        let data = CFData::from_buffer("The quick brown fox jumps over the lazy dog".as_bytes());
173        let hash = Builder::new()
174            .type_(DigestType::md5())
175            .execute(&data)
176            .unwrap();
177        assert_eq!(
178            hex::encode(hash.bytes()),
179            "9e107d9d372bb6826bd81d3542a419d6"
180        );
181    }
182
183    #[test]
184    fn hmac_sha1() {
185        let data = CFData::from_buffer("The quick brown fox jumps over the lazy dog".as_bytes());
186        let key = CFData::from_buffer(b"key");
187        let hash = Builder::new()
188            .type_(DigestType::hmac_sha1())
189            .hmac_key(key)
190            .execute(&data)
191            .unwrap();
192        assert_eq!(
193            hex::encode(hash.bytes()),
194            "de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9"
195        );
196    }
197}