security_framework/os/macos/
digest_transform.rs1use core_foundation::base::{CFIndex, TCFType};
4use core_foundation::data::CFData;
5use core_foundation::error::CFError;
6use core_foundation::string::CFString;
7use core_foundation_sys::base::CFTypeRef;
8use core_foundation_sys::data::CFDataRef;
9use core_foundation_sys::string::CFStringRef;
10use security_framework_sys::digest_transform::*;
11use security_framework_sys::transform::kSecTransformInputAttributeName;
12use std::ptr;
13
14use crate::os::macos::transform::SecTransform;
15
16#[derive(Debug, Copy, Clone)]
17pub 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
76pub 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 #[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 #[inline]
106 pub fn type_(&mut self, digest_type: DigestType) -> &mut Self {
107 self.digest_type = Some(digest_type);
108 self
109 }
110
111 #[inline]
116 pub fn length(&mut self, digest_length: CFIndex) -> &mut Self {
117 self.digest_length = Some(digest_length);
118 self
119 }
120
121 #[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 pub fn execute(&self, data: &CFData) -> Result<CFData, CFError> {
133 unsafe {
134 let digest_type = match self.digest_type {
135 Some(ref digest_type) => digest_type.to_type(),
136 None => ptr::null(),
137 };
138
139 let digest_length = self.digest_length.unwrap_or(0);
140
141 let mut error = ptr::null_mut();
142 let transform = SecDigestTransformCreate(digest_type, digest_length, &mut error);
143 if transform.is_null() {
144 return Err(CFError::wrap_under_create_rule(error));
145 }
146 let mut transform = SecTransform::wrap_under_create_rule(transform);
147
148 if let Some(ref hmac_key) = self.hmac_key {
149 let key = CFString::wrap_under_get_rule(kSecDigestHMACKeyAttribute);
150 transform.set_attribute(&key, hmac_key)?;
151 }
152
153 let key = CFString::wrap_under_get_rule(kSecTransformInputAttributeName);
154 transform.set_attribute(&key, data)?;
155
156 let result = transform.execute()?;
157 Ok(CFData::wrap_under_get_rule(result.as_CFTypeRef() as CFDataRef))
158 }
159 }
160}
161
162#[cfg(test)]
163mod test {
164 use super::*;
165
166 #[test]
167 fn md5() {
168 let data = CFData::from_buffer("The quick brown fox jumps over the lazy dog".as_bytes());
169 let hash = Builder::new().type_(DigestType::md5()).execute(&data).unwrap();
170 assert_eq!(hex::encode(hash.bytes()), "9e107d9d372bb6826bd81d3542a419d6");
171 }
172
173 #[test]
174 fn hmac_sha1() {
175 let data = CFData::from_buffer("The quick brown fox jumps over the lazy dog".as_bytes());
176 let key = CFData::from_buffer(b"key");
177 let hash = Builder::new().type_(DigestType::hmac_sha1()).hmac_key(key).execute(&data).unwrap();
178 assert_eq!(hex::encode(hash.bytes()), "de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9");
179 }
180}