1use {
27 crate::{
28 embedded_signature::{
29 read_and_validate_blob_header, CodeSigningMagic, RequirementBlob, RequirementSetBlob,
30 },
31 error::AppleCodesignError,
32 },
33 bcder::Oid,
34 chrono::TimeZone,
35 scroll::{IOwrite, Pread},
36 std::{
37 borrow::Cow,
38 cmp::Ordering,
39 fmt::{Debug, Display},
40 io::Write,
41 ops::{Deref, DerefMut},
42 },
43};
44
45const OPCODE_FLAG_MASK: u32 = 0xff000000;
46const OPCODE_VALUE_MASK: u32 = 0x00ffffff;
47
48#[allow(unused)]
50const OPCODE_FLAG_DEFAULT_FALSE: u32 = 0x80000000;
51
52#[allow(unused)]
54const OPCODE_FLAG_SKIP: u32 = 0x40000000;
55
56#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
58#[repr(u32)]
59pub enum RequirementType {
60 Host,
62 Guest,
64 Designated,
66 Library,
68 Plugin,
70 Unknown(u32),
72}
73
74impl From<u32> for RequirementType {
75 fn from(v: u32) -> Self {
76 match v {
77 1 => Self::Host,
78 2 => Self::Guest,
79 3 => Self::Designated,
80 4 => Self::Library,
81 5 => Self::Plugin,
82 _ => Self::Unknown(v),
83 }
84 }
85}
86
87impl From<RequirementType> for u32 {
88 fn from(t: RequirementType) -> Self {
89 match t {
90 RequirementType::Host => 1,
91 RequirementType::Guest => 2,
92 RequirementType::Designated => 3,
93 RequirementType::Library => 4,
94 RequirementType::Plugin => 5,
95 RequirementType::Unknown(v) => v,
96 }
97 }
98}
99
100impl PartialOrd for RequirementType {
101 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
102 Some(self.cmp(other))
103 }
104}
105
106impl Ord for RequirementType {
107 fn cmp(&self, other: &Self) -> Ordering {
108 u32::from(*self).cmp(&u32::from(*other))
109 }
110}
111
112impl std::fmt::Display for RequirementType {
113 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
114 match self {
115 Self::Host => f.write_str("host(1)"),
116 Self::Guest => f.write_str("guest(2)"),
117 Self::Designated => f.write_str("designated(3)"),
118 Self::Library => f.write_str("library(4)"),
119 Self::Plugin => f.write_str("plugin(5)"),
120 Self::Unknown(v) => f.write_fmt(format_args!("unknown({v})")),
121 }
122 }
123}
124
125fn read_data(data: &[u8]) -> Result<(&[u8], &[u8]), AppleCodesignError> {
126 let length = data.pread_with::<u32>(0, scroll::BE)?;
127 let value = &data[4..4 + length as usize];
128
129 let offset = 4 + length as usize;
131
132 let offset = match offset % 4 {
133 0 => offset,
134 extra => offset + 4 - extra,
135 };
136
137 let remaining = &data[offset..];
138
139 Ok((value, remaining))
140}
141
142fn write_data(dest: &mut impl Write, data: &[u8]) -> Result<(), AppleCodesignError> {
143 dest.iowrite_with(data.len() as u32, scroll::BE)?;
144 dest.write_all(data)?;
145
146 match data.len() % 4 {
147 0 => {}
148 pad => {
149 for _ in 0..4 - pad {
150 dest.iowrite(0u8)?;
151 }
152 }
153 }
154
155 Ok(())
156}
157
158fn format_certificate_slot(slot: i32) -> String {
160 match slot {
161 -1 => "root".to_string(),
162 0 => "leaf".to_string(),
163 _ => format!("{slot}"),
164 }
165}
166
167#[derive(Clone, Debug, Eq, PartialEq)]
172pub enum CodeRequirementValue<'a> {
173 String(Cow<'a, str>),
174 Bytes(Cow<'a, [u8]>),
175}
176
177impl<'a> From<&'a [u8]> for CodeRequirementValue<'a> {
178 fn from(value: &'a [u8]) -> Self {
179 let is_ascii_printable = |c: &u8| -> bool {
180 c.is_ascii_alphanumeric() || c.is_ascii_whitespace() || c.is_ascii_punctuation()
181 };
182
183 if value.iter().all(is_ascii_printable) {
184 Self::String(unsafe { std::str::from_utf8_unchecked(value) }.into())
185 } else {
186 Self::Bytes(value.into())
187 }
188 }
189}
190
191impl<'a> From<&'a str> for CodeRequirementValue<'a> {
192 fn from(s: &'a str) -> Self {
193 Self::String(s.into())
194 }
195}
196
197impl<'a> From<Cow<'a, str>> for CodeRequirementValue<'a> {
198 fn from(v: Cow<'a, str>) -> Self {
199 Self::String(v)
200 }
201}
202
203impl From<String> for CodeRequirementValue<'static> {
204 fn from(v: String) -> Self {
205 Self::String(Cow::Owned(v))
206 }
207}
208
209impl<'a> Display for CodeRequirementValue<'a> {
210 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
211 match self {
212 Self::String(s) => f.write_str(s),
213 Self::Bytes(data) => f.write_fmt(format_args!("{}", hex::encode(data))),
214 }
215 }
216}
217
218impl<'a> CodeRequirementValue<'a> {
219 fn write_encoded(&self, dest: &mut impl Write) -> Result<(), AppleCodesignError> {
223 match self {
224 Self::Bytes(data) => write_data(dest, data),
225 Self::String(s) => write_data(dest, s.as_bytes()),
226 }
227 }
228}
229
230#[derive(Clone, Copy, Debug, PartialEq)]
232#[repr(u32)]
233enum RequirementOpCode {
234 False = 0,
235 True = 1,
236 Identifier = 2,
237 AnchorApple = 3,
238 AnchorCertificateHash = 4,
239 InfoKeyValueLegacy = 5,
240 And = 6,
241 Or = 7,
242 CodeDirectoryHash = 8,
243 Not = 9,
244 InfoPlistExpression = 10,
245 CertificateField = 11,
246 CertificateTrusted = 12,
247 AnchorTrusted = 13,
248 CertificateGeneric = 14,
249 AnchorAppleGeneric = 15,
250 EntitlementsField = 16,
251 CertificatePolicy = 17,
252 NamedAnchor = 18,
253 NamedCode = 19,
254 Platform = 20,
255 Notarized = 21,
256 CertificateFieldDate = 22,
257 LegacyDeveloperId = 23,
258}
259
260impl TryFrom<u32> for RequirementOpCode {
261 type Error = AppleCodesignError;
262
263 fn try_from(v: u32) -> Result<Self, Self::Error> {
264 match v {
265 0 => Ok(Self::False),
266 1 => Ok(Self::True),
267 2 => Ok(Self::Identifier),
268 3 => Ok(Self::AnchorApple),
269 4 => Ok(Self::AnchorCertificateHash),
270 5 => Ok(Self::InfoKeyValueLegacy),
271 6 => Ok(Self::And),
272 7 => Ok(Self::Or),
273 8 => Ok(Self::CodeDirectoryHash),
274 9 => Ok(Self::Not),
275 10 => Ok(Self::InfoPlistExpression),
276 11 => Ok(Self::CertificateField),
277 12 => Ok(Self::CertificateTrusted),
278 13 => Ok(Self::AnchorTrusted),
279 14 => Ok(Self::CertificateGeneric),
280 15 => Ok(Self::AnchorAppleGeneric),
281 16 => Ok(Self::EntitlementsField),
282 17 => Ok(Self::CertificatePolicy),
283 18 => Ok(Self::NamedAnchor),
284 19 => Ok(Self::NamedCode),
285 20 => Ok(Self::Platform),
286 21 => Ok(Self::Notarized),
287 22 => Ok(Self::CertificateFieldDate),
288 23 => Ok(Self::LegacyDeveloperId),
289 _ => Err(AppleCodesignError::RequirementUnknownOpcode(v)),
290 }
291 }
292}
293
294impl RequirementOpCode {
295 pub fn parse_payload<'a>(
300 &self,
301 data: &'a [u8],
302 ) -> Result<(CodeRequirementExpression<'a>, &'a [u8]), AppleCodesignError> {
303 match self {
304 Self::False => Ok((CodeRequirementExpression::False, data)),
305 Self::True => Ok((CodeRequirementExpression::True, data)),
306 Self::Identifier => {
307 let (value, data) = read_data(data)?;
308 let s = std::str::from_utf8(value).map_err(|_| {
309 AppleCodesignError::RequirementMalformed("identifier value not a UTF-8 string")
310 })?;
311
312 Ok((CodeRequirementExpression::Identifier(Cow::from(s)), data))
313 }
314 Self::AnchorApple => Ok((CodeRequirementExpression::AnchorApple, data)),
315 Self::AnchorCertificateHash => {
316 let slot = data.pread_with::<i32>(0, scroll::BE)?;
317 let digest_length = data.pread_with::<u32>(4, scroll::BE)?;
318 let digest = &data[8..8 + digest_length as usize];
319
320 Ok((
321 CodeRequirementExpression::AnchorCertificateHash(slot, digest.into()),
322 &data[8 + digest_length as usize..],
323 ))
324 }
325 Self::InfoKeyValueLegacy => {
326 let (key, data) = read_data(data)?;
327
328 let key = std::str::from_utf8(key).map_err(|_| {
329 AppleCodesignError::RequirementMalformed("info key not a UTF-8 string")
330 })?;
331
332 let (value, data) = read_data(data)?;
333
334 let value = std::str::from_utf8(value).map_err(|_| {
335 AppleCodesignError::RequirementMalformed("info value not a UTF-8 string")
336 })?;
337
338 Ok((
339 CodeRequirementExpression::InfoKeyValueLegacy(key.into(), value.into()),
340 data,
341 ))
342 }
343 Self::And => {
344 let (a, data) = CodeRequirementExpression::from_bytes(data)?;
345 let (b, data) = CodeRequirementExpression::from_bytes(data)?;
346
347 Ok((
348 CodeRequirementExpression::And(Box::new(a), Box::new(b)),
349 data,
350 ))
351 }
352 Self::Or => {
353 let (a, data) = CodeRequirementExpression::from_bytes(data)?;
354 let (b, data) = CodeRequirementExpression::from_bytes(data)?;
355
356 Ok((
357 CodeRequirementExpression::Or(Box::new(a), Box::new(b)),
358 data,
359 ))
360 }
361 Self::CodeDirectoryHash => {
362 let (value, data) = read_data(data)?;
363
364 Ok((
365 CodeRequirementExpression::CodeDirectoryHash(value.into()),
366 data,
367 ))
368 }
369 Self::Not => {
370 let (expr, data) = CodeRequirementExpression::from_bytes(data)?;
371
372 Ok((CodeRequirementExpression::Not(Box::new(expr)), data))
373 }
374 Self::InfoPlistExpression => {
375 let (key, data) = read_data(data)?;
376
377 let key = std::str::from_utf8(key).map_err(|_| {
378 AppleCodesignError::RequirementMalformed("key is not valid UTF-8")
379 })?;
380
381 let (expr, data) = CodeRequirementMatchExpression::from_bytes(data)?;
382
383 Ok((
384 CodeRequirementExpression::InfoPlistKeyField(key.into(), expr),
385 data,
386 ))
387 }
388 Self::CertificateField => {
389 let slot = data.pread_with::<i32>(0, scroll::BE)?;
390
391 let (field, data) = read_data(&data[4..])?;
392
393 let field = std::str::from_utf8(field).map_err(|_| {
394 AppleCodesignError::RequirementMalformed("certificate field is not valid UTF-8")
395 })?;
396
397 let (expr, data) = CodeRequirementMatchExpression::from_bytes(data)?;
398
399 Ok((
400 CodeRequirementExpression::CertificateField(slot, field.into(), expr),
401 data,
402 ))
403 }
404 Self::CertificateTrusted => {
405 let slot = data.pread_with::<i32>(0, scroll::BE)?;
406
407 Ok((
408 CodeRequirementExpression::CertificateTrusted(slot),
409 &data[4..],
410 ))
411 }
412 Self::AnchorTrusted => Ok((CodeRequirementExpression::AnchorTrusted, data)),
413 Self::CertificateGeneric => {
414 let slot = data.pread_with::<i32>(0, scroll::BE)?;
415
416 let (oid, data) = read_data(&data[4..])?;
417
418 let (expr, data) = CodeRequirementMatchExpression::from_bytes(data)?;
419
420 Ok((
421 CodeRequirementExpression::CertificateGeneric(slot, Oid(oid), expr),
422 data,
423 ))
424 }
425 Self::AnchorAppleGeneric => Ok((CodeRequirementExpression::AnchorAppleGeneric, data)),
426 Self::EntitlementsField => {
427 let (key, data) = read_data(data)?;
428
429 let key = std::str::from_utf8(key).map_err(|_| {
430 AppleCodesignError::RequirementMalformed("entitlement key is not UTF-8")
431 })?;
432
433 let (expr, data) = CodeRequirementMatchExpression::from_bytes(data)?;
434
435 Ok((
436 CodeRequirementExpression::EntitlementsKey(key.into(), expr),
437 data,
438 ))
439 }
440 Self::CertificatePolicy => {
441 let slot = data.pread_with::<i32>(0, scroll::BE)?;
442
443 let (oid, data) = read_data(&data[4..])?;
444
445 let (expr, data) = CodeRequirementMatchExpression::from_bytes(data)?;
446
447 Ok((
448 CodeRequirementExpression::CertificatePolicy(slot, Oid(oid), expr),
449 data,
450 ))
451 }
452 Self::NamedAnchor => {
453 let (name, data) = read_data(data)?;
454
455 let name = std::str::from_utf8(name).map_err(|_| {
456 AppleCodesignError::RequirementMalformed("named anchor isn't UTF-8")
457 })?;
458
459 Ok((CodeRequirementExpression::NamedAnchor(name.into()), data))
460 }
461 Self::NamedCode => {
462 let (name, data) = read_data(data)?;
463
464 let name = std::str::from_utf8(name).map_err(|_| {
465 AppleCodesignError::RequirementMalformed("named code isn't UTF-8")
466 })?;
467
468 Ok((CodeRequirementExpression::NamedCode(name.into()), data))
469 }
470 Self::Platform => {
471 let value = data.pread_with::<u32>(0, scroll::BE)?;
472
473 Ok((CodeRequirementExpression::Platform(value), &data[4..]))
474 }
475 Self::Notarized => Ok((CodeRequirementExpression::Notarized, data)),
476 Self::CertificateFieldDate => {
477 let slot = data.pread_with::<i32>(0, scroll::BE)?;
478
479 let (oid, data) = read_data(&data[4..])?;
480
481 let (expr, data) = CodeRequirementMatchExpression::from_bytes(data)?;
482
483 Ok((
484 CodeRequirementExpression::CertificateFieldDate(slot, Oid(oid), expr),
485 data,
486 ))
487 }
488 Self::LegacyDeveloperId => Ok((CodeRequirementExpression::LegacyDeveloperId, data)),
489 }
490 }
491}
492
493#[derive(Clone, Debug, PartialEq)]
495pub enum CodeRequirementExpression<'a> {
496 False,
502
503 True,
509
510 Identifier(Cow<'a, str>),
516
517 AnchorApple,
523
524 AnchorCertificateHash(i32, Cow<'a, [u8]>),
530
531 InfoKeyValueLegacy(Cow<'a, str>, Cow<'a, str>),
537
538 And(
544 Box<CodeRequirementExpression<'a>>,
545 Box<CodeRequirementExpression<'a>>,
546 ),
547
548 Or(
554 Box<CodeRequirementExpression<'a>>,
555 Box<CodeRequirementExpression<'a>>,
556 ),
557
558 CodeDirectoryHash(Cow<'a, [u8]>),
564
565 Not(Box<CodeRequirementExpression<'a>>),
571
572 InfoPlistKeyField(Cow<'a, str>, CodeRequirementMatchExpression<'a>),
580
581 CertificateField(i32, Cow<'a, str>, CodeRequirementMatchExpression<'a>),
587
588 CertificateTrusted(i32),
594
595 AnchorTrusted,
601
602 CertificateGeneric(i32, Oid<&'a [u8]>, CodeRequirementMatchExpression<'a>),
608
609 AnchorAppleGeneric,
615
616 EntitlementsKey(Cow<'a, str>, CodeRequirementMatchExpression<'a>),
622
623 CertificatePolicy(i32, Oid<&'a [u8]>, CodeRequirementMatchExpression<'a>),
629
630 NamedAnchor(Cow<'a, str>),
636
637 NamedCode(Cow<'a, str>),
643
644 Platform(u32),
650
651 Notarized,
657
658 CertificateFieldDate(i32, Oid<&'a [u8]>, CodeRequirementMatchExpression<'a>),
664
665 LegacyDeveloperId,
667}
668
669impl<'a> Display for CodeRequirementExpression<'a> {
670 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
671 match self {
672 Self::False => f.write_str("never"),
673 Self::True => f.write_str("always"),
674 Self::Identifier(value) => f.write_fmt(format_args!("identifier \"{value}\"")),
675 Self::AnchorApple => f.write_str("anchor apple"),
676 Self::AnchorCertificateHash(slot, digest) => f.write_fmt(format_args!(
677 "certificate {} = H\"{}\"",
678 format_certificate_slot(*slot),
679 hex::encode(digest)
680 )),
681 Self::InfoKeyValueLegacy(key, value) => {
682 f.write_fmt(format_args!("info[{key}] = \"{value}\""))
683 }
684 Self::And(a, b) => f.write_fmt(format_args!("({a}) and ({b})")),
685 Self::Or(a, b) => f.write_fmt(format_args!("({a}) or ({b})")),
686 Self::CodeDirectoryHash(digest) => {
687 f.write_fmt(format_args!("cdhash H\"{}\"", hex::encode(digest)))
688 }
689 Self::Not(expr) => f.write_fmt(format_args!("!({expr})")),
690 Self::InfoPlistKeyField(key, expr) => f.write_fmt(format_args!("info [{key}] {expr}")),
691 Self::CertificateField(slot, field, expr) => f.write_fmt(format_args!(
692 "certificate {}[{}] {}",
693 format_certificate_slot(*slot),
694 field,
695 expr
696 )),
697 Self::CertificateTrusted(slot) => {
698 f.write_fmt(format_args!("certificate {slot} trusted"))
699 }
700 Self::AnchorTrusted => f.write_str("anchor trusted"),
701 Self::CertificateGeneric(slot, oid, expr) => f.write_fmt(format_args!(
702 "certificate {}[field.{}] {}",
703 format_certificate_slot(*slot),
704 oid,
705 expr
706 )),
707 Self::AnchorAppleGeneric => f.write_str("anchor apple generic"),
708 Self::EntitlementsKey(key, expr) => {
709 f.write_fmt(format_args!("entitlement [{key}] {expr}"))
710 }
711 Self::CertificatePolicy(slot, oid, expr) => f.write_fmt(format_args!(
712 "certificate {}[policy.{}] {}",
713 format_certificate_slot(*slot),
714 oid,
715 expr
716 )),
717 Self::NamedAnchor(name) => f.write_fmt(format_args!("anchor apple {name}")),
718 Self::NamedCode(name) => f.write_fmt(format_args!("({name})")),
719 Self::Platform(platform) => f.write_fmt(format_args!("platform = {platform}")),
720 Self::Notarized => f.write_str("notarized"),
721 Self::CertificateFieldDate(slot, oid, expr) => f.write_fmt(format_args!(
722 "certificate {}[timestamp.{}] {}",
723 format_certificate_slot(*slot),
724 oid,
725 expr
726 )),
727 Self::LegacyDeveloperId => f.write_str("legacy"),
728 }
729 }
730}
731
732impl<'a> From<&CodeRequirementExpression<'a>> for RequirementOpCode {
733 fn from(e: &CodeRequirementExpression) -> Self {
734 match e {
735 CodeRequirementExpression::False => RequirementOpCode::False,
736 CodeRequirementExpression::True => RequirementOpCode::True,
737 CodeRequirementExpression::Identifier(_) => RequirementOpCode::Identifier,
738 CodeRequirementExpression::AnchorApple => RequirementOpCode::AnchorApple,
739 CodeRequirementExpression::AnchorCertificateHash(_, _) => {
740 RequirementOpCode::AnchorCertificateHash
741 }
742 CodeRequirementExpression::InfoKeyValueLegacy(_, _) => {
743 RequirementOpCode::InfoKeyValueLegacy
744 }
745 CodeRequirementExpression::And(_, _) => RequirementOpCode::And,
746 CodeRequirementExpression::Or(_, _) => RequirementOpCode::Or,
747 CodeRequirementExpression::CodeDirectoryHash(_) => RequirementOpCode::CodeDirectoryHash,
748 CodeRequirementExpression::Not(_) => RequirementOpCode::Not,
749 CodeRequirementExpression::InfoPlistKeyField(_, _) => {
750 RequirementOpCode::InfoPlistExpression
751 }
752 CodeRequirementExpression::CertificateField(_, _, _) => {
753 RequirementOpCode::CertificateField
754 }
755 CodeRequirementExpression::CertificateTrusted(_) => {
756 RequirementOpCode::CertificateTrusted
757 }
758 CodeRequirementExpression::AnchorTrusted => RequirementOpCode::AnchorTrusted,
759 CodeRequirementExpression::CertificateGeneric(_, _, _) => {
760 RequirementOpCode::CertificateGeneric
761 }
762 CodeRequirementExpression::AnchorAppleGeneric => RequirementOpCode::AnchorAppleGeneric,
763 CodeRequirementExpression::EntitlementsKey(_, _) => {
764 RequirementOpCode::EntitlementsField
765 }
766 CodeRequirementExpression::CertificatePolicy(_, _, _) => {
767 RequirementOpCode::CertificatePolicy
768 }
769 CodeRequirementExpression::NamedAnchor(_) => RequirementOpCode::NamedAnchor,
770 CodeRequirementExpression::NamedCode(_) => RequirementOpCode::NamedCode,
771 CodeRequirementExpression::Platform(_) => RequirementOpCode::Platform,
772 CodeRequirementExpression::Notarized => RequirementOpCode::Notarized,
773 CodeRequirementExpression::CertificateFieldDate(_, _, _) => {
774 RequirementOpCode::CertificateFieldDate
775 }
776 CodeRequirementExpression::LegacyDeveloperId => RequirementOpCode::LegacyDeveloperId,
777 }
778 }
779}
780
781impl<'a> CodeRequirementExpression<'a> {
782 pub fn from_bytes(data: &'a [u8]) -> Result<(Self, &'a [u8]), AppleCodesignError> {
786 let opcode_raw = data.pread_with::<u32>(0, scroll::BE)?;
787
788 let _flags = opcode_raw & OPCODE_FLAG_MASK;
789 let opcode = opcode_raw & OPCODE_VALUE_MASK;
790
791 let data = &data[4..];
792
793 let opcode = RequirementOpCode::try_from(opcode)?;
794
795 opcode.parse_payload(data)
796 }
797
798 pub fn write_to(&self, dest: &mut impl Write) -> Result<(), AppleCodesignError> {
800 dest.iowrite_with(RequirementOpCode::from(self) as u32, scroll::BE)?;
801
802 match self {
803 Self::False => {}
804 Self::True => {}
805 Self::Identifier(s) => {
806 write_data(dest, s.as_bytes())?;
807 }
808 Self::AnchorApple => {}
809 Self::AnchorCertificateHash(slot, hash) => {
810 dest.iowrite_with(*slot, scroll::BE)?;
811 write_data(dest, hash)?;
812 }
813 Self::InfoKeyValueLegacy(key, value) => {
814 write_data(dest, key.as_bytes())?;
815 write_data(dest, value.as_bytes())?;
816 }
817 Self::And(a, b) => {
818 a.write_to(dest)?;
819 b.write_to(dest)?;
820 }
821 Self::Or(a, b) => {
822 a.write_to(dest)?;
823 b.write_to(dest)?;
824 }
825 Self::CodeDirectoryHash(hash) => {
826 write_data(dest, hash)?;
827 }
828 Self::Not(expr) => {
829 expr.write_to(dest)?;
830 }
831 Self::InfoPlistKeyField(key, m) => {
832 write_data(dest, key.as_bytes())?;
833 m.write_to(dest)?;
834 }
835 Self::CertificateField(slot, field, m) => {
836 dest.iowrite_with(*slot, scroll::BE)?;
837 write_data(dest, field.as_bytes())?;
838 m.write_to(dest)?;
839 }
840 Self::CertificateTrusted(slot) => {
841 dest.iowrite_with(*slot, scroll::BE)?;
842 }
843 Self::AnchorTrusted => {}
844 Self::CertificateGeneric(slot, oid, m) => {
845 dest.iowrite_with(*slot, scroll::BE)?;
846 write_data(dest, oid.as_ref())?;
847 m.write_to(dest)?;
848 }
849 Self::AnchorAppleGeneric => {}
850 Self::EntitlementsKey(key, m) => {
851 write_data(dest, key.as_bytes())?;
852 m.write_to(dest)?;
853 }
854 Self::CertificatePolicy(slot, oid, m) => {
855 dest.iowrite_with(*slot, scroll::BE)?;
856 write_data(dest, oid.as_ref())?;
857 m.write_to(dest)?;
858 }
859 Self::NamedAnchor(value) => {
860 write_data(dest, value.as_bytes())?;
861 }
862 Self::NamedCode(value) => {
863 write_data(dest, value.as_bytes())?;
864 }
865 Self::Platform(value) => {
866 dest.iowrite_with(*value, scroll::BE)?;
867 }
868 Self::Notarized => {}
869 Self::CertificateFieldDate(slot, oid, m) => {
870 dest.iowrite_with(*slot, scroll::BE)?;
871 write_data(dest, oid.as_ref())?;
872 m.write_to(dest)?;
873 }
874 Self::LegacyDeveloperId => {}
875 }
876
877 Ok(())
878 }
879
880 pub fn to_bytes(&self) -> Result<Vec<u8>, AppleCodesignError> {
884 let mut res = vec![];
885
886 self.write_to(&mut res)?;
887
888 Ok(res)
889 }
890}
891
892#[derive(Clone, Copy, Debug, PartialEq)]
894#[repr(u32)]
895enum MatchType {
896 Exists = 0,
897 Equal = 1,
898 Contains = 2,
899 BeginsWith = 3,
900 EndsWith = 4,
901 LessThan = 5,
902 GreaterThan = 6,
903 LessThanEqual = 7,
904 GreaterThanEqual = 8,
905 On = 9,
906 Before = 10,
907 After = 11,
908 OnOrBefore = 12,
909 OnOrAfter = 13,
910 Absent = 14,
911}
912
913impl TryFrom<u32> for MatchType {
914 type Error = AppleCodesignError;
915
916 fn try_from(v: u32) -> Result<Self, Self::Error> {
917 match v {
918 0 => Ok(Self::Exists),
919 1 => Ok(Self::Equal),
920 2 => Ok(Self::Contains),
921 3 => Ok(Self::BeginsWith),
922 4 => Ok(Self::EndsWith),
923 5 => Ok(Self::LessThan),
924 6 => Ok(Self::GreaterThan),
925 7 => Ok(Self::LessThanEqual),
926 8 => Ok(Self::GreaterThanEqual),
927 9 => Ok(Self::On),
928 10 => Ok(Self::Before),
929 11 => Ok(Self::After),
930 12 => Ok(Self::OnOrBefore),
931 13 => Ok(Self::OnOrAfter),
932 14 => Ok(Self::Absent),
933 _ => Err(AppleCodesignError::RequirementUnknownMatchExpression(v)),
934 }
935 }
936}
937
938impl MatchType {
939 pub fn parse_payload<'a>(
941 &self,
942 data: &'a [u8],
943 ) -> Result<(CodeRequirementMatchExpression<'a>, &'a [u8]), AppleCodesignError> {
944 match self {
945 Self::Exists => Ok((CodeRequirementMatchExpression::Exists, data)),
946 Self::Equal => {
947 let (value, data) = read_data(data)?;
948
949 Ok((CodeRequirementMatchExpression::Equal(value.into()), data))
950 }
951 Self::Contains => {
952 let (value, data) = read_data(data)?;
953
954 Ok((CodeRequirementMatchExpression::Contains(value.into()), data))
955 }
956 Self::BeginsWith => {
957 let (value, data) = read_data(data)?;
958
959 Ok((
960 CodeRequirementMatchExpression::BeginsWith(value.into()),
961 data,
962 ))
963 }
964 Self::EndsWith => {
965 let (value, data) = read_data(data)?;
966
967 Ok((CodeRequirementMatchExpression::EndsWith(value.into()), data))
968 }
969 Self::LessThan => {
970 let (value, data) = read_data(data)?;
971
972 Ok((CodeRequirementMatchExpression::LessThan(value.into()), data))
973 }
974 Self::GreaterThan => {
975 let (value, data) = read_data(data)?;
976
977 Ok((
978 CodeRequirementMatchExpression::GreaterThan(value.into()),
979 data,
980 ))
981 }
982 Self::LessThanEqual => {
983 let (value, data) = read_data(data)?;
984
985 Ok((
986 CodeRequirementMatchExpression::LessThanEqual(value.into()),
987 data,
988 ))
989 }
990 Self::GreaterThanEqual => {
991 let (value, data) = read_data(data)?;
992
993 Ok((
994 CodeRequirementMatchExpression::GreaterThanEqual(value.into()),
995 data,
996 ))
997 }
998 Self::On => {
999 let value = data.pread_with::<i64>(0, scroll::BE)?;
1000
1001 Ok((
1002 CodeRequirementMatchExpression::On(
1003 chrono::Utc
1004 .timestamp_opt(value, 0)
1005 .single()
1006 .ok_or(AppleCodesignError::BadTime)?,
1007 ),
1008 &data[8..],
1009 ))
1010 }
1011 Self::Before => {
1012 let value = data.pread_with::<i64>(0, scroll::BE)?;
1013
1014 Ok((
1015 CodeRequirementMatchExpression::Before(
1016 chrono::Utc
1017 .timestamp_opt(value, 0)
1018 .single()
1019 .ok_or(AppleCodesignError::BadTime)?,
1020 ),
1021 &data[8..],
1022 ))
1023 }
1024 Self::After => {
1025 let value = data.pread_with::<i64>(0, scroll::BE)?;
1026
1027 Ok((
1028 CodeRequirementMatchExpression::After(
1029 chrono::Utc
1030 .timestamp_opt(value, 0)
1031 .single()
1032 .ok_or(AppleCodesignError::BadTime)?,
1033 ),
1034 &data[8..],
1035 ))
1036 }
1037 Self::OnOrBefore => {
1038 let value = data.pread_with::<i64>(0, scroll::BE)?;
1039
1040 Ok((
1041 CodeRequirementMatchExpression::OnOrBefore(
1042 chrono::Utc
1043 .timestamp_opt(value, 0)
1044 .single()
1045 .ok_or(AppleCodesignError::BadTime)?,
1046 ),
1047 &data[8..],
1048 ))
1049 }
1050 Self::OnOrAfter => {
1051 let value = data.pread_with::<i64>(0, scroll::BE)?;
1052
1053 Ok((
1054 CodeRequirementMatchExpression::OnOrAfter(
1055 chrono::Utc
1056 .timestamp_opt(value, 0)
1057 .single()
1058 .ok_or(AppleCodesignError::BadTime)?,
1059 ),
1060 &data[8..],
1061 ))
1062 }
1063 Self::Absent => Ok((CodeRequirementMatchExpression::Absent, data)),
1064 }
1065 }
1066}
1067
1068#[derive(Clone, Debug, Eq, PartialEq)]
1070pub enum CodeRequirementMatchExpression<'a> {
1071 Exists,
1077
1078 Equal(CodeRequirementValue<'a>),
1084
1085 Contains(CodeRequirementValue<'a>),
1091
1092 BeginsWith(CodeRequirementValue<'a>),
1098
1099 EndsWith(CodeRequirementValue<'a>),
1105
1106 LessThan(CodeRequirementValue<'a>),
1112
1113 GreaterThan(CodeRequirementValue<'a>),
1117
1118 LessThanEqual(CodeRequirementValue<'a>),
1124
1125 GreaterThanEqual(CodeRequirementValue<'a>),
1131
1132 On(chrono::DateTime<chrono::Utc>),
1136
1137 Before(chrono::DateTime<chrono::Utc>),
1141
1142 After(chrono::DateTime<chrono::Utc>),
1146
1147 OnOrBefore(chrono::DateTime<chrono::Utc>),
1151
1152 OnOrAfter(chrono::DateTime<chrono::Utc>),
1156
1157 Absent,
1163}
1164
1165impl<'a> Display for CodeRequirementMatchExpression<'a> {
1166 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1167 match self {
1168 Self::Exists => f.write_str("/* exists */"),
1169 Self::Equal(value) => f.write_fmt(format_args!("= \"{value}\"")),
1170 Self::Contains(value) => f.write_fmt(format_args!("~ \"{value}\"")),
1171 Self::BeginsWith(value) => f.write_fmt(format_args!("= \"{value}*\"")),
1172 Self::EndsWith(value) => f.write_fmt(format_args!("= \"*{value}\"")),
1173 Self::LessThan(value) => f.write_fmt(format_args!("< \"{value}\"")),
1174 Self::GreaterThan(value) => f.write_fmt(format_args!("> \"{value}\"")),
1175 Self::LessThanEqual(value) => f.write_fmt(format_args!("<= \"{value}\"")),
1176 Self::GreaterThanEqual(value) => f.write_fmt(format_args!(">= \"{value}\"")),
1177 Self::On(value) => f.write_fmt(format_args!("= \"{value}\"")),
1178 Self::Before(value) => f.write_fmt(format_args!("< \"{value}\"")),
1179 Self::After(value) => f.write_fmt(format_args!("> \"{value}\"")),
1180 Self::OnOrBefore(value) => f.write_fmt(format_args!("<= \"{value}\"")),
1181 Self::OnOrAfter(value) => f.write_fmt(format_args!(">= \"{value}\"")),
1182 Self::Absent => f.write_str("absent"),
1183 }
1184 }
1185}
1186
1187impl<'a> From<&CodeRequirementMatchExpression<'a>> for MatchType {
1188 fn from(m: &CodeRequirementMatchExpression<'a>) -> Self {
1189 match m {
1190 CodeRequirementMatchExpression::Exists => MatchType::Exists,
1191 CodeRequirementMatchExpression::Equal(_) => MatchType::Equal,
1192 CodeRequirementMatchExpression::Contains(_) => MatchType::Contains,
1193 CodeRequirementMatchExpression::BeginsWith(_) => MatchType::BeginsWith,
1194 CodeRequirementMatchExpression::EndsWith(_) => MatchType::EndsWith,
1195 CodeRequirementMatchExpression::LessThan(_) => MatchType::LessThan,
1196 CodeRequirementMatchExpression::GreaterThan(_) => MatchType::GreaterThan,
1197 CodeRequirementMatchExpression::LessThanEqual(_) => MatchType::LessThanEqual,
1198 CodeRequirementMatchExpression::GreaterThanEqual(_) => MatchType::GreaterThanEqual,
1199 CodeRequirementMatchExpression::On(_) => MatchType::On,
1200 CodeRequirementMatchExpression::Before(_) => MatchType::Before,
1201 CodeRequirementMatchExpression::After(_) => MatchType::After,
1202 CodeRequirementMatchExpression::OnOrBefore(_) => MatchType::OnOrBefore,
1203 CodeRequirementMatchExpression::OnOrAfter(_) => MatchType::OnOrAfter,
1204 CodeRequirementMatchExpression::Absent => MatchType::Absent,
1205 }
1206 }
1207}
1208
1209impl<'a> CodeRequirementMatchExpression<'a> {
1210 pub fn from_bytes(data: &'a [u8]) -> Result<(Self, &'a [u8]), AppleCodesignError> {
1214 let typ = data.pread_with::<u32>(0, scroll::BE)?;
1215
1216 let typ = MatchType::try_from(typ)?;
1217
1218 typ.parse_payload(&data[4..])
1219 }
1220
1221 pub fn write_to(&self, dest: &mut impl Write) -> Result<(), AppleCodesignError> {
1223 dest.iowrite_with(MatchType::from(self) as u32, scroll::BE)?;
1224
1225 match self {
1226 Self::Exists => {}
1227 Self::Equal(value) => value.write_encoded(dest)?,
1228 Self::Contains(value) => value.write_encoded(dest)?,
1229 Self::BeginsWith(value) => value.write_encoded(dest)?,
1230 Self::EndsWith(value) => value.write_encoded(dest)?,
1231 Self::LessThan(value) => value.write_encoded(dest)?,
1232 Self::GreaterThan(value) => value.write_encoded(dest)?,
1233 Self::LessThanEqual(value) => value.write_encoded(dest)?,
1234 Self::GreaterThanEqual(value) => value.write_encoded(dest)?,
1235 Self::On(value) => dest.iowrite_with(value.timestamp(), scroll::BE)?,
1236 Self::Before(value) => dest.iowrite_with(value.timestamp(), scroll::BE)?,
1237 Self::After(value) => dest.iowrite_with(value.timestamp(), scroll::BE)?,
1238 Self::OnOrBefore(value) => dest.iowrite_with(value.timestamp(), scroll::BE)?,
1239 Self::OnOrAfter(value) => dest.iowrite_with(value.timestamp(), scroll::BE)?,
1240 Self::Absent => {}
1241 }
1242
1243 Ok(())
1244 }
1245}
1246
1247#[derive(Clone, Debug, Default, PartialEq)]
1249pub struct CodeRequirements<'a>(Vec<CodeRequirementExpression<'a>>);
1250
1251impl<'a> Deref for CodeRequirements<'a> {
1252 type Target = Vec<CodeRequirementExpression<'a>>;
1253
1254 fn deref(&self) -> &Self::Target {
1255 &self.0
1256 }
1257}
1258
1259impl<'a> DerefMut for CodeRequirements<'a> {
1260 fn deref_mut(&mut self) -> &mut Self::Target {
1261 &mut self.0
1262 }
1263}
1264
1265impl<'a> Display for CodeRequirements<'a> {
1266 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1267 for (i, expr) in self.0.iter().enumerate() {
1268 f.write_fmt(format_args!("{i}: {expr};"))?;
1269 }
1270
1271 Ok(())
1272 }
1273}
1274
1275impl<'a> From<Vec<CodeRequirementExpression<'a>>> for CodeRequirements<'a> {
1276 fn from(v: Vec<CodeRequirementExpression<'a>>) -> Self {
1277 Self(v)
1278 }
1279}
1280
1281impl<'a> CodeRequirements<'a> {
1282 pub fn parse_binary(data: &'a [u8]) -> Result<(Self, &'a [u8]), AppleCodesignError> {
1287 let count = data.pread_with::<u32>(0, scroll::BE)?;
1288 let mut data = &data[4..];
1289
1290 let mut elements = Vec::with_capacity(count as usize);
1291
1292 for _ in 0..count {
1293 let res = CodeRequirementExpression::from_bytes(data)?;
1294
1295 elements.push(res.0);
1296 data = res.1;
1297 }
1298
1299 Ok((Self(elements), data))
1300 }
1301
1302 pub fn parse_blob(data: &'a [u8]) -> Result<(Self, &'a [u8]), AppleCodesignError> {
1306 let data = read_and_validate_blob_header(
1307 data,
1308 u32::from(CodeSigningMagic::Requirement),
1309 "code requirement blob",
1310 )
1311 .map_err(|_| AppleCodesignError::RequirementMalformed("blob header"))?;
1312
1313 Self::parse_binary(data)
1314 }
1315
1316 pub fn write_to(&self, dest: &mut impl Write) -> Result<(), AppleCodesignError> {
1320 dest.iowrite_with(self.0.len() as u32, scroll::BE)?;
1321 for e in &self.0 {
1322 e.write_to(dest)?;
1323 }
1324
1325 Ok(())
1326 }
1327
1328 pub fn to_blob_data(&self) -> Result<Vec<u8>, AppleCodesignError> {
1335 let mut payload = vec![];
1336 self.write_to(&mut payload)?;
1337
1338 let mut dest = Vec::with_capacity(payload.len() + 8);
1339 dest.iowrite_with(u32::from(CodeSigningMagic::Requirement), scroll::BE)?;
1340 dest.iowrite_with(dest.capacity() as u32, scroll::BE)?;
1341 dest.write_all(&payload)?;
1342
1343 Ok(dest)
1344 }
1345
1346 pub fn add_to_requirement_set(
1348 &self,
1349 requirements_set: &mut RequirementSetBlob,
1350 slot: RequirementType,
1351 ) -> Result<(), AppleCodesignError> {
1352 let blob = RequirementBlob::try_from(self)?;
1353
1354 requirements_set.set_requirements(slot, blob);
1355
1356 Ok(())
1357 }
1358}
1359
1360impl<'a> TryFrom<&CodeRequirements<'a>> for RequirementBlob<'static> {
1361 type Error = AppleCodesignError;
1362
1363 fn try_from(requirements: &CodeRequirements<'a>) -> Result<Self, Self::Error> {
1364 let mut data = Vec::<u8>::new();
1365 requirements.write_to(&mut data)?;
1366
1367 Ok(Self {
1368 data: Cow::Owned(data),
1369 })
1370 }
1371}
1372
1373#[cfg(test)]
1374mod test {
1375 use super::*;
1376
1377 fn verify_roundtrip(reqs: &CodeRequirements, source: &[u8]) {
1378 let mut dest = Vec::<u8>::new();
1379 reqs.write_to(&mut dest).unwrap();
1380 assert_eq!(dest.as_slice(), source);
1381 }
1382
1383 #[test]
1384 fn parse_false() {
1385 let source = hex::decode("0000000100000000").unwrap();
1386
1387 let (els, data) = CodeRequirements::parse_binary(&source).unwrap();
1388
1389 assert_eq!(
1390 els,
1391 CodeRequirements(vec![CodeRequirementExpression::False])
1392 );
1393 assert!(data.is_empty());
1394 verify_roundtrip(&els, &source);
1395 }
1396
1397 #[test]
1398 fn parse_true() {
1399 let source = hex::decode("0000000100000001").unwrap();
1400
1401 let (els, data) = CodeRequirements::parse_binary(&source).unwrap();
1402
1403 assert_eq!(els, CodeRequirements(vec![CodeRequirementExpression::True]));
1404 assert!(data.is_empty());
1405 verify_roundtrip(&els, &source);
1406 }
1407
1408 #[test]
1409 fn parse_identifier() {
1410 let source = hex::decode("000000010000000200000007666f6f2e62617200").unwrap();
1411
1412 let (els, data) = CodeRequirements::parse_binary(&source).unwrap();
1413
1414 assert_eq!(
1415 els,
1416 CodeRequirements(vec![CodeRequirementExpression::Identifier(
1417 "foo.bar".into()
1418 )])
1419 );
1420 assert!(data.is_empty());
1421 verify_roundtrip(&els, &source);
1422 }
1423
1424 #[test]
1425 fn parse_anchor_apple() {
1426 let source = hex::decode("0000000100000003").unwrap();
1427
1428 let (els, data) = CodeRequirements::parse_binary(&source).unwrap();
1429
1430 assert_eq!(
1431 els,
1432 CodeRequirements(vec![CodeRequirementExpression::AnchorApple])
1433 );
1434 assert!(data.is_empty());
1435 verify_roundtrip(&els, &source);
1436 }
1437
1438 #[test]
1439 fn parse_anchor_certificate_hash() {
1440 let source =
1441 hex::decode("0000000100000004ffffffff00000014deadbeefdeadbeefdeadbeefdeadbeefdeadbeef")
1442 .unwrap();
1443
1444 let (els, data) = CodeRequirements::parse_binary(&source).unwrap();
1445
1446 assert_eq!(
1447 els,
1448 CodeRequirements(vec![CodeRequirementExpression::AnchorCertificateHash(
1449 -1,
1450 hex::decode("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef")
1451 .unwrap()
1452 .into()
1453 )])
1454 );
1455 assert!(data.is_empty());
1456 verify_roundtrip(&els, &source);
1457 }
1458
1459 #[test]
1460 fn parse_and() {
1461 let source = hex::decode("00000001000000060000000100000000").unwrap();
1462
1463 let (els, data) = CodeRequirements::parse_binary(&source).unwrap();
1464
1465 assert_eq!(
1466 els,
1467 CodeRequirements(vec![CodeRequirementExpression::And(
1468 Box::new(CodeRequirementExpression::True),
1469 Box::new(CodeRequirementExpression::False)
1470 )])
1471 );
1472 assert!(data.is_empty());
1473 verify_roundtrip(&els, &source);
1474 }
1475
1476 #[test]
1477 fn parse_or() {
1478 let source = hex::decode("00000001000000070000000100000000").unwrap();
1479
1480 let (els, data) = CodeRequirements::parse_binary(&source).unwrap();
1481
1482 assert_eq!(
1483 els,
1484 CodeRequirements(vec![CodeRequirementExpression::Or(
1485 Box::new(CodeRequirementExpression::True),
1486 Box::new(CodeRequirementExpression::False)
1487 )])
1488 );
1489 assert!(data.is_empty());
1490 verify_roundtrip(&els, &source);
1491 }
1492
1493 #[test]
1494 fn parse_code_directory_hash() {
1495 let source =
1496 hex::decode("000000010000000800000014deadbeefdeadbeefdeadbeefdeadbeefdeadbeef")
1497 .unwrap();
1498
1499 let (els, data) = CodeRequirements::parse_binary(&source).unwrap();
1500
1501 assert_eq!(
1502 els,
1503 CodeRequirements(vec![CodeRequirementExpression::CodeDirectoryHash(
1504 hex::decode("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef")
1505 .unwrap()
1506 .into()
1507 )])
1508 );
1509 assert!(data.is_empty());
1510 verify_roundtrip(&els, &source);
1511 }
1512
1513 #[test]
1514 fn parse_not() {
1515 let source = hex::decode("000000010000000900000001").unwrap();
1516
1517 let (els, data) = CodeRequirements::parse_binary(&source).unwrap();
1518
1519 assert_eq!(
1520 els,
1521 CodeRequirements(vec![CodeRequirementExpression::Not(Box::new(
1522 CodeRequirementExpression::True
1523 ))])
1524 );
1525 assert!(data.is_empty());
1526 verify_roundtrip(&els, &source);
1527 }
1528
1529 #[test]
1530 fn parse_info_plist_key_field() {
1531 let source = hex::decode("000000010000000a000000036b65790000000000").unwrap();
1532
1533 let (els, data) = CodeRequirements::parse_binary(&source).unwrap();
1534
1535 assert_eq!(
1536 els,
1537 CodeRequirements(vec![CodeRequirementExpression::InfoPlistKeyField(
1538 "key".into(),
1539 CodeRequirementMatchExpression::Exists
1540 )])
1541 );
1542 assert!(data.is_empty());
1543 verify_roundtrip(&els, &source);
1544 }
1545
1546 #[test]
1547 fn parse_certificate_field() {
1548 let source =
1549 hex::decode("000000010000000bffffffff0000000a7375626a6563742e434e000000000000")
1550 .unwrap();
1551
1552 let (els, data) = CodeRequirements::parse_binary(&source).unwrap();
1553
1554 assert_eq!(
1555 els,
1556 CodeRequirements(vec![CodeRequirementExpression::CertificateField(
1557 -1,
1558 "subject.CN".into(),
1559 CodeRequirementMatchExpression::Exists
1560 )])
1561 );
1562 assert!(data.is_empty());
1563 verify_roundtrip(&els, &source);
1564 }
1565
1566 #[test]
1567 fn parse_certificate_trusted() {
1568 let source = hex::decode("000000010000000cffffffff").unwrap();
1569
1570 let (els, data) = CodeRequirements::parse_binary(&source).unwrap();
1571
1572 assert_eq!(
1573 els,
1574 CodeRequirements(vec![CodeRequirementExpression::CertificateTrusted(-1)])
1575 );
1576 assert!(data.is_empty());
1577 verify_roundtrip(&els, &source);
1578 }
1579
1580 #[test]
1581 fn parse_anchor_trusted() {
1582 let source = hex::decode("000000010000000d").unwrap();
1583
1584 let (els, data) = CodeRequirements::parse_binary(&source).unwrap();
1585
1586 assert_eq!(
1587 els,
1588 CodeRequirements(vec![CodeRequirementExpression::AnchorTrusted])
1589 );
1590 assert!(data.is_empty());
1591 verify_roundtrip(&els, &source);
1592 }
1593
1594 #[test]
1595 fn parse_certificate_generic() {
1596 let source = hex::decode("000000010000000effffffff000000035504030000000000").unwrap();
1597
1598 let (els, data) = CodeRequirements::parse_binary(&source).unwrap();
1599
1600 assert_eq!(
1601 els,
1602 CodeRequirements(vec![CodeRequirementExpression::CertificateGeneric(
1603 -1,
1604 Oid(&[0x55, 4, 3]),
1605 CodeRequirementMatchExpression::Exists
1606 )])
1607 );
1608 assert!(data.is_empty());
1609 verify_roundtrip(&els, &source);
1610 }
1611
1612 #[test]
1613 fn parse_anchor_apple_generic() {
1614 let source = hex::decode("000000010000000f").unwrap();
1615
1616 let (els, data) = CodeRequirements::parse_binary(&source).unwrap();
1617
1618 assert_eq!(
1619 els,
1620 CodeRequirements(vec![CodeRequirementExpression::AnchorAppleGeneric])
1621 );
1622 assert!(data.is_empty());
1623 verify_roundtrip(&els, &source);
1624 }
1625
1626 #[test]
1627 fn parse_entitlements_key() {
1628 let source = hex::decode("0000000100000010000000036b65790000000000").unwrap();
1629
1630 let (els, data) = CodeRequirements::parse_binary(&source).unwrap();
1631
1632 assert_eq!(
1633 els,
1634 CodeRequirements(vec![CodeRequirementExpression::EntitlementsKey(
1635 "key".into(),
1636 CodeRequirementMatchExpression::Exists
1637 )])
1638 );
1639 assert!(data.is_empty());
1640 verify_roundtrip(&els, &source);
1641 }
1642
1643 #[test]
1644 fn parse_certificate_policy() {
1645 let source = hex::decode("0000000100000011ffffffff000000035504030000000000").unwrap();
1646
1647 let (els, data) = CodeRequirements::parse_binary(&source).unwrap();
1648
1649 assert_eq!(
1650 els,
1651 CodeRequirements(vec![CodeRequirementExpression::CertificatePolicy(
1652 -1,
1653 Oid(&[0x55, 4, 3]),
1654 CodeRequirementMatchExpression::Exists
1655 )])
1656 );
1657 assert!(data.is_empty());
1658 verify_roundtrip(&els, &source);
1659 }
1660
1661 #[test]
1662 fn parse_named_anchor() {
1663 let source = hex::decode("000000010000001200000003666f6f00").unwrap();
1664
1665 let (els, data) = CodeRequirements::parse_binary(&source).unwrap();
1666
1667 assert_eq!(
1668 els,
1669 CodeRequirements(vec![CodeRequirementExpression::NamedAnchor("foo".into())])
1670 );
1671 assert!(data.is_empty());
1672 verify_roundtrip(&els, &source);
1673 }
1674
1675 #[test]
1676 fn parse_named_code() {
1677 let source = hex::decode("000000010000001300000003666f6f00").unwrap();
1678
1679 let (els, data) = CodeRequirements::parse_binary(&source).unwrap();
1680
1681 assert_eq!(
1682 els,
1683 CodeRequirements(vec![CodeRequirementExpression::NamedCode("foo".into())])
1684 );
1685 assert!(data.is_empty());
1686 verify_roundtrip(&els, &source);
1687 }
1688
1689 #[test]
1690 fn parse_platform() {
1691 let source = hex::decode("00000001000000140000000a").unwrap();
1692
1693 let (els, data) = CodeRequirements::parse_binary(&source).unwrap();
1694
1695 assert_eq!(
1696 els,
1697 CodeRequirements(vec![CodeRequirementExpression::Platform(10)])
1698 );
1699 assert!(data.is_empty());
1700 verify_roundtrip(&els, &source);
1701 }
1702
1703 #[test]
1704 fn parse_notarized() {
1705 let source = hex::decode("0000000100000015").unwrap();
1706
1707 let (els, data) = CodeRequirements::parse_binary(&source).unwrap();
1708
1709 assert_eq!(
1710 els,
1711 CodeRequirements(vec![CodeRequirementExpression::Notarized])
1712 );
1713 assert!(data.is_empty());
1714 verify_roundtrip(&els, &source);
1715 }
1716
1717 #[test]
1718 fn parse_certificate_field_date() {
1719 let source = hex::decode("0000000100000016ffffffff000000035504030000000000").unwrap();
1720
1721 let (els, data) = CodeRequirements::parse_binary(&source).unwrap();
1722
1723 assert_eq!(
1724 els,
1725 CodeRequirements(vec![CodeRequirementExpression::CertificateFieldDate(
1726 -1,
1727 Oid(&[0x55, 4, 3]),
1728 CodeRequirementMatchExpression::Exists,
1729 )])
1730 );
1731 assert!(data.is_empty());
1732 verify_roundtrip(&els, &source);
1733 }
1734
1735 #[test]
1736 fn parse_legacy() {
1737 let source = hex::decode("0000000100000017").unwrap();
1738
1739 let (els, data) = CodeRequirements::parse_binary(&source).unwrap();
1740
1741 assert_eq!(
1742 els,
1743 CodeRequirements(vec![CodeRequirementExpression::LegacyDeveloperId])
1744 );
1745 assert!(data.is_empty());
1746 verify_roundtrip(&els, &source);
1747 }
1748
1749 #[test]
1750 fn parse_blob() {
1751 let source = hex::decode("fade0c00000000100000000100000000").unwrap();
1752
1753 let (els, data) = CodeRequirements::parse_blob(&source).unwrap();
1754
1755 assert_eq!(
1756 els,
1757 CodeRequirements(vec![CodeRequirementExpression::False])
1758 );
1759 assert!(data.is_empty());
1760
1761 let dest = els.to_blob_data().unwrap();
1762 assert_eq!(source, dest);
1763 }
1764
1765 #[test]
1766 fn parse_match_exists() {
1767 let source = hex::decode("000000010000000a000000036b65790000000000").unwrap();
1768
1769 let (els, data) = CodeRequirements::parse_binary(&source).unwrap();
1770
1771 assert_eq!(
1772 els,
1773 CodeRequirements(vec![CodeRequirementExpression::InfoPlistKeyField(
1774 "key".into(),
1775 CodeRequirementMatchExpression::Exists
1776 )])
1777 );
1778 assert!(data.is_empty());
1779 verify_roundtrip(&els, &source);
1780 }
1781
1782 #[test]
1783 fn parse_match_absent() {
1784 let source = hex::decode("000000010000000a000000036b6579000000000e").unwrap();
1785
1786 let (els, data) = CodeRequirements::parse_binary(&source).unwrap();
1787
1788 assert_eq!(
1789 els,
1790 CodeRequirements(vec![CodeRequirementExpression::InfoPlistKeyField(
1791 "key".into(),
1792 CodeRequirementMatchExpression::Absent
1793 )])
1794 );
1795 assert!(data.is_empty());
1796 verify_roundtrip(&els, &source);
1797 }
1798
1799 #[test]
1800 fn parse_match_equal() {
1801 let source =
1802 hex::decode("000000010000000a000000036b657900000000010000000576616c7565000000")
1803 .unwrap();
1804
1805 let (els, data) = CodeRequirements::parse_binary(&source).unwrap();
1806
1807 assert_eq!(
1808 els,
1809 CodeRequirements(vec![CodeRequirementExpression::InfoPlistKeyField(
1810 "key".into(),
1811 CodeRequirementMatchExpression::Equal(b"value".as_ref().into())
1812 )])
1813 );
1814 assert!(data.is_empty());
1815 verify_roundtrip(&els, &source);
1816 }
1817
1818 #[test]
1819 fn parse_match_contains() {
1820 let source =
1821 hex::decode("000000010000000a000000036b657900000000020000000576616c7565000000")
1822 .unwrap();
1823
1824 let (els, data) = CodeRequirements::parse_binary(&source).unwrap();
1825
1826 assert_eq!(
1827 els,
1828 CodeRequirements(vec![CodeRequirementExpression::InfoPlistKeyField(
1829 "key".into(),
1830 CodeRequirementMatchExpression::Contains(b"value".as_ref().into())
1831 )])
1832 );
1833 assert!(data.is_empty());
1834 verify_roundtrip(&els, &source);
1835 }
1836
1837 #[test]
1838 fn parse_match_begins_with() {
1839 let source =
1840 hex::decode("000000010000000a000000036b657900000000030000000576616c7565000000")
1841 .unwrap();
1842
1843 let (els, data) = CodeRequirements::parse_binary(&source).unwrap();
1844
1845 assert_eq!(
1846 els,
1847 CodeRequirements(vec![CodeRequirementExpression::InfoPlistKeyField(
1848 "key".into(),
1849 CodeRequirementMatchExpression::BeginsWith(b"value".as_ref().into())
1850 )])
1851 );
1852 assert!(data.is_empty());
1853 verify_roundtrip(&els, &source);
1854 }
1855
1856 #[test]
1857 fn parse_match_ends_with() {
1858 let source =
1859 hex::decode("000000010000000a000000036b657900000000040000000576616c7565000000")
1860 .unwrap();
1861
1862 let (els, data) = CodeRequirements::parse_binary(&source).unwrap();
1863
1864 assert_eq!(
1865 els,
1866 CodeRequirements(vec![CodeRequirementExpression::InfoPlistKeyField(
1867 "key".into(),
1868 CodeRequirementMatchExpression::EndsWith(b"value".as_ref().into())
1869 )])
1870 );
1871 assert!(data.is_empty());
1872 verify_roundtrip(&els, &source);
1873 }
1874
1875 #[test]
1876 fn parse_match_less_than() {
1877 let source =
1878 hex::decode("000000010000000a000000036b657900000000050000000576616c7565000000")
1879 .unwrap();
1880
1881 let (els, data) = CodeRequirements::parse_binary(&source).unwrap();
1882
1883 assert_eq!(
1884 els,
1885 CodeRequirements(vec![CodeRequirementExpression::InfoPlistKeyField(
1886 "key".into(),
1887 CodeRequirementMatchExpression::LessThan(b"value".as_ref().into())
1888 )])
1889 );
1890 assert!(data.is_empty());
1891 verify_roundtrip(&els, &source);
1892 }
1893
1894 #[test]
1895 fn parse_match_greater_than() {
1896 let source =
1897 hex::decode("000000010000000a000000036b657900000000060000000576616c7565000000")
1898 .unwrap();
1899
1900 let (els, data) = CodeRequirements::parse_binary(&source).unwrap();
1901
1902 assert_eq!(
1903 els,
1904 CodeRequirements(vec![CodeRequirementExpression::InfoPlistKeyField(
1905 "key".into(),
1906 CodeRequirementMatchExpression::GreaterThan(b"value".as_ref().into())
1907 )])
1908 );
1909 assert!(data.is_empty());
1910 verify_roundtrip(&els, &source);
1911 }
1912
1913 #[test]
1914 fn parse_match_less_than_equal() {
1915 let source =
1916 hex::decode("000000010000000a000000036b657900000000070000000576616c7565000000")
1917 .unwrap();
1918
1919 let (els, data) = CodeRequirements::parse_binary(&source).unwrap();
1920
1921 assert_eq!(
1922 els,
1923 CodeRequirements(vec![CodeRequirementExpression::InfoPlistKeyField(
1924 "key".into(),
1925 CodeRequirementMatchExpression::LessThanEqual(b"value".as_ref().into())
1926 )])
1927 );
1928 assert!(data.is_empty());
1929 verify_roundtrip(&els, &source);
1930 }
1931
1932 #[test]
1933 fn parse_match_greater_than_equal() {
1934 let source =
1935 hex::decode("000000010000000a000000036b657900000000080000000576616c7565000000")
1936 .unwrap();
1937
1938 let (els, data) = CodeRequirements::parse_binary(&source).unwrap();
1939
1940 assert_eq!(
1941 els,
1942 CodeRequirements(vec![CodeRequirementExpression::InfoPlistKeyField(
1943 "key".into(),
1944 CodeRequirementMatchExpression::GreaterThanEqual(b"value".as_ref().into())
1945 )])
1946 );
1947 assert!(data.is_empty());
1948 verify_roundtrip(&els, &source);
1949 }
1950
1951 #[test]
1952 fn parse_match_on() {
1953 let source =
1954 hex::decode("000000010000000a000000036b6579000000000900000000605fca30").unwrap();
1955
1956 let (els, data) = CodeRequirements::parse_binary(&source).unwrap();
1957
1958 assert_eq!(
1959 els,
1960 CodeRequirements(vec![CodeRequirementExpression::InfoPlistKeyField(
1961 "key".into(),
1962 CodeRequirementMatchExpression::On(
1963 chrono::Utc.timestamp_opt(1616890416, 0).unwrap()
1964 ),
1965 )])
1966 );
1967 assert!(data.is_empty());
1968 verify_roundtrip(&els, &source);
1969 }
1970
1971 #[test]
1972 fn parse_match_before() {
1973 let source =
1974 hex::decode("000000010000000a000000036b6579000000000a00000000605fca30").unwrap();
1975
1976 let (els, data) = CodeRequirements::parse_binary(&source).unwrap();
1977
1978 assert_eq!(
1979 els,
1980 CodeRequirements(vec![CodeRequirementExpression::InfoPlistKeyField(
1981 "key".into(),
1982 CodeRequirementMatchExpression::Before(
1983 chrono::Utc.timestamp_opt(1616890416, 0).unwrap()
1984 ),
1985 )])
1986 );
1987 assert!(data.is_empty());
1988 verify_roundtrip(&els, &source);
1989 }
1990
1991 #[test]
1992 fn parse_match_after() {
1993 let source =
1994 hex::decode("000000010000000a000000036b6579000000000b00000000605fca30").unwrap();
1995
1996 let (els, data) = CodeRequirements::parse_binary(&source).unwrap();
1997
1998 assert_eq!(
1999 els,
2000 CodeRequirements(vec![CodeRequirementExpression::InfoPlistKeyField(
2001 "key".into(),
2002 CodeRequirementMatchExpression::After(
2003 chrono::Utc.timestamp_opt(1616890416, 0).unwrap()
2004 ),
2005 )])
2006 );
2007 assert!(data.is_empty());
2008 verify_roundtrip(&els, &source);
2009 }
2010
2011 #[test]
2012 fn parse_match_on_or_before() {
2013 let source =
2014 hex::decode("000000010000000a000000036b6579000000000c00000000605fca30").unwrap();
2015
2016 let (els, data) = CodeRequirements::parse_binary(&source).unwrap();
2017
2018 assert_eq!(
2019 els,
2020 CodeRequirements(vec![CodeRequirementExpression::InfoPlistKeyField(
2021 "key".into(),
2022 CodeRequirementMatchExpression::OnOrBefore(
2023 chrono::Utc.timestamp_opt(1616890416, 0).unwrap()
2024 ),
2025 )])
2026 );
2027 assert!(data.is_empty());
2028 verify_roundtrip(&els, &source);
2029 }
2030
2031 #[test]
2032 fn parse_match_on_or_after() {
2033 let source =
2034 hex::decode("000000010000000a000000036b6579000000000d00000000605fca30").unwrap();
2035
2036 let (els, data) = CodeRequirements::parse_binary(&source).unwrap();
2037
2038 assert_eq!(
2039 els,
2040 CodeRequirements(vec![CodeRequirementExpression::InfoPlistKeyField(
2041 "key".into(),
2042 CodeRequirementMatchExpression::OnOrAfter(
2043 chrono::Utc.timestamp_opt(1616890416, 0).unwrap()
2044 ),
2045 )])
2046 );
2047 assert!(data.is_empty());
2048 verify_roundtrip(&els, &source);
2049 }
2050}