Improve error handling and fix issues

This commit is contained in:
nab138
2025-08-08 22:16:09 -04:00
parent a17b2be2ac
commit 74f5af717c
16 changed files with 475 additions and 437 deletions

View File

@@ -8,27 +8,26 @@ default = []
vendored-botan = ["botan/vendored"]
[dependencies]
serde = { version = "1.0.219", features = ["derive"] }
serde_json = { version = "1.0.142" }
base64 = "0.13.1"
serde = { version = "1", features = ["derive"] }
serde_json = { version = "1" }
base64 = "0.22"
srp = { version = "0.6.0", path = "./rustcrypto-srp" }
pbkdf2 = { version = "0.11.0" }
sha2 = { version = "0.10.6" }
rand = { version = "0.8.5" }
rustls = { version = "0.20.7" }
rustls-pemfile = { version = "1.0.1" }
plist = { version = "1.7.2" }
pbkdf2 = "0.11"
sha2 = "0.10"
rand = "0.9"
rustls = "0.23"
rustls-pemfile = "2.2"
plist = "1.7.2"
hmac = "0.12.1"
num-bigint = "0.4.3"
cbc = { version = "0.1.2", features = ["std"] }
aes = "0.8.2"
pkcs7 = "0.3.0"
pkcs7 = "0.4.1"
reqwest = { version = "0.11.14", features = ["blocking", "json", "default-tls"] }
omnisette = {path = "../omnisette", features = ["remote-anisette-v3"]}
thiserror = "1.0.58"
thiserror = "2"
tokio = "1"
botan = { version = "0.11.1" }
chrono = { version = "0.4", features = ["serde"] }
botan = "0.12.0"
[dev-dependencies]
tokio = { version = "1", features = ["rt", "macros"] }

View File

@@ -14,15 +14,15 @@ rust-version = "1.56"
[dependencies]
num-bigint = "0.4"
generic-array = "0.14"
generic-array = "1"
digest = "0.10"
lazy_static = "1.2"
subtle = "2.4"
base64 = "0.21.0"
base64 = "0.22"
[dev-dependencies]
hex-literal = "0.3"
hex-literal = "1"
num-traits = "0.2"
rand = "0.8"
sha1 = "0.10.6"
sha2 = "0.10.8"
rand = "0.9"
sha1 = "0.10"
sha2 = "0.10"

View File

@@ -15,11 +15,7 @@ impl AnisetteData {
let mut b = AnisetteHeaders::get_anisette_headers_provider(config.clone())?;
let base_headers = b.provider.get_authentication_headers().await?;
Ok(AnisetteData {
base_headers,
generated_at: SystemTime::now(),
config,
})
Ok(AnisetteData { base_headers, generated_at: SystemTime::now(), config })
}
pub fn needs_refresh(&self) -> bool {
@@ -69,7 +65,7 @@ impl AnisetteData {
"X-Apple-App-Info".to_owned(),
"com.apple.gs.xcode.auth".to_owned(),
);
headers.insert("X-Xcode-Version".to_owned(), "14.2 (14C18)".to_owned());
headers.insert("X-Xcode-Version".to_owned(), "11.2 (11B41)".to_owned());
}
if cpd {

View File

@@ -1,5 +1,6 @@
use crate::{anisette::AnisetteData, Error};
use aes::cipher::block_padding::Pkcs7;
use base64::{engine::general_purpose, Engine};
use botan::Cipher;
use cbc::cipher::{BlockDecryptMut, KeyIvInit};
use hmac::{Hmac, Mac};
@@ -269,7 +270,6 @@ impl AppleAccount {
return Err(err_check.err().unwrap());
}
// --- D code logic starts here ---
let encrypted_token = res
.get("et")
.ok_or(Error::Parse)?
@@ -286,7 +286,7 @@ impl AppleAccount {
"Encrypted token is in an unknown format.".to_string(),
));
}
let iv = &encrypted_token[3..19]; // 16 bytes
let iv = &encrypted_token[3..19];
let ciphertext_and_tag = &encrypted_token[19..];
if sk.len() != 32 {
@@ -296,8 +296,6 @@ impl AppleAccount {
return Err(Error::Parse);
}
// Botan AES-256/GCM decryption with 16-byte IV and 3-byte AAD
// true = encrypt, false = decrypt
let mut cipher = Cipher::new("AES-256/GCM", botan::CipherDirection::Decrypt)
.map_err(|_| Error::Parse)?;
cipher.set_key(sk).map_err(|_| Error::Parse)?;
@@ -357,14 +355,17 @@ impl AppleAccount {
///
/// let anisette = AnisetteData::new();
/// let account = AppleAccount::login(
/// || ("test@waffle.me", "password")
/// || "123123",
/// || Ok(("test@waffle.me", "password"))
/// || Ok("123123"),
/// anisette
/// );
/// ```
/// Note: You would not provide the 2FA code like this, you would have to actually ask input for it.
//TODO: add login_with_anisette and login, where login autodetcts anisette
pub async fn login_with_anisette<F: Fn() -> Result<(String, String), String>, G: Fn() -> Result<String, String>>(
pub async fn login_with_anisette<
F: Fn() -> Result<(String, String), String>,
G: Fn() -> Result<String, String>,
>(
appleid_closure: F,
tfa_closure: G,
anisette: AnisetteData,
@@ -378,15 +379,25 @@ impl AppleAccount {
match response {
LoginState::NeedsDevice2FA => response = _self.send_2fa_to_devices().await?,
LoginState::Needs2FAVerification => {
response = _self.verify_2fa(tfa_closure().map_err(|e| {
Error::AuthSrpWithMessage(0, format!("Failed to get 2FA code: {}", e))
})?).await?
response = _self
.verify_2fa(tfa_closure().map_err(|e| {
Error::AuthSrpWithMessage(0, format!("Failed to get 2FA code: {}", e))
})?)
.await?
}
LoginState::NeedsSMS2FA => response = _self.send_sms_2fa_to_devices(1).await?,
LoginState::NeedsSMS2FAVerification(body) => {
response = _self.verify_sms_2fa(tfa_closure().map_err(|e| {
Error::AuthSrpWithMessage(0, format!("Failed to get SMS 2FA code: {}", e))
})?, body).await?
response = _self
.verify_sms_2fa(
tfa_closure().map_err(|e| {
Error::AuthSrpWithMessage(
0,
format!("Failed to get SMS 2FA code: {}", e),
)
})?,
body,
)
.await?
}
LoginState::NeedsLogin => {
response = _self.login_email_pass(&username, &password).await?
@@ -735,7 +746,7 @@ impl AppleAccount {
let dsid = spd.get("adsid").unwrap().as_string().unwrap();
let token = spd.get("GsIdmsToken").unwrap().as_string().unwrap();
let identity_token = base64::encode(format!("{}:{}", dsid, token));
let identity_token = general_purpose::STANDARD.encode(format!("{}:{}", dsid, token));
let valid_anisette = self.get_anisette().await;

View File

@@ -1,8 +1,5 @@
// use icloud_auth::ani
use std::sync::Arc;
use base64::engine::{general_purpose, Engine};
use num_bigint::BigUint;
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use srp::{
client::{SrpClient, SrpClientVerifier},
@@ -11,15 +8,18 @@ use srp::{
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn auth_debug() {
// not a real account
let bytes_a = base64::decode("XChHXELsQ+ljxTFbvRMUsGJxiDIlOh9f8e+JzoegmVcOdAXXtPNzkHpAbAgSjyA+vXrTA93+BUu8EJ9+4xZu9g==").unwrap();
let bytes_a = general_purpose::STANDARD.decode("XChHXELsQ+ljxTFbvRMUsGJxiDIlOh9f8e+JzoegmVcOdAXXtPNzkHpAbAgSjyA+vXrTA93+BUu8EJ9+4xZu9g==").unwrap();
let username = "apple3@f1sh.me";
let password = "WaffleTest123";
let salt = base64::decode("6fK6ailLUcp2kJswJVrKjQ==").unwrap();
let salt = general_purpose::STANDARD
.decode("6fK6ailLUcp2kJswJVrKjQ==")
.unwrap();
let iters = 20832;
let mut password_hasher = sha2::Sha256::new();
@@ -41,7 +41,9 @@ mod tests {
// apub: N2XHuh/4P1urPoBvDocF0RCRIl2pliZYqg9p6wGH0nnJdckJPn3M00jEqoM4teqH03HjG1murdcZiNHb5YayufW//+asW01XB7nYIIVvGiUFLRypYITEKYWBQ6h2q02GaZspYJKy98V8Fwcvr0ri+al7zJo1X1aoRKINyjV5TywhhwmTleI1qJkf+JBRYKKqO1XFtOTpQsysWD3ZJdK3K78kSgT3q0kXE3oDRMiHPAO77GFJZErYTuvI6QPRbOgcrn+RKV6AsjR5tUQAoSGRdtibdZTAQijJg788qVg+OFVCNZoY9GYVxa+Ze1bPGdkkgCYicTE8iNFG9KlJ+QpKgQ==
let a_random = base64::decode("ywN1O32vmBogb5Fyt9M7Tn8bbzLtDDbcYgPFpSy8n9E=").unwrap();
let a_random = general_purpose::STANDARD
.decode("ywN1O32vmBogb5Fyt9M7Tn8bbzLtDDbcYgPFpSy8n9E=")
.unwrap();
let client = SrpClient::<Sha256>::new(&G_2048);
let a_pub_compute =
@@ -49,14 +51,21 @@ mod tests {
// expect it to be same to a_pub
println!(
"compute a_pub: {:?}",
base64::encode(&a_pub_compute.to_bytes_be())
general_purpose::STANDARD.encode(&a_pub_compute.to_bytes_be())
);
let b_pub = base64::decode("HlWxsRmNi/9DCGxYCoqCTfdSvpbx3mrgFLQfOsgf3Rojn7MQQN/g63PwlBghUcVVB4//yAaRRnz/VIByl8thA9AKuVZl8k52PAHKSh4e7TuXSeYCFr0+GYu8/hFdMDl42219uzSuOXuaKGVKq6hxEAf3n3uXXgQRkXWtLFJ5nn1wq/emf46hYAHzc/pYyvckAdh9WDCw95IXbzKD8LcPw/0ZQoydMuXgW2ZKZ52fiyEs94IZ7L5RLL7jY1nVdwtsp2fxeqiZ3DNmVZ2GdNrbJGT//160tyd2evtUtehr8ygXNzjWdjV0cc4+1F38ywSPFyieVzVTYzDywRllgo3A5A==").unwrap();
println!("fixed b_pub: {:?}", base64::encode(&b_pub));
let b_pub = general_purpose::STANDARD.decode("HlWxsRmNi/9DCGxYCoqCTfdSvpbx3mrgFLQfOsgf3Rojn7MQQN/g63PwlBghUcVVB4//yAaRRnz/VIByl8thA9AKuVZl8k52PAHKSh4e7TuXSeYCFr0+GYu8/hFdMDl42219uzSuOXuaKGVKq6hxEAf3n3uXXgQRkXWtLFJ5nn1wq/emf46hYAHzc/pYyvckAdh9WDCw95IXbzKD8LcPw/0ZQoydMuXgW2ZKZ52fiyEs94IZ7L5RLL7jY1nVdwtsp2fxeqiZ3DNmVZ2GdNrbJGT//160tyd2evtUtehr8ygXNzjWdjV0cc4+1F38ywSPFyieVzVTYzDywRllgo3A5A==").unwrap();
println!(
"fixed b_pub: {:?}",
general_purpose::STANDARD.encode(&b_pub)
);
println!("");
println!("salt: {:?} iterations: {:?}", base64::encode(&salt), iters);
println!(
"salt: {:?} iterations: {:?}",
general_purpose::STANDARD.encode(&salt),
iters
);
let verifier: SrpClientVerifier<Sha256> = SrpClient::<Sha256>::process_reply(
&client,