diff --git a/Cargo.lock b/Cargo.lock index f8b0d2e48..da41018ab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,17 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + [[package]] name = "ahash" version = "0.8.12" @@ -209,7 +220,7 @@ dependencies = [ "object", "oid-registry", "once_cell", - "p12", + "p12-keystore", "p256 0.13.2", "pem", "pkcs1", @@ -341,6 +352,7 @@ dependencies = [ "num-traits", "rusticata-macros", "thiserror 2.0.18", + "time", ] [[package]] @@ -1072,6 +1084,18 @@ dependencies = [ "cc", ] +[[package]] +name = "cms" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b77c319abfd5219629c45c34c89ba945ed3c5e49fcde9d16b6c3885f118a730" +dependencies = [ + "const-oid", + "der 0.7.10", + "spki 0.7.3", + "x509-cert", +] + [[package]] name = "colorchoice" version = "1.0.4" @@ -1351,6 +1375,20 @@ dependencies = [ "zeroize", ] +[[package]] +name = "der-parser" +version = "10.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6" +dependencies = [ + "asn1-rs", + "displaydoc", + "nom", + "num-bigint", + "num-traits", + "rusticata-macros", +] + [[package]] name = "der_derive" version = "0.7.3" @@ -2822,20 +2860,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e" [[package]] -name = "p12" -version = "0.6.3" +name = "p12-keystore" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4873306de53fe82e7e484df31e1e947d61514b6ea2ed6cd7b45d63006fd9224" +checksum = "e8d55319bae67f92141ce4da80c5392acd3d1323bd8312c1ffdfb018927d07d7" dependencies = [ + "base64", "cbc", - "cipher", + "cms", + "der 0.7.10", "des", - "getrandom 0.2.17", + "hex", "hmac", - "lazy_static", + "pkcs12", + "pkcs5", + "rand 0.9.2", "rc2", "sha1", - "yasna", + "sha2", + "thiserror 2.0.17", + "x509-parser", ] [[package]] @@ -2979,6 +3023,36 @@ dependencies = [ "spki 0.7.3", ] +[[package]] +name = "pkcs12" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "695b3df3d3cc1015f12d70235e35b6b79befc5fa7a9b95b951eab1dd07c9efc2" +dependencies = [ + "cms", + "const-oid", + "der 0.7.10", + "digest", + "spki 0.7.3", + "x509-cert", + "zeroize", +] + +[[package]] +name = "pkcs5" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e847e2c91a18bfa887dd028ec33f2fe6f25db77db3619024764914affe8b69a6" +dependencies = [ + "aes", + "cbc", + "der 0.7.10", + "pbkdf2", + "scrypt", + "sha2", + "spki 0.7.3", +] + [[package]] name = "pkcs8" version = "0.9.0" @@ -3597,6 +3671,15 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" +[[package]] +name = "salsa20" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" +dependencies = [ + "cipher", +] + [[package]] name = "same-file" version = "1.0.6" @@ -3635,6 +3718,17 @@ dependencies = [ "syn", ] +[[package]] +name = "scrypt" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" +dependencies = [ + "pbkdf2", + "salsa20", + "sha2", +] + [[package]] name = "sct" version = "0.7.1" @@ -5055,6 +5149,23 @@ dependencies = [ "zeroize", ] +[[package]] +name = "x509-parser" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4569f339c0c402346d4a75a9e39cf8dad310e287eef1ff56d4c68e5067f53460" +dependencies = [ + "asn1-rs", + "data-encoding", + "der-parser", + "lazy_static", + "nom", + "oid-registry", + "rusticata-macros", + "thiserror 2.0.17", + "time", +] + [[package]] name = "xattr" version = "1.6.1" diff --git a/apple-codesign/Cargo.toml b/apple-codesign/Cargo.toml index 51538b906..bd82d91e9 100644 --- a/apple-codesign/Cargo.toml +++ b/apple-codesign/Cargo.toml @@ -47,7 +47,7 @@ num-traits = "0.2.19" object = { version = "0.38.1", features = ["write"] } oid-registry = "0.8.1" once_cell = "1.21.3" -p12 = "0.6.3" +p12-keystore = "=0.2.0" p256 = { version = "0.13.2", default-features = false, features = ["arithmetic", "pkcs8", "std"] } pem = "3.0.6" pkcs1 = { version = "0.7.5", features = ["alloc", "std", "pkcs8"] } diff --git a/apple-codesign/src/cli/mod.rs b/apple-codesign/src/cli/mod.rs index 80f7a025e..c68172b03 100644 --- a/apple-codesign/src/cli/mod.rs +++ b/apple-codesign/src/cli/mod.rs @@ -603,14 +603,27 @@ impl CliCommand for GenerateSelfSignedCertificate { if let Some(path) = &self.p12_path { let password = get_pkcs12_password(self.p12_password.clone(), None::)?; - let pfx = p12::PFX::new( - &cert.encode_der()?, - &key_pair.to_pkcs8_one_asymmetric_key_der(), - None, - &password, - "code-signing", - ) - .ok_or_else(|| { + let chain = vec![p12_keystore::Certificate::from_der(&cert.encode_der()?).map_err(|e| { + AppleCodesignError::CliGeneralError(format!("error processing certificate: {e:?}").into()) + })?]; + + let local_key_id = { + use sha2::{Digest, Sha256}; + let mut hasher = Sha256::new(); + hasher.update(&key_pair.to_pkcs8_one_asymmetric_key_der()); + let hash = hasher.finalize(); + hash[..8].to_vec() + }; + + let keychain = p12_keystore::PrivateKeyChain::new(&key_pair.to_pkcs8_one_asymmetric_key_der(), local_key_id, chain); + + let entry = p12_keystore::KeyStoreEntry::PrivateKeyChain(keychain); + + let mut keystore = p12_keystore::KeyStore::new(); + keystore.add_entry("code-signing", entry); + let writer = keystore.writer(&password); + + let pfx = writer.write().map_err(|_e| { AppleCodesignError::CliGeneralError("failed to create PFX structure".into()) })?; @@ -619,7 +632,7 @@ impl CliCommand for GenerateSelfSignedCertificate { if let Some(parent) = path.parent() { std::fs::create_dir_all(parent)?; } - std::fs::write(path, pfx.to_der())?; + std::fs::write(path, pfx)?; wrote_file = true; } diff --git a/apple-codesign/src/cryptography.rs b/apple-codesign/src/cryptography.rs index f6a1dda59..c1f560722 100644 --- a/apple-codesign/src/cryptography.rs +++ b/apple-codesign/src/cryptography.rs @@ -682,19 +682,19 @@ impl MultiDigest { } } -fn bmp_string(s: &str) -> Vec { - let utf16: Vec = s.encode_utf16().collect(); +// fn bmp_string(s: &str) -> Vec { +// let utf16: Vec = s.encode_utf16().collect(); - let mut bytes = Vec::with_capacity(utf16.len() * 2 + 2); - for c in utf16 { - bytes.push((c / 256) as u8); - bytes.push((c % 256) as u8); - } - bytes.push(0x00); - bytes.push(0x00); +// let mut bytes = Vec::with_capacity(utf16.len() * 2 + 2); +// for c in utf16 { +// bytes.push((c / 256) as u8); +// bytes.push((c % 256) as u8); +// } +// bytes.push(0x00); +// bytes.push(0x00); - bytes -} +// bytes +// } /// Parse PFX data into a key pair. /// @@ -708,93 +708,23 @@ pub fn parse_pfx_data( data: &[u8], password: &str, ) -> Result<(CapturedX509Certificate, InMemoryPrivateKey), AppleCodesignError> { - let pfx = p12::PFX::parse(data).map_err(|e| { - AppleCodesignError::PfxParseError(format!("data does not appear to be PFX: {e:?}")) + let keystore = p12_keystore::KeyStore::from_pkcs12(data, password).map_err(|e| { + AppleCodesignError::PfxParseError(format!("error when processing p12: {e:?}")) })?; - if !pfx.verify_mac(password) { - return Err(AppleCodesignError::PfxBadPassword); - } - // Apple's certificate export format consists of regular data content info - // with inner ContentInfo components holding the key and certificate. - let data = match pfx.auth_safe { - p12::ContentInfo::Data(data) => data, - _ => { + // with chain variable holding the key and certificate. + let chain = match keystore.private_key_chain() { + Some((_alias, chain)) => chain, + None => { return Err(AppleCodesignError::PfxParseError( "unexpected PFX content info".to_string(), )); } }; - let content_infos = yasna::parse_der(&data, |reader| { - reader.collect_sequence_of(p12::ContentInfo::parse) - }) - .map_err(|e| { - AppleCodesignError::PfxParseError(format!("failed parsing inner ContentInfo: {e:?}")) - })?; - - let bmp_password = bmp_string(password); - - let mut certificate = None; - let mut signing_key = None; - - for content in content_infos { - let bags_data = match content { - p12::ContentInfo::Data(inner) => inner, - p12::ContentInfo::EncryptedData(encrypted) => { - encrypted.data(&bmp_password).ok_or_else(|| { - AppleCodesignError::PfxParseError( - "failed decrypting inner EncryptedData".to_string(), - ) - })? - } - p12::ContentInfo::OtherContext(_) => { - return Err(AppleCodesignError::PfxParseError( - "unexpected OtherContent content in inner PFX data".to_string(), - )); - } - }; - - let bags = yasna::parse_ber(&bags_data, |reader| { - reader.collect_sequence_of(p12::SafeBag::parse) - }) - .map_err(|e| { - AppleCodesignError::PfxParseError(format!( - "failed parsing SafeBag within inner Data: {e:?}" - )) - })?; - - for bag in bags { - match bag.bag { - p12::SafeBagKind::CertBag(cert_bag) => match cert_bag { - p12::CertBag::X509(cert_data) => { - certificate = Some(CapturedX509Certificate::from_der(cert_data)?); - } - p12::CertBag::SDSI(_) => { - return Err(AppleCodesignError::PfxParseError( - "unexpected SDSI certificate data".to_string(), - )); - } - }, - p12::SafeBagKind::Pkcs8ShroudedKeyBag(key_bag) => { - let decrypted = key_bag.decrypt(&bmp_password).ok_or_else(|| { - AppleCodesignError::PfxParseError( - "error decrypting PKCS8 shrouded key bag; is the password correct?" - .to_string(), - ) - })?; - - signing_key = Some(InMemoryPrivateKey::from_pkcs8_der(decrypted)?); - } - p12::SafeBagKind::OtherBagKind(_) => { - return Err(AppleCodesignError::PfxParseError( - "unexpected bag type in inner PFX content".to_string(), - )); - } - } - } - } + let certificate = Some(CapturedX509Certificate::from_der(chain.chain()[0].as_der())?); + let signing_key = Some(InMemoryPrivateKey::from_pkcs8_der(chain.key())?); match (certificate, signing_key) { (Some(certificate), Some(signing_key)) => Ok((certificate, signing_key)), diff --git a/apple-codesign/src/macho_signing.rs b/apple-codesign/src/macho_signing.rs index ccd70b1e6..7bfc86984 100644 --- a/apple-codesign/src/macho_signing.rs +++ b/apple-codesign/src/macho_signing.rs @@ -382,7 +382,9 @@ impl<'data> MachOSigner<'data> { if binaries.len() > 1 { create_universal_macho(writer, binaries.iter().map(|x| x.as_slice()))?; } else { - writer.write_all(&binaries[0])?; + for binary in binaries { + writer.write(&binary)?; + } } Ok(())