1use {
8 crate::{
9 certificate::{AppleCertificate, CodeSigningCertificateExtension},
10 code_directory::CodeSignatureFlags,
11 code_requirement::CodeRequirementExpression,
12 cryptography::DigestType,
13 embedded_signature::{Blob, RequirementBlob},
14 environment_constraints::EncodedEnvironmentConstraints,
15 error::AppleCodesignError,
16 macho::{MachFile, parse_version_nibbles},
17 },
18 glob::Pattern,
19 goblin::mach::cputype::{
20 CPU_TYPE_ARM, CPU_TYPE_ARM64, CPU_TYPE_ARM64_32, CPU_TYPE_X86_64, CpuType,
21 },
22 log::{error, info},
23 std::{
24 collections::{BTreeMap, BTreeSet},
25 fmt::Formatter,
26 },
27 x509_certificate::{CapturedX509Certificate, KeyInfoSigner},
28};
29
30#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
59pub enum SettingsScope {
60 Main,
71
72 Path(String),
80
81 MultiArchIndex(usize),
86
87 MultiArchCpuType(CpuType),
91
92 PathMultiArchIndex(String, usize),
97
98 PathMultiArchCpuType(String, CpuType),
103}
104
105impl std::fmt::Display for SettingsScope {
106 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
107 match self {
108 Self::Main => f.write_str("main signing target"),
109 Self::Path(path) => f.write_fmt(format_args!("path {path}")),
110 Self::MultiArchIndex(index) => f.write_fmt(format_args!(
111 "fat/universal Mach-O binaries at index {index}"
112 )),
113 Self::MultiArchCpuType(cpu_type) => f.write_fmt(format_args!(
114 "fat/universal Mach-O binaries for CPU {cpu_type}"
115 )),
116 Self::PathMultiArchIndex(path, index) => f.write_fmt(format_args!(
117 "fat/universal Mach-O binaries at index {index} under path {path}"
118 )),
119 Self::PathMultiArchCpuType(path, cpu_type) => f.write_fmt(format_args!(
120 "fat/universal Mach-O binaries for CPU {cpu_type} under path {path}"
121 )),
122 }
123 }
124}
125
126impl SettingsScope {
127 fn parse_at_expr(
128 at_expr: &str,
129 ) -> Result<(Option<usize>, Option<CpuType>), AppleCodesignError> {
130 match at_expr.parse::<usize>() {
131 Ok(index) => Ok((Some(index), None)),
132 Err(_) => {
133 if at_expr.starts_with('[') && at_expr.ends_with(']') {
134 let v = &at_expr[1..at_expr.len() - 1];
135 let parts = v.split('=').collect::<Vec<_>>();
136
137 if parts.len() == 2 {
138 let (key, value) = (parts[0], parts[1]);
139
140 if key != "cpu_type" {
141 return Err(AppleCodesignError::ParseSettingsScope(format!(
142 "in '@{at_expr}', {key} not recognized; must be cpu_type"
143 )));
144 }
145
146 if let Some(cpu_type) = match value {
147 "arm" => Some(CPU_TYPE_ARM),
148 "arm64" => Some(CPU_TYPE_ARM64),
149 "arm64_32" => Some(CPU_TYPE_ARM64_32),
150 "x86_64" => Some(CPU_TYPE_X86_64),
151 _ => None,
152 } {
153 return Ok((None, Some(cpu_type)));
154 }
155
156 match value.parse::<u32>() {
157 Ok(cpu_type) => Ok((None, Some(cpu_type as CpuType))),
158 Err(_) => Err(AppleCodesignError::ParseSettingsScope(format!(
159 "in '@{at_expr}', cpu_arch value {value} not recognized"
160 ))),
161 }
162 } else {
163 Err(AppleCodesignError::ParseSettingsScope(format!(
164 "'{v}' sub-expression isn't of form <key>=<value>"
165 )))
166 }
167 } else {
168 Err(AppleCodesignError::ParseSettingsScope(format!(
169 "in '{at_expr}', @ expression not recognized"
170 )))
171 }
172 }
173 }
174 }
175}
176
177impl AsRef<SettingsScope> for SettingsScope {
178 fn as_ref(&self) -> &SettingsScope {
179 self
180 }
181}
182
183impl TryFrom<&str> for SettingsScope {
184 type Error = AppleCodesignError;
185
186 fn try_from(s: &str) -> Result<Self, Self::Error> {
187 if s == "@main" {
188 Ok(Self::Main)
189 } else if let Some(at_expr) = s.strip_prefix('@') {
190 match Self::parse_at_expr(at_expr)? {
191 (Some(index), None) => Ok(Self::MultiArchIndex(index)),
192 (None, Some(cpu_type)) => Ok(Self::MultiArchCpuType(cpu_type)),
193 _ => panic!("this shouldn't happen"),
194 }
195 } else {
196 let parts = s.rsplitn(2, '@').collect::<Vec<_>>();
198
199 match parts.len() {
200 1 => Ok(Self::Path(s.to_string())),
201 2 => {
202 let (at_expr, path) = (parts[0], parts[1]);
204
205 match Self::parse_at_expr(at_expr)? {
206 (Some(index), None) => {
207 Ok(Self::PathMultiArchIndex(path.to_string(), index))
208 }
209 (None, Some(cpu_type)) => {
210 Ok(Self::PathMultiArchCpuType(path.to_string(), cpu_type))
211 }
212 _ => panic!("this shouldn't happen"),
213 }
214 }
215 _ => panic!("this shouldn't happen"),
216 }
217 }
218 }
219}
220
221#[derive(Clone, Debug)]
223pub enum DesignatedRequirementMode {
224 Auto,
227
228 Explicit(Vec<Vec<u8>>),
230}
231
232#[derive(Clone, Copy, Debug, Eq, PartialEq)]
234pub enum ScopedSetting {
235 Digest,
236 BinaryIdentifier,
237 Entitlements,
238 DesignatedRequirements,
239 CodeSignatureFlags,
240 RuntimeVersion,
241 InfoPlist,
242 CodeResources,
243 ExtraDigests,
244 LaunchConstraintsSelf,
245 LaunchConstraintsParent,
246 LaunchConstraintsResponsible,
247 LibraryConstraints,
248}
249
250impl ScopedSetting {
251 pub fn all() -> &'static [Self] {
252 &[
253 Self::Digest,
254 Self::BinaryIdentifier,
255 Self::Entitlements,
256 Self::DesignatedRequirements,
257 Self::CodeSignatureFlags,
258 Self::RuntimeVersion,
259 Self::InfoPlist,
260 Self::CodeResources,
261 Self::ExtraDigests,
262 Self::LaunchConstraintsSelf,
263 Self::LaunchConstraintsParent,
264 Self::LaunchConstraintsResponsible,
265 Self::LibraryConstraints,
266 ]
267 }
268
269 pub fn inherit_nested_bundle() -> &'static [Self] {
270 &[Self::Digest, Self::ExtraDigests, Self::RuntimeVersion]
271 }
272
273 pub fn inherit_nested_macho() -> &'static [Self] {
274 &[Self::Digest, Self::ExtraDigests, Self::RuntimeVersion]
275 }
276}
277
278#[derive(Clone, Default)]
297pub struct SigningSettings<'key> {
298 signing_key: Option<(&'key dyn KeyInfoSigner, CapturedX509Certificate)>,
300 certificates: Vec<CapturedX509Certificate>,
301 signing_time: Option<chrono::DateTime<chrono::Utc>>,
302 path_exclusion_patterns: Vec<Pattern>,
303 shallow: bool,
304 for_notarization: bool,
305
306 digest_type: BTreeMap<SettingsScope, DigestType>,
310 team_id: BTreeMap<SettingsScope, String>,
311 identifiers: BTreeMap<SettingsScope, String>,
312 entitlements: BTreeMap<SettingsScope, plist::Value>,
313 designated_requirement: BTreeMap<SettingsScope, DesignatedRequirementMode>,
314 code_signature_flags: BTreeMap<SettingsScope, CodeSignatureFlags>,
315 runtime_version: BTreeMap<SettingsScope, semver::Version>,
316 info_plist_data: BTreeMap<SettingsScope, Vec<u8>>,
317 code_resources_data: BTreeMap<SettingsScope, Vec<u8>>,
318 extra_digests: BTreeMap<SettingsScope, BTreeSet<DigestType>>,
319 launch_constraints_self: BTreeMap<SettingsScope, EncodedEnvironmentConstraints>,
320 launch_constraints_parent: BTreeMap<SettingsScope, EncodedEnvironmentConstraints>,
321 launch_constraints_responsible: BTreeMap<SettingsScope, EncodedEnvironmentConstraints>,
322 library_constraints: BTreeMap<SettingsScope, EncodedEnvironmentConstraints>,
323}
324
325impl<'key> SigningSettings<'key> {
326 pub fn signing_key(&self) -> Option<(&'key dyn KeyInfoSigner, &CapturedX509Certificate)> {
328 self.signing_key.as_ref().map(|(key, cert)| (*key, cert))
329 }
330
331 pub fn set_signing_key(
338 &mut self,
339 private: &'key dyn KeyInfoSigner,
340 public: CapturedX509Certificate,
341 ) {
342 self.signing_key = Some((private, public));
343 }
344
345 pub fn certificate_chain(&self) -> &[CapturedX509Certificate] {
347 &self.certificates
348 }
349
350 pub fn chain_apple_certificates(&mut self) -> Option<Vec<CapturedX509Certificate>> {
356 if let Some((_, cert)) = &self.signing_key {
357 if let Some(chain) = cert.apple_root_certificate_chain() {
358 let chain = chain.into_iter().skip(1).collect::<Vec<_>>();
360 self.certificates.extend(chain.clone());
361 Some(chain)
362 } else {
363 None
364 }
365 } else {
366 None
367 }
368 }
369
370 pub fn signing_certificate_apple_signed(&self) -> bool {
372 if let Some((_, cert)) = &self.signing_key {
373 cert.chains_to_apple_root_ca()
374 } else {
375 false
376 }
377 }
378
379 pub fn chain_certificate(&mut self, cert: CapturedX509Certificate) {
390 self.certificates.push(cert);
391 }
392
393 pub fn chain_certificate_der(
398 &mut self,
399 data: impl AsRef<[u8]>,
400 ) -> Result<(), AppleCodesignError> {
401 self.chain_certificate(CapturedX509Certificate::from_der(data.as_ref())?);
402
403 Ok(())
404 }
405
406 pub fn chain_certificate_pem(
415 &mut self,
416 data: impl AsRef<[u8]>,
417 ) -> Result<(), AppleCodesignError> {
418 self.chain_certificate(CapturedX509Certificate::from_pem(data.as_ref())?);
419
420 Ok(())
421 }
422
423 pub fn signing_time(&self) -> Option<chrono::DateTime<chrono::Utc>> {
427 self.signing_time
428 }
429
430 pub fn set_signing_time(&mut self, time: chrono::DateTime<chrono::Utc>) {
434 self.signing_time = Some(time);
435 }
436
437 pub fn team_id(&self) -> Option<&str> {
439 self.team_id.get(&SettingsScope::Main).map(|x| x.as_str())
440 }
441
442 pub fn set_team_id(&mut self, value: impl ToString) {
444 self.team_id.insert(SettingsScope::Main, value.to_string());
445 }
446
447 pub fn set_team_id_from_signing_certificate(&mut self) -> Option<&str> {
464 if !self.signing_certificate_apple_signed() {
466 None
467 } else if let Some((_, cert)) = &self.signing_key {
468 if let Some(team_id) = cert.apple_team_id() {
469 self.set_team_id(team_id);
470 Some(
471 self.team_id
472 .get(&SettingsScope::Main)
473 .expect("we just set a team id"),
474 )
475 } else {
476 None
477 }
478 } else {
479 None
480 }
481 }
482
483 pub fn path_exclusion_pattern_matches(&self, path: &str) -> bool {
485 self.path_exclusion_patterns
486 .iter()
487 .any(|pattern| pattern.matches(path))
488 }
489
490 pub fn add_path_exclusion(&mut self, v: &str) -> Result<(), AppleCodesignError> {
492 self.path_exclusion_patterns.push(Pattern::new(v)?);
493 Ok(())
494 }
495
496 pub fn shallow(&self) -> bool {
501 self.shallow
502 }
503
504 pub fn set_shallow(&mut self, v: bool) {
506 self.shallow = v;
507 }
508
509 pub fn for_notarization(&self) -> bool {
514 self.for_notarization
515 }
516
517 pub fn set_for_notarization(&mut self, v: bool) {
519 self.for_notarization = v;
520 }
521
522 pub fn digest_type(&self, scope: impl AsRef<SettingsScope>) -> DigestType {
524 self.digest_type
525 .get(scope.as_ref())
526 .copied()
527 .unwrap_or_default()
528 }
529
530 pub fn set_digest_type(&mut self, scope: SettingsScope, digest_type: DigestType) {
536 self.digest_type.insert(scope, digest_type);
537 }
538
539 pub fn binary_identifier(&self, scope: impl AsRef<SettingsScope>) -> Option<&str> {
541 self.identifiers.get(scope.as_ref()).map(|s| s.as_str())
542 }
543
544 pub fn set_binary_identifier(&mut self, scope: SettingsScope, value: impl ToString) {
554 self.identifiers.insert(scope, value.to_string());
555 }
556
557 pub fn entitlements_plist(&self, scope: impl AsRef<SettingsScope>) -> Option<&plist::Value> {
561 self.entitlements.get(scope.as_ref())
562 }
563
564 pub fn entitlements_xml(
566 &self,
567 scope: impl AsRef<SettingsScope>,
568 ) -> Result<Option<String>, AppleCodesignError> {
569 if let Some(value) = self.entitlements_plist(scope) {
570 let mut buffer = vec![];
571 let writer = std::io::Cursor::new(&mut buffer);
572 value
573 .to_writer_xml(writer)
574 .map_err(AppleCodesignError::PlistSerializeXml)?;
575
576 Ok(Some(
577 String::from_utf8(buffer).expect("plist XML serialization should produce UTF-8"),
578 ))
579 } else {
580 Ok(None)
581 }
582 }
583
584 pub fn set_entitlements_xml(
589 &mut self,
590 scope: SettingsScope,
591 value: impl ToString,
592 ) -> Result<(), AppleCodesignError> {
593 let cursor = std::io::Cursor::new(value.to_string().into_bytes());
594 let value =
595 plist::Value::from_reader_xml(cursor).map_err(AppleCodesignError::PlistParseXml)?;
596
597 self.entitlements.insert(scope, value);
598
599 Ok(())
600 }
601
602 pub fn designated_requirement(
604 &self,
605 scope: impl AsRef<SettingsScope>,
606 ) -> &DesignatedRequirementMode {
607 self.designated_requirement
608 .get(scope.as_ref())
609 .unwrap_or(&DesignatedRequirementMode::Auto)
610 }
611
612 pub fn set_designated_requirement_expression(
619 &mut self,
620 scope: SettingsScope,
621 expr: &CodeRequirementExpression,
622 ) -> Result<(), AppleCodesignError> {
623 self.designated_requirement.insert(
624 scope,
625 DesignatedRequirementMode::Explicit(vec![expr.to_bytes()?]),
626 );
627
628 Ok(())
629 }
630
631 pub fn set_designated_requirement_bytes(
638 &mut self,
639 scope: SettingsScope,
640 data: impl AsRef<[u8]>,
641 ) -> Result<(), AppleCodesignError> {
642 let blob = RequirementBlob::from_blob_bytes(data.as_ref())?;
643
644 self.designated_requirement.insert(
645 scope,
646 DesignatedRequirementMode::Explicit(
647 blob.parse_expressions()?
648 .iter()
649 .map(|x| x.to_bytes())
650 .collect::<Result<Vec<_>, AppleCodesignError>>()?,
651 ),
652 );
653
654 Ok(())
655 }
656
657 pub fn set_auto_designated_requirement(&mut self, scope: SettingsScope) {
667 self.designated_requirement
668 .insert(scope, DesignatedRequirementMode::Auto);
669 }
670
671 pub fn code_signature_flags(
673 &self,
674 scope: impl AsRef<SettingsScope>,
675 ) -> Option<CodeSignatureFlags> {
676 let mut flags = self.code_signature_flags.get(scope.as_ref()).copied();
677
678 if self.for_notarization {
679 flags.get_or_insert(CodeSignatureFlags::default());
680
681 flags.as_mut().map(|flags| {
682 if !flags.contains(CodeSignatureFlags::RUNTIME) {
683 info!("adding hardened runtime flag because notarization mode enabled");
684 }
685
686 flags.insert(CodeSignatureFlags::RUNTIME);
687 });
688 }
689
690 flags
691 }
692
693 pub fn set_code_signature_flags(&mut self, scope: SettingsScope, flags: CodeSignatureFlags) {
697 self.code_signature_flags.insert(scope, flags);
698 }
699
700 pub fn add_code_signature_flags(
705 &mut self,
706 scope: SettingsScope,
707 flags: CodeSignatureFlags,
708 ) -> CodeSignatureFlags {
709 let existing = self
710 .code_signature_flags
711 .get(&scope)
712 .copied()
713 .unwrap_or_else(CodeSignatureFlags::empty);
714
715 let new = existing | flags;
716
717 self.code_signature_flags.insert(scope, new);
718
719 new
720 }
721
722 pub fn remove_code_signature_flags(
727 &mut self,
728 scope: SettingsScope,
729 flags: CodeSignatureFlags,
730 ) -> CodeSignatureFlags {
731 let existing = self
732 .code_signature_flags
733 .get(&scope)
734 .copied()
735 .unwrap_or_else(CodeSignatureFlags::empty);
736
737 let new = existing - flags;
738
739 self.code_signature_flags.insert(scope, new);
740
741 new
742 }
743
744 pub fn info_plist_data(&self, scope: impl AsRef<SettingsScope>) -> Option<&[u8]> {
746 self.info_plist_data
747 .get(scope.as_ref())
748 .map(|x| x.as_slice())
749 }
750
751 pub fn runtime_version(&self, scope: impl AsRef<SettingsScope>) -> Option<&semver::Version> {
755 self.runtime_version.get(scope.as_ref())
756 }
757
758 pub fn set_runtime_version(&mut self, scope: SettingsScope, version: semver::Version) {
763 self.runtime_version.insert(scope, version);
764 }
765
766 pub fn set_info_plist_data(&mut self, scope: SettingsScope, data: Vec<u8>) {
781 self.info_plist_data.insert(scope, data);
782 }
783
784 pub fn code_resources_data(&self, scope: impl AsRef<SettingsScope>) -> Option<&[u8]> {
786 self.code_resources_data
787 .get(scope.as_ref())
788 .map(|x| x.as_slice())
789 }
790
791 pub fn set_code_resources_data(&mut self, scope: SettingsScope, data: Vec<u8>) {
807 self.code_resources_data.insert(scope, data);
808 }
809
810 pub fn extra_digests(&self, scope: impl AsRef<SettingsScope>) -> Option<&BTreeSet<DigestType>> {
812 self.extra_digests.get(scope.as_ref())
813 }
814
815 pub fn add_extra_digest(&mut self, scope: SettingsScope, digest_type: DigestType) {
825 self.extra_digests
826 .entry(scope)
827 .or_default()
828 .insert(digest_type);
829 }
830
831 pub fn all_digests(&self, scope: SettingsScope) -> Vec<DigestType> {
833 let mut res = vec![self.digest_type(scope.clone())];
834
835 if let Some(extra) = self.extra_digests(scope) {
836 res.extend(extra.iter());
837 }
838
839 res
840 }
841
842 pub fn launch_constraints_self(
844 &self,
845 scope: impl AsRef<SettingsScope>,
846 ) -> Option<&EncodedEnvironmentConstraints> {
847 self.launch_constraints_self.get(scope.as_ref())
848 }
849
850 pub fn set_launch_constraints_self(
852 &mut self,
853 scope: SettingsScope,
854 constraints: EncodedEnvironmentConstraints,
855 ) {
856 self.launch_constraints_self.insert(scope, constraints);
857 }
858
859 pub fn launch_constraints_parent(
861 &self,
862 scope: impl AsRef<SettingsScope>,
863 ) -> Option<&EncodedEnvironmentConstraints> {
864 self.launch_constraints_parent.get(scope.as_ref())
865 }
866
867 pub fn set_launch_constraints_parent(
869 &mut self,
870 scope: SettingsScope,
871 constraints: EncodedEnvironmentConstraints,
872 ) {
873 self.launch_constraints_parent.insert(scope, constraints);
874 }
875
876 pub fn launch_constraints_responsible(
878 &self,
879 scope: impl AsRef<SettingsScope>,
880 ) -> Option<&EncodedEnvironmentConstraints> {
881 self.launch_constraints_responsible.get(scope.as_ref())
882 }
883
884 pub fn set_launch_constraints_responsible(
886 &mut self,
887 scope: SettingsScope,
888 constraints: EncodedEnvironmentConstraints,
889 ) {
890 self.launch_constraints_responsible
891 .insert(scope, constraints);
892 }
893
894 pub fn library_constraints(
896 &self,
897 scope: impl AsRef<SettingsScope>,
898 ) -> Option<&EncodedEnvironmentConstraints> {
899 self.library_constraints.get(scope.as_ref())
900 }
901
902 pub fn set_library_constraints(
904 &mut self,
905 scope: SettingsScope,
906 constraints: EncodedEnvironmentConstraints,
907 ) {
908 self.library_constraints.insert(scope, constraints);
909 }
910
911 pub fn import_settings_from_macho(&mut self, data: &[u8]) -> Result<(), AppleCodesignError> {
918 info!("inferring default signing settings from Mach-O binary");
919
920 let mut seen_identifier = None;
921
922 for macho in MachFile::parse(data)?.into_iter() {
923 let index = macho.index.unwrap_or(0);
924
925 let scope_main = SettingsScope::Main;
926 let scope_index = SettingsScope::MultiArchIndex(index);
927 let scope_arch = SettingsScope::MultiArchCpuType(macho.macho.header.cputype());
928
929 let need_sha1_sha256 = if let Some(targeting) = macho.find_targeting()? {
937 let sha256_version = targeting.platform.sha256_digest_support()?;
938
939 if !sha256_version.matches(&targeting.minimum_os_version) {
940 info!(
941 "activating SHA-1 digests because minimum OS target {} is not {}",
942 targeting.minimum_os_version, sha256_version
943 );
944 true
945 } else {
946 false
947 }
948 } else {
949 info!("activating SHA-1 digests because no platform targeting in Mach-O");
950 true
951 };
952
953 if need_sha1_sha256 {
954 self.set_digest_type(scope_main.clone(), DigestType::Sha1);
959 self.add_extra_digest(scope_main.clone(), DigestType::Sha256);
960 self.extra_digests.remove(&scope_arch);
961 self.extra_digests.remove(&scope_index);
962 }
963
964 if let Some(info_plist) = macho.embedded_info_plist()? {
967 if self.info_plist_data(&scope_main).is_some()
968 || self.info_plist_data(&scope_index).is_some()
969 || self.info_plist_data(&scope_arch).is_some()
970 {
971 info!("using Info.plist data from settings");
972 } else {
973 info!("preserving Info.plist data already present in Mach-O");
974 self.set_info_plist_data(scope_index.clone(), info_plist);
975 }
976 }
977
978 if let Some(sig) = macho.code_signature()? {
979 if let Some(cd) = sig.code_directory()? {
980 if self.binary_identifier(&scope_main).is_some()
981 || self.binary_identifier(&scope_index).is_some()
982 || self.binary_identifier(&scope_arch).is_some()
983 {
984 info!("using binary identifier from settings");
985 } else if let Some(initial_identifier) = &seen_identifier {
986 if initial_identifier != &*cd.ident {
990 info!(
991 "identifiers within Mach-O do not agree (initial: {initial_identifier}, subsequent: {}); reconciling to {initial_identifier}",
992 cd.ident
993 );
994 self.set_binary_identifier(scope_index.clone(), initial_identifier);
995 }
996 } else {
997 info!(
998 "preserving existing binary identifier in Mach-O ({})",
999 cd.ident
1000 );
1001 self.set_binary_identifier(scope_index.clone(), cd.ident.to_string());
1002 seen_identifier = Some(cd.ident.to_string());
1003 }
1004
1005 if self.team_id.contains_key(&scope_main)
1006 || self.team_id.contains_key(&scope_index)
1007 || self.team_id.contains_key(&scope_arch)
1008 {
1009 info!("using team ID from settings");
1010 } else if let Some(team_id) = cd.team_name {
1011 if self.signing_certificate_apple_signed() {
1014 info!(
1015 "preserving team ID in existing Mach-O signature ({})",
1016 team_id
1017 );
1018 self.team_id
1019 .insert(scope_index.clone(), team_id.to_string());
1020 } else {
1021 info!(
1022 "dropping team ID {} because not signing with an Apple signed certificate",
1023 team_id
1024 );
1025 }
1026 }
1027
1028 if self.code_signature_flags(&scope_main).is_some()
1029 || self.code_signature_flags(&scope_index).is_some()
1030 || self.code_signature_flags(&scope_arch).is_some()
1031 {
1032 info!("using code signature flags from settings");
1033 } else if !cd.flags.is_empty() {
1034 info!(
1035 "preserving code signature flags in existing Mach-O signature ({:?})",
1036 cd.flags
1037 );
1038 self.set_code_signature_flags(scope_index.clone(), cd.flags);
1039 }
1040
1041 if self.runtime_version(&scope_main).is_some()
1042 || self.runtime_version(&scope_index).is_some()
1043 || self.runtime_version(&scope_arch).is_some()
1044 {
1045 info!("using runtime version from settings");
1046 } else if let Some(version) = cd.runtime {
1047 let version = parse_version_nibbles(version);
1048
1049 info!(
1050 "preserving runtime version in existing Mach-O signature ({})",
1051 version
1052 );
1053 self.set_runtime_version(scope_index.clone(), version);
1054 }
1055 }
1056
1057 if let Some(entitlements) = sig.entitlements()? {
1058 if self.entitlements_plist(&scope_main).is_some()
1059 || self.entitlements_plist(&scope_index).is_some()
1060 || self.entitlements_plist(&scope_arch).is_some()
1061 {
1062 info!("using entitlements from settings");
1063 } else {
1064 info!("preserving existing entitlements in Mach-O");
1065 self.set_entitlements_xml(
1066 SettingsScope::MultiArchIndex(index),
1067 entitlements.as_str(),
1068 )?;
1069 }
1070 }
1071
1072 if let Some(constraints) = sig.launch_constraints_self()? {
1073 if self.launch_constraints_self(&scope_main).is_some()
1074 || self.launch_constraints_self(&scope_index).is_some()
1075 || self.launch_constraints_self(&scope_arch).is_some()
1076 {
1077 info!("using self launch constraints from settings");
1078 } else {
1079 info!("preserving existing self launch constraints in Mach-O");
1080 self.set_launch_constraints_self(
1081 SettingsScope::MultiArchIndex(index),
1082 constraints.parse_encoded_constraints()?,
1083 );
1084 }
1085 }
1086
1087 if let Some(constraints) = sig.launch_constraints_parent()? {
1088 if self.launch_constraints_parent(&scope_main).is_some()
1089 || self.launch_constraints_parent(&scope_index).is_some()
1090 || self.launch_constraints_parent(&scope_arch).is_some()
1091 {
1092 info!("using parent launch constraints from settings");
1093 } else {
1094 info!("preserving existing parent launch constraints in Mach-O");
1095 self.set_launch_constraints_parent(
1096 SettingsScope::MultiArchIndex(index),
1097 constraints.parse_encoded_constraints()?,
1098 );
1099 }
1100 }
1101
1102 if let Some(constraints) = sig.launch_constraints_responsible()? {
1103 if self.launch_constraints_responsible(&scope_main).is_some()
1104 || self.launch_constraints_responsible(&scope_index).is_some()
1105 || self.launch_constraints_responsible(&scope_arch).is_some()
1106 {
1107 info!("using responsible process launch constraints from settings");
1108 } else {
1109 info!(
1110 "preserving existing responsible process launch constraints in Mach-O"
1111 );
1112 self.set_launch_constraints_responsible(
1113 SettingsScope::MultiArchIndex(index),
1114 constraints.parse_encoded_constraints()?,
1115 );
1116 }
1117 }
1118
1119 if let Some(constraints) = sig.library_constraints()? {
1120 if self.library_constraints(&scope_main).is_some()
1121 || self.library_constraints(&scope_index).is_some()
1122 || self.library_constraints(&scope_arch).is_some()
1123 {
1124 info!("using library constraints from settings");
1125 } else {
1126 info!("preserving existing library constraints in Mach-O");
1127 self.set_library_constraints(
1128 SettingsScope::MultiArchIndex(index),
1129 constraints.parse_encoded_constraints()?,
1130 );
1131 }
1132 }
1133 }
1134 }
1135
1136 Ok(())
1137 }
1138
1139 #[must_use]
1141 pub fn as_nested_bundle_settings(&self, bundle_path: &str) -> Self {
1142 self.clone_strip_prefix(
1143 bundle_path,
1144 format!("{bundle_path}/"),
1145 ScopedSetting::inherit_nested_bundle(),
1146 )
1147 }
1148
1149 #[must_use]
1151 pub fn as_bundle_main_executable_settings(&self, path: &str) -> Self {
1152 self.clone_strip_prefix(path, path.to_string(), ScopedSetting::all())
1153 }
1154
1155 #[must_use]
1159 pub fn as_bundle_macho_settings(&self, path: &str) -> Self {
1160 self.clone_strip_prefix(
1161 path,
1162 path.to_string(),
1163 ScopedSetting::inherit_nested_macho(),
1164 )
1165 }
1166
1167 #[must_use]
1173 pub fn as_universal_macho_settings(&self, index: usize, cpu_type: CpuType) -> Self {
1174 self.clone_with_filter_map(|_, key| {
1175 if key == SettingsScope::Main
1176 || key == SettingsScope::MultiArchCpuType(cpu_type)
1177 || key == SettingsScope::MultiArchIndex(index)
1178 {
1179 Some(SettingsScope::Main)
1180 } else {
1181 None
1182 }
1183 })
1184 }
1185
1186 fn clone_strip_prefix(
1189 &self,
1190 main_path: &str,
1191 prefix: String,
1192 preserve_settings: &[ScopedSetting],
1193 ) -> Self {
1194 self.clone_with_filter_map(|setting, key| match key {
1195 SettingsScope::Main => {
1196 if preserve_settings.contains(&setting) {
1197 Some(SettingsScope::Main)
1198 } else {
1199 None
1200 }
1201 }
1202 SettingsScope::Path(path) => {
1203 if path == main_path {
1204 Some(SettingsScope::Main)
1205 } else {
1206 path.strip_prefix(&prefix)
1207 .map(|path| SettingsScope::Path(path.to_string()))
1208 }
1209 }
1210
1211 SettingsScope::MultiArchIndex(index) => {
1215 if preserve_settings.contains(&setting) {
1216 Some(SettingsScope::MultiArchIndex(index))
1217 } else {
1218 None
1219 }
1220 }
1221 SettingsScope::MultiArchCpuType(cpu_type) => {
1222 if preserve_settings.contains(&setting) {
1223 Some(SettingsScope::MultiArchCpuType(cpu_type))
1224 } else {
1225 None
1226 }
1227 }
1228
1229 SettingsScope::PathMultiArchIndex(path, index) => {
1230 if path == main_path {
1231 Some(SettingsScope::MultiArchIndex(index))
1232 } else {
1233 path.strip_prefix(&prefix)
1234 .map(|path| SettingsScope::PathMultiArchIndex(path.to_string(), index))
1235 }
1236 }
1237 SettingsScope::PathMultiArchCpuType(path, cpu_type) => {
1238 if path == main_path {
1239 Some(SettingsScope::MultiArchCpuType(cpu_type))
1240 } else {
1241 path.strip_prefix(&prefix)
1242 .map(|path| SettingsScope::PathMultiArchCpuType(path.to_string(), cpu_type))
1243 }
1244 }
1245 })
1246 }
1247
1248 fn clone_with_filter_map(
1249 &self,
1250 key_map: impl Fn(ScopedSetting, SettingsScope) -> Option<SettingsScope>,
1251 ) -> Self {
1252 Self {
1253 signing_key: self.signing_key.clone(),
1254 certificates: self.certificates.clone(),
1255 signing_time: self.signing_time,
1256 team_id: self.team_id.clone(),
1257 path_exclusion_patterns: self.path_exclusion_patterns.clone(),
1258 shallow: self.shallow,
1259 for_notarization: self.for_notarization,
1260 digest_type: self
1261 .digest_type
1262 .clone()
1263 .into_iter()
1264 .filter_map(|(key, value)| {
1265 key_map(ScopedSetting::Digest, key).map(|key| (key, value))
1266 })
1267 .collect::<BTreeMap<_, _>>(),
1268 identifiers: self
1269 .identifiers
1270 .clone()
1271 .into_iter()
1272 .filter_map(|(key, value)| {
1273 key_map(ScopedSetting::BinaryIdentifier, key).map(|key| (key, value))
1274 })
1275 .collect::<BTreeMap<_, _>>(),
1276 entitlements: self
1277 .entitlements
1278 .clone()
1279 .into_iter()
1280 .filter_map(|(key, value)| {
1281 key_map(ScopedSetting::Entitlements, key).map(|key| (key, value))
1282 })
1283 .collect::<BTreeMap<_, _>>(),
1284 designated_requirement: self
1285 .designated_requirement
1286 .clone()
1287 .into_iter()
1288 .filter_map(|(key, value)| {
1289 key_map(ScopedSetting::DesignatedRequirements, key).map(|key| (key, value))
1290 })
1291 .collect::<BTreeMap<_, _>>(),
1292 code_signature_flags: self
1293 .code_signature_flags
1294 .clone()
1295 .into_iter()
1296 .filter_map(|(key, value)| {
1297 key_map(ScopedSetting::CodeSignatureFlags, key).map(|key| (key, value))
1298 })
1299 .collect::<BTreeMap<_, _>>(),
1300 runtime_version: self
1301 .runtime_version
1302 .clone()
1303 .into_iter()
1304 .filter_map(|(key, value)| {
1305 key_map(ScopedSetting::RuntimeVersion, key).map(|key| (key, value))
1306 })
1307 .collect::<BTreeMap<_, _>>(),
1308 info_plist_data: self
1309 .info_plist_data
1310 .clone()
1311 .into_iter()
1312 .filter_map(|(key, value)| {
1313 key_map(ScopedSetting::InfoPlist, key).map(|key| (key, value))
1314 })
1315 .collect::<BTreeMap<_, _>>(),
1316 code_resources_data: self
1317 .code_resources_data
1318 .clone()
1319 .into_iter()
1320 .filter_map(|(key, value)| {
1321 key_map(ScopedSetting::CodeResources, key).map(|key| (key, value))
1322 })
1323 .collect::<BTreeMap<_, _>>(),
1324 extra_digests: self
1325 .extra_digests
1326 .clone()
1327 .into_iter()
1328 .filter_map(|(key, value)| {
1329 key_map(ScopedSetting::ExtraDigests, key).map(|key| (key, value))
1330 })
1331 .collect::<BTreeMap<_, _>>(),
1332 launch_constraints_self: self
1333 .launch_constraints_self
1334 .clone()
1335 .into_iter()
1336 .filter_map(|(key, value)| {
1337 key_map(ScopedSetting::LaunchConstraintsSelf, key).map(|key| (key, value))
1338 })
1339 .collect::<BTreeMap<_, _>>(),
1340 launch_constraints_parent: self
1341 .launch_constraints_parent
1342 .clone()
1343 .into_iter()
1344 .filter_map(|(key, value)| {
1345 key_map(ScopedSetting::LaunchConstraintsParent, key).map(|key| (key, value))
1346 })
1347 .collect::<BTreeMap<_, _>>(),
1348 launch_constraints_responsible: self
1349 .launch_constraints_responsible
1350 .clone()
1351 .into_iter()
1352 .filter_map(|(key, value)| {
1353 key_map(ScopedSetting::LaunchConstraintsResponsible, key)
1354 .map(|key| (key, value))
1355 })
1356 .collect::<BTreeMap<_, _>>(),
1357 library_constraints: self
1358 .library_constraints
1359 .clone()
1360 .into_iter()
1361 .filter_map(|(key, value)| {
1362 key_map(ScopedSetting::LibraryConstraints, key).map(|key| (key, value))
1363 })
1364 .collect::<BTreeMap<_, _>>(),
1365 }
1366 }
1367
1368 pub fn ensure_for_notarization_settings(&self) -> Result<(), AppleCodesignError> {
1372 if !self.for_notarization {
1373 return Ok(());
1374 }
1375
1376 let mut have_error = false;
1377
1378 if let Some((_, cert)) = self.signing_key() {
1379 if !cert.chains_to_apple_root_ca() && !cert.is_test_apple_signed_certificate() {
1380 error!(
1381 "--for-notarization requires use of an Apple-issued signing certificate; current certificate is not signed by Apple"
1382 );
1383 error!(
1384 "hint: use a signing certificate issued by Apple that is signed by an Apple certificate authority"
1385 );
1386 have_error = true;
1387 }
1388
1389 if !cert.apple_code_signing_extensions().into_iter().any(|e| {
1390 e == CodeSigningCertificateExtension::DeveloperIdApplication
1391 || e == CodeSigningCertificateExtension::DeveloperIdInstaller
1392 || e == CodeSigningCertificateExtension::DeveloperIdKernel {}
1393 }) {
1394 error!(
1395 "--for-notarization requires use of a Developer ID signing certificate; current certificate doesn't appear to be such a certificate"
1396 );
1397 error!(
1398 "hint: use a `Developer ID Application`, `Developer ID Installer`, or `Developer ID Kernel` certificate"
1399 );
1400 have_error = true;
1401 }
1402 } else {
1403 error!(
1404 "--for-notarization requires use of a Developer ID signing certificate; no signing certificate was provided"
1405 );
1406 have_error = true;
1407 }
1408
1409 if have_error {
1410 Err(AppleCodesignError::ForNotarizationInvalidSettings)
1411 } else {
1412 Ok(())
1413 }
1414 }
1415}
1416
1417#[cfg(test)]
1418mod tests {
1419 use {super::*, indoc::indoc};
1420
1421 const ENTITLEMENTS_XML: &str = indoc! {r#"
1422 <?xml version="1.0" encoding="UTF-8"?>
1423 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
1424 <plist version="1.0">
1425 <dict>
1426 <key>application-identifier</key>
1427 <string>appid</string>
1428 <key>com.apple.developer.team-identifier</key>
1429 <string>ABCDEF</string>
1430 </dict>
1431 </plist>
1432 "#};
1433
1434 #[test]
1435 fn parse_settings_scope() {
1436 assert_eq!(
1437 SettingsScope::try_from("@main").unwrap(),
1438 SettingsScope::Main
1439 );
1440 assert_eq!(
1441 SettingsScope::try_from("@0").unwrap(),
1442 SettingsScope::MultiArchIndex(0)
1443 );
1444 assert_eq!(
1445 SettingsScope::try_from("@42").unwrap(),
1446 SettingsScope::MultiArchIndex(42)
1447 );
1448 assert_eq!(
1449 SettingsScope::try_from("@[cpu_type=7]").unwrap(),
1450 SettingsScope::MultiArchCpuType(7)
1451 );
1452 assert_eq!(
1453 SettingsScope::try_from("@[cpu_type=arm]").unwrap(),
1454 SettingsScope::MultiArchCpuType(CPU_TYPE_ARM)
1455 );
1456 assert_eq!(
1457 SettingsScope::try_from("@[cpu_type=arm64]").unwrap(),
1458 SettingsScope::MultiArchCpuType(CPU_TYPE_ARM64)
1459 );
1460 assert_eq!(
1461 SettingsScope::try_from("@[cpu_type=arm64_32]").unwrap(),
1462 SettingsScope::MultiArchCpuType(CPU_TYPE_ARM64_32)
1463 );
1464 assert_eq!(
1465 SettingsScope::try_from("@[cpu_type=x86_64]").unwrap(),
1466 SettingsScope::MultiArchCpuType(CPU_TYPE_X86_64)
1467 );
1468 assert_eq!(
1469 SettingsScope::try_from("foo/bar").unwrap(),
1470 SettingsScope::Path("foo/bar".into())
1471 );
1472 assert_eq!(
1473 SettingsScope::try_from("foo/bar@0").unwrap(),
1474 SettingsScope::PathMultiArchIndex("foo/bar".into(), 0)
1475 );
1476 assert_eq!(
1477 SettingsScope::try_from("foo/bar@[cpu_type=7]").unwrap(),
1478 SettingsScope::PathMultiArchCpuType("foo/bar".into(), 7_u32)
1479 );
1480 }
1481
1482 #[test]
1483 fn as_nested_macho_settings() {
1484 let mut main_settings = SigningSettings::default();
1485 main_settings.set_binary_identifier(SettingsScope::Main, "ident");
1486 main_settings
1487 .set_code_signature_flags(SettingsScope::Main, CodeSignatureFlags::FORCE_EXPIRATION);
1488
1489 main_settings.set_code_signature_flags(
1490 SettingsScope::MultiArchIndex(0),
1491 CodeSignatureFlags::FORCE_HARD,
1492 );
1493 main_settings.set_code_signature_flags(
1494 SettingsScope::MultiArchCpuType(CPU_TYPE_X86_64),
1495 CodeSignatureFlags::RESTRICT,
1496 );
1497 main_settings.set_info_plist_data(SettingsScope::MultiArchIndex(0), b"index_0".to_vec());
1498 main_settings.set_info_plist_data(
1499 SettingsScope::MultiArchCpuType(CPU_TYPE_X86_64),
1500 b"cpu_x86_64".to_vec(),
1501 );
1502
1503 let macho_settings = main_settings.as_universal_macho_settings(0, CPU_TYPE_ARM64);
1504 assert_eq!(
1505 macho_settings.binary_identifier(SettingsScope::Main),
1506 Some("ident")
1507 );
1508 assert_eq!(
1509 macho_settings.code_signature_flags(SettingsScope::Main),
1510 Some(CodeSignatureFlags::FORCE_HARD)
1511 );
1512 assert_eq!(
1513 macho_settings.info_plist_data(SettingsScope::Main),
1514 Some(b"index_0".as_ref())
1515 );
1516
1517 let macho_settings = main_settings.as_universal_macho_settings(0, CPU_TYPE_X86_64);
1518 assert_eq!(
1519 macho_settings.binary_identifier(SettingsScope::Main),
1520 Some("ident")
1521 );
1522 assert_eq!(
1523 macho_settings.code_signature_flags(SettingsScope::Main),
1524 Some(CodeSignatureFlags::RESTRICT)
1525 );
1526 assert_eq!(
1527 macho_settings.info_plist_data(SettingsScope::Main),
1528 Some(b"cpu_x86_64".as_ref())
1529 );
1530 }
1531
1532 #[test]
1533 fn as_bundle_macho_settings() {
1534 let mut main_settings = SigningSettings::default();
1535 main_settings.set_info_plist_data(SettingsScope::Main, b"main".to_vec());
1536 main_settings.set_info_plist_data(
1537 SettingsScope::Path("Contents/MacOS/main".into()),
1538 b"main_exe".to_vec(),
1539 );
1540 main_settings.set_info_plist_data(
1541 SettingsScope::PathMultiArchIndex("Contents/MacOS/main".into(), 0),
1542 b"main_exe_index_0".to_vec(),
1543 );
1544 main_settings.set_info_plist_data(
1545 SettingsScope::PathMultiArchCpuType("Contents/MacOS/main".into(), CPU_TYPE_X86_64),
1546 b"main_exe_x86_64".to_vec(),
1547 );
1548
1549 let macho_settings = main_settings.as_bundle_macho_settings("Contents/MacOS/main");
1550 assert_eq!(
1551 macho_settings.info_plist_data(SettingsScope::Main),
1552 Some(b"main_exe".as_ref())
1553 );
1554 assert_eq!(
1555 macho_settings.info_plist_data,
1556 [
1557 (SettingsScope::Main, b"main_exe".to_vec()),
1558 (
1559 SettingsScope::MultiArchIndex(0),
1560 b"main_exe_index_0".to_vec()
1561 ),
1562 (
1563 SettingsScope::MultiArchCpuType(CPU_TYPE_X86_64),
1564 b"main_exe_x86_64".to_vec()
1565 ),
1566 ]
1567 .iter()
1568 .cloned()
1569 .collect::<BTreeMap<SettingsScope, Vec<u8>>>()
1570 );
1571 }
1572
1573 #[test]
1574 fn as_nested_bundle_settings() {
1575 let mut main_settings = SigningSettings::default();
1576 main_settings.set_info_plist_data(SettingsScope::Main, b"main".to_vec());
1577 main_settings.set_info_plist_data(
1578 SettingsScope::Path("Contents/MacOS/main".into()),
1579 b"main_exe".to_vec(),
1580 );
1581 main_settings.set_info_plist_data(
1582 SettingsScope::Path("Contents/MacOS/nested.app".into()),
1583 b"bundle".to_vec(),
1584 );
1585 main_settings.set_info_plist_data(
1586 SettingsScope::PathMultiArchIndex("Contents/MacOS/nested.app".into(), 0),
1587 b"bundle_index_0".to_vec(),
1588 );
1589 main_settings.set_info_plist_data(
1590 SettingsScope::PathMultiArchCpuType(
1591 "Contents/MacOS/nested.app".into(),
1592 CPU_TYPE_X86_64,
1593 ),
1594 b"bundle_x86_64".to_vec(),
1595 );
1596 main_settings.set_info_plist_data(
1597 SettingsScope::Path("Contents/MacOS/nested.app/Contents/MacOS/nested".into()),
1598 b"nested_main_exe".to_vec(),
1599 );
1600 main_settings.set_info_plist_data(
1601 SettingsScope::PathMultiArchIndex(
1602 "Contents/MacOS/nested.app/Contents/MacOS/nested".into(),
1603 0,
1604 ),
1605 b"nested_main_exe_index_0".to_vec(),
1606 );
1607 main_settings.set_info_plist_data(
1608 SettingsScope::PathMultiArchCpuType(
1609 "Contents/MacOS/nested.app/Contents/MacOS/nested".into(),
1610 CPU_TYPE_X86_64,
1611 ),
1612 b"nested_main_exe_x86_64".to_vec(),
1613 );
1614
1615 let bundle_settings = main_settings.as_nested_bundle_settings("Contents/MacOS/nested.app");
1616 assert_eq!(
1617 bundle_settings.info_plist_data(SettingsScope::Main),
1618 Some(b"bundle".as_ref())
1619 );
1620 assert_eq!(
1621 bundle_settings.info_plist_data(SettingsScope::Path("Contents/MacOS/nested".into())),
1622 Some(b"nested_main_exe".as_ref())
1623 );
1624 assert_eq!(
1625 bundle_settings.info_plist_data,
1626 [
1627 (SettingsScope::Main, b"bundle".to_vec()),
1628 (SettingsScope::MultiArchIndex(0), b"bundle_index_0".to_vec()),
1629 (
1630 SettingsScope::MultiArchCpuType(CPU_TYPE_X86_64),
1631 b"bundle_x86_64".to_vec()
1632 ),
1633 (
1634 SettingsScope::Path("Contents/MacOS/nested".into()),
1635 b"nested_main_exe".to_vec()
1636 ),
1637 (
1638 SettingsScope::PathMultiArchIndex("Contents/MacOS/nested".into(), 0),
1639 b"nested_main_exe_index_0".to_vec()
1640 ),
1641 (
1642 SettingsScope::PathMultiArchCpuType(
1643 "Contents/MacOS/nested".into(),
1644 CPU_TYPE_X86_64
1645 ),
1646 b"nested_main_exe_x86_64".to_vec()
1647 ),
1648 ]
1649 .iter()
1650 .cloned()
1651 .collect::<BTreeMap<SettingsScope, Vec<u8>>>()
1652 );
1653 }
1654
1655 #[test]
1656 fn entitlements_handling() -> Result<(), AppleCodesignError> {
1657 let mut settings = SigningSettings::default();
1658 settings.set_entitlements_xml(SettingsScope::Main, ENTITLEMENTS_XML)?;
1659
1660 let s = settings.entitlements_xml(SettingsScope::Main)?;
1661 assert_eq!(s, Some("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>application-identifier</key>\n\t<string>appid</string>\n\t<key>com.apple.developer.team-identifier</key>\n\t<string>ABCDEF</string>\n</dict>\n</plist>".into()));
1662
1663 Ok(())
1664 }
1665
1666 #[test]
1667 fn for_notarization_handling() -> Result<(), AppleCodesignError> {
1668 let mut settings = SigningSettings::default();
1669 settings.set_for_notarization(true);
1670
1671 assert_eq!(
1672 settings.code_signature_flags(SettingsScope::Main),
1673 Some(CodeSignatureFlags::RUNTIME)
1674 );
1675
1676 assert_eq!(
1677 settings
1678 .as_bundle_macho_settings("")
1679 .code_signature_flags(SettingsScope::Main),
1680 Some(CodeSignatureFlags::RUNTIME)
1681 );
1682
1683 Ok(())
1684 }
1685}