Git · dadoum.zone
Repositories
Help
Report an Issue
apple-crates
Code
Commits
Branches
Tags
Search
Tree:
198bf39
Branches
Tags
main
apple-crates
aps
src
macos_signer.rs
Add FairPlay crate reimplementing fairplay-decrypt and fix APS
Dadoum
commited
198bf39
at 2026-10-25 21:12:14
macos_signer.rs
Blame
History
Raw
mod constants; #[cfg(test)] mod vectors; use crate::{ActivationSignature, ActivationSigner, activation::sign_sha1}; use constants::{CERTIFICATE_AUTHORITY_CHAIN, IDENTITIES, SigningIdentity}; use rsa::{ BigUint, RsaPrivateKey, rand_core::{OsRng, RngCore}, }; /// Signs MacOS activation requests using apsd's embedded FairPlay identities. /// Uses only Rust cryptography and constants; no Apple library or hardware is required. pub struct MacOsActivationSigner { private_key: RsaPrivateKey, certificate: &'static [u8], } impl MacOsActivationSigner { /// Selects one of the ten software activation identities recovered from apsd. pub fn new() -> Self { let identity = &IDENTITIES[OsRng.next_u32() as usize % IDENTITIES.len()]; Self::from_identity(identity) } fn from_identity(identity: &SigningIdentity) -> Self { let private_key = RsaPrivateKey::from_p_q( BigUint::from_bytes_be(&identity.p), BigUint::from_bytes_be(&identity.q), 65537u32.into(), ) .expect("embedded activation primes must form a valid RSA key"); Self { private_key, certificate: identity.certificate, } } } impl Default for MacOsActivationSigner { fn default() -> Self { Self::new() } } impl ActivationSigner for MacOsActivationSigner { type Error = rsa::Error; fn sign(&self, xml: &[u8]) -> Result<ActivationSignature, Self::Error> { Ok(ActivationSignature { signature: sign_sha1(&self.private_key, xml)?, certificate_chain: [self.certificate, CERTIFICATE_AUTHORITY_CHAIN].concat(), }) } } #[cfg(test)] mod tests { use super::*; use rsa::{RsaPublicKey, pkcs8::DecodePublicKey}; use x509_cert::{ Certificate, der::{Decode, Encode}, }; #[test] fn signatures_match_native_apsd_for_every_identity() { let mut covered = [false; 10]; for &(index, message, expected_signature) in vectors::VECTORS { let identity = &IDENTITIES[index]; let signer = MacOsActivationSigner::from_identity(identity); let signed = signer.sign(message).unwrap(); assert_eq!(signed.signature, expected_signature, "identity {index}"); assert_eq!( signed.certificate_chain, [identity.certificate, CERTIFICATE_AUTHORITY_CHAIN].concat() ); covered[index] = true; } assert!(covered.into_iter().all(|covered| covered)); } #[test] fn keys_match_certificates() { for identity in &IDENTITIES { let signer = MacOsActivationSigner::from_identity(identity); signer.private_key.validate().unwrap(); let certificate = Certificate::from_der(identity.certificate).unwrap(); let public = RsaPublicKey::from_public_key_der( &certificate .tbs_certificate .subject_public_key_info .to_der() .unwrap(), ) .unwrap(); assert_eq!(signer.private_key.to_public_key(), public); } } }