11use std:: collections:: { HashMap , HashSet } ;
2+ use std:: future:: Future ;
23use std:: io:: Cursor ;
34use std:: num:: ParseIntError ;
45use std:: ops:: { Deref , DerefMut , Range } ;
@@ -23,8 +24,9 @@ use base64::Engine;
2324use prost:: Message ;
2425use reqwest:: header:: { HeaderMap , HeaderValue } ;
2526use reqwest:: { Certificate , Client , Proxy } ;
26- use serde:: de:: value;
27+ use serde:: de:: { DeserializeOwned , value} ;
2728use serde:: { Deserialize , Deserializer , Serialize , Serializer } ;
29+ use serde_json:: json;
2830use sha2:: { Digest , Sha256 } ;
2931use thiserror:: Error ;
3032use tokio:: select;
@@ -35,12 +37,12 @@ use uuid::Uuid;
3537use std:: io:: { Write , Read } ;
3638use std:: fmt:: { Display , Write as FmtWrite } ;
3739
38- use rand:: thread_rng;
40+ use rand:: { Rng , thread_rng} ;
3941use rand:: seq:: SliceRandom ;
4042use futures:: FutureExt ;
4143
4244use crate :: ids:: CompactECKey ;
43- use crate :: PushError ;
45+ use crate :: { APSConnection , OSConfig , PushError } ;
4446
4547pub const APNS_BAG : & str = "http://init-p01st.push.apple.com/bag" ;
4648pub const IDS_BAG : & str = "https://init.ess.apple.com/WebObjects/VCInit.woa/wa/getBag?ix=3" ;
@@ -453,11 +455,51 @@ impl CarrierAddress {
453455#[ serde( rename_all = "PascalCase" ) ]
454456struct Carrier {
455457 phone_number_registration_gateway_address : CarrierAddress ,
458+ carrier_entitlements : CarrierEntitlements ,
456459}
457460
458461const CARRIER_CONFIG : & str = "https://itunes.apple.com/WebObjects/MZStore.woa/wa/com.apple.jingle.appserver.client.MZITunesClientCheck/version?languageCode=en" ;
459462
460- pub async fn get_gateways_for_mccmnc ( mccmnc : & str ) -> Result < String , PushError > {
463+
464+ #[ derive( Deserialize ) ]
465+ #[ serde( rename_all = "PascalCase" ) ]
466+ pub struct CarrierEntitlements {
467+ server_address : String ,
468+ user_agent : Option < String > ,
469+ protocol_version : Option < String > ,
470+ }
471+
472+ impl CarrierEntitlements {
473+ async fn invoke ( & self , requests : & [ serde_json:: Value ] , config : & dyn OSConfig ) -> Result < Vec < serde_json:: Value > , PushError > {
474+ let user_agent = self . user_agent . as_ref ( ) . map ( |i| i. as_str ( ) ) . unwrap_or ( "Entitlement/$version ($device) iOS/$iOSVersion ($build) Carrier Settings/$carrierBundleVersion" )
475+ . replace ( "$version" , "2" )
476+ . replace ( "$device" , "iPhone" )
477+ . replace ( "$iOSVersion" , & config. get_debug_meta ( ) . user_version )
478+ . replace ( "$build" , & config. get_register_meta ( ) . software_version ) ;
479+
480+ let value = gzip_normal ( & serde_json:: to_vec ( requests) ?) ?;
481+
482+ Ok ( REQWEST . post ( & self . server_address )
483+ . header ( "Content-Type" , "application/json" )
484+ . header ( "x-country-iso-code" , "us" )
485+ . header ( "Accept" , "application/json" )
486+ . header ( "Content-Encoding" , "gzip" )
487+ . header ( "User-Agent" , format ! ( "{} Carrier Settings/50.0.2" , user_agent) )
488+ . header ( "Accept-Language" , "en" )
489+ . header ( "Accept-Encoding" , "gzip" )
490+ . header ( "x-protocol-version" , self . protocol_version . as_ref ( ) . map ( |i| i. as_str ( ) ) . unwrap_or ( "2" ) )
491+ . body ( value)
492+ . send ( ) . await ?
493+ . json ( ) . await ?)
494+ }
495+ }
496+
497+ pub struct CarrierConfig {
498+ pub gateway : String ,
499+ carrier : CarrierEntitlements ,
500+ }
501+
502+ pub async fn get_gateways_for_mccmnc ( mccmnc : & str ) -> Result < CarrierConfig , PushError > {
461503 let data = REQWEST . get ( CARRIER_CONFIG )
462504 . send ( ) . await ?;
463505
@@ -482,13 +524,132 @@ pub async fn get_gateways_for_mccmnc(mccmnc: &str) -> Result<String, PushError>
482524 let mut out = vec ! [ ] ;
483525 archive. by_name ( & carrier. to_string ( ) ) . unwrap ( ) . read_to_end ( & mut out) ?;
484526
527+ info ! ( "here {:?}" , plist:: from_bytes:: <Value >( & out) ?) ;
528+
485529 let parsed_file: Carrier = plist:: from_bytes ( & out) ?;
486- return Ok ( parsed_file. phone_number_registration_gateway_address . vec ( ) . choose ( & mut thread_rng ( ) ) . ok_or ( PushError :: CarrierNotFound ) ?. clone ( ) )
530+ return Ok ( CarrierConfig {
531+ gateway : parsed_file. phone_number_registration_gateway_address . vec ( ) . choose ( & mut thread_rng ( ) ) . ok_or ( PushError :: CarrierNotFound ) ?. clone ( ) ,
532+ carrier : parsed_file. carrier_entitlements ,
533+ } )
487534 }
488535
489536 Err ( PushError :: CarrierNotFound )
490537}
491538
539+ #[ derive( Serialize , Deserialize ) ]
540+ pub struct EntitlementAuthState {
541+ device_account_identifier : String ,
542+ unique_id : String ,
543+ token : Option < String > ,
544+ subscriber : String ,
545+ mccmnc : String ,
546+ }
547+
548+ fn find_entitlement_result < T : DeserializeOwned > ( entitlements : & [ serde_json:: Value ] , id : u64 ) -> Result < T , PushError > {
549+ let entitlement = entitlements. iter ( ) . find ( |i| {
550+ let serde_json:: Value :: Object ( o) = i else { return false } ;
551+ let Some ( serde_json:: Value :: Number ( n) ) = o. get ( "response-id" ) else { return false } ;
552+ n. as_u64 ( ) . expect ( "not u64" ) == id
553+ } ) . expect ( "Entitlement response not found!" ) ;
554+ Ok ( serde_json:: from_value ( entitlement. clone ( ) ) ?)
555+ }
556+
557+ #[ derive( Deserialize , Serialize , Debug ) ]
558+ #[ serde( rename_all = "kebab-case" ) ]
559+ pub struct PhoneNumberResponse {
560+ pub phone_number : String ,
561+ pub signature : String ,
562+ }
563+
564+ #[ derive( Deserialize , Serialize , Debug ) ]
565+ pub struct EntitlementsResponse {
566+ pub phone : PhoneNumberResponse ,
567+ pub host : String ,
568+ }
569+
570+ impl EntitlementAuthState {
571+
572+ pub fn new ( subscriber : String , mccmnc : String , imei : String ) -> Self {
573+ Self {
574+ device_account_identifier : Uuid :: new_v4 ( ) . to_string ( ) . to_uppercase ( ) ,
575+ unique_id : imei,
576+ subscriber,
577+ mccmnc,
578+ token : None ,
579+ }
580+ }
581+
582+ pub async fn get_entitlements < Fut : Future < Output = Result < String , PushError > > > ( & mut self , config : & dyn OSConfig , aps : & APSConnection , process_challenge : impl FnOnce ( String ) -> Fut ) -> Result < EntitlementsResponse , PushError > {
583+ let entitlements = get_gateways_for_mccmnc ( & self . mccmnc ) . await ?. carrier ;
584+
585+ let mut starting_id = rand:: thread_rng ( ) . gen_range ( 3 ..5 ) ;
586+
587+ let auth_challenge = if let Some ( token) = & self . token {
588+ json ! ( {
589+ "device-account-identifier" : & self . device_account_identifier,
590+ "auth-type" : "EAP-AKA" ,
591+ "action-name" : "getAuthentication" ,
592+ "subscriber-id" : base64_encode( & [ b"\x02 \x00 \x00 ;\x01 " , self . subscriber. as_bytes( ) ] . concat( ) ) ,
593+ "request-id" : starting_id,
594+ "unique-id" : & self . unique_id,
595+ "token" : token,
596+ } )
597+ } else {
598+ let challenge = entitlements. invoke ( & [
599+ json ! ( {
600+ "device-account-identifier" : & self . device_account_identifier,
601+ "auth-type" : "EAP-AKA" ,
602+ "action-name" : "getAuthentication" ,
603+ "subscriber-id" : base64_encode( & [ b"\x02 \x00 \x00 ;\x01 " , self . subscriber. as_bytes( ) ] . concat( ) ) ,
604+ "request-id" : starting_id,
605+ "unique-id" : & self . unique_id
606+ } )
607+ ] , config) . await ?;
608+
609+ #[ derive( Deserialize ) ]
610+ struct ChallengeResponse {
611+ challenge : String ,
612+ }
613+ let result: ChallengeResponse = find_entitlement_result ( & challenge, starting_id) ?;
614+
615+ let response = process_challenge ( result. challenge ) . await ?;
616+
617+ starting_id = rand:: thread_rng ( ) . gen_range ( 10 ..13 ) ;
618+
619+ json ! ( {
620+ "payload" : response,
621+ "action-name" : "postChallenge" ,
622+ "request-id" : starting_id,
623+ } )
624+ } ;
625+
626+ let response = entitlements. invoke ( & [
627+ auth_challenge,
628+ json ! ( {
629+ "client-nonce" : base64_encode( & aps. get_token( ) . await ) ,
630+ "action-name" : "getPhoneNumber" ,
631+ "request-id" : starting_id + 1 ,
632+ } )
633+ ] , config) . await ?;
634+
635+ #[ derive( Deserialize ) ]
636+ #[ serde( rename_all = "kebab-case" ) ]
637+ struct AuthResponse {
638+ token : String ,
639+ }
640+
641+ let auth: AuthResponse = find_entitlement_result ( & response, starting_id) ?;
642+ self . token = Some ( auth. token ) ;
643+
644+ let phone: PhoneNumberResponse = find_entitlement_result ( & response, starting_id + 1 ) ?;
645+
646+ Ok ( EntitlementsResponse {
647+ phone,
648+ host : entitlements. server_address . clone ( )
649+ } )
650+ }
651+ }
652+
492653
493654
494655pub trait Resource : Send + Sync + Sized {
0 commit comments