use {
crate::{apple_certificates::KnownCertificate, error::AppleCodesignError},
bcder::{
encode::{PrimitiveContent, Values},
ConstOid, Oid,
},
bytes::Bytes,
pkcs8::EncodePrivateKey,
std::{
fmt::{Display, Formatter},
str::FromStr,
},
x509_certificate::{
certificate::KeyUsage, rfc4519::OID_COUNTRY_NAME, CapturedX509Certificate,
InMemorySigningKeyPair, KeyAlgorithm, X509CertificateBuilder,
},
};
const OID_EXTENDED_KEY_USAGE: ConstOid = Oid(&[85, 29, 37]);
const OID_EKU_PURPOSE_CODE_SIGNING: ConstOid = Oid(&[43, 6, 1, 5, 5, 7, 3, 3]);
const OID_EKU_PURPOSE_SAFARI_DEVELOPER: ConstOid = Oid(&[42, 134, 72, 134, 247, 99, 100, 4, 8]);
const OID_EKU_PURPOSE_3RD_PARTY_MAC_DEVELOPER_INSTALLER: ConstOid =
Oid(&[42, 134, 72, 134, 247, 99, 100, 4, 9]);
const OID_EKU_PURPOSE_DEVELOPER_ID_INSTALLER: ConstOid =
Oid(&[42, 134, 72, 134, 247, 99, 100, 4, 13]);
const ALL_OID_EKUS: &[&ConstOid; 4] = &[
&OID_EKU_PURPOSE_CODE_SIGNING,
&OID_EKU_PURPOSE_SAFARI_DEVELOPER,
&OID_EKU_PURPOSE_3RD_PARTY_MAC_DEVELOPER_INSTALLER,
&OID_EKU_PURPOSE_DEVELOPER_ID_INSTALLER,
];
const OID_EXTENSION_APPLE_SIGNING: ConstOid = Oid(&[42, 134, 72, 134, 247, 99, 100, 6, 1, 1]);
const OID_EXTENSION_IPHONE_DEVELOPER: ConstOid = Oid(&[42, 134, 72, 134, 247, 99, 100, 6, 1, 2]);
const OID_EXTENSION_IPHONE_OS_APPLICATION_SIGNING: ConstOid =
Oid(&[42, 134, 72, 134, 247, 99, 100, 6, 1, 3]);
const OID_EXTENSION_APPLE_DEVELOPER_CERTIFICATE_SUBMISSION: ConstOid =
Oid(&[42, 134, 72, 134, 247, 99, 100, 6, 1, 4]);
const OID_EXTENSION_SAFARI_DEVELOPER: ConstOid = Oid(&[42, 134, 72, 134, 247, 99, 100, 6, 1, 5]);
const OID_EXTENSION_IPHONE_OS_VPN_SIGNING: ConstOid =
Oid(&[42, 134, 72, 134, 247, 99, 100, 6, 1, 6]);
const OID_EXTENSION_APPLE_MAC_APP_SIGNING_DEVELOPMENT: ConstOid =
Oid(&[42, 134, 72, 134, 247, 99, 100, 6, 1, 7]);
const OID_EXTENSION_APPLE_MAC_APP_SIGNING_SUBMISSION: ConstOid =
Oid(&[42, 134, 72, 134, 247, 99, 100, 6, 1, 8]);
const OID_EXTENSION_APPLE_MAC_APP_STORE_CODE_SIGNING: ConstOid =
Oid(&[42, 134, 72, 134, 247, 99, 100, 6, 1, 9]);
const OID_EXTENSION_APPLE_MAC_APP_STORE_INSTALLER_SIGNING: ConstOid =
Oid(&[42, 134, 72, 134, 247, 99, 100, 6, 1, 10]);
const OID_EXTENSION_MAC_DEVELOPER: ConstOid = Oid(&[42, 134, 72, 134, 247, 99, 100, 6, 1, 12]);
const OID_EXTENSION_DEVELOPER_ID_APPLICATION: ConstOid =
Oid(&[42, 134, 72, 134, 247, 99, 100, 6, 1, 13]);
const OID_EXTENSION_DEVELOPER_ID_INSTALLER: ConstOid =
Oid(&[42, 134, 72, 134, 247, 99, 100, 6, 1, 14]);
const OID_EXTENSION_PASSBOOK_SIGNING: ConstOid = Oid(&[42, 134, 72, 134, 247, 99, 100, 6, 1, 16]);
const OID_EXTENSION_WEBSITE_PUSH_NOTIFICATION_SIGNING: ConstOid =
Oid(&[42, 134, 72, 134, 247, 99, 100, 6, 1, 17]);
const OID_EXTENSION_DEVELOPER_ID_KERNEL: ConstOid =
Oid(&[42, 134, 72, 134, 247, 99, 100, 6, 1, 18]);
const OID_EXTENSION_DEVELOPER_ID_DATE: ConstOid = Oid(&[42, 134, 72, 134, 247, 99, 100, 6, 1, 33]);
const OID_EXTENSION_TEST_FLIGHT: ConstOid = Oid(&[42, 134, 72, 134, 247, 99, 100, 6, 1, 25, 1]);
const ALL_OID_NON_CA_EXTENSIONS: &[&ConstOid; 18] = &[
&OID_EXTENSION_APPLE_SIGNING,
&OID_EXTENSION_IPHONE_DEVELOPER,
&OID_EXTENSION_IPHONE_OS_APPLICATION_SIGNING,
&OID_EXTENSION_APPLE_DEVELOPER_CERTIFICATE_SUBMISSION,
&OID_EXTENSION_SAFARI_DEVELOPER,
&OID_EXTENSION_IPHONE_OS_VPN_SIGNING,
&OID_EXTENSION_APPLE_MAC_APP_SIGNING_DEVELOPMENT,
&OID_EXTENSION_APPLE_MAC_APP_SIGNING_SUBMISSION,
&OID_EXTENSION_APPLE_MAC_APP_STORE_CODE_SIGNING,
&OID_EXTENSION_APPLE_MAC_APP_STORE_INSTALLER_SIGNING,
&OID_EXTENSION_MAC_DEVELOPER,
&OID_EXTENSION_DEVELOPER_ID_APPLICATION,
&OID_EXTENSION_DEVELOPER_ID_INSTALLER,
&OID_EXTENSION_PASSBOOK_SIGNING,
&OID_EXTENSION_WEBSITE_PUSH_NOTIFICATION_SIGNING,
&OID_EXTENSION_DEVELOPER_ID_KERNEL,
&OID_EXTENSION_DEVELOPER_ID_DATE,
&OID_EXTENSION_TEST_FLIGHT,
];
pub const OID_USER_ID: ConstOid = Oid(&[9, 146, 38, 137, 147, 242, 44, 100, 1, 1]);
const OID_EMAIL_ADDRESS: ConstOid = Oid(&[42, 134, 72, 134, 247, 13, 1, 9, 1]);
const OID_CA_EXTENSION_APPLE_WORLDWIDE_DEVELOPER_RELATIONS: ConstOid =
Oid(&[42, 134, 72, 134, 247, 99, 100, 6, 2, 1]);
const OID_CA_EXTENSION_APPLE_APPLICATION_INTEGRATION: ConstOid =
Oid(&[42, 134, 72, 134, 247, 99, 100, 6, 2, 3]);
const OID_CA_EXTENSION_DEVELOPER_ID: ConstOid = Oid(&[42, 134, 72, 134, 247, 99, 100, 6, 2, 6]);
const OID_CA_EXTENSION_APPLE_TIMESTAMP: ConstOid = Oid(&[42, 134, 72, 134, 247, 99, 100, 6, 2, 9]);
const OID_CA_EXTENSION_DEVELOPER_AUTHENTICATION: ConstOid =
Oid(&[42, 134, 72, 134, 247, 99, 100, 6, 2, 11]);
const OID_CA_EXTENSION_APPLE_APPLICATION_INTEGRATION_G3: ConstOid =
Oid(&[42, 134, 72, 134, 247, 99, 100, 6, 2, 14]);
const OID_CA_EXTENSION_APPLE_WORLDWIDE_DEVELOPER_RELATIONS_G2: ConstOid =
Oid(&[42, 134, 72, 134, 247, 99, 100, 6, 2, 15]);
const OID_CA_EXTENSION_APPLE_SOFTWARE_UPDATE_CERTIFICATION: ConstOid =
Oid(&[42, 134, 72, 134, 247, 99, 100, 6, 2, 19]);
const OID_CA_EXTENSION_APPLE_APPLICATION_INTEGRATION_G1: ConstOid =
Oid(&[42, 134, 72, 134, 247, 99, 100, 6, 2, 31]);
const ALL_OID_CA_EXTENSIONS: &[&ConstOid; 9] = &[
&OID_CA_EXTENSION_APPLE_WORLDWIDE_DEVELOPER_RELATIONS,
&OID_CA_EXTENSION_APPLE_APPLICATION_INTEGRATION,
&OID_CA_EXTENSION_DEVELOPER_ID,
&OID_CA_EXTENSION_APPLE_TIMESTAMP,
&OID_CA_EXTENSION_DEVELOPER_AUTHENTICATION,
&OID_CA_EXTENSION_APPLE_APPLICATION_INTEGRATION_G3,
&OID_CA_EXTENSION_APPLE_WORLDWIDE_DEVELOPER_RELATIONS_G2,
&OID_CA_EXTENSION_APPLE_SOFTWARE_UPDATE_CERTIFICATION,
&OID_CA_EXTENSION_APPLE_APPLICATION_INTEGRATION_G1,
];
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ExtendedKeyUsagePurpose {
CodeSigning,
SafariDeveloper,
ThirdPartyMacDeveloperInstaller,
DeveloperIdInstaller,
}
impl ExtendedKeyUsagePurpose {
pub fn all() -> Vec<Self> {
vec![
Self::CodeSigning,
Self::SafariDeveloper,
Self::ThirdPartyMacDeveloperInstaller,
Self::DeveloperIdInstaller,
]
}
pub fn all_oids() -> &'static [&'static ConstOid] {
ALL_OID_EKUS
}
pub fn as_oid(&self) -> ConstOid {
match self {
Self::CodeSigning => OID_EKU_PURPOSE_CODE_SIGNING,
Self::SafariDeveloper => OID_EKU_PURPOSE_SAFARI_DEVELOPER,
Self::ThirdPartyMacDeveloperInstaller => {
OID_EKU_PURPOSE_3RD_PARTY_MAC_DEVELOPER_INSTALLER
}
Self::DeveloperIdInstaller => OID_EKU_PURPOSE_DEVELOPER_ID_INSTALLER,
}
}
}
impl Display for ExtendedKeyUsagePurpose {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
ExtendedKeyUsagePurpose::CodeSigning => f.write_str("Code Signing"),
ExtendedKeyUsagePurpose::SafariDeveloper => f.write_str("Safari Developer"),
ExtendedKeyUsagePurpose::ThirdPartyMacDeveloperInstaller => {
f.write_str("3rd Party Mac Developer Installer Packaging Signing")
}
ExtendedKeyUsagePurpose::DeveloperIdInstaller => f.write_str("Developer ID Installer"),
}
}
}
impl TryFrom<&Oid> for ExtendedKeyUsagePurpose {
type Error = AppleCodesignError;
fn try_from(oid: &Oid) -> Result<Self, Self::Error> {
if oid.as_ref() == OID_EKU_PURPOSE_CODE_SIGNING.as_ref() {
Ok(Self::CodeSigning)
} else if oid.as_ref() == OID_EKU_PURPOSE_SAFARI_DEVELOPER.as_ref() {
Ok(Self::SafariDeveloper)
} else if oid.as_ref() == OID_EKU_PURPOSE_3RD_PARTY_MAC_DEVELOPER_INSTALLER.as_ref() {
Ok(Self::ThirdPartyMacDeveloperInstaller)
} else if oid.as_ref() == OID_EKU_PURPOSE_DEVELOPER_ID_INSTALLER.as_ref() {
Ok(Self::DeveloperIdInstaller)
} else {
Err(AppleCodesignError::OidIsntCertificateAuthority)
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CodeSigningCertificateExtension {
AppleSigning,
IPhoneDeveloper,
IPhoneOsApplicationSigning,
AppleDeveloperCertificateSubmission,
SafariDeveloper,
IPhoneOsVpnSigning,
AppleMacAppSigningDevelopment,
AppleMacAppSigningSubmission,
AppleMacAppStoreCodeSigning,
AppleMacAppStoreInstallerSigning,
MacDeveloper,
DeveloperIdApplication,
DeveloperIdDate,
DeveloperIdInstaller,
ApplePayPassbookSigning,
WebsitePushNotificationSigning,
DeveloperIdKernel,
TestFlight,
}
impl CodeSigningCertificateExtension {
pub fn all() -> Vec<Self> {
vec![
Self::AppleSigning,
Self::IPhoneDeveloper,
Self::IPhoneOsApplicationSigning,
Self::AppleDeveloperCertificateSubmission,
Self::SafariDeveloper,
Self::IPhoneOsVpnSigning,
Self::AppleMacAppSigningDevelopment,
Self::AppleMacAppSigningSubmission,
Self::AppleMacAppStoreCodeSigning,
Self::AppleMacAppStoreInstallerSigning,
Self::MacDeveloper,
Self::DeveloperIdApplication,
Self::DeveloperIdDate,
Self::DeveloperIdInstaller,
Self::ApplePayPassbookSigning,
Self::WebsitePushNotificationSigning,
Self::DeveloperIdKernel,
Self::TestFlight,
]
}
pub fn all_oids() -> &'static [&'static ConstOid] {
ALL_OID_NON_CA_EXTENSIONS
}
pub fn as_oid(&self) -> ConstOid {
match self {
Self::AppleSigning => OID_EXTENSION_APPLE_SIGNING,
Self::IPhoneDeveloper => OID_EXTENSION_IPHONE_DEVELOPER,
Self::IPhoneOsApplicationSigning => OID_EXTENSION_IPHONE_OS_APPLICATION_SIGNING,
Self::AppleDeveloperCertificateSubmission => {
OID_EXTENSION_APPLE_DEVELOPER_CERTIFICATE_SUBMISSION
}
Self::SafariDeveloper => OID_EXTENSION_SAFARI_DEVELOPER,
Self::IPhoneOsVpnSigning => OID_EXTENSION_IPHONE_OS_VPN_SIGNING,
Self::AppleMacAppSigningDevelopment => OID_EXTENSION_APPLE_MAC_APP_SIGNING_DEVELOPMENT,
Self::AppleMacAppSigningSubmission => OID_EXTENSION_APPLE_MAC_APP_SIGNING_SUBMISSION,
Self::AppleMacAppStoreCodeSigning => OID_EXTENSION_APPLE_MAC_APP_STORE_CODE_SIGNING,
Self::AppleMacAppStoreInstallerSigning => {
OID_EXTENSION_APPLE_MAC_APP_STORE_INSTALLER_SIGNING
}
Self::MacDeveloper => OID_EXTENSION_MAC_DEVELOPER,
Self::DeveloperIdApplication => OID_EXTENSION_DEVELOPER_ID_APPLICATION,
Self::DeveloperIdDate => OID_EXTENSION_DEVELOPER_ID_DATE,
Self::DeveloperIdInstaller => OID_EXTENSION_DEVELOPER_ID_INSTALLER,
Self::ApplePayPassbookSigning => OID_EXTENSION_PASSBOOK_SIGNING,
Self::WebsitePushNotificationSigning => OID_EXTENSION_WEBSITE_PUSH_NOTIFICATION_SIGNING,
Self::DeveloperIdKernel => OID_EXTENSION_DEVELOPER_ID_KERNEL,
Self::TestFlight => OID_EXTENSION_TEST_FLIGHT,
}
}
}
impl Display for CodeSigningCertificateExtension {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
CodeSigningCertificateExtension::AppleSigning => f.write_str("Apple Signing"),
CodeSigningCertificateExtension::IPhoneDeveloper => f.write_str("iPhone Developer"),
CodeSigningCertificateExtension::IPhoneOsApplicationSigning => {
f.write_str("Apple iPhone OS Application Signing")
}
CodeSigningCertificateExtension::AppleDeveloperCertificateSubmission => {
f.write_str("Apple Developer Certificate (Submission)")
}
CodeSigningCertificateExtension::SafariDeveloper => f.write_str("Safari Developer"),
CodeSigningCertificateExtension::IPhoneOsVpnSigning => {
f.write_str("Apple iPhone OS VPN Signing")
}
CodeSigningCertificateExtension::AppleMacAppSigningDevelopment => {
f.write_str("Apple Mac App Signing (Development)")
}
CodeSigningCertificateExtension::AppleMacAppSigningSubmission => {
f.write_str("Apple Mac App Signing Submission")
}
CodeSigningCertificateExtension::AppleMacAppStoreCodeSigning => {
f.write_str("Mac App Store Code Signing")
}
CodeSigningCertificateExtension::AppleMacAppStoreInstallerSigning => {
f.write_str("Mac App Store Installer Signing")
}
CodeSigningCertificateExtension::MacDeveloper => f.write_str("Mac Developer"),
CodeSigningCertificateExtension::DeveloperIdApplication => {
f.write_str("Developer ID Application")
}
CodeSigningCertificateExtension::DeveloperIdDate => f.write_str("Developer ID Date"),
CodeSigningCertificateExtension::DeveloperIdInstaller => {
f.write_str("Developer ID Installer")
}
CodeSigningCertificateExtension::ApplePayPassbookSigning => {
f.write_str("Apple Pay Passbook Signing")
}
CodeSigningCertificateExtension::WebsitePushNotificationSigning => {
f.write_str("Web Site Push Notifications Signing")
}
CodeSigningCertificateExtension::DeveloperIdKernel => {
f.write_str("Developer ID Kernel")
}
CodeSigningCertificateExtension::TestFlight => f.write_str("TestFlight"),
}
}
}
impl TryFrom<&Oid> for CodeSigningCertificateExtension {
type Error = AppleCodesignError;
fn try_from(oid: &Oid) -> Result<Self, Self::Error> {
let o = oid.as_ref();
if o == OID_EXTENSION_APPLE_SIGNING.as_ref() {
Ok(Self::AppleSigning)
} else if o == OID_EXTENSION_IPHONE_DEVELOPER.as_ref() {
Ok(Self::IPhoneDeveloper)
} else if o == OID_EXTENSION_IPHONE_OS_APPLICATION_SIGNING.as_ref() {
Ok(Self::IPhoneOsApplicationSigning)
} else if o == OID_EXTENSION_APPLE_DEVELOPER_CERTIFICATE_SUBMISSION.as_ref() {
Ok(Self::AppleDeveloperCertificateSubmission)
} else if o == OID_EXTENSION_SAFARI_DEVELOPER.as_ref() {
Ok(Self::SafariDeveloper)
} else if o == OID_EXTENSION_IPHONE_OS_VPN_SIGNING.as_ref() {
Ok(Self::IPhoneOsVpnSigning)
} else if o == OID_EXTENSION_APPLE_MAC_APP_SIGNING_DEVELOPMENT.as_ref() {
Ok(Self::AppleMacAppSigningDevelopment)
} else if o == OID_EXTENSION_APPLE_MAC_APP_SIGNING_SUBMISSION.as_ref() {
Ok(Self::AppleMacAppSigningSubmission)
} else if o == OID_EXTENSION_APPLE_MAC_APP_STORE_CODE_SIGNING.as_ref() {
Ok(Self::AppleMacAppStoreCodeSigning)
} else if o == OID_EXTENSION_APPLE_MAC_APP_STORE_INSTALLER_SIGNING.as_ref() {
Ok(Self::AppleMacAppStoreInstallerSigning)
} else if o == OID_EXTENSION_MAC_DEVELOPER.as_ref() {
Ok(Self::MacDeveloper)
} else if o == OID_EXTENSION_DEVELOPER_ID_APPLICATION.as_ref() {
Ok(Self::DeveloperIdApplication)
} else if o == OID_EXTENSION_DEVELOPER_ID_INSTALLER.as_ref() {
Ok(Self::DeveloperIdInstaller)
} else if o == OID_EXTENSION_PASSBOOK_SIGNING.as_ref() {
Ok(Self::ApplePayPassbookSigning)
} else if o == OID_EXTENSION_WEBSITE_PUSH_NOTIFICATION_SIGNING.as_ref() {
Ok(Self::WebsitePushNotificationSigning)
} else if o == OID_EXTENSION_DEVELOPER_ID_KERNEL.as_ref() {
Ok(Self::DeveloperIdKernel)
} else if o == OID_EXTENSION_DEVELOPER_ID_DATE.as_ref() {
Ok(Self::DeveloperIdDate)
} else if o == OID_EXTENSION_TEST_FLIGHT.as_ref() {
Ok(Self::TestFlight)
} else {
Err(AppleCodesignError::OidIsntCodeSigningExtension)
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CertificateAuthorityExtension {
AppleWorldwideDeveloperRelations,
AppleApplicationIntegration,
DeveloperId,
AppleTimestamp,
DeveloperAuthentication,
AppleApplicationIntegrationG3,
AppleWorldwideDeveloperRelationsG2,
AppleSoftwareUpdateCertification,
AppleApplicationIntegrationG1,
}
impl CertificateAuthorityExtension {
pub fn all() -> Vec<Self> {
vec![
Self::AppleWorldwideDeveloperRelations,
Self::AppleApplicationIntegration,
Self::DeveloperId,
Self::AppleTimestamp,
Self::DeveloperAuthentication,
Self::AppleApplicationIntegrationG3,
Self::AppleWorldwideDeveloperRelationsG2,
Self::AppleSoftwareUpdateCertification,
Self::AppleApplicationIntegrationG1,
]
}
pub fn all_oids() -> &'static [&'static ConstOid] {
ALL_OID_CA_EXTENSIONS
}
pub fn as_oid(&self) -> ConstOid {
match self {
Self::AppleWorldwideDeveloperRelations => {
OID_CA_EXTENSION_APPLE_WORLDWIDE_DEVELOPER_RELATIONS
}
Self::AppleApplicationIntegration => OID_CA_EXTENSION_APPLE_APPLICATION_INTEGRATION,
Self::DeveloperId => OID_CA_EXTENSION_DEVELOPER_ID,
Self::AppleTimestamp => OID_CA_EXTENSION_APPLE_TIMESTAMP,
Self::DeveloperAuthentication => OID_CA_EXTENSION_DEVELOPER_AUTHENTICATION,
Self::AppleApplicationIntegrationG3 => {
OID_CA_EXTENSION_APPLE_APPLICATION_INTEGRATION_G3
}
Self::AppleWorldwideDeveloperRelationsG2 => {
OID_CA_EXTENSION_APPLE_WORLDWIDE_DEVELOPER_RELATIONS_G2
}
Self::AppleSoftwareUpdateCertification => {
OID_CA_EXTENSION_APPLE_SOFTWARE_UPDATE_CERTIFICATION
}
Self::AppleApplicationIntegrationG1 => {
OID_CA_EXTENSION_APPLE_APPLICATION_INTEGRATION_G1
}
}
}
}
impl Display for CertificateAuthorityExtension {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
CertificateAuthorityExtension::AppleWorldwideDeveloperRelations => {
f.write_str("Apple Worldwide Developer Relations")
}
CertificateAuthorityExtension::AppleApplicationIntegration => {
f.write_str("Apple Application Integration")
}
CertificateAuthorityExtension::DeveloperId => {
f.write_str("Developer ID Certification Authority")
}
CertificateAuthorityExtension::AppleTimestamp => f.write_str("Apple Timestamp"),
CertificateAuthorityExtension::DeveloperAuthentication => {
f.write_str("Developer Authentication Certification Authority")
}
CertificateAuthorityExtension::AppleApplicationIntegrationG3 => {
f.write_str("Apple Application Integration CA - G3")
}
CertificateAuthorityExtension::AppleWorldwideDeveloperRelationsG2 => {
f.write_str("Apple Worldwide Developer Relations CA - G2")
}
CertificateAuthorityExtension::AppleSoftwareUpdateCertification => {
f.write_str("Apple Software Update Certification")
}
CertificateAuthorityExtension::AppleApplicationIntegrationG1 => {
f.write_str("Apple Application Integration CA - G1")
}
}
}
}
impl TryFrom<&Oid> for CertificateAuthorityExtension {
type Error = AppleCodesignError;
fn try_from(oid: &Oid) -> Result<Self, Self::Error> {
if oid.as_ref() == OID_CA_EXTENSION_APPLE_WORLDWIDE_DEVELOPER_RELATIONS.as_ref() {
Ok(Self::AppleWorldwideDeveloperRelations)
} else if oid.as_ref() == OID_CA_EXTENSION_APPLE_APPLICATION_INTEGRATION.as_ref() {
Ok(Self::AppleApplicationIntegration)
} else if oid.as_ref() == OID_CA_EXTENSION_DEVELOPER_ID.as_ref() {
Ok(Self::DeveloperId)
} else if oid.as_ref() == OID_CA_EXTENSION_APPLE_TIMESTAMP.as_ref() {
Ok(Self::AppleTimestamp)
} else if oid.as_ref() == OID_CA_EXTENSION_DEVELOPER_AUTHENTICATION.as_ref() {
Ok(Self::DeveloperAuthentication)
} else if oid.as_ref() == OID_CA_EXTENSION_APPLE_APPLICATION_INTEGRATION_G3.as_ref() {
Ok(Self::AppleApplicationIntegrationG3)
} else if oid.as_ref() == OID_CA_EXTENSION_APPLE_WORLDWIDE_DEVELOPER_RELATIONS_G2.as_ref() {
Ok(Self::AppleWorldwideDeveloperRelationsG2)
} else if oid.as_ref() == OID_CA_EXTENSION_APPLE_SOFTWARE_UPDATE_CERTIFICATION.as_ref() {
Ok(Self::AppleSoftwareUpdateCertification)
} else if oid.as_ref() == OID_CA_EXTENSION_APPLE_APPLICATION_INTEGRATION_G1.as_ref() {
Ok(Self::AppleApplicationIntegrationG1)
} else {
Err(AppleCodesignError::OidIsntCertificateAuthority)
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CertificateProfile {
MacInstallerDistribution,
AppleDistribution,
AppleDevelopment,
DeveloperIdApplication,
DeveloperIdInstaller,
}
impl CertificateProfile {
pub fn all() -> &'static [Self] {
&[
Self::MacInstallerDistribution,
Self::AppleDistribution,
Self::AppleDevelopment,
Self::DeveloperIdApplication,
Self::DeveloperIdInstaller,
]
}
pub fn str_names() -> [&'static str; 5] {
[
"mac-installer-distribution",
"apple-distribution",
"apple-development",
"developer-id-application",
"developer-id-installer",
]
}
}
impl Display for CertificateProfile {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
CertificateProfile::MacInstallerDistribution => {
f.write_str("mac-installer-distribution")
}
CertificateProfile::AppleDistribution => f.write_str("apple-distribution"),
CertificateProfile::AppleDevelopment => f.write_str("apple-development"),
CertificateProfile::DeveloperIdApplication => f.write_str("developer-id-application"),
CertificateProfile::DeveloperIdInstaller => f.write_str("developer-id-installer"),
}
}
}
impl FromStr for CertificateProfile {
type Err = AppleCodesignError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"apple-distribution" => Ok(Self::AppleDistribution),
"apple-development" => Ok(Self::AppleDevelopment),
"developer-id-application" => Ok(Self::DeveloperIdApplication),
"developer-id-installer" => Ok(Self::DeveloperIdInstaller),
"mac-installer-distribution" => Ok(Self::MacInstallerDistribution),
_ => Err(AppleCodesignError::UnknownCertificateProfile(s.to_string())),
}
}
}
pub trait AppleCertificate: Sized {
fn is_apple_root_ca(&self) -> bool;
fn is_apple_intermediate_ca(&self) -> bool;
fn apple_ca_extensions(&self) -> Vec<CertificateAuthorityExtension>;
fn apple_extended_key_usage_purposes(&self) -> Vec<ExtendedKeyUsagePurpose>;
fn apple_code_signing_extensions(&self) -> Vec<CodeSigningCertificateExtension>;
fn apple_guess_profile(&self) -> Option<CertificateProfile>;
fn apple_issuing_chain(&self) -> Vec<KnownCertificate>;
fn chains_to_apple_root_ca(&self) -> bool;
fn apple_root_certificate_chain(&self) -> Option<Vec<CapturedX509Certificate>>;
fn apple_team_id(&self) -> Option<String>;
fn is_test_apple_signed_certificate(&self) -> bool;
}
impl AppleCertificate for CapturedX509Certificate {
fn is_apple_root_ca(&self) -> bool {
KnownCertificate::all_roots().contains(&self)
}
fn is_apple_intermediate_ca(&self) -> bool {
KnownCertificate::all().contains(&self) && !KnownCertificate::all_roots().contains(&self)
}
fn apple_ca_extensions(&self) -> Vec<CertificateAuthorityExtension> {
let cert: &x509_certificate::rfc5280::Certificate = self.as_ref();
cert.iter_extensions()
.filter_map(|extension| CertificateAuthorityExtension::try_from(&extension.id).ok())
.collect::<Vec<_>>()
}
fn apple_extended_key_usage_purposes(&self) -> Vec<ExtendedKeyUsagePurpose> {
let cert: &x509_certificate::rfc5280::Certificate = self.as_ref();
cert.iter_extensions()
.filter_map(|extension| {
if extension.id.as_ref() == OID_EXTENDED_KEY_USAGE.as_ref() {
if let Some(oid) = extension.try_decode_sequence_single_oid() {
if let Ok(purpose) = ExtendedKeyUsagePurpose::try_from(&oid) {
Some(purpose)
} else {
None
}
} else {
None
}
} else {
None
}
})
.collect::<Vec<_>>()
}
fn apple_code_signing_extensions(&self) -> Vec<CodeSigningCertificateExtension> {
let cert: &x509_certificate::rfc5280::Certificate = self.as_ref();
cert.iter_extensions()
.filter_map(|extension| {
if let Ok(value) = CodeSigningCertificateExtension::try_from(&extension.id) {
Some(value)
} else {
None
}
})
.collect::<Vec<_>>()
}
fn apple_guess_profile(&self) -> Option<CertificateProfile> {
let ekus = self.apple_extended_key_usage_purposes();
let signing = self.apple_code_signing_extensions();
if ekus.contains(&ExtendedKeyUsagePurpose::DeveloperIdInstaller) {
Some(CertificateProfile::DeveloperIdInstaller)
} else if ekus.contains(&ExtendedKeyUsagePurpose::ThirdPartyMacDeveloperInstaller) {
Some(CertificateProfile::MacInstallerDistribution)
} else if signing.contains(&CodeSigningCertificateExtension::DeveloperIdApplication) {
Some(CertificateProfile::DeveloperIdApplication)
} else if signing.contains(&CodeSigningCertificateExtension::IPhoneDeveloper)
&& signing.contains(&CodeSigningCertificateExtension::MacDeveloper)
{
Some(CertificateProfile::AppleDevelopment)
} else if signing.contains(&CodeSigningCertificateExtension::AppleMacAppSigningDevelopment)
&& signing
.contains(&CodeSigningCertificateExtension::AppleDeveloperCertificateSubmission)
{
Some(CertificateProfile::AppleDistribution)
} else {
None
}
}
fn apple_issuing_chain(&self) -> Vec<KnownCertificate> {
self.resolve_signing_chain(KnownCertificate::all().iter().copied())
.into_iter()
.filter_map(|cert| KnownCertificate::try_from(cert).ok())
.collect::<Vec<_>>()
}
fn chains_to_apple_root_ca(&self) -> bool {
if self.is_apple_root_ca() {
true
} else {
self.resolve_signing_chain(KnownCertificate::all().iter().copied())
.into_iter()
.any(|cert| cert.is_apple_root_ca())
}
}
fn apple_root_certificate_chain(&self) -> Option<Vec<CapturedX509Certificate>> {
let mut chain = vec![self.clone()];
for cert in self.resolve_signing_chain(KnownCertificate::all().iter().copied()) {
chain.push(cert.clone());
if cert.is_apple_root_ca() {
break;
}
}
if chain.last().unwrap().is_apple_root_ca() {
Some(chain)
} else {
None
}
}
fn apple_team_id(&self) -> Option<String> {
self.subject_name()
.find_first_attribute_string(Oid(
x509_certificate::rfc4519::OID_ORGANIZATIONAL_UNIT_NAME
.as_ref()
.into(),
))
.unwrap_or(None)
}
fn is_test_apple_signed_certificate(&self) -> bool {
if let Ok(digest) = self.sha256_fingerprint() {
hex::encode(digest)
== "5939ad5770d8b977b38d07533754371314744e87a8d606433f689e9bc6b980a0"
} else {
false
}
}
}
pub trait AppleCertificateBuilder: Sized {
fn apple_subject(
&mut self,
team_id: &str,
person_name: &str,
country: &str,
) -> Result<(), AppleCodesignError>;
fn apple_email_address(&mut self, address: &str) -> Result<(), AppleCodesignError>;
fn apple_extended_key_usage(
&mut self,
usage: ExtendedKeyUsagePurpose,
) -> Result<(), AppleCodesignError>;
fn apple_code_signing_certificate_extension(
&mut self,
extension: CodeSigningCertificateExtension,
) -> Result<(), AppleCodesignError>;
fn apple_certificate_profile(
&mut self,
profile: CertificateProfile,
) -> Result<(), AppleCodesignError>;
fn apple_code_signing_extensions(&self) -> Vec<CodeSigningCertificateExtension>;
}
impl AppleCertificateBuilder for X509CertificateBuilder {
fn apple_subject(
&mut self,
team_id: &str,
person_name: &str,
country: &str,
) -> Result<(), AppleCodesignError> {
self.subject()
.append_utf8_string(Oid(OID_USER_ID.as_ref().into()), team_id)
.map_err(|e| AppleCodesignError::CertificateBuildError(format!("{e:?}")))?;
let extensions = self.apple_code_signing_extensions();
let common_name =
if extensions.contains(&CodeSigningCertificateExtension::DeveloperIdApplication) {
format!("Developer ID Application: {person_name} ({team_id})")
} else if extensions.contains(&CodeSigningCertificateExtension::DeveloperIdInstaller) {
format!("Developer ID Installer: {person_name} ({team_id})")
} else if extensions
.contains(&CodeSigningCertificateExtension::AppleDeveloperCertificateSubmission)
{
format!("Apple Distribution: {person_name} ({team_id})")
} else if extensions
.contains(&CodeSigningCertificateExtension::AppleMacAppSigningSubmission)
{
format!("3rd Party Mac Developer Installer: {person_name} ({team_id})")
} else if extensions.contains(&CodeSigningCertificateExtension::MacDeveloper) {
format!("Apple Development: {person_name} ({team_id})")
} else {
format!("{person_name} ({team_id})")
};
self.subject()
.append_common_name_utf8_string(&common_name)
.map_err(|e| AppleCodesignError::CertificateBuildError(format!("{e:?}")))?;
self.subject()
.append_organizational_unit_utf8_string(team_id)
.map_err(|e| AppleCodesignError::CertificateBuildError(format!("{e:?}")))?;
self.subject()
.append_organization_utf8_string(person_name)
.map_err(|e| AppleCodesignError::CertificateBuildError(format!("{e:?}")))?;
self.subject()
.append_printable_string(Oid(OID_COUNTRY_NAME.as_ref().into()), country)
.map_err(|e| AppleCodesignError::CertificateBuildError(format!("{e:?}")))?;
Ok(())
}
fn apple_email_address(&mut self, address: &str) -> Result<(), AppleCodesignError> {
self.subject()
.append_utf8_string(Oid(OID_EMAIL_ADDRESS.as_ref().into()), address)
.map_err(|e| AppleCodesignError::CertificateBuildError(format!("{e:?}")))?;
Ok(())
}
fn apple_extended_key_usage(
&mut self,
usage: ExtendedKeyUsagePurpose,
) -> Result<(), AppleCodesignError> {
let payload =
bcder::encode::sequence(Oid(Bytes::copy_from_slice(usage.as_oid().as_ref())).encode())
.to_captured(bcder::Mode::Der);
self.add_extension_der_data(
Oid(OID_EXTENDED_KEY_USAGE.as_ref().into()),
true,
payload.as_slice(),
);
Ok(())
}
fn apple_code_signing_certificate_extension(
&mut self,
extension: CodeSigningCertificateExtension,
) -> Result<(), AppleCodesignError> {
let (critical, payload) = match extension {
CodeSigningCertificateExtension::IPhoneDeveloper => {
(true, Bytes::copy_from_slice(&[0x05, 0x00]))
}
CodeSigningCertificateExtension::AppleDeveloperCertificateSubmission => {
(true, Bytes::copy_from_slice(&[0x05, 0x00]))
}
CodeSigningCertificateExtension::AppleMacAppSigningDevelopment => {
(true, Bytes::copy_from_slice(&[0x05, 0x00]))
}
CodeSigningCertificateExtension::AppleMacAppSigningSubmission => {
(true, Bytes::copy_from_slice(&[0x05, 0x00]))
}
CodeSigningCertificateExtension::MacDeveloper => {
(true, Bytes::copy_from_slice(&[0x05, 0x00]))
}
CodeSigningCertificateExtension::DeveloperIdApplication => {
(true, Bytes::copy_from_slice(&[0x05, 0x00]))
}
CodeSigningCertificateExtension::DeveloperIdInstaller => {
(true, Bytes::copy_from_slice(&[0x05, 0x00]))
}
_ => {
return Err(AppleCodesignError::CertificateBuildError(format!(
"don't know how to handle code signing extension {extension:?}"
)));
}
};
self.add_extension_der_data(
Oid(Bytes::copy_from_slice(extension.as_oid().as_ref())),
critical,
payload,
);
Ok(())
}
fn apple_certificate_profile(
&mut self,
profile: CertificateProfile,
) -> Result<(), AppleCodesignError> {
match profile {
CertificateProfile::DeveloperIdApplication => {
self.constraint_not_ca();
self.apple_extended_key_usage(ExtendedKeyUsagePurpose::CodeSigning)?;
self.key_usage(KeyUsage::DigitalSignature);
self.apple_code_signing_certificate_extension(
CodeSigningCertificateExtension::DeveloperIdApplication,
)?;
}
CertificateProfile::DeveloperIdInstaller => {
self.constraint_not_ca();
self.apple_extended_key_usage(ExtendedKeyUsagePurpose::DeveloperIdInstaller)?;
self.key_usage(KeyUsage::DigitalSignature);
self.apple_code_signing_certificate_extension(
CodeSigningCertificateExtension::DeveloperIdInstaller,
)?;
}
CertificateProfile::AppleDevelopment => {
self.constraint_not_ca();
self.apple_extended_key_usage(ExtendedKeyUsagePurpose::CodeSigning)?;
self.key_usage(KeyUsage::DigitalSignature);
self.apple_code_signing_certificate_extension(
CodeSigningCertificateExtension::IPhoneDeveloper,
)?;
self.apple_code_signing_certificate_extension(
CodeSigningCertificateExtension::MacDeveloper,
)?;
}
CertificateProfile::AppleDistribution => {
self.constraint_not_ca();
self.apple_extended_key_usage(ExtendedKeyUsagePurpose::CodeSigning)?;
self.key_usage(KeyUsage::DigitalSignature);
self.apple_code_signing_certificate_extension(
CodeSigningCertificateExtension::AppleMacAppSigningDevelopment,
)?;
self.apple_code_signing_certificate_extension(
CodeSigningCertificateExtension::AppleDeveloperCertificateSubmission,
)?;
}
CertificateProfile::MacInstallerDistribution => {
self.constraint_not_ca();
self.apple_extended_key_usage(
ExtendedKeyUsagePurpose::ThirdPartyMacDeveloperInstaller,
)?;
self.key_usage(KeyUsage::DigitalSignature);
self.apple_code_signing_certificate_extension(
CodeSigningCertificateExtension::AppleMacAppSigningSubmission,
)?;
}
}
Ok(())
}
fn apple_code_signing_extensions(&self) -> Vec<CodeSigningCertificateExtension> {
self.extensions()
.iter()
.filter_map(|ext| {
if let Ok(e) = CodeSigningCertificateExtension::try_from(&ext.id) {
Some(e)
} else {
None
}
})
.collect::<Vec<_>>()
}
}
pub fn create_self_signed_code_signing_certificate(
algorithm: KeyAlgorithm,
profile: CertificateProfile,
team_id: &str,
person_name: &str,
country: &str,
validity_duration: chrono::Duration,
) -> Result<(CapturedX509Certificate, InMemorySigningKeyPair), AppleCodesignError> {
let mut builder = X509CertificateBuilder::default();
builder.apple_certificate_profile(profile)?;
builder.apple_subject(team_id, person_name, country)?;
builder.validity_duration(validity_duration);
if matches!(algorithm, KeyAlgorithm::Rsa) {
let private_key = rsa::RsaPrivateKey::new(&mut rand::thread_rng(), 2048).map_err(|e| {
AppleCodesignError::CertificateBuildError(format!("error generating RSA key: {}", e))
})?;
let key_pair = InMemorySigningKeyPair::from_pkcs8_der(
private_key
.to_pkcs8_der()
.map_err(|e| {
AppleCodesignError::CertificateGeneric(format!(
"error converting RSA key to DER: {}",
e
))
})?
.as_bytes(),
)?;
let cert = builder.create_with_key_pair(&key_pair)?;
Ok((cert, key_pair))
} else {
Ok(builder.create_with_random_keypair(algorithm)?)
}
}
#[cfg(test)]
mod tests {
use {
super::*,
cryptographic_message_syntax::{SignedData, SignedDataBuilder, SignerBuilder},
x509_certificate::EcdsaCurve,
};
#[test]
fn generate_self_signed_certificate_ecdsa() {
for curve in EcdsaCurve::all() {
create_self_signed_code_signing_certificate(
KeyAlgorithm::Ecdsa(*curve),
CertificateProfile::DeveloperIdInstaller,
"team1",
"Joe Developer",
"US",
chrono::Duration::hours(1),
)
.unwrap();
}
}
#[test]
fn generate_self_signed_certificate_ed25519() {
create_self_signed_code_signing_certificate(
KeyAlgorithm::Ed25519,
CertificateProfile::DeveloperIdInstaller,
"team2",
"Joe Developer",
"US",
chrono::Duration::hours(1),
)
.unwrap();
}
#[test]
fn generate_all_profiles() {
for profile in CertificateProfile::all() {
create_self_signed_code_signing_certificate(
KeyAlgorithm::Ed25519,
*profile,
"team",
"Joe Developer",
"Wakanda",
chrono::Duration::hours(1),
)
.unwrap();
}
}
#[test]
fn cms_self_signed_certificate_signing_ecdsa() {
for curve in EcdsaCurve::all() {
let (cert, signing_key) = create_self_signed_code_signing_certificate(
KeyAlgorithm::Ecdsa(*curve),
CertificateProfile::DeveloperIdInstaller,
"team",
"Joe Developer",
"US",
chrono::Duration::hours(1),
)
.unwrap();
let plaintext = "hello, world";
let cms = SignedDataBuilder::default()
.certificate(cert.clone())
.content_inline(plaintext.as_bytes().to_vec())
.signer(SignerBuilder::new(&signing_key, cert.clone()))
.build_der()
.unwrap();
let signed_data = SignedData::parse_ber(&cms).unwrap();
for signer in signed_data.signers() {
signer
.verify_signature_with_signed_data(&signed_data)
.unwrap();
}
}
}
#[test]
fn cms_self_signed_certificate_signing_ed25519() {
let (cert, signing_key) = create_self_signed_code_signing_certificate(
KeyAlgorithm::Ed25519,
CertificateProfile::DeveloperIdInstaller,
"team",
"Joe Developer",
"US",
chrono::Duration::hours(1),
)
.unwrap();
let plaintext = "hello, world";
let cms = SignedDataBuilder::default()
.certificate(cert.clone())
.content_inline(plaintext.as_bytes().to_vec())
.signer(SignerBuilder::new(&signing_key, cert))
.build_der()
.unwrap();
let signed_data = SignedData::parse_ber(&cms).unwrap();
for signer in signed_data.signers() {
signer
.verify_signature_with_signed_data(&signed_data)
.unwrap();
}
}
#[test]
fn third_mac_mac() {
let der = include_bytes!("testdata/apple-signed-3rd-party-mac.cer");
let cert = CapturedX509Certificate::from_der(der.to_vec()).unwrap();
assert_eq!(
cert.apple_extended_key_usage_purposes(),
vec![ExtendedKeyUsagePurpose::ThirdPartyMacDeveloperInstaller]
);
assert_eq!(
cert.apple_code_signing_extensions(),
vec![CodeSigningCertificateExtension::AppleMacAppSigningSubmission]
);
assert_eq!(
cert.apple_guess_profile(),
Some(CertificateProfile::MacInstallerDistribution)
);
assert_eq!(
cert.apple_issuing_chain(),
vec![
KnownCertificate::WwdrG3,
KnownCertificate::AppleRootCa,
KnownCertificate::AppleComputerIncRoot
]
);
assert!(cert.chains_to_apple_root_ca());
assert_eq!(
cert.apple_root_certificate_chain(),
Some(vec![
cert.clone(),
(*KnownCertificate::WwdrG3).clone(),
(*KnownCertificate::AppleRootCa).clone()
])
);
assert_eq!(cert.apple_team_id(), Some("MK22MZP987".into()));
let mut builder = X509CertificateBuilder::default();
builder
.apple_certificate_profile(CertificateProfile::MacInstallerDistribution)
.unwrap();
let built = builder
.create_with_random_keypair(KeyAlgorithm::Ecdsa(EcdsaCurve::Secp256r1))
.unwrap()
.0;
assert_eq!(
built.apple_extended_key_usage_purposes(),
cert.apple_extended_key_usage_purposes()
);
assert_eq!(
built.apple_code_signing_extensions(),
cert.apple_code_signing_extensions()
);
assert_eq!(built.apple_guess_profile(), cert.apple_guess_profile());
assert_eq!(built.apple_issuing_chain(), vec![]);
assert!(!built.chains_to_apple_root_ca());
assert!(built.apple_root_certificate_chain().is_none());
}
#[test]
fn apple_development() {
let der = include_bytes!("testdata/apple-signed-apple-development.cer");
let cert = CapturedX509Certificate::from_der(der.to_vec()).unwrap();
assert_eq!(
cert.apple_extended_key_usage_purposes(),
vec![ExtendedKeyUsagePurpose::CodeSigning]
);
assert_eq!(
cert.apple_code_signing_extensions(),
vec![
CodeSigningCertificateExtension::IPhoneDeveloper,
CodeSigningCertificateExtension::MacDeveloper
]
);
assert_eq!(
cert.apple_guess_profile(),
Some(CertificateProfile::AppleDevelopment)
);
assert_eq!(
cert.apple_issuing_chain(),
vec![
KnownCertificate::WwdrG3,
KnownCertificate::AppleRootCa,
KnownCertificate::AppleComputerIncRoot
],
);
assert!(cert.chains_to_apple_root_ca());
assert_eq!(
cert.apple_root_certificate_chain(),
Some(vec![
cert.clone(),
(*KnownCertificate::WwdrG3).clone(),
(*KnownCertificate::AppleRootCa).clone()
])
);
assert_eq!(cert.apple_team_id(), Some("MK22MZP987".into()));
let mut builder = X509CertificateBuilder::default();
builder
.apple_certificate_profile(CertificateProfile::AppleDevelopment)
.unwrap();
let built = builder
.create_with_random_keypair(KeyAlgorithm::Ecdsa(EcdsaCurve::Secp256r1))
.unwrap()
.0;
assert_eq!(
built.apple_extended_key_usage_purposes(),
cert.apple_extended_key_usage_purposes()
);
assert_eq!(
built.apple_code_signing_extensions(),
cert.apple_code_signing_extensions()
);
assert_eq!(built.apple_guess_profile(), cert.apple_guess_profile());
assert_eq!(built.apple_issuing_chain(), vec![]);
assert!(!built.chains_to_apple_root_ca());
assert!(built.apple_root_certificate_chain().is_none());
}
#[test]
fn apple_distribution() {
let der = include_bytes!("testdata/apple-signed-apple-distribution.cer");
let cert = CapturedX509Certificate::from_der(der.to_vec()).unwrap();
assert_eq!(
cert.apple_extended_key_usage_purposes(),
vec![ExtendedKeyUsagePurpose::CodeSigning]
);
assert_eq!(
cert.apple_code_signing_extensions(),
vec![
CodeSigningCertificateExtension::AppleMacAppSigningDevelopment,
CodeSigningCertificateExtension::AppleDeveloperCertificateSubmission
]
);
assert_eq!(
cert.apple_guess_profile(),
Some(CertificateProfile::AppleDistribution)
);
assert_eq!(
cert.apple_issuing_chain(),
vec![
KnownCertificate::WwdrG3,
KnownCertificate::AppleRootCa,
KnownCertificate::AppleComputerIncRoot
],
);
assert!(cert.chains_to_apple_root_ca());
assert_eq!(
cert.apple_root_certificate_chain(),
Some(vec![
cert.clone(),
(*KnownCertificate::WwdrG3).clone(),
(*KnownCertificate::AppleRootCa).clone()
])
);
assert_eq!(cert.apple_team_id(), Some("MK22MZP987".into()));
let mut builder = X509CertificateBuilder::default();
builder
.apple_certificate_profile(CertificateProfile::AppleDistribution)
.unwrap();
let built = builder
.create_with_random_keypair(KeyAlgorithm::Ecdsa(EcdsaCurve::Secp256r1))
.unwrap()
.0;
assert_eq!(
built.apple_extended_key_usage_purposes(),
cert.apple_extended_key_usage_purposes()
);
assert_eq!(
built.apple_code_signing_extensions(),
cert.apple_code_signing_extensions()
);
assert_eq!(built.apple_guess_profile(), cert.apple_guess_profile());
assert_eq!(built.apple_issuing_chain(), vec![]);
assert!(!built.chains_to_apple_root_ca());
assert!(built.apple_root_certificate_chain().is_none());
}
#[test]
fn apple_developer_id_application() {
let der = include_bytes!("testdata/apple-signed-developer-id-application.cer");
let cert = CapturedX509Certificate::from_der(der.to_vec()).unwrap();
assert_eq!(
cert.apple_extended_key_usage_purposes(),
vec![ExtendedKeyUsagePurpose::CodeSigning]
);
assert_eq!(
cert.apple_code_signing_extensions(),
vec![
CodeSigningCertificateExtension::DeveloperIdDate,
CodeSigningCertificateExtension::DeveloperIdApplication
]
);
assert_eq!(
cert.apple_guess_profile(),
Some(CertificateProfile::DeveloperIdApplication)
);
assert_eq!(
cert.apple_issuing_chain(),
vec![
KnownCertificate::DeveloperIdG1,
KnownCertificate::AppleRootCa,
KnownCertificate::AppleComputerIncRoot
]
);
assert!(cert.chains_to_apple_root_ca());
assert_eq!(
cert.apple_root_certificate_chain(),
Some(vec![
cert.clone(),
(*KnownCertificate::DeveloperIdG1).clone(),
(*KnownCertificate::AppleRootCa).clone()
])
);
assert_eq!(cert.apple_team_id(), Some("MK22MZP987".into()));
let mut builder = X509CertificateBuilder::default();
builder
.apple_certificate_profile(CertificateProfile::DeveloperIdApplication)
.unwrap();
let built = builder
.create_with_random_keypair(KeyAlgorithm::Ecdsa(EcdsaCurve::Secp256r1))
.unwrap()
.0;
assert_eq!(
built.apple_extended_key_usage_purposes(),
cert.apple_extended_key_usage_purposes()
);
assert_eq!(
built.apple_code_signing_extensions(),
cert.apple_code_signing_extensions()
.into_iter()
.filter(|e| !matches!(e, CodeSigningCertificateExtension::DeveloperIdDate))
.collect::<Vec<_>>()
);
assert_eq!(built.apple_guess_profile(), cert.apple_guess_profile());
assert_eq!(built.apple_issuing_chain(), vec![]);
assert!(!built.chains_to_apple_root_ca());
assert!(built.apple_root_certificate_chain().is_none());
}
#[test]
fn apple_developer_id_installer() {
let der = include_bytes!("testdata/apple-signed-developer-id-installer.cer");
let cert = CapturedX509Certificate::from_der(der.to_vec()).unwrap();
assert_eq!(
cert.apple_extended_key_usage_purposes(),
vec![ExtendedKeyUsagePurpose::DeveloperIdInstaller]
);
assert_eq!(
cert.apple_code_signing_extensions(),
vec![
CodeSigningCertificateExtension::DeveloperIdDate,
CodeSigningCertificateExtension::DeveloperIdInstaller
]
);
assert_eq!(
cert.apple_guess_profile(),
Some(CertificateProfile::DeveloperIdInstaller)
);
assert_eq!(
cert.apple_issuing_chain(),
vec![
KnownCertificate::DeveloperIdG1,
KnownCertificate::AppleRootCa,
KnownCertificate::AppleComputerIncRoot
]
);
assert!(cert.chains_to_apple_root_ca());
assert_eq!(
cert.apple_root_certificate_chain(),
Some(vec![
cert.clone(),
(*KnownCertificate::DeveloperIdG1).clone(),
(*KnownCertificate::AppleRootCa).clone()
])
);
assert_eq!(cert.apple_team_id(), Some("MK22MZP987".into()));
let mut builder = X509CertificateBuilder::default();
builder
.apple_certificate_profile(CertificateProfile::DeveloperIdInstaller)
.unwrap();
let built = builder
.create_with_random_keypair(KeyAlgorithm::Ecdsa(EcdsaCurve::Secp256r1))
.unwrap()
.0;
assert_eq!(
built.apple_extended_key_usage_purposes(),
cert.apple_extended_key_usage_purposes()
);
assert_eq!(
built.apple_code_signing_extensions(),
cert.apple_code_signing_extensions()
.into_iter()
.filter(|e| !matches!(e, CodeSigningCertificateExtension::DeveloperIdDate))
.collect::<Vec<_>>()
);
assert_eq!(built.apple_guess_profile(), cert.apple_guess_profile());
assert_eq!(built.apple_issuing_chain(), vec![]);
assert!(!built.chains_to_apple_root_ca());
assert!(built.apple_root_certificate_chain().is_none());
}
}