Dadoum commited on 2026-09-23 20:14:53
Showing 2 changed files, with 62 additions and 14 deletions.
| ... | ... |
@@ -288,39 +288,39 @@ pub async fn login( |
| 288 | 288 |
let response_dict = response_plist |
| 289 | 289 |
.get("Response")
|
| 290 | 290 |
.and_then(|response| response.as_dictionary()) |
| 291 |
- .ok_or(AuthError::Structure(response_plist.clone()))?; |
|
| 291 |
+ .ok_or_else(|| AuthError::Structure(response_plist.clone()))?; |
|
| 292 | 292 |
|
| 293 | 293 |
let status = response_dict |
| 294 | 294 |
.get("Status")
|
| 295 | 295 |
.and_then(|status| status.as_dictionary()) |
| 296 |
- .ok_or(AuthError::Structure(response_plist.clone()))?; |
|
| 296 |
+ .ok_or_else(|| AuthError::Structure(response_plist.clone()))?; |
|
| 297 | 297 |
|
| 298 | 298 |
parse_status(status).map_err(AuthError::Apple)?; |
| 299 | 299 |
|
| 300 | 300 |
let iteration_count = response_dict |
| 301 | 301 |
.get("i")
|
| 302 | 302 |
.and_then(|iteration_count| iteration_count.as_unsigned_integer()) |
| 303 |
- .ok_or(AuthError::Structure(response_plist.clone()))?; |
|
| 303 |
+ .ok_or_else(|| AuthError::Structure(response_plist.clone()))?; |
|
| 304 | 304 |
|
| 305 | 305 |
let salt = response_dict |
| 306 | 306 |
.get("s")
|
| 307 | 307 |
.and_then(|salt| salt.as_data()) |
| 308 |
- .ok_or(AuthError::Structure(response_plist.clone()))?; |
|
| 308 |
+ .ok_or_else(|| AuthError::Structure(response_plist.clone()))?; |
|
| 309 | 309 |
|
| 310 | 310 |
let selected_protocol = response_dict |
| 311 | 311 |
.get("sp")
|
| 312 | 312 |
.and_then(|selected_protocol| selected_protocol.as_string()) |
| 313 |
- .ok_or(AuthError::Structure(response_plist.clone()))?; |
|
| 313 |
+ .ok_or_else(|| AuthError::Structure(response_plist.clone()))?; |
|
| 314 | 314 |
|
| 315 | 315 |
let cookie = response_dict |
| 316 | 316 |
.get("c")
|
| 317 | 317 |
.and_then(|cookie| cookie.as_string()) |
| 318 |
- .ok_or(AuthError::Structure(response_plist.clone()))?; |
|
| 318 |
+ .ok_or_else(|| AuthError::Structure(response_plist.clone()))?; |
|
| 319 | 319 |
|
| 320 | 320 |
let b = response_dict |
| 321 | 321 |
.get("B")
|
| 322 | 322 |
.and_then(|b| b.as_data()) |
| 323 |
- .ok_or(AuthError::Structure(response_plist.clone()))?; |
|
| 323 |
+ .ok_or_else(|| AuthError::Structure(response_plist.clone()))?; |
|
| 324 | 324 |
|
| 325 | 325 |
let hashed_password: Vec<u8> = match selected_protocol {
|
| 326 | 326 |
// SRP with a 2048/4096-bit long A. |
| ... | ... |
@@ -385,14 +385,14 @@ pub async fn login( |
| 385 | 385 |
let response_dict = response_plist |
| 386 | 386 |
.get("Response")
|
| 387 | 387 |
.and_then(|response| response.as_dictionary()) |
| 388 |
- .ok_or(AuthError::Structure(response_plist.clone()))?; |
|
| 388 |
+ .ok_or_else(|| AuthError::Structure(response_plist.clone()))?; |
|
| 389 | 389 |
|
| 390 | 390 |
// println!("Response: {response_dict:?}");
|
| 391 | 391 |
|
| 392 | 392 |
let status = response_dict |
| 393 | 393 |
.get("Status")
|
| 394 | 394 |
.and_then(|status| status.as_dictionary()) |
| 395 |
- .ok_or(AuthError::Structure(response_plist.clone()))?; |
|
| 395 |
+ .ok_or_else(|| AuthError::Structure(response_plist.clone()))?; |
|
| 396 | 396 |
|
| 397 | 397 |
parse_status(status)?; |
| 398 | 398 |
|
| ... | ... |
@@ -410,7 +410,7 @@ pub async fn login( |
| 410 | 410 |
let server_reply = response_dict |
| 411 | 411 |
.get("M2")
|
| 412 | 412 |
.and_then(|server_reply| server_reply.as_data()) |
| 413 |
- .ok_or(AuthError::Structure(response_plist.clone()))?; |
|
| 413 |
+ .ok_or_else(|| AuthError::Structure(response_plist.clone()))?; |
|
| 414 | 414 |
|
| 415 | 415 |
verifier |
| 416 | 416 |
.verify_server(server_reply) |
| ... | ... |
@@ -463,7 +463,7 @@ pub async fn login( |
| 463 | 463 |
.get("au")
|
| 464 | 464 |
.and_then(|action_url| action_url.as_string()) |
| 465 | 465 |
.map(|action_url| action_url.to_string()) |
| 466 |
- .ok_or(AuthError::Structure(response_plist))?; |
|
| 466 |
+ .ok_or_else(|| AuthError::Structure(response_plist))?; |
|
| 467 | 467 |
|
| 468 | 468 |
Ok(AuthOutcome::SecondaryActionRequired( |
| 469 | 469 |
server_provided_data, |
| ... | ... |
@@ -475,7 +475,7 @@ pub async fn login( |
| 475 | 475 |
let sim = status |
| 476 | 476 |
.get("X-Apple-I-MD-DATA")
|
| 477 | 477 |
.and_then(|sim| sim.as_string()) |
| 478 |
- .ok_or(AuthError::Structure(response_plist.clone()))?; |
|
| 478 |
+ .ok_or_else(|| AuthError::Structure(response_plist.clone()))?; |
|
| 479 | 479 |
|
| 480 | 480 |
let sim = BASE64_STANDARD |
| 481 | 481 |
.decode(sim) |
| ... | ... |
@@ -487,7 +487,7 @@ pub async fn login( |
| 487 | 487 |
let url_switching_data = status |
| 488 | 488 |
.get("X-Apple-I-Data")
|
| 489 | 489 |
.and_then(|data| data.as_string()) |
| 490 |
- .ok_or(AuthError::Structure(response_plist.clone()))?; |
|
| 490 |
+ .ok_or_else(|| AuthError::Structure(response_plist.clone()))?; |
|
| 491 | 491 |
|
| 492 | 492 |
Ok(AuthOutcome::UrlSwitchingRequired( |
| 493 | 493 |
url_switching_data.to_string(), |
| ... | ... |
@@ -495,3 +495,51 @@ pub async fn login( |
| 495 | 495 |
} |
| 496 | 496 |
} |
| 497 | 497 |
} |
| 498 |
+ |
|
| 499 |
+#[derive(Error, Debug)] |
|
| 500 |
+pub enum ValidateCodeError {
|
|
| 501 |
+ #[error("The provided code is not valid.")]
|
|
| 502 |
+ InvalidCode, |
|
| 503 |
+ #[error("Could not log-in to Apple servers: {0}")]
|
|
| 504 |
+ Apple(#[from] AppleError), |
|
| 505 |
+ #[error("Cannot generate device authentication data: {0}")]
|
|
| 506 |
+ Anisette(#[from] ADIError), |
|
| 507 |
+ #[error("Network error: {0}")]
|
|
| 508 |
+ Network(#[from] reqwest::Error), |
|
| 509 |
+ // vvv Internal errors vvv |
|
| 510 |
+ #[error("Invalid URL bag")]
|
|
| 511 |
+ InvalidURLBag, |
|
| 512 |
+ #[error("Cannot parse server response: {0}")]
|
|
| 513 |
+ Parsing(plist::Error), |
|
| 514 |
+} |
|
| 515 |
+ |
|
| 516 |
+pub async fn validate_code( |
|
| 517 |
+ http_session: &AnisetteHTTPSession<'_, '_>, |
|
| 518 |
+ second_factor_code: u32 |
|
| 519 |
+) -> Result<(), ValidateCodeError> {
|
|
| 520 |
+ let validate_code_url = http_session |
|
| 521 |
+ .url_bag() |
|
| 522 |
+ .get("validateCode")
|
|
| 523 |
+ .and_then(Value::as_string) |
|
| 524 |
+ .ok_or(ValidateCodeError::InvalidURLBag)?; |
|
| 525 |
+ |
|
| 526 |
+ let response = http_session |
|
| 527 |
+ .anisette_request_builder(Method::POST, validate_code_url)? |
|
| 528 |
+ .header("security-code", second_factor_code.to_string())
|
|
| 529 |
+ .send() |
|
| 530 |
+ .await? |
|
| 531 |
+ .error_for_status()? |
|
| 532 |
+ .bytes() |
|
| 533 |
+ .await?; |
|
| 534 |
+ |
|
| 535 |
+ let response_plist: Dictionary = |
|
| 536 |
+ plist::from_bytes(&response).map_err(ValidateCodeError::Parsing)?; |
|
| 537 |
+ |
|
| 538 |
+ parse_status(&response_plist) |
|
| 539 |
+ .map_err(|err| |
|
| 540 |
+ match err {
|
|
| 541 |
+ AppleError { code: -21669, .. } => ValidateCodeError::InvalidCode,
|
|
| 542 |
+ err => ValidateCodeError::Apple(err) |
|
| 543 |
+ } |
|
| 544 |
+ ) |
|
| 545 |
+} |
| ... | ... |
@@ -30,7 +30,7 @@ pub struct DeviceData {
|
| 30 | 30 |
#[serde(rename = "dn")] |
| 31 | 31 |
pub device_name: Option<String>, |
| 32 | 32 |
#[serde(rename = "event")] |
| 33 |
- pub event: Option<String>, |
|
| 33 |
+ pub event: Option<String>, // liveness |
|
| 34 | 34 |
#[serde(rename = "imei")] |
| 35 | 35 |
pub imei: Option<String>, |
| 36 | 36 |
#[serde(rename = "loc")] |
| 37 | 37 |