Git · dadoum.zone
Repositories
Help
Report an Issue
apple-crates
Code
Commits
Branches
Tags
Search
Tree:
a74924e
Branches
Tags
main
apple-crates
sharing
src
csr.rs
Refactor and unify a lot of things for another project and implement airdrop support
Dadoum
commited
a74924e
at 2026-10-25 22:15:58
csr.rs
Blame
History
Raw
//! The client-side crypto from `Sharing.framework`: an RSA key and a PKCS#10 //! CSR, the request body `createCertificate` expects. //! //! `SFAppleIDCreateKeyPair` generates a **software RSA key** //! (`SecKeyCreateRandomKey`, `kSecAttrKeyTypeRSA`, `kSecAttrIsPermanent`, //! `kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly`) — no Secure Enclave, no //! access control, no attestation. `SFAppleIDCreateCertificateRequestPEMStringForKeyPair` //! wraps the key in a PKCS#10 CSR. We reproduce both with RustCrypto. use der::EncodePem; use der::pem::LineEnding; use rsa::RsaPrivateKey; use rsa::pkcs1v15::{Signature, SigningKey}; use rsa::pkcs8::EncodePrivateKey; use rsa::rand_core::OsRng; use x509_cert::builder::{Builder, RequestBuilder}; use x509_cert::name::Name; use crate::SharingError; /// The AirDrop identity key: an RSA private key with its PEM PKCS#10 CSR. pub struct IdentityKey { pub private_key: RsaPrivateKey, /// PEM PKCS#8 of the private key, to persist in `~/.omdrop/keys/key.pem`. pub private_key_pem: String, /// PEM PKCS#10 CSR, the `csr` field of the `createCertificate` body. pub csr_pem: String, } /// Historical AirDrop identity key size. Apple has used 1280-bit RSA for the /// AirDrop identity (per the PrivateDrop analysis); confirm against a capture /// before trusting it in production. 2048 is the safe, widely-accepted default. pub const AIRDROP_RSA_BITS: usize = 2048; fn crypto(e: impl std::fmt::Display) -> SharingError { SharingError::Crypto(e.to_string()) } /// Generate a fresh identity key and its CSR. `common_name` is the CSR /// subject's CN; sharingd uses the account's identifier, but the IDMS server /// re-issues the subject, so the exact value is not load-bearing. pub fn generate_identity_key(common_name: &str, bits: usize) -> Result<IdentityKey, SharingError> { let private_key = RsaPrivateKey::new(&mut OsRng, bits).map_err(crypto)?; let private_key_pem = private_key .to_pkcs8_pem(LineEnding::LF) .map_err(crypto)? .to_string(); let signer = SigningKey::<sha2_010::Sha256>::new(private_key.clone()); let subject: Name = format!("CN={common_name}").parse().map_err(crypto)?; let csr = RequestBuilder::new(subject, &signer) .map_err(crypto)? .build::<Signature>() .map_err(crypto)?; let csr_pem = csr.to_pem(LineEnding::LF).map_err(crypto)?; Ok(IdentityKey { private_key, private_key_pem, csr_pem, }) } #[cfg(test)] mod tests { use super::*; #[test] fn generates_a_pkcs10_csr() { // 1024 bits: just to keep the test fast; not a recommended size. let key = generate_identity_key("Test", 1024).unwrap(); assert!(key.csr_pem.contains("BEGIN CERTIFICATE REQUEST")); assert!(key.private_key_pem.contains("BEGIN PRIVATE KEY")); } }