1use {
43 crate::{
44 code_directory::CodeDirectoryBlob,
45 code_requirement::{CodeRequirements, RequirementType},
46 cryptography::DigestType,
47 environment_constraints::EncodedEnvironmentConstraints,
48 AppleCodesignError, Result,
49 },
50 cryptographic_message_syntax::SignedData,
51 scroll::{IOwrite, Pread},
52 std::{borrow::Cow, cmp::Ordering, collections::HashMap, io::Write},
53};
54
55#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
57pub enum CodeSigningMagic {
58 Requirement,
60 RequirementSet,
62 CodeDirectory,
64 EmbeddedSignature,
68 EmbeddedSignatureOld,
70 Entitlements,
72 EntitlementsDer,
74 EnvironmentContraintsDer,
78 DetachedSignature,
80 BlobWrapper,
84 Unknown(u32),
86}
87
88impl From<u32> for CodeSigningMagic {
89 fn from(v: u32) -> Self {
90 match v {
91 0xfade0c00 => Self::Requirement,
92 0xfade0c01 => Self::RequirementSet,
93 0xfade0c02 => Self::CodeDirectory,
94 0xfade0cc0 => Self::EmbeddedSignature,
95 0xfade0b02 => Self::EmbeddedSignatureOld,
96 0xfade7171 => Self::Entitlements,
97 0xfade7172 => Self::EntitlementsDer,
98 0xfade8181 => Self::EnvironmentContraintsDer,
99 0xfade0cc1 => Self::DetachedSignature,
100 0xfade0b01 => Self::BlobWrapper,
101 _ => Self::Unknown(v),
102 }
103 }
104}
105
106impl From<CodeSigningMagic> for u32 {
107 fn from(magic: CodeSigningMagic) -> u32 {
108 match magic {
109 CodeSigningMagic::Requirement => 0xfade0c00,
110 CodeSigningMagic::RequirementSet => 0xfade0c01,
111 CodeSigningMagic::CodeDirectory => 0xfade0c02,
112 CodeSigningMagic::EmbeddedSignature => 0xfade0cc0,
113 CodeSigningMagic::EmbeddedSignatureOld => 0xfade0b02,
114 CodeSigningMagic::Entitlements => 0xfade7171,
115 CodeSigningMagic::EntitlementsDer => 0xfade7172,
116 CodeSigningMagic::EnvironmentContraintsDer => 0xfade8181,
117 CodeSigningMagic::DetachedSignature => 0xfade0cc1,
118 CodeSigningMagic::BlobWrapper => 0xfade0b01,
119 CodeSigningMagic::Unknown(v) => v,
120 }
121 }
122}
123
124impl From<u8> for DigestType {
127 fn from(v: u8) -> Self {
128 match v {
129 0 => Self::None,
130 1 => Self::Sha1,
131 2 => Self::Sha256,
132 3 => Self::Sha256Truncated,
133 4 => Self::Sha384,
134 5 => Self::Sha512,
135 _ => Self::Unknown(v),
136 }
137 }
138}
139
140impl From<DigestType> for u8 {
141 fn from(v: DigestType) -> u8 {
142 match v {
143 DigestType::None => 0,
144 DigestType::Sha1 => 1,
145 DigestType::Sha256 => 2,
146 DigestType::Sha256Truncated => 3,
147 DigestType::Sha384 => 4,
148 DigestType::Sha512 => 5,
149 DigestType::Unknown(v) => v,
150 }
151 }
152}
153
154#[derive(Clone, Copy, PartialEq, Eq, Hash)]
156pub enum CodeSigningSlot {
157 CodeDirectory,
158 Info,
160 RequirementSet,
162 ResourceDir,
164 Application,
166 Entitlements,
168 RepSpecific,
170 EntitlementsDer,
172 LaunchConstraintsSelf,
174 LaunchConstraintsParent,
176 LaunchConstraintsResponsibleProcess,
178 LibraryConstraints,
180
181 AlternateCodeDirectory0,
188 AlternateCodeDirectory1,
189 AlternateCodeDirectory2,
190 AlternateCodeDirectory3,
191 AlternateCodeDirectory4,
192 Signature,
194 Identification,
195 Ticket,
197 Unknown(u32),
198}
199
200impl std::fmt::Debug for CodeSigningSlot {
201 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
202 match self {
203 Self::CodeDirectory => {
204 f.write_fmt(format_args!("CodeDirectory ({})", u32::from(*self)))
205 }
206 Self::Info => f.write_fmt(format_args!("Info ({})", u32::from(*self))),
207 Self::RequirementSet => {
208 f.write_fmt(format_args!("RequirementSet ({})", u32::from(*self)))
209 }
210 Self::ResourceDir => f.write_fmt(format_args!("Resources ({})", u32::from(*self))),
211 Self::Application => f.write_fmt(format_args!("Application ({})", u32::from(*self))),
212 Self::Entitlements => f.write_fmt(format_args!("Entitlements ({})", u32::from(*self))),
213 Self::RepSpecific => f.write_fmt(format_args!("Rep Specific ({})", u32::from(*self))),
214 Self::EntitlementsDer => {
215 f.write_fmt(format_args!("DER Entitlements ({})", u32::from(*self)))
216 }
217 Self::LaunchConstraintsSelf => f.write_fmt(format_args!(
218 "DER Launch Constraints on Self ({})",
219 u32::from(*self)
220 )),
221 Self::LaunchConstraintsParent => f.write_fmt(format_args!(
222 "DER Launch Constraints on Parent ({})",
223 u32::from(*self)
224 )),
225 Self::LaunchConstraintsResponsibleProcess => f.write_fmt(format_args!(
226 "DER Launch Constraints on Responsible Process ({})",
227 u32::from(*self)
228 )),
229 Self::LibraryConstraints => f.write_fmt(format_args!(
230 "DER Launch Constraints on Loaded Libraries ({})",
231 u32::from(*self)
232 )),
233 Self::AlternateCodeDirectory0 => f.write_fmt(format_args!(
234 "CodeDirectory Alternate #0 ({})",
235 u32::from(*self)
236 )),
237 Self::AlternateCodeDirectory1 => f.write_fmt(format_args!(
238 "CodeDirectory Alternate #1 ({})",
239 u32::from(*self)
240 )),
241 Self::AlternateCodeDirectory2 => f.write_fmt(format_args!(
242 "CodeDirectory Alternate #2 ({})",
243 u32::from(*self)
244 )),
245 Self::AlternateCodeDirectory3 => f.write_fmt(format_args!(
246 "CodeDirectory Alternate #3 ({})",
247 u32::from(*self)
248 )),
249 Self::AlternateCodeDirectory4 => f.write_fmt(format_args!(
250 "CodeDirectory Alternate #4 ({})",
251 u32::from(*self)
252 )),
253 Self::Signature => f.write_fmt(format_args!("CMS Signature ({})", u32::from(*self))),
254 Self::Identification => {
255 f.write_fmt(format_args!("Identification ({})", u32::from(*self)))
256 }
257 Self::Ticket => f.write_fmt(format_args!("Ticket ({})", u32::from(*self))),
258 Self::Unknown(value) => f.write_fmt(format_args!("Unknown ({value})")),
259 }
260 }
261}
262
263impl From<u32> for CodeSigningSlot {
264 fn from(v: u32) -> Self {
265 match v {
266 0 => Self::CodeDirectory,
267 1 => Self::Info,
268 2 => Self::RequirementSet,
269 3 => Self::ResourceDir,
270 4 => Self::Application,
271 5 => Self::Entitlements,
272 6 => Self::RepSpecific,
273 7 => Self::EntitlementsDer,
274 8 => Self::LaunchConstraintsSelf,
275 9 => Self::LaunchConstraintsParent,
276 10 => Self::LaunchConstraintsResponsibleProcess,
277 11 => Self::LibraryConstraints,
278 0x1000 => Self::AlternateCodeDirectory0,
279 0x1001 => Self::AlternateCodeDirectory1,
280 0x1002 => Self::AlternateCodeDirectory2,
281 0x1003 => Self::AlternateCodeDirectory3,
282 0x1004 => Self::AlternateCodeDirectory4,
283 0x10000 => Self::Signature,
284 0x10001 => Self::Identification,
285 0x10002 => Self::Ticket,
286 _ => Self::Unknown(v),
287 }
288 }
289}
290
291impl From<CodeSigningSlot> for u32 {
292 fn from(v: CodeSigningSlot) -> Self {
293 match v {
294 CodeSigningSlot::CodeDirectory => 0,
295 CodeSigningSlot::Info => 1,
296 CodeSigningSlot::RequirementSet => 2,
297 CodeSigningSlot::ResourceDir => 3,
298 CodeSigningSlot::Application => 4,
299 CodeSigningSlot::Entitlements => 5,
300 CodeSigningSlot::RepSpecific => 6,
301 CodeSigningSlot::EntitlementsDer => 7,
302 CodeSigningSlot::LaunchConstraintsSelf => 8,
303 CodeSigningSlot::LaunchConstraintsParent => 9,
304 CodeSigningSlot::LaunchConstraintsResponsibleProcess => 10,
305 CodeSigningSlot::LibraryConstraints => 11,
306 CodeSigningSlot::AlternateCodeDirectory0 => 0x1000,
307 CodeSigningSlot::AlternateCodeDirectory1 => 0x1001,
308 CodeSigningSlot::AlternateCodeDirectory2 => 0x1002,
309 CodeSigningSlot::AlternateCodeDirectory3 => 0x1003,
310 CodeSigningSlot::AlternateCodeDirectory4 => 0x1004,
311 CodeSigningSlot::Signature => 0x10000,
312 CodeSigningSlot::Identification => 0x10001,
313 CodeSigningSlot::Ticket => 0x10002,
314 CodeSigningSlot::Unknown(v) => v,
315 }
316 }
317}
318
319impl PartialOrd for CodeSigningSlot {
320 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
321 Some(self.cmp(other))
322 }
323}
324
325impl Ord for CodeSigningSlot {
326 fn cmp(&self, other: &Self) -> Ordering {
327 u32::from(*self).cmp(&u32::from(*other))
328 }
329}
330
331impl CodeSigningSlot {
332 pub fn has_external_content(&self) -> bool {
334 matches!(self, Self::Info | Self::ResourceDir)
335 }
336
337 pub fn is_alternative_code_directory(&self) -> bool {
339 matches!(
340 self,
341 CodeSigningSlot::AlternateCodeDirectory0
342 | CodeSigningSlot::AlternateCodeDirectory1
343 | CodeSigningSlot::AlternateCodeDirectory2
344 | CodeSigningSlot::AlternateCodeDirectory3
345 | CodeSigningSlot::AlternateCodeDirectory4
346 )
347 }
348
349 pub fn is_code_directory_specials_expressible(&self) -> bool {
351 *self >= Self::Info && *self <= Self::LibraryConstraints
352 }
353}
354
355#[repr(C)]
356#[derive(Clone, Pread)]
357struct BlobIndex {
358 typ: u32,
360 offset: u32,
361}
362
363impl std::fmt::Debug for BlobIndex {
364 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
365 f.debug_struct("BlobIndex")
366 .field("type", &CodeSigningSlot::from(self.typ))
367 .field("offset", &self.offset)
368 .finish()
369 }
370}
371
372fn read_blob_header(data: &[u8]) -> Result<(u32, usize, &[u8]), scroll::Error> {
376 let magic = data.pread_with(0, scroll::BE)?;
377 let length = data.pread_with::<u32>(4, scroll::BE)?;
378
379 Ok((magic, length as usize, &data[8..]))
380}
381
382pub(crate) fn read_and_validate_blob_header<'a>(
383 data: &'a [u8],
384 expected_magic: u32,
385 what: &'static str,
386) -> Result<&'a [u8], AppleCodesignError> {
387 let (magic, _, data) = read_blob_header(data)?;
388
389 if magic != expected_magic {
390 Err(AppleCodesignError::BadMagic(what))
391 } else {
392 Ok(data)
393 }
394}
395
396pub fn create_superblob<'a>(
398 magic: CodeSigningMagic,
399 blobs: impl Iterator<Item = &'a (CodeSigningSlot, Vec<u8>)>,
400) -> Result<Vec<u8>, AppleCodesignError> {
401 let blobs = blobs.collect::<Vec<_>>();
403
404 let mut cursor = std::io::Cursor::new(Vec::<u8>::new());
405
406 let mut blob_data = Vec::new();
407 let mut total_length: u32 = 4 + 4 + 4;
409 total_length += 8 * blobs.len() as u32;
411
412 let mut indices = Vec::with_capacity(blobs.len());
413
414 for (slot, blob) in blobs {
415 blob_data.push(blob);
416
417 indices.push(BlobIndex {
418 typ: u32::from(*slot),
419 offset: total_length,
420 });
421
422 total_length += blob.len() as u32;
423 }
424
425 cursor.iowrite_with(u32::from(magic), scroll::BE)?;
426 cursor.iowrite_with(total_length, scroll::BE)?;
427 cursor.iowrite_with(indices.len() as u32, scroll::BE)?;
428 for index in indices {
429 cursor.iowrite_with(index.typ, scroll::BE)?;
430 cursor.iowrite_with(index.offset, scroll::BE)?;
431 }
432 for data in blob_data {
433 cursor.write_all(data)?;
434 }
435
436 Ok(cursor.into_inner())
437}
438
439#[derive(Clone)]
447pub struct BlobEntry<'a> {
448 pub index: usize,
450
451 pub slot: CodeSigningSlot,
453
454 pub offset: usize,
458
459 pub magic: CodeSigningMagic,
461
462 pub length: usize,
464
465 pub data: &'a [u8],
467}
468
469impl<'a> std::fmt::Debug for BlobEntry<'a> {
470 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
471 f.debug_struct("BlobEntry")
472 .field("index", &self.index)
473 .field("slot", &self.slot)
474 .field("offset", &self.offset)
475 .field("length", &self.length)
476 .field("magic", &self.magic)
477 .finish()
479 }
480}
481
482impl<'a> BlobEntry<'a> {
483 pub fn into_parsed_blob(self) -> Result<ParsedBlob<'a>, AppleCodesignError> {
485 self.try_into()
486 }
487
488 pub fn payload(&self) -> Result<&'a [u8], AppleCodesignError> {
492 Ok(read_blob_header(self.data)?.2)
493 }
494
495 pub fn digest_with(&self, hash: DigestType) -> Result<Vec<u8>, AppleCodesignError> {
497 hash.digest_data(self.data)
498 }
499}
500
501pub trait Blob<'a>
503where
504 Self: Sized,
505{
506 fn magic() -> u32;
508
509 fn from_blob_bytes(data: &'a [u8]) -> Result<Self, AppleCodesignError>;
514
515 fn serialize_payload(&self) -> Result<Vec<u8>, AppleCodesignError>;
519
520 fn to_blob_bytes(&self) -> Result<Vec<u8>, AppleCodesignError> {
525 let mut res = Vec::new();
526 res.iowrite_with(Self::magic(), scroll::BE)?;
527
528 let payload = self.serialize_payload()?;
529 res.iowrite_with(payload.len() as u32 + 8, scroll::BE)?;
531
532 res.extend(payload);
533
534 Ok(res)
535 }
536
537 fn digest_with(&self, hash_type: DigestType) -> Result<Vec<u8>, AppleCodesignError> {
542 hash_type.digest_data(&self.to_blob_bytes()?)
543 }
544}
545
546pub struct RequirementBlob<'a> {
551 pub data: Cow<'a, [u8]>,
552}
553
554impl<'a> Blob<'a> for RequirementBlob<'a> {
555 fn magic() -> u32 {
556 u32::from(CodeSigningMagic::Requirement)
557 }
558
559 fn from_blob_bytes(data: &'a [u8]) -> Result<Self, AppleCodesignError> {
560 let data = read_and_validate_blob_header(data, Self::magic(), "requirement blob")?;
561
562 Ok(Self { data: data.into() })
563 }
564
565 fn serialize_payload(&self) -> Result<Vec<u8>, AppleCodesignError> {
566 Ok(self.data.to_vec())
567 }
568}
569
570impl<'a> std::fmt::Debug for RequirementBlob<'a> {
571 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
572 f.write_fmt(format_args!("RequirementBlob({})", hex::encode(&self.data)))
573 }
574}
575
576impl<'a> RequirementBlob<'a> {
577 pub fn to_owned(&self) -> RequirementBlob<'static> {
578 RequirementBlob {
579 data: Cow::Owned(self.data.clone().into_owned()),
580 }
581 }
582
583 pub fn parse_expressions(&self) -> Result<CodeRequirements, AppleCodesignError> {
585 Ok(CodeRequirements::parse_binary(&self.data)?.0)
586 }
587}
588
589#[derive(Debug, Default)]
593pub struct RequirementSetBlob<'a> {
594 pub requirements: HashMap<RequirementType, RequirementBlob<'a>>,
595}
596
597impl<'a> Blob<'a> for RequirementSetBlob<'a> {
598 fn magic() -> u32 {
599 u32::from(CodeSigningMagic::RequirementSet)
600 }
601
602 fn from_blob_bytes(data: &'a [u8]) -> Result<Self, AppleCodesignError> {
603 read_and_validate_blob_header(data, Self::magic(), "requirement set blob")?;
604
605 let offset = &mut 8;
609 let count = data.gread_with::<u32>(offset, scroll::BE)?;
610
611 let mut indices = Vec::with_capacity(count as usize);
612 for _ in 0..count {
613 indices.push((
614 data.gread_with::<u32>(offset, scroll::BE)?,
615 data.gread_with::<u32>(offset, scroll::BE)?,
616 ));
617 }
618
619 let mut requirements = HashMap::with_capacity(indices.len());
620
621 for (i, (flavor, offset)) in indices.iter().enumerate() {
622 let typ = RequirementType::from(*flavor);
623
624 let end_offset = if i == indices.len() - 1 {
625 data.len()
626 } else {
627 indices[i + 1].1 as usize
628 };
629
630 let requirement_data = &data[*offset as usize..end_offset];
631
632 requirements.insert(typ, RequirementBlob::from_blob_bytes(requirement_data)?);
633 }
634
635 Ok(Self { requirements })
636 }
637
638 fn serialize_payload(&self) -> Result<Vec<u8>, AppleCodesignError> {
639 let mut res = Vec::new();
640
641 let data_start_offset = 8 + 4 + (8 * self.requirements.len() as u32);
644 let mut written_requirements_data = 0;
645
646 res.iowrite_with(self.requirements.len() as u32, scroll::BE)?;
647
648 for (typ, requirement) in &self.requirements {
650 res.iowrite_with(u32::from(*typ), scroll::BE)?;
651 res.iowrite_with(data_start_offset + written_requirements_data, scroll::BE)?;
652 written_requirements_data += requirement.to_blob_bytes()?.len() as u32;
653 }
654
655 for requirement in self.requirements.values() {
657 res.write_all(&requirement.to_blob_bytes()?)?;
658 }
659
660 Ok(res)
661 }
662}
663
664impl<'a> RequirementSetBlob<'a> {
665 pub fn to_owned(&self) -> RequirementSetBlob<'static> {
666 RequirementSetBlob {
667 requirements: self
668 .requirements
669 .iter()
670 .map(|(flavor, blob)| (*flavor, blob.to_owned()))
671 .collect::<HashMap<_, _>>(),
672 }
673 }
674
675 pub fn set_requirements(&mut self, slot: RequirementType, blob: RequirementBlob<'a>) {
677 self.requirements.insert(slot, blob);
678 }
679}
680
681#[derive(Debug)]
683pub struct EmbeddedSignatureBlob<'a> {
684 data: &'a [u8],
685}
686
687impl<'a> Blob<'a> for EmbeddedSignatureBlob<'a> {
688 fn magic() -> u32 {
689 u32::from(CodeSigningMagic::EmbeddedSignature)
690 }
691
692 fn from_blob_bytes(data: &'a [u8]) -> Result<Self, AppleCodesignError> {
693 Ok(Self {
694 data: read_and_validate_blob_header(data, Self::magic(), "embedded signature blob")?,
695 })
696 }
697
698 fn serialize_payload(&self) -> Result<Vec<u8>, AppleCodesignError> {
699 Ok(self.data.to_vec())
700 }
701}
702
703#[derive(Debug)]
705pub struct EmbeddedSignatureOldBlob<'a> {
706 data: &'a [u8],
707}
708
709impl<'a> Blob<'a> for EmbeddedSignatureOldBlob<'a> {
710 fn magic() -> u32 {
711 u32::from(CodeSigningMagic::EmbeddedSignatureOld)
712 }
713
714 fn from_blob_bytes(data: &'a [u8]) -> Result<Self, AppleCodesignError> {
715 Ok(Self {
716 data: read_and_validate_blob_header(
717 data,
718 Self::magic(),
719 "old embedded signature blob",
720 )?,
721 })
722 }
723
724 fn serialize_payload(&self) -> Result<Vec<u8>, AppleCodesignError> {
725 Ok(self.data.to_vec())
726 }
727}
728
729#[derive(Debug)]
735pub struct EntitlementsBlob<'a> {
736 plist: Cow<'a, str>,
737}
738
739impl<'a> Blob<'a> for EntitlementsBlob<'a> {
740 fn magic() -> u32 {
741 u32::from(CodeSigningMagic::Entitlements)
742 }
743
744 fn from_blob_bytes(data: &'a [u8]) -> Result<Self, AppleCodesignError> {
745 let data = read_and_validate_blob_header(data, Self::magic(), "entitlements blob")?;
746 let s = std::str::from_utf8(data).map_err(AppleCodesignError::EntitlementsBadUtf8)?;
747
748 Ok(Self { plist: s.into() })
749 }
750
751 fn serialize_payload(&self) -> Result<Vec<u8>, AppleCodesignError> {
752 Ok(self.plist.as_bytes().to_vec())
753 }
754}
755
756impl<'a> EntitlementsBlob<'a> {
757 pub fn from_string(s: &(impl ToString + ?Sized)) -> Self {
759 Self {
760 plist: s.to_string().into(),
761 }
762 }
763
764 pub fn as_str(&self) -> &str {
766 &self.plist
767 }
768}
769
770impl<'a> std::fmt::Display for EntitlementsBlob<'a> {
771 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
772 f.write_str(&self.plist)
773 }
774}
775
776#[derive(Debug)]
777pub struct EntitlementsDerBlob<'a> {
778 der: Cow<'a, [u8]>,
779}
780
781impl<'a> Blob<'a> for EntitlementsDerBlob<'a> {
782 fn magic() -> u32 {
783 u32::from(CodeSigningMagic::EntitlementsDer)
784 }
785
786 fn from_blob_bytes(data: &'a [u8]) -> Result<Self, AppleCodesignError> {
787 let der = read_and_validate_blob_header(data, Self::magic(), "DER entitlements blob")?;
788
789 Ok(Self { der: der.into() })
790 }
791
792 fn serialize_payload(&self) -> Result<Vec<u8>, AppleCodesignError> {
793 Ok(self.der.to_vec())
794 }
795}
796
797impl<'a> EntitlementsDerBlob<'a> {
798 pub fn from_plist(v: &plist::Value) -> Result<Self, AppleCodesignError> {
806 let der = crate::plist_der::der_encode_plist(v)?;
807
808 Ok(Self { der: der.into() })
809 }
810
811 pub fn parse_der(&self) -> Result<plist::Value, AppleCodesignError> {
813 crate::plist_der::der_decode_plist(self.der.as_ref())
814 }
815
816 pub fn plist_xml(&self) -> Result<Vec<u8>, AppleCodesignError> {
818 let mut buffer = vec![];
819 self.parse_der()?.to_writer_xml(&mut buffer)?;
820
821 Ok(buffer)
822 }
823}
824
825#[derive(Debug)]
829pub struct ConstraintsDerBlob<'a> {
830 der: Cow<'a, [u8]>,
831}
832
833impl<'a> Blob<'a> for ConstraintsDerBlob<'a> {
834 fn magic() -> u32 {
835 u32::from(CodeSigningMagic::EnvironmentContraintsDer)
836 }
837
838 fn from_blob_bytes(data: &'a [u8]) -> Result<Self> {
839 let der = read_and_validate_blob_header(
840 data,
841 Self::magic(),
842 "DER encoded environment constraints blob",
843 )?;
844
845 Ok(Self { der: der.into() })
846 }
847
848 fn serialize_payload(&self) -> Result<Vec<u8>> {
849 Ok(self.der.to_vec())
850 }
851}
852
853impl<'a> ConstraintsDerBlob<'a> {
854 pub fn from_encoded_constraints(v: &EncodedEnvironmentConstraints) -> Result<Self> {
856 let der = v.der_encode()?;
857
858 Ok(Self { der: der.into() })
859 }
860
861 pub fn parse_der_plist(&self) -> Result<plist::Value> {
863 crate::plist_der::der_decode_plist(self.der.as_ref())
864 }
865
866 pub fn parse_encoded_constraints(&self) -> Result<EncodedEnvironmentConstraints> {
868 EncodedEnvironmentConstraints::from_der(self.der.as_ref())
869 }
870
871 pub fn plist_xml(&self) -> Result<Vec<u8>> {
873 let mut buffer = vec![];
874 self.parse_der_plist()?.to_writer_xml(&mut buffer)?;
875
876 Ok(buffer)
877 }
878}
879
880#[derive(Debug)]
882pub struct DetachedSignatureBlob<'a> {
883 data: &'a [u8],
884}
885
886impl<'a> Blob<'a> for DetachedSignatureBlob<'a> {
887 fn magic() -> u32 {
888 u32::from(CodeSigningMagic::DetachedSignature)
889 }
890
891 fn from_blob_bytes(data: &'a [u8]) -> Result<Self, AppleCodesignError> {
892 Ok(Self {
893 data: read_and_validate_blob_header(data, Self::magic(), "detached signature blob")?,
894 })
895 }
896
897 fn serialize_payload(&self) -> Result<Vec<u8>, AppleCodesignError> {
898 Ok(self.data.to_vec())
899 }
900}
901
902pub struct BlobWrapperBlob<'a> {
904 data: Cow<'a, [u8]>,
905}
906
907impl<'a> Blob<'a> for BlobWrapperBlob<'a> {
908 fn magic() -> u32 {
909 u32::from(CodeSigningMagic::BlobWrapper)
910 }
911
912 fn from_blob_bytes(data: &'a [u8]) -> Result<Self, AppleCodesignError> {
913 Ok(Self {
914 data: read_and_validate_blob_header(data, Self::magic(), "blob wrapper blob")?.into(),
915 })
916 }
917
918 fn serialize_payload(&self) -> Result<Vec<u8>, AppleCodesignError> {
919 Ok(self.data.to_vec())
920 }
921}
922
923impl<'a> std::fmt::Debug for BlobWrapperBlob<'a> {
924 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
925 f.write_fmt(format_args!("{}", hex::encode(&self.data)))
926 }
927}
928
929impl<'a> BlobWrapperBlob<'a> {
930 pub fn from_data_borrowed(data: &'a [u8]) -> BlobWrapperBlob<'a> {
932 Self { data: data.into() }
933 }
934}
935
936impl BlobWrapperBlob<'static> {
937 pub fn from_data_owned(data: Vec<u8>) -> BlobWrapperBlob<'static> {
939 Self { data: data.into() }
940 }
941}
942
943pub struct OtherBlob<'a> {
945 pub magic: u32,
946 pub data: &'a [u8],
947}
948
949impl<'a> Blob<'a> for OtherBlob<'a> {
950 fn magic() -> u32 {
951 u32::MAX
953 }
954
955 fn from_blob_bytes(data: &'a [u8]) -> Result<Self, AppleCodesignError> {
956 let (magic, _, data) = read_blob_header(data)?;
957
958 Ok(Self { magic, data })
959 }
960
961 fn serialize_payload(&self) -> Result<Vec<u8>, AppleCodesignError> {
962 Ok(self.data.to_vec())
963 }
964
965 fn to_blob_bytes(&self) -> Result<Vec<u8>, AppleCodesignError> {
967 let mut res = Vec::with_capacity(self.data.len() + 8);
968 res.iowrite_with(self.magic, scroll::BE)?;
969 res.iowrite_with(self.data.len() as u32 + 8, scroll::BE)?;
970 res.write_all(self.data)?;
971
972 Ok(res)
973 }
974}
975
976impl<'a> std::fmt::Debug for OtherBlob<'a> {
977 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
978 f.write_fmt(format_args!("{}", hex::encode(self.data)))
979 }
980}
981
982#[derive(Debug)]
986pub enum BlobData<'a> {
987 Requirement(Box<RequirementBlob<'a>>),
988 RequirementSet(Box<RequirementSetBlob<'a>>),
989 CodeDirectory(Box<CodeDirectoryBlob<'a>>),
990 EmbeddedSignature(Box<EmbeddedSignatureBlob<'a>>),
991 EmbeddedSignatureOld(Box<EmbeddedSignatureOldBlob<'a>>),
992 Entitlements(Box<EntitlementsBlob<'a>>),
993 EntitlementsDer(Box<EntitlementsDerBlob<'a>>),
994 ConstraintsDer(Box<ConstraintsDerBlob<'a>>),
995 DetachedSignature(Box<DetachedSignatureBlob<'a>>),
996 BlobWrapper(Box<BlobWrapperBlob<'a>>),
997 Other(Box<OtherBlob<'a>>),
998}
999
1000impl<'a> Blob<'a> for BlobData<'a> {
1001 fn magic() -> u32 {
1002 u32::MAX
1003 }
1004
1005 fn from_blob_bytes(data: &'a [u8]) -> Result<Self, AppleCodesignError> {
1007 let (magic, length, _) = read_blob_header(data)?;
1008
1009 let data = &data[0..length];
1012
1013 let magic = CodeSigningMagic::from(magic);
1014
1015 Ok(match magic {
1016 CodeSigningMagic::Requirement => {
1017 Self::Requirement(Box::new(RequirementBlob::from_blob_bytes(data)?))
1018 }
1019 CodeSigningMagic::RequirementSet => {
1020 Self::RequirementSet(Box::new(RequirementSetBlob::from_blob_bytes(data)?))
1021 }
1022 CodeSigningMagic::CodeDirectory => {
1023 Self::CodeDirectory(Box::new(CodeDirectoryBlob::from_blob_bytes(data)?))
1024 }
1025 CodeSigningMagic::EmbeddedSignature => {
1026 Self::EmbeddedSignature(Box::new(EmbeddedSignatureBlob::from_blob_bytes(data)?))
1027 }
1028 CodeSigningMagic::EmbeddedSignatureOld => Self::EmbeddedSignatureOld(Box::new(
1029 EmbeddedSignatureOldBlob::from_blob_bytes(data)?,
1030 )),
1031 CodeSigningMagic::Entitlements => {
1032 Self::Entitlements(Box::new(EntitlementsBlob::from_blob_bytes(data)?))
1033 }
1034 CodeSigningMagic::EntitlementsDer => {
1035 Self::EntitlementsDer(Box::new(EntitlementsDerBlob::from_blob_bytes(data)?))
1036 }
1037 CodeSigningMagic::EnvironmentContraintsDer => {
1038 Self::ConstraintsDer(Box::new(ConstraintsDerBlob::from_blob_bytes(data)?))
1039 }
1040 CodeSigningMagic::DetachedSignature => {
1041 Self::DetachedSignature(Box::new(DetachedSignatureBlob::from_blob_bytes(data)?))
1042 }
1043 CodeSigningMagic::BlobWrapper => {
1044 Self::BlobWrapper(Box::new(BlobWrapperBlob::from_blob_bytes(data)?))
1045 }
1046 _ => Self::Other(Box::new(OtherBlob::from_blob_bytes(data)?)),
1047 })
1048 }
1049
1050 fn serialize_payload(&self) -> Result<Vec<u8>, AppleCodesignError> {
1051 match self {
1052 Self::Requirement(b) => b.serialize_payload(),
1053 Self::RequirementSet(b) => b.serialize_payload(),
1054 Self::CodeDirectory(b) => b.serialize_payload(),
1055 Self::EmbeddedSignature(b) => b.serialize_payload(),
1056 Self::EmbeddedSignatureOld(b) => b.serialize_payload(),
1057 Self::Entitlements(b) => b.serialize_payload(),
1058 Self::EntitlementsDer(b) => b.serialize_payload(),
1059 Self::ConstraintsDer(b) => b.serialize_payload(),
1060 Self::DetachedSignature(b) => b.serialize_payload(),
1061 Self::BlobWrapper(b) => b.serialize_payload(),
1062 Self::Other(b) => b.serialize_payload(),
1063 }
1064 }
1065
1066 fn to_blob_bytes(&self) -> Result<Vec<u8>, AppleCodesignError> {
1067 match self {
1068 Self::Requirement(b) => b.to_blob_bytes(),
1069 Self::RequirementSet(b) => b.to_blob_bytes(),
1070 Self::CodeDirectory(b) => b.to_blob_bytes(),
1071 Self::EmbeddedSignature(b) => b.to_blob_bytes(),
1072 Self::EmbeddedSignatureOld(b) => b.to_blob_bytes(),
1073 Self::Entitlements(b) => b.to_blob_bytes(),
1074 Self::EntitlementsDer(b) => b.to_blob_bytes(),
1075 Self::ConstraintsDer(b) => b.to_blob_bytes(),
1076 Self::DetachedSignature(b) => b.to_blob_bytes(),
1077 Self::BlobWrapper(b) => b.to_blob_bytes(),
1078 Self::Other(b) => b.to_blob_bytes(),
1079 }
1080 }
1081}
1082
1083impl<'a> From<RequirementBlob<'a>> for BlobData<'a> {
1084 fn from(b: RequirementBlob<'a>) -> Self {
1085 Self::Requirement(Box::new(b))
1086 }
1087}
1088
1089impl<'a> From<RequirementSetBlob<'a>> for BlobData<'a> {
1090 fn from(b: RequirementSetBlob<'a>) -> Self {
1091 Self::RequirementSet(Box::new(b))
1092 }
1093}
1094
1095impl<'a> From<CodeDirectoryBlob<'a>> for BlobData<'a> {
1096 fn from(b: CodeDirectoryBlob<'a>) -> Self {
1097 Self::CodeDirectory(Box::new(b))
1098 }
1099}
1100
1101impl<'a> From<EmbeddedSignatureBlob<'a>> for BlobData<'a> {
1102 fn from(b: EmbeddedSignatureBlob<'a>) -> Self {
1103 Self::EmbeddedSignature(Box::new(b))
1104 }
1105}
1106
1107impl<'a> From<EmbeddedSignatureOldBlob<'a>> for BlobData<'a> {
1108 fn from(b: EmbeddedSignatureOldBlob<'a>) -> Self {
1109 Self::EmbeddedSignatureOld(Box::new(b))
1110 }
1111}
1112
1113impl<'a> From<EntitlementsBlob<'a>> for BlobData<'a> {
1114 fn from(b: EntitlementsBlob<'a>) -> Self {
1115 Self::Entitlements(Box::new(b))
1116 }
1117}
1118
1119impl<'a> From<EntitlementsDerBlob<'a>> for BlobData<'a> {
1120 fn from(b: EntitlementsDerBlob<'a>) -> Self {
1121 Self::EntitlementsDer(Box::new(b))
1122 }
1123}
1124
1125impl<'a> From<ConstraintsDerBlob<'a>> for BlobData<'a> {
1126 fn from(b: ConstraintsDerBlob<'a>) -> Self {
1127 Self::ConstraintsDer(Box::new(b))
1128 }
1129}
1130
1131impl<'a> From<DetachedSignatureBlob<'a>> for BlobData<'a> {
1132 fn from(b: DetachedSignatureBlob<'a>) -> Self {
1133 Self::DetachedSignature(Box::new(b))
1134 }
1135}
1136
1137impl<'a> From<BlobWrapperBlob<'a>> for BlobData<'a> {
1138 fn from(b: BlobWrapperBlob<'a>) -> Self {
1139 Self::BlobWrapper(Box::new(b))
1140 }
1141}
1142
1143impl<'a> From<OtherBlob<'a>> for BlobData<'a> {
1144 fn from(b: OtherBlob<'a>) -> Self {
1145 Self::Other(Box::new(b))
1146 }
1147}
1148
1149#[derive(Debug)]
1151pub struct ParsedBlob<'a> {
1152 pub blob_entry: BlobEntry<'a>,
1154
1155 pub blob: BlobData<'a>,
1157}
1158
1159impl<'a> ParsedBlob<'a> {
1160 pub fn digest_with(&self, hash: DigestType) -> Result<Vec<u8>, AppleCodesignError> {
1162 hash.digest_data(self.blob_entry.data)
1163 }
1164}
1165
1166impl<'a> TryFrom<BlobEntry<'a>> for ParsedBlob<'a> {
1167 type Error = AppleCodesignError;
1168
1169 fn try_from(blob_entry: BlobEntry<'a>) -> Result<Self, Self::Error> {
1170 let blob = BlobData::from_blob_bytes(blob_entry.data)?;
1171
1172 Ok(Self { blob_entry, blob })
1173 }
1174}
1175
1176pub struct EmbeddedSignature<'a> {
1181 pub magic: CodeSigningMagic,
1183 pub length: u32,
1185 pub count: u32,
1187
1188 pub data: &'a [u8],
1190
1191 pub blobs: Vec<BlobEntry<'a>>,
1193}
1194
1195impl<'a> std::fmt::Debug for EmbeddedSignature<'a> {
1196 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1197 f.debug_struct("SuperBlob")
1198 .field("magic", &self.magic)
1199 .field("length", &self.length)
1200 .field("count", &self.count)
1201 .field("blobs", &self.blobs)
1202 .finish()
1203 }
1204}
1205
1206impl<'a> EmbeddedSignature<'a> {
1208 pub fn from_bytes(data: &'a [u8]) -> Result<Self, AppleCodesignError> {
1214 let offset = &mut 0;
1215
1216 let magic = data.gread_with::<u32>(offset, scroll::BE)?.into();
1218
1219 if magic != CodeSigningMagic::EmbeddedSignature {
1220 return Err(AppleCodesignError::BadMagic(
1221 "embedded signature super blob",
1222 ));
1223 }
1224
1225 let length = data.gread_with(offset, scroll::BE)?;
1226 let count = data.gread_with(offset, scroll::BE)?;
1227
1228 let mut blob_indices = Vec::with_capacity(count as usize);
1238 for _ in 0..count {
1239 blob_indices.push(data.gread_with::<BlobIndex>(offset, scroll::BE)?);
1240 }
1241
1242 let mut blobs = Vec::with_capacity(blob_indices.len());
1243
1244 for (i, index) in blob_indices.iter().enumerate() {
1245 let end_offset = if i == blob_indices.len() - 1 {
1246 data.len()
1247 } else {
1248 blob_indices[i + 1].offset as usize
1249 };
1250
1251 let full_slice = &data[index.offset as usize..end_offset];
1252 let (magic, blob_length, _) = read_blob_header(full_slice)?;
1253
1254 let blob_data = match blob_length.cmp(&full_slice.len()) {
1256 Ordering::Greater => {
1257 return Err(AppleCodesignError::SuperblobMalformed);
1258 }
1259 Ordering::Equal => full_slice,
1260 Ordering::Less => &full_slice[0..blob_length],
1261 };
1262
1263 blobs.push(BlobEntry {
1264 index: i,
1265 slot: index.typ.into(),
1266 offset: index.offset as usize,
1267 magic: magic.into(),
1268 length: blob_length,
1269 data: blob_data,
1270 });
1271 }
1272
1273 Ok(Self {
1274 magic,
1275 length,
1276 count,
1277 data,
1278 blobs,
1279 })
1280 }
1281
1282 pub fn find_slot(&self, slot: CodeSigningSlot) -> Option<&BlobEntry<'a>> {
1284 self.blobs.iter().find(|e| e.slot == slot)
1285 }
1286
1287 pub fn find_slot_parsed(
1288 &self,
1289 slot: CodeSigningSlot,
1290 ) -> Result<Option<ParsedBlob<'a>>, AppleCodesignError> {
1291 if let Some(entry) = self.find_slot(slot) {
1292 Ok(Some(entry.clone().into_parsed_blob()?))
1293 } else {
1294 Ok(None)
1295 }
1296 }
1297
1298 pub fn code_directory(&self) -> Result<Option<Box<CodeDirectoryBlob<'a>>>, AppleCodesignError> {
1305 if let Some(parsed) = self.find_slot_parsed(CodeSigningSlot::CodeDirectory)? {
1306 if let BlobData::CodeDirectory(cd) = parsed.blob {
1307 Ok(Some(cd))
1308 } else {
1309 Err(AppleCodesignError::BadMagic("code directory blob"))
1310 }
1311 } else {
1312 Ok(None)
1313 }
1314 }
1315
1316 pub fn alternate_code_directories(
1321 &self,
1322 ) -> Result<Vec<(CodeSigningSlot, Box<CodeDirectoryBlob<'a>>)>, AppleCodesignError> {
1323 let slots = [
1324 CodeSigningSlot::AlternateCodeDirectory0,
1325 CodeSigningSlot::AlternateCodeDirectory1,
1326 CodeSigningSlot::AlternateCodeDirectory2,
1327 CodeSigningSlot::AlternateCodeDirectory3,
1328 CodeSigningSlot::AlternateCodeDirectory4,
1329 ];
1330
1331 let mut res = vec![];
1332
1333 for slot in slots {
1334 if let Some(parsed) = self.find_slot_parsed(slot)? {
1335 if let BlobData::CodeDirectory(cd) = parsed.blob {
1336 res.push((slot, cd));
1337 } else {
1338 return Err(AppleCodesignError::BadMagic(
1339 "wrong blob magic in alternative code directory slot",
1340 ));
1341 }
1342 }
1343 }
1344
1345 Ok(res)
1346 }
1347
1348 pub fn all_code_directories(
1350 &self,
1351 ) -> Result<Vec<(CodeSigningSlot, Box<CodeDirectoryBlob<'a>>)>, AppleCodesignError> {
1352 let mut res = vec![];
1353
1354 if let Some(cd) = self.code_directory()? {
1355 res.push((CodeSigningSlot::CodeDirectory, cd));
1356 }
1357
1358 res.extend(self.alternate_code_directories()?);
1359
1360 Ok(res)
1361 }
1362
1363 pub fn code_directory_for_digest(
1365 &self,
1366 digest: DigestType,
1367 ) -> Result<Option<Box<CodeDirectoryBlob<'a>>>, AppleCodesignError> {
1368 for (_, cd) in self.all_code_directories()? {
1369 if cd.digest_type == digest {
1370 return Ok(Some(cd));
1371 }
1372 }
1373
1374 Ok(None)
1375 }
1376
1377 pub fn preferred_code_directory(
1382 &self,
1383 ) -> Result<Box<CodeDirectoryBlob<'a>>, AppleCodesignError> {
1384 if let Some(cd) = self.code_directory_for_digest(DigestType::Sha256)? {
1385 Ok(cd)
1386 } else if let Some(cd) = self.code_directory_for_digest(DigestType::Sha1)? {
1387 Ok(cd)
1388 } else if let Some(cd) = self.code_directory()? {
1389 Ok(cd)
1390 } else {
1391 Err(AppleCodesignError::BinaryNoCodeDirectory)
1392 }
1393 }
1394
1395 pub fn entitlements(&self) -> Result<Option<Box<EntitlementsBlob<'a>>>, AppleCodesignError> {
1402 if let Some(parsed) = self.find_slot_parsed(CodeSigningSlot::Entitlements)? {
1403 if let BlobData::Entitlements(entitlements) = parsed.blob {
1404 Ok(Some(entitlements))
1405 } else {
1406 Err(AppleCodesignError::BadMagic("entitlements blob"))
1407 }
1408 } else {
1409 Ok(None)
1410 }
1411 }
1412
1413 pub fn entitlements_der(
1415 &self,
1416 ) -> Result<Option<Box<EntitlementsDerBlob<'a>>>, AppleCodesignError> {
1417 if let Some(parsed) = self.find_slot_parsed(CodeSigningSlot::EntitlementsDer)? {
1418 if let BlobData::EntitlementsDer(entitlements) = parsed.blob {
1419 Ok(Some(entitlements))
1420 } else {
1421 Err(AppleCodesignError::BadMagic("DER entitlements blob"))
1422 }
1423 } else {
1424 Ok(None)
1425 }
1426 }
1427
1428 pub fn code_requirements(
1435 &self,
1436 ) -> Result<Option<Box<RequirementSetBlob<'a>>>, AppleCodesignError> {
1437 if let Some(parsed) = self.find_slot_parsed(CodeSigningSlot::RequirementSet)? {
1438 if let BlobData::RequirementSet(reqs) = parsed.blob {
1439 Ok(Some(reqs))
1440 } else {
1441 Err(AppleCodesignError::BadMagic("requirements blob"))
1442 }
1443 } else {
1444 Ok(None)
1445 }
1446 }
1447
1448 pub fn launch_constraints_self(&self) -> Result<Option<Box<ConstraintsDerBlob<'a>>>> {
1450 if let Some(parsed) = self.find_slot_parsed(CodeSigningSlot::LaunchConstraintsSelf)? {
1451 if let BlobData::ConstraintsDer(blob) = parsed.blob {
1452 Ok(Some(blob))
1453 } else {
1454 Err(AppleCodesignError::BadMagic("self launch constraints blob"))
1455 }
1456 } else {
1457 Ok(None)
1458 }
1459 }
1460
1461 pub fn launch_constraints_parent(&self) -> Result<Option<Box<ConstraintsDerBlob<'a>>>> {
1463 if let Some(parsed) = self.find_slot_parsed(CodeSigningSlot::LaunchConstraintsParent)? {
1464 if let BlobData::ConstraintsDer(blob) = parsed.blob {
1465 Ok(Some(blob))
1466 } else {
1467 Err(AppleCodesignError::BadMagic(
1468 "parent launch constraints blob",
1469 ))
1470 }
1471 } else {
1472 Ok(None)
1473 }
1474 }
1475
1476 pub fn launch_constraints_responsible(&self) -> Result<Option<Box<ConstraintsDerBlob<'a>>>> {
1478 if let Some(parsed) =
1479 self.find_slot_parsed(CodeSigningSlot::LaunchConstraintsResponsibleProcess)?
1480 {
1481 if let BlobData::ConstraintsDer(blob) = parsed.blob {
1482 Ok(Some(blob))
1483 } else {
1484 Err(AppleCodesignError::BadMagic(
1485 "responsible process launch constraints blob",
1486 ))
1487 }
1488 } else {
1489 Ok(None)
1490 }
1491 }
1492
1493 pub fn library_constraints(&self) -> Result<Option<Box<ConstraintsDerBlob<'a>>>> {
1495 if let Some(parsed) = self.find_slot_parsed(CodeSigningSlot::LibraryConstraints)? {
1496 if let BlobData::ConstraintsDer(blob) = parsed.blob {
1497 Ok(Some(blob))
1498 } else {
1499 Err(AppleCodesignError::BadMagic("library constraints blob"))
1500 }
1501 } else {
1502 Ok(None)
1503 }
1504 }
1505
1506 pub fn signature_data(&self) -> Result<Option<&'a [u8]>, AppleCodesignError> {
1511 if let Some(parsed) = self.find_slot(CodeSigningSlot::Signature) {
1512 ParsedBlob::try_from(parsed.clone())?;
1514
1515 Ok(Some(parsed.payload()?))
1516 } else {
1517 Ok(None)
1518 }
1519 }
1520
1521 pub fn signed_data(&self) -> Result<Option<SignedData>, AppleCodesignError> {
1523 if let Some(data) = self.signature_data()? {
1524 if data.is_empty() {
1527 Ok(None)
1528 } else {
1529 let signed_data = SignedData::parse_ber(data)?;
1530
1531 Ok(Some(signed_data))
1532 }
1533 } else {
1534 Ok(None)
1535 }
1536 }
1537}