Skip to content

Commit 706dcc8

Browse files
committed
iCloud Passwords
1 parent e68b302 commit 706dcc8

8 files changed

Lines changed: 713 additions & 29 deletions

File tree

src/auth.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use aes::{cipher::consts::U16, Aes128};
44
use cloudkit_proto::{octagon_pairing_message::{self, Step5}, CuttlefishPeer, OctagonPairingMessage, OctagonWrapper, SignedInfo};
55
use deku::{DekuRead, DekuWrite};
66
use hkdf::Hkdf;
7-
use icloud_auth::{AppleAccount, CircleSendMessage, LoginState};
7+
use icloud_auth::{AppleAccount, CircleSendMessage, GenerateVerificationTokenRequest, LoginState};
88
use keystore::{KeystoreAccessRules, KeystoreDigest, KeystorePadding, KeystorePublicKey, KeystoreSignKey, RsaKey};
99
use log::{debug, info, warn};
1010
use omnisette::{AnisetteClient, AnisetteProvider};
@@ -144,6 +144,10 @@ impl<T: AnisetteProvider> TokenProvider<T> {
144144
Ok(plist::from_bytes(&data)?)
145145
}
146146

147+
pub async fn generate_verification_token(&self, request: GenerateVerificationTokenRequest) -> Result<String, PushError> {
148+
Ok(self.account.lock().await.generate_verification_token(request).await?)
149+
}
150+
147151
pub async fn get_gsa_token(&self, token: &str) -> Option<String> {
148152
self.account.lock().await.get_token(token).await
149153
}

src/error.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,4 +198,6 @@ pub enum PushError {
198198
CircleNotFound(String),
199199
#[error("Keystore error {0}!")]
200200
KeystoreError(#[from] KeystoreError),
201+
#[error("Unknown TOTP algorithm {0}!")]
202+
UnknownTotpAlgorithm(u32),
201203
}

src/icloud/keychain.rs

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use aes_gcm::KeyInit;
2626
use aes_siv::{siv::CmacSiv, Aes256SivAead};
2727

2828
use cloudkit_proto::CuttlefishEstablishResponse;
29-
use crate::{TokenProvider, cloudkit::{SaveRecordOperation, ZoneDeleteOperation, ZoneSaveOperation, record_identifier, should_reset}, keychain, pcs::PCSKey};
29+
use crate::{TokenProvider, cloudkit::{DeleteRecordOperation, SaveRecordOperation, ZoneDeleteOperation, ZoneSaveOperation, record_identifier, should_reset}, keychain, pcs::PCSKey, util::{ec_key_from_apple, ec_key_to_apple}};
3030
use aes::{cipher::{consts::{U12, U16, U32}, Unsigned}, Aes128, Aes256};
3131
use sha2::{digest::FixedOutputReset, Digest, Sha256, Sha384};
3232
use srp::{client::{SrpClient, SrpClientVerifier}, groups::G_2048, server::SrpServer};
@@ -181,7 +181,6 @@ impl CuttlefishEncItem {
181181
self.parentkeyref.record_identifier.as_ref().unwrap().value.as_ref().unwrap().name()
182182
}
183183

184-
// TODO not secure for raw passwords length, padding is lazy. Only cryptographic keys for now
185184
fn encrypt(&mut self, uuid: &str, key: &SivKey, data: Dictionary) -> Result<(), PushError> {
186185
let record_key: [u8; 64] = rand::random();
187186
self.wrappedkey = base64_encode(&key.encrypt(&record_key));
@@ -195,8 +194,15 @@ impl CuttlefishEncItem {
195194
headers.extend(aad.into_values());
196195

197196
let mut data = plist_to_bin(&data)?;
198-
data.push(0x80); // lazy padding
199-
data.push(0x00);
197+
data.push(0x80);
198+
199+
const BLOCK_PAD_SIZE: usize = 20;
200+
let mut blocks = (data.len() + (BLOCK_PAD_SIZE - 1)) / BLOCK_PAD_SIZE;
201+
if blocks == 1 {
202+
blocks += 1;
203+
}
204+
205+
data.resize(blocks * BLOCK_PAD_SIZE, 0);
200206

201207
let data = cipher.encrypt::<&[Vec<u8>], &Vec<u8>>(&headers, &data).unwrap();
202208
self.data = [&iv[..], &data].concat();
@@ -425,21 +431,6 @@ fn msg_from_bin(bin: &[u8], header_len: usize, section_count: usize) -> (Vec<u8>
425431
(header, offsets.collect())
426432
}
427433

428-
fn ec_key_from_apple(apple: &[u8]) -> EcKey<Private> {
429-
let curve = EcGroup::from_curve_name(Nid::SECP384R1).unwrap();
430-
let mut num_context_ref = BigNumContext::new().unwrap();
431-
let main_point = EcPoint::from_bytes(&curve, &apple[..97], &mut num_context_ref).unwrap();
432-
EcKey::from_private_components(&curve, &BigNum::from_slice(&apple[97..]).unwrap(), &main_point).unwrap()
433-
}
434-
435-
fn ec_key_to_apple(key: &EcKey<Private>) -> Vec<u8> {
436-
let mut num_context_ref = BigNumContext::new().unwrap();
437-
let mut point = key.public_key().to_bytes(&key.group(), PointConversionForm::UNCOMPRESSED, &mut num_context_ref).unwrap();
438-
assert_eq!(point.len(), 97);
439-
point.extend(key.private_key().to_vec());
440-
point
441-
}
442-
443434
struct KeyVaultMessage {
444435
header: Vec<u8>,
445436
sections: Vec<Vec<u8>>,
@@ -1256,6 +1247,21 @@ impl<P: AnisetteProvider> KeychainClient<P> {
12561247

12571248
Ok(())
12581249
}
1250+
1251+
pub async fn delete_keychain(&self, uuid: &str, zone: &str) -> Result<(), PushError> {
1252+
let security_container = self.get_security_container().await?;
1253+
let record_zone = security_container.private_zone(zone.to_string());
1254+
1255+
security_container.perform(&CloudKitSession::new(), DeleteRecordOperation::new(record_identifier(record_zone, uuid))).await?;
1256+
1257+
let mut state = self.state.write().await;
1258+
let zone = state.items.entry(zone.to_string()).or_default();
1259+
zone.keys.remove(uuid);
1260+
1261+
(self.update_state)(&state);
1262+
1263+
Ok(())
1264+
}
12591265

12601266
pub async fn insert_keychain(&self, uuid: &str, zone: &str, class: &str, mut dict: Dictionary, pcs: Option<&PCSMeta>, associated_tag: Option<&str>) -> Result<(), PushError> {
12611267
let security_container = self.get_security_container().await?;
@@ -1287,7 +1293,7 @@ impl<P: AnisetteProvider> KeychainClient<P> {
12871293
item.encrypt(&uuid, &key.decode(&state.get_cloudkey_access_key()?), dict.clone())?;
12881294

12891295
let mut ops = vec![SaveRecordOperation::new(
1290-
record_identifier(record_zone.clone(), &uuid), item, None, false)];
1296+
record_identifier(record_zone.clone(), &uuid), item, None, true)];
12911297

12921298
if let Some(tag) = associated_tag {
12931299
ops.push(SaveRecordOperation::new(record_identifier(record_zone.clone(), tag), CuttlefishCurrentItem {
@@ -1684,13 +1690,15 @@ impl<P: AnisetteProvider> KeychainClient<P> {
16841690
&*[&cipertext.ciphertext()[..], &cipertext.authentication_code()[..]].concat()).map_err(|_| PushError::AESGCMError)?;
16851691

16861692
let decoded = OtInternalBottle::decode(Cursor::new(&result))?;
1693+
1694+
let key_group = EcGroup::from_curve_name(Nid::SECP384R1).unwrap();
16871695

16881696
// reconstruct a keychain identity for our other peer
16891697
Ok(KeychainUserIdentity {
16901698
identifier: outer_bottle.peer_id().to_string(),
16911699
info: peer.0.permanent_info.clone().unwrap(),
1692-
signing_key: SoftEcKey(ec_key_from_apple(decoded.signing_key.as_ref().unwrap().key_data.as_ref().unwrap())),
1693-
encryption_key: SoftEcKey(ec_key_from_apple(decoded.encryption_key.as_ref().unwrap().key_data.as_ref().unwrap())),
1700+
signing_key: SoftEcKey(ec_key_from_apple(decoded.signing_key.as_ref().unwrap().key_data.as_ref().unwrap(), &key_group)),
1701+
encryption_key: SoftEcKey(ec_key_from_apple(decoded.encryption_key.as_ref().unwrap().key_data.as_ref().unwrap(), &key_group)),
16941702
current_state: peer.get_dynamic_info()?,
16951703
})
16961704
}

src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ pub mod findmy;
1111
pub mod facetime;
1212
pub mod icloud;
1313
pub mod statuskit;
14+
pub mod passwords;
1415
pub use imessage::cloud_messages;
1516
pub use imessage::posterkit;
1617
pub use util::KeyedArchive;
@@ -34,7 +35,7 @@ pub mod mmcsp {
3435
use std::collections::HashMap;
3536
use std::fmt::Debug;
3637

37-
pub use icloud_auth::{DefaultAnisetteProvider, default_provider, ArcAnisetteClient, LoginClientInfo, LoginState, AppleAccount, VerifyBody, TrustedPhoneNumber};
38+
pub use icloud_auth::{DefaultAnisetteProvider, GenerateVerificationTokenRequest, default_provider, ArcAnisetteClient, LoginClientInfo, LoginState, AppleAccount, VerifyBody, TrustedPhoneNumber};
3839

3940
use activation::ActivationInfo;
4041
pub use aps::{APSConnectionResource, APSConnection, APSMessage, APSState};

0 commit comments

Comments
 (0)