Skip to content

Commit 55cb25a

Browse files
committed
Fix token decoding
1 parent 0afd7d7 commit 55cb25a

File tree

1 file changed

+39
-8
lines changed

1 file changed

+39
-8
lines changed

icloud-auth/src/client.rs

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ pub struct AuthTokenRequest {
9090

9191
pub struct FetchedToken {
9292
token: String,
93-
expiration: u64,
93+
expiration: SystemTime,
9494
}
9595

9696
pub struct AppleAccount<T: AnisetteProvider> {
@@ -208,8 +208,8 @@ impl<T: AnisetteProvider> AppleAccount<T> {
208208
pub fn new_with_anisette(client_info: LoginClientInfo, anisette: ArcAnisetteClient<T>) -> Result<Self, crate::Error> {
209209
let client = ClientBuilder::new()
210210
.cookie_store(true)
211-
// .proxy(Proxy::https("https://192.168.86.82:8084").unwrap())
212211
.add_root_certificate(Certificate::from_der(APPLE_ROOT)?)
212+
// .proxy(Proxy::https("https://192.168.86.82:8080").unwrap())
213213
// .danger_accept_invalid_certs(true)
214214
.http1_title_case_headers()
215215
.connection_verbose(true)
@@ -328,8 +328,7 @@ impl<T: AnisetteProvider> AppleAccount<T> {
328328
let has_valid_token = if !self.tokens.is_empty() {
329329
let data = self.tokens.get(token)?; // if it's not here, we don't have one
330330

331-
let time = SystemTime::UNIX_EPOCH + Duration::from_secs(data.expiration);
332-
time.elapsed().is_err()
331+
data.expiration.elapsed().is_err()
333332
} else {
334333
false
335334
};
@@ -695,7 +694,11 @@ impl<T: AnisetteProvider> AppleAccount<T> {
695694
let keys: HashMap<String, FetchedToken> = dict.iter().filter_map(|(service, value)| {
696695
Some((service.clone(), FetchedToken {
697696
token: value.as_dictionary()?.get("token")?.as_string()?.to_string(),
698-
expiration: value.as_dictionary()?.get("expiry")?.as_unsigned_integer()?,
697+
expiration: if let Some(expiry) = value.as_dictionary()?.get("expiry") {
698+
SystemTime::UNIX_EPOCH + Duration::from_millis(expiry.as_unsigned_integer()?)
699+
} else {
700+
SystemTime::now() + Duration::from_secs(value.as_dictionary()?.get("duration")?.as_unsigned_integer()?)
701+
},
699702
}))
700703
}).collect();
701704
self.tokens = keys;
@@ -863,12 +866,32 @@ impl<T: AnisetteProvider> AppleAccount<T> {
863866

864867
Self::check_error(&res)?;
865868

869+
// this endpoint is stupid
870+
// in the SMS 2fa endpoint, all tokens have format ID:TOKEN:DURATION:EXP (MS SINCE EPOCH)
871+
872+
// here, well, the PE token has no duration OR expiration (ID:TOKEN)
873+
// the HB token has format ID:TOKEN:EXP
874+
// and the GS tokens (I have checked, god knows there's one that has a different format to mess with me) have format ID:TOKEN:DURATION
875+
// conclusion
876+
877+
// so what to do? Don't trust apple at all. For PET, assume 300s if no duration. For everything else, guess whether the token is epoch time or duration
878+
// by seeing if the number is greater than 40 years in milliseconds. No token should reasonably have a duration longer than that (besides otherwise its in secs)
879+
866880
self.tokens = headers.get_all("X-Apple-GS-Token").iter().chain(headers.get_all("X-Apple-HB-Token").iter()).map(|header| {
867881
let decoded = String::from_utf8(base64::decode(&header.as_bytes()).expect("Not base64!")).expect("Decoded not utf8!");
868882
let parts = decoded.split(":").collect::<Vec<&str>>();
883+
let exp = parts.get(2).expect("No epxiration date?").parse().expect("Bad expiration format?");
884+
885+
let time = if exp > 40 * 365 * 24 * 60 * 60 * 1000 {
886+
// ms since epoch
887+
SystemTime::UNIX_EPOCH + Duration::from_millis(exp)
888+
} else {
889+
SystemTime::now() + Duration::from_secs(exp)
890+
};
891+
869892
(parts[0].to_string(), FetchedToken {
870893
token: parts[1].to_string(),
871-
expiration: parts.get(2).expect("No epxiration date?").parse().expect("Bad expiration format?"),
894+
expiration: time,
872895
})
873896
}).collect();
874897

@@ -902,10 +925,18 @@ impl<T: AnisetteProvider> AppleAccount<T> {
902925
self.tokens = res.headers().get_all("X-Apple-GS-Token").iter().chain(res.headers().get_all("X-Apple-HB-Token").iter()).map(|header| {
903926
let decoded = String::from_utf8(base64::decode(&header.as_bytes()).expect("Not base64!")).expect("Decoded not utf8!");
904927
let parts = decoded.split(":").collect::<Vec<&str>>();
928+
929+
let exp = parts.get(3).unwrap_or(parts.get(2).expect("no epxiration")).parse().expect("Bad expiration format?");
930+
let time = if exp > 40 * 365 * 24 * 60 * 60 * 1000 {
931+
// ms since epoch
932+
SystemTime::UNIX_EPOCH + Duration::from_millis(exp)
933+
} else {
934+
SystemTime::now() + Duration::from_secs(exp)
935+
};
905936

906937
(parts[0].to_string(), FetchedToken {
907938
token: parts[1].to_string(),
908-
expiration: parts.get(3).expect("No epxiration date?").parse().expect("Bad expiration format?"),
939+
expiration: time,
909940
})
910941
}).collect();
911942

@@ -919,7 +950,7 @@ impl<T: AnisetteProvider> AppleAccount<T> {
919950

920951
fn parse_pet_header(&mut self, data: &str) {
921952
let decoded = String::from_utf8(base64::decode(data).unwrap()).unwrap();
922-
self.tokens.insert("com.apple.gs.idms.pet".to_string(), FetchedToken { token: decoded.split(":").nth(1).unwrap().to_string(), expiration: (decoded.split(":").nth(2).expect("No pet token").parse::<u64>().expect("Bad pet format") * 1000u64) + (SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_millis() as u64) });
953+
self.tokens.insert("com.apple.gs.idms.pet".to_string(), FetchedToken { token: decoded.split(":").nth(1).unwrap().to_string(), expiration: SystemTime::now() + Duration::from_secs(decoded.split(":").nth(2).map(|a| a.parse::<u64>().expect("Bad pet format")).unwrap_or(300)) });
923954
}
924955

925956
fn check_error(res: &plist::Dictionary) -> Result<(), Error> {

0 commit comments

Comments
 (0)