Skip to content

Commit 0cd1f0f

Browse files
committed
Bugfixes
1 parent 0809507 commit 0cd1f0f

File tree

6 files changed

+78
-45
lines changed

6 files changed

+78
-45
lines changed

src/cloudkit.rs

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use std::str::FromStr;
1818
use uuid::Uuid;
1919
use aes_gcm::KeyInit;
2020

21-
use crate::{auth::{MobileMeDelegateResponse, TokenProvider}, ids::CompactECKey, keychain::KeychainClient, mmcs::{get_headers, get_mmcs, put_authorize_body, put_mmcs, AuthorizedOperation, MMCSConfig, PreparedPut}, mmcsp::FordChunk, pcs::{PCSKey, PCSPrivateKey, PCSService, PCSShareProtection}, prepare_put, util::{base64_decode, base64_encode, decode_hex, decode_uleb128, encode_hex, encode_uleb128, gzip_normal, kdf_ctr_hmac, rfc6637_unwrap_key, REQWEST}, FileContainer, OSConfig, PushError};
21+
use crate::{auth::{MobileMeDelegateResponse, TokenProvider}, ids::CompactECKey, keychain::KeychainClient, mmcs::{get_headers, get_mmcs, put_authorize_body, put_mmcs, AuthorizedOperation, MMCSConfig, PreparedPut}, mmcsp::FordChunk, pcs::{PCSKey, PCSKeys, PCSPrivateKey, PCSService, PCSShareProtection}, prepare_put, util::{base64_decode, base64_encode, decode_hex, decode_uleb128, encode_hex, encode_uleb128, gzip_normal, kdf_ctr_hmac, rfc6637_unwrap_key, REQWEST}, FileContainer, OSConfig, PushError};
2222

2323
fn undelimit_response(resp: &mut impl Read) -> Vec<Vec<u8>> {
2424
let mut response: Vec<Vec<u8>> = vec![];
@@ -43,15 +43,15 @@ pub struct FetchedRecords {
4343
}
4444

4545
impl FetchedRecords {
46-
pub fn get_record<R: CloudKitRecord>(&self, record_id: &str, key: Option<&PCSKeys>) -> R {
46+
pub fn get_record<R: CloudKitRecord>(&self, record_id: &str, key: Option<&PCSZoneConfig>) -> R {
4747
self.responses.iter().find_map(|response| {
4848
let r = response.record_retrieve_response.as_ref().expect("No retrieve response?").record.as_ref().expect("No record?");
4949
if r.record_identifier.as_ref().expect("No record id?").value.as_ref().expect("No identifier").name.as_ref().expect("No name?") == record_id {
5050
let got_type = r.r#type.as_ref().expect("no TYpe").name.as_ref().expect("No ta");
5151
if got_type.as_str() != R::record_type() {
5252
panic!("Wrong record type, got {} expected {}", got_type, R::record_type());
5353
}
54-
let key = key.map(|k| pcs_key_for_record(r, k).expect("PCS key failed"));
54+
let key = key.map(|k| pcs_keys_for_record(r, k).expect("PCS key failed"));
5555
Some(R::from_record_encrypted(&r.record_field, key.as_ref().map(|k| (k, r.record_identifier.as_ref().unwrap()))))
5656
} else { None }
5757
}).expect("No record found?")
@@ -100,14 +100,16 @@ pub trait CloudKitOp {
100100
}
101101
}
102102

103-
pub fn pcs_key_for_record(record: &Record, keys: &PCSKeys) -> Result<PCSKey, PushError> {
103+
pub fn pcs_keys_for_record(record: &Record, keys: &PCSZoneConfig) -> Result<PCSKeys, PushError> {
104104
let Some(protection) = &record.protection_info else {
105105
let Some(pcskey) = &record.pcs_key else { panic!("No PCS Key??") };
106-
let keys = keys.default_record_keys.iter().find(|i| i.key_id().ok().map(|id| pcskey == &id[..pcskey.len()]).unwrap_or(false));
106+
if !keys.default_record_keys.iter().any(|i| i.key_id().ok().map(|id| pcskey == &id[..pcskey.len()]).unwrap_or(false)) {
107+
return Err(PushError::PCSRecordKeyMissing);
108+
}
107109

108-
return Ok(keys.ok_or(PushError::PCSRecordKeyMissing)?.clone())
110+
return Ok(PCSKeys(keys.default_record_keys.clone()))
109111
};
110-
Ok(keys.decode_record_protection(protection)?.remove(0))
112+
Ok(PCSKeys(keys.decode_record_protection(protection)?))
111113
}
112114

113115
pub struct UploadAssetOperation(pub cloudkit_proto::AssetUploadTokenRetrieveRequest);
@@ -191,7 +193,7 @@ impl CloudKitOp for SaveRecordOperation {
191193

192194
impl SaveRecordOperation {
193195
// new with a *custom* record protection entry
194-
pub fn new_protected<R: CloudKitRecord>(id: RecordIdentifier, record: R, key: &PCSKeys, update: Option<String>) -> (Self, String) {
196+
pub fn new_protected<R: CloudKitRecord>(id: RecordIdentifier, record: R, key: &PCSZoneConfig, update: Option<String>) -> (Self, String) {
195197
// create a key for this record
196198
let record_protection = PCSShareProtection::create(&key.zone_keys[0], &[]).unwrap();
197199
let der = rasn::der::encode(&record_protection).unwrap();
@@ -219,7 +221,7 @@ impl SaveRecordOperation {
219221
}), tag)
220222
}
221223

222-
pub fn new<R: CloudKitRecord>(id: RecordIdentifier, record: R, key: Option<&PCSKeys>, update: bool) -> Self {
224+
pub fn new<R: CloudKitRecord>(id: RecordIdentifier, record: R, key: Option<&PCSZoneConfig>, update: bool) -> Self {
223225
Self(cloudkit_proto::RecordSaveRequest {
224226
record: Some(cloudkit_proto::Record {
225227
record_identifier: Some(id.clone()),
@@ -244,14 +246,14 @@ pub struct FetchedRecord {
244246
}
245247

246248
impl FetchedRecord {
247-
pub fn get_record<R: CloudKitRecord>(&self, key: Option<&PCSKeys>) -> R {
249+
pub fn get_record<R: CloudKitRecord>(&self, key: Option<&PCSZoneConfig>) -> R {
248250
let r = self.response.record_retrieve_response.as_ref().expect("No retrieve response?").record.as_ref().expect("No record?");
249251

250252
let got_type = r.r#type.as_ref().expect("no TYpe").name.as_ref().expect("No ta");
251253
if got_type.as_str() != R::record_type() {
252254
panic!("Wrong record type, got {} expected {}", got_type, R::record_type());
253255
}
254-
let key = key.map(|k| pcs_key_for_record(r, k).expect("no PCS key"));
256+
let key = key.map(|k| pcs_keys_for_record(r, k).expect("no PCS key"));
255257
R::from_record_encrypted(&r.record_field, key.as_ref().map(|k| (k, r.record_identifier.as_ref().unwrap())))
256258
}
257259

@@ -793,14 +795,14 @@ pub struct QueryResult<T: CloudKitRecord> {
793795
}
794796

795797
#[derive(Clone)]
796-
pub struct PCSKeys {
798+
pub struct PCSZoneConfig {
797799
zone_keys: Vec<CompactECKey<Private>>,
798800
zone_protection_tag: Option<String>,
799801
default_record_keys: Vec<PCSKey>,
800802
pub record_prot_tag: Option<String>,
801803
}
802804

803-
impl PCSKeys {
805+
impl PCSZoneConfig {
804806

805807
fn decode_record_protection(&self, protection: &ProtectionInfo) -> Result<Vec<PCSKey>, PushError> {
806808
let record_protection: PCSShareProtection = rasn::der::decode(protection.protection_info()).expect("Bad record protection?");
@@ -819,7 +821,7 @@ pub struct CloudKitOpenContainer<'t, T: AnisetteProvider> {
819821
container: CloudKitContainer<'t>,
820822
pub user_id: String,
821823
pub client: Arc<CloudKitClient<T>>,
822-
pub keys: Mutex<HashMap<String, PCSKeys>>,
824+
pub keys: Mutex<HashMap<String, PCSZoneConfig>>,
823825
}
824826

825827
impl<'t, T: AnisetteProvider> Deref for CloudKitOpenContainer<'t, T> {
@@ -850,7 +852,7 @@ impl<'t, T: AnisetteProvider> CloudKitOpenContainer<'t, T> {
850852
cached_keys.remove(&zone_name);
851853
}
852854

853-
pub async fn get_zone_encryption_config(&self, zone: &cloudkit_proto::RecordZoneIdentifier, client: &KeychainClient<T>, pcs_service: &PCSService<'_>) -> Result<PCSKeys, PushError> {
855+
pub async fn get_zone_encryption_config(&self, zone: &cloudkit_proto::RecordZoneIdentifier, client: &KeychainClient<T>, pcs_service: &PCSService<'_>) -> Result<PCSZoneConfig, PushError> {
854856
let mut cached_keys = self.keys.lock().await;
855857
let zone_name = zone.value.as_ref().unwrap().name().to_string();
856858
if let Some(key) = cached_keys.get(&zone_name) {
@@ -888,7 +890,7 @@ impl<'t, T: AnisetteProvider> CloudKitOpenContainer<'t, T> {
888890

889891
let (_parent_key, keys) = zone_protection.decrypt_with_keychain(&data, pcs_service)?;
890892

891-
let mut keys = PCSKeys {
893+
let mut keys = PCSZoneConfig {
892894
zone_keys: keys,
893895
zone_protection_tag: zone.protection_info.as_ref().unwrap().protection_info_tag.clone(),
894896
default_record_keys: vec![],

src/error.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,4 +183,6 @@ pub enum PushError {
183183
CircleOver,
184184
#[error("Too many requests!")]
185185
TooManyRequests,
186+
#[error("PCS Master key not found!")]
187+
MasterKeyNotFound,
186188
}

src/findmy.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use tokio::sync::broadcast;
2020
use aes_gcm::KeyInit;
2121
use uuid::Uuid;
2222
use crate::{cloudkit::{should_reset, SaveRecordOperation}, util::{base64_decode, base64_encode, bin_deserialize, bin_deserialize_opt_vec, bin_serialize, bin_serialize_opt_vec}};
23-
use crate::{aps::APSInterestToken, auth::{MobileMeDelegateResponse, TokenProvider}, cloudkit::{pcs_key_for_record, record_identifier, CloudKitClient, CloudKitContainer, CloudKitOpenContainer, CloudKitSession, FetchRecordChangesOperation, FetchRecordOperation, ALL_ASSETS, NO_ASSETS}, ids::{identity_manager::{DeliveryHandle, IDSSendMessage, IdentityManager, MessageTarget, Raw}, user::IDSService, IDSRecvMessage}, keychain::{derive_key_into, KeychainClient}, login_apple_delegates, pcs::PCSService, util::{duration_since_epoch, encode_hex, REQWEST}, APSConnection, APSMessage, LoginDelegate, OSConfig, PushError};
23+
use crate::{aps::APSInterestToken, auth::{MobileMeDelegateResponse, TokenProvider}, cloudkit::{pcs_keys_for_record, record_identifier, CloudKitClient, CloudKitContainer, CloudKitOpenContainer, CloudKitSession, FetchRecordChangesOperation, FetchRecordOperation, ALL_ASSETS, NO_ASSETS}, ids::{identity_manager::{DeliveryHandle, IDSSendMessage, IdentityManager, MessageTarget, Raw}, user::IDSService, IDSRecvMessage}, keychain::{derive_key_into, KeychainClient}, login_apple_delegates, pcs::PCSService, util::{duration_since_epoch, encode_hex, REQWEST}, APSConnection, APSMessage, LoginDelegate, OSConfig, PushError};
2424

2525
pub const MULTIPLEX_SERVICE: IDSService = IDSService {
2626
name: "com.apple.private.alloy.multiplex1",
@@ -443,7 +443,7 @@ impl<P: AnisetteProvider> FindMyClient<P> {
443443
let protection_info_tag = protection_info.protection_info_tag().to_string();
444444

445445
if record.r#type.as_ref().unwrap().name() == MasterBeaconRecord::record_type() {
446-
let item = MasterBeaconRecord::from_record_encrypted(&record.record_field, Some((&pcs_key_for_record(&record, &key)?, record.record_identifier.as_ref().unwrap())));
446+
let item = MasterBeaconRecord::from_record_encrypted(&record.record_field, Some((&pcs_keys_for_record(&record, &key)?, record.record_identifier.as_ref().unwrap())));
447447

448448
info!("Got beacon {:?} {}", item, identifier);
449449

@@ -453,7 +453,7 @@ impl<P: AnisetteProvider> FindMyClient<P> {
453453
beacon_records.insert(identifier, item);
454454
}
455455
} else if record.r#type.as_ref().unwrap().name() == BeaconNamingRecord::record_type() {
456-
let item = BeaconNamingRecord::from_record_encrypted(&record.record_field, Some((&pcs_key_for_record(&record, &key)?, record.record_identifier.as_ref().unwrap())));
456+
let item = BeaconNamingRecord::from_record_encrypted(&record.record_field, Some((&pcs_keys_for_record(&record, &key)?, record.record_identifier.as_ref().unwrap())));
457457

458458
if let Some(accessory) = accessories.get_mut(&item.associated_beacon) {
459459
accessory.naming = item;
@@ -462,7 +462,7 @@ impl<P: AnisetteProvider> FindMyClient<P> {
462462
naming_records.insert(item.associated_beacon.clone(), (identifier, Some(protection_info_tag), item));
463463
}
464464
} else if record.r#type.as_ref().unwrap().name() == KeyAlignmentRecord::record_type() {
465-
let item = KeyAlignmentRecord::from_record_encrypted(&record.record_field, Some((&pcs_key_for_record(&record, &key)?, record.record_identifier.as_ref().unwrap())));
465+
let item = KeyAlignmentRecord::from_record_encrypted(&record.record_field, Some((&pcs_keys_for_record(&record, &key)?, record.record_identifier.as_ref().unwrap())));
466466

467467
if let Some(accessory) = accessories.get_mut(&item.beacon_identifier) {
468468
accessory.alignment = item.clone();

src/imessage/cloud_messages.proto

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ message MessageProto2 {
1212

1313
message MessageProto {
1414
uint32 unk1 = 1; // always 1, is finished or ck sync state
15-
optional string groupTitle = 2;
15+
optional string subject = 2;
1616
optional string text = 3;
1717
optional bytes attributedBody = 4;
1818
optional string balloonBundleId = 5;

src/imessage/cloud_messages.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use cloudkit_proto::RecordIdentifier;
2727
use log::info;
2828
use uuid::Uuid;
2929
use crate::cloud_messages::cloudmessagesp::{ChatProto, MessageProto, MessageProto2, MessageProto3, MessageProto4};
30-
use crate::cloudkit::{pcs_key_for_record, record_identifier, CloudKitSession, CloudKitUploadRequest, DeleteRecordOperation, FetchRecordChangesOperation, FetchRecordOperation, FetchedRecords, SaveRecordOperation, ZoneDeleteOperation, ALL_ASSETS, NO_ASSETS};
30+
use crate::cloudkit::{pcs_keys_for_record, record_identifier, CloudKitSession, CloudKitUploadRequest, DeleteRecordOperation, FetchRecordChangesOperation, FetchRecordOperation, FetchedRecords, SaveRecordOperation, ZoneDeleteOperation, ALL_ASSETS, NO_ASSETS};
3131
use crate::mmcs::{prepare_put_v2, PreparedPut};
3232
use crate::pcs::{get_boundary_key, PCSKey, PCSService};
3333
use bitflags::bitflags;
@@ -408,8 +408,9 @@ pub struct AttachmentMetaExtra {
408408
pub struct AttachmentMeta {
409409
#[serde(rename = "mimet")]
410410
pub mime_type: Option<String>,
411+
// yes, these dates can be negative
411412
#[serde(rename = "sdt")]
412-
pub start_date: u64,
413+
pub start_date: i64,
413414
#[serde(rename = "tb")]
414415
pub total_bytes: u64,
415416
#[serde(rename = "st")]
@@ -435,7 +436,7 @@ pub struct AttachmentMeta {
435436
#[serde(rename = "t")]
436437
pub uti: Option<String>, // uti type
437438
#[serde(rename = "cdt")]
438-
pub created_date: u64,
439+
pub created_date: i64,
439440
pub pathc: Option<String>, // also transfer name
440441
#[serde(rename = "mdh")]
441442
pub md5: Option<String>, // first 8 bytes of md5 hash of file
@@ -494,7 +495,7 @@ impl<P: AnisetteProvider> CloudMessagesClient<P> {
494495
};
495496
if record.r#type.as_ref().unwrap().name() != T::record_type() { continue }
496497

497-
let pcskey = match pcs_key_for_record(&record, &key) {
498+
let pcskey = match pcs_keys_for_record(&record, &key) {
498499
Ok(key) => key,
499500
Err(PushError::PCSRecordKeyMissing) => {
500501
container.clear_cache_zone_encryption_config(&zone).await;

src/pcs.rs

Lines changed: 48 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use aes::{cipher::consts::U12, Aes128};
55
use aes_gcm::{AesGcm, Nonce, Tag};
66
use chrono::Utc;
77
use cloudkit_proto::CloudKitEncryptor;
8-
use log::info;
8+
use log::{info, warn};
99
use omnisette::AnisetteProvider;
1010
use openssl::{bn::{BigNum, BigNumContext}, ec::{EcGroup, EcKey, EcPoint, PointConversionForm}, hash::MessageDigest, nid::Nid, pkcs5::pbkdf2_hmac, pkey::{HasPublic, PKey, Private}, sha::sha256, sign::{Signer, Verifier}};
1111
use plist::{Dictionary, Value};
@@ -318,8 +318,14 @@ impl PCSPrivateKey {
318318

319319
let decoded: PCSPrivateKey = rasn::der::decode(&key).expect("Failed to decode private key!");
320320

321-
if !decoded.verify_with_keychain(keychain, dict.get("atyp").expect("No dat?").as_data().expect("Not data")).unwrap() {
322-
panic!("PCS Master key verification failed!");
321+
match decoded.verify_with_keychain(keychain, dict.get("atyp").expect("No dat?").as_data().expect("Not data")) {
322+
Ok(true) => {},
323+
Ok(false) => {
324+
panic!("PCS Master key verification failed!");
325+
}
326+
Err(e) => {
327+
warn!("PCS master key verification failed {e}");
328+
}
323329
}
324330

325331
decoded
@@ -358,7 +364,8 @@ impl PCSPrivateKey {
358364
public.verify(&self.signing_key())
359365
} else {
360366
let account = Value::Data(signature.keyid.to_vec());
361-
let item = keychain.items["ProtectedCloudStorage"].keys.values().find(|x| x.get("atyp") == Some(&account)).unwrap();
367+
let item = keychain.items["ProtectedCloudStorage"].keys.values().find(|x| x.get("atyp") == Some(&account))
368+
.ok_or(PushError::MasterKeyNotFound)?;
362369
let key = item.get("v_Data").expect("No dat?").as_data().expect("Not data");
363370

364371
let decoded: PCSPrivateKey = rasn::der::decode(&key).unwrap();
@@ -374,6 +381,21 @@ impl PCSPrivateKey {
374381
}
375382
}
376383

384+
fn get_ciphertext_key(ciphertext: &[u8]) -> (Vec<u8>, usize) {
385+
let encryption_version = ciphertext[0];
386+
if encryption_version != 3 {
387+
panic!("Unimplemented encryption version {encryption_version}");
388+
}
389+
390+
let second_keyid_part_len = ciphertext[3] as usize;
391+
let total_tag = [
392+
&ciphertext[1..3],
393+
&ciphertext[4..4 + second_keyid_part_len]
394+
].concat();
395+
396+
(total_tag, 4 + second_keyid_part_len)
397+
}
398+
377399
#[derive(AsnType, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, Debug)]
378400
pub struct PCSKeyRef {
379401
keytype: u32,
@@ -443,28 +465,20 @@ impl PCSKey {
443465
fn decrypt(&self, ciphertext: &[u8], aad: &[u8]) -> Result<Vec<u8>, PushError> {
444466
let encryption_key = kdf_ctr_hmac(&self.0, "encryption key key m".as_bytes(), &[], self.0.len());
445467

446-
let encryption_version = ciphertext[0];
447-
if encryption_version != 3 {
448-
panic!("Unimplemented encryption version {encryption_version}");
468+
let (required_key, header_len) = get_ciphertext_key(ciphertext);
469+
470+
if &required_key[..] != &self.key_id()?[..required_key.len()] {
471+
panic!("Mismatched key id! Data {} key {}", encode_hex(&ciphertext[..16]), encode_hex(&self.key_id()?));
449472
}
450473

451474
let tag_len = 12;
452-
let second_keyid_part_len = ciphertext[3] as usize;
453-
let total_tag = [
454-
&ciphertext[1..3],
455-
&ciphertext[4..4 + second_keyid_part_len]
456-
].concat();
457475

458-
if &total_tag[..] != &self.key_id()?[..total_tag.len()] {
459-
panic!("Mismatched key id!");
460-
}
461-
462-
let iv = &ciphertext[4 + second_keyid_part_len..4 + second_keyid_part_len + 12];
463-
let firstaad = &ciphertext[0..4 + second_keyid_part_len];
476+
let iv = &ciphertext[header_len..header_len + 12];
477+
let firstaad = &ciphertext[0..header_len];
464478
let gcm = AesGcm::<Aes128, U12, U12>::new(encryption_key[..].try_into().expect("Bad key size!"));
465-
let tag = &ciphertext[4 + second_keyid_part_len + 12..4 + second_keyid_part_len + 12 + tag_len];
479+
let tag = &ciphertext[header_len + 12..header_len + 12 + tag_len];
466480

467-
let mut text = ciphertext[4 + second_keyid_part_len + 12 + tag_len..].to_vec();
481+
let mut text = ciphertext[header_len + 12 + tag_len..].to_vec();
468482

469483
gcm.decrypt_in_place_detached(Nonce::from_slice(iv), &[firstaad, aad].concat(), &mut text, Tag::from_slice(tag)).expect("GCM error?");
470484
Ok(text)
@@ -509,6 +523,20 @@ impl CloudKitEncryptor for PCSKey {
509523
}
510524
}
511525

526+
pub struct PCSKeys(pub Vec<PCSKey>);
527+
impl CloudKitEncryptor for PCSKeys {
528+
fn decrypt_data(&self, dec: &[u8], context: &[u8]) -> Vec<u8> {
529+
let (required_key, _) = get_ciphertext_key(dec);
530+
531+
let key = self.0.iter().find(|k| &k.key_id().unwrap()[..required_key.len()] == &required_key[..]).expect("required key not found!");
532+
key.decrypt(dec, context).expect("Decryption failed")
533+
}
534+
535+
fn encrypt_data(&self, enc: &[u8], context: &[u8]) -> Vec<u8> {
536+
self.0.first().expect("PCS keyset empty?").encrypt(enc, context).expect("Encryption failed")
537+
}
538+
}
539+
512540
#[derive(AsnType, Encode, Decode, Debug, Default)]
513541
pub struct PCSShareProtectionSignatureData {
514542
// 5 is the version. non-exist is 1, 5 is 2, 4 is 3,

0 commit comments

Comments
 (0)