
    =[j                       U d Z ddlmZ ddlZddlZddlZddlZddlZddlZddl	Z	ddl
Z
ddlZddlZddlmZ ddlmZmZ ddlmZ ddlmZmZmZmZmZ ddlmZ dd	lmZmZ dd
l m!Z! ddl"m#Z#m$Z$m%Z%m&Z&m'Z'm(Z(m)Z) ddl*m+Z+m,Z, ddl-m.Z.  ej/        e0          Z1dZ2dZ3dZ4dZ5dZ6dZ7dZ8dZ9dZ:dZ;dZ<dZ=dZ>dZ?dZ@ ejA        d          B                    d          ZCdZDdZEddd ZFd!eGd"<   d#ZHd$ZId%ZJd&ZK ed'(           G d) d*                      ZL G d+ d,e#          ZM ed'(           G d- d.                      ZNe G d/ d0                      ZO ed'(           G d1 d2                      ZP ed'(           G d3 d4                      ZQ ed'(           G d5 d6                      ZR ed'(           G d7 d8                      ZS ed'(           G d9 d:                      ZT ed'(           G d; d<                      ZU ed'(           G d= d>                      ZV G d? d@e          ZW G dA dBeX          ZY G dC dDe          ZZddHZ[ddLZ\ddPZ]ddSZ^ddTZ_ddUZ`ddVZaddXZbddZZcdd[Zddd\Zedd]Zfdd^ZgddbZhddcZiddfZjddiZkddjZlddnZmddqZnddrZoddsZpdduZqddvdwdd{Zrdd}Zsdd~ZtddZu G d d          Zv G d d          ZwdS )z=Internal helpers for Apple's HSA2 trusted-device bridge flow.    )annotationsN)Error)	dataclassfield)
HTMLParser)AnyCallableMappingOptionalProtocol)urlparse)hashesserialization)ec)	BaseModel
ConfigDictField	StrictInt	StrictStrValidationErrorfield_validator)$PyiCloudTrustedDevicePromptException*PyiCloudTrustedDeviceVerificationException)TrustedDeviceBridgeProverz/bridge/step/0z/bridge/step/{step}z/bridge/code/validateiQ       	   
            s   s   doneasciiz$258EAFA5-E914-47DA-95CA-C5AB0DC85B11g      >@zwebsocket.push.apple.comz websocket.sandbox.push.apple.com)prodsandboxdict[str, str]WEBSOCKET_ENVIRONMENT_HOSTS      i  i  T)frozenc                      e Zd ZU dZdZded<   dZded<   dZd	ed
<    ee	          Z
ded<    ee	          Zded<   dZded<   edd            ZddZdS )Hsa2BootContextzABridge-related HSA2 boot data parsed from Apple's HTML bootstrap. strauth_initial_routeFboolhas_trusted_devices tuple[str, ...]auth_factorsdefault_factorydict[str, Any]bridge_initiate_dataphone_number_verificationNOptional[str]source_app_idauth_optionsMapping[str, Any]return'Hsa2BootContext'c           	        |                     d          }t          |t                    si }|                     d          }t          |t                    s|                     d          }t          |t                    si }|                     d          }t          |t                    sg }|                     d          }|t	          |          } | t	          |                     d          pd          t          |                     d                    t          d	 |D                       t          |          t          |          |
          S )zBBuild a normalized boot context from Apple's auth-options payload.bridgeInitiateDataphoneNumberVerificationauthFactorssourceAppIdNauthInitialRouter,   hasTrustedDevicesc              3  D   K   | ]}t          |t                    |V  d S N
isinstancer-   .0factors     \/home/thesage/.hermes/hermes-agent/venv/lib/python3.11/site-packages/pyicloud/hsa2_bridge.py	<genexpr>z4Hsa2BootContext.from_auth_options.<locals>.<genexpr>j   sE        !Z5L5L         r.   r0   r3   r7   r8   r:   )getrI   dictlistr-   r/   tuple)clsr;   r7   r8   r3   r:   s         rM   from_auth_optionsz!Hsa2BootContext.from_auth_optionsP   sv     ,//0DEE.55 	&#% $0$4$45N$O$O!3T:: 	(<(@(@)) )% 3T:: 	+(*%#''66,-- 	L$((77$..Ms"<#3#34F#G#G#M2NN $\%5%56I%J%J K K  %1     "&&:!;!;&*+D&E&E'	
 	
 	
 		
rO   c                f   | j         | j        t          | j                  d}| j        rt          | j                  |d<   | j        rXt          | j                  |d<   | j                            d          }t          |t
                    rt          |          |d<   | j	        
| j	        |d<   |S )z?Return parsed boot data in the shape expected by the auth flow.)rD   rE   rB   r@   rA   trustedPhoneNumberNrC   )
r.   r0   rS   r3   r7   rR   r8   rQ   rI   r:   )self	auth_datatrusted_phone_numbers      rM   as_auth_datazHsa2BootContext.as_auth_datar   s     !% 7!%!9 122%
 %
	
 $ 	N.243L.M.MI*+) 	M378V3W3WI/0#'#A#E#E$$ $  .55 M267K2L2L	./)'+'9Im$rO   )r;   r<   r=   r>   r=   r6   )__name__
__module____qualname____doc__r.   __annotations__r0   r3   r   rR   r7   r8   r:   classmethodrV   r\   r1   rO   rM   r+   r+   E   s         KK      %%%%%$&L&&&&+05+F+F+FFFFF05d0K0K0KKKKK#'M''''
 
 
 [
B     rO   r+   c                     e Zd ZU dZ eddd          Z edd          Zded	<    edd
          Z	ded<    edd          Z
ded<    edd          Zded<   dZded<   dZded<   dZded<   dZded<   dZded<   dZded<    edd          Zded<    edd          Zded<    ed	d          ed$d                         Z edddddd          ed$d!                        Z ed          ed%d#                        ZdS )&_BridgePushPayloadModelz7Strict validator for Apple's bridge push JSON envelope.allowT)extrapopulate_by_namearbitrary_types_allowedNsessionUUID)defaultaliaszOptional[StrictStr]session_uuidflowidflow_idnextStepzOptional[StrictStr | StrictInt]	next_step	ruiURLKeyr9   rui_url_keytxnidsaltmididmsdatar   akdatadataencryptedCodeencrypted_coder   zOptional[StrictInt]
error_codevaluer=   c                P    |#|                                 st          d          |S )z(Reject blank bridge session identifiers.Nz$sessionUUID/flowid must not be blankstrip
ValueErrorrU   r}   s     rM   _validate_session_uuidz._BridgePushPayloadModel._validate_session_uuid   s+     U[[]]CDDDrO   c                P    |#|                                 st          d          |S )z7Reject present-but-blank optional bridge string fields.Nz(Bridge payload strings must not be blankr   r   s     rM   $_validate_optional_non_empty_stringsz<_BridgePushPayloadModel._validate_optional_non_empty_strings   s+     U[[]]GHHHrO   Optional[str | int]c                v    t          |t                    r#|                                st          d          |S )z>Reject blank next-step markers while allowing ints or strings.znextStep must not be blank)rI   r-   r   r   r   s     rM   _validate_next_stepz+_BridgePushPayloadModel._validate_next_step   s9     eS!! 	;%++-- 	;9:::rO   )r}   r9   r=   r9   )r}   r   r=   r   )r^   r_   r`   ra   r   model_configr   rm   rb   ro   rq   rs   rt   ru   rv   rw   rx   ry   r{   r|   r   rc   r   r   r   r1   rO   rM   re   re      s        AA: $  L ).d-(P(P(PLPPPP#(5X#F#F#FGFFFF16t:1V1V1VIVVVV!&t;!G!G!GKGGGG!%E%%%% $D$$$$#C####$(H((((F $D$$$$*/%O*T*T*TNTTTT&+eD&E&E&EJEEEE_^Y//   [ 0/ _     [  _[!!   [ "!  rO   re   c                      e Zd ZU dZded<   ded<   dZded<   dZded	<   dZded
<   dZded<   dZ	ded<   dZ
ded<   dZded<   dZded<   dZded<   dZded<   edd            ZdS )BridgePushPayloadzHDecoded bridge push metadata needed to bootstrap trusted-device prompts.r6   payloadr-   rm   Nr9   rq   rs   rt   ru   rv   rw   r   rx   ry   r{   Optional[int]r|   r=   'BridgePushPayload'c                z   	 t                               |          }n"# t          $ r}t          d          |d}~ww xY w|j        p|j        }|st          d           | |||j        t          |j                  nd|j        |j	        |j
        |j        |j        |j        |j        |j        |j                  S )z7Validate and normalize one decoded bridge push payload.z-Malformed trusted-device bridge push payload.NzATrusted-device bridge push payload is missing sessionUUID/flowid.)r   rm   rq   rs   rt   ru   rv   rw   rx   ry   r{   r|   )re   model_validater   r   rm   ro   rq   r-   rs   rt   ru   rv   rw   rx   ry   r{   r|   )rU   r   	validatedexcresolved_session_uuids        rM   from_payloadzBridgePushPayload.from_payload   s    	/>>wGGII 	 	 	6? 	 !* 6 K):K$ 	6S   s.,5,?,KI'(((QU!-/'#$3 +
 
 
 	
s    
<7<)r   r6   r=   r   )r^   r_   r`   ra   rb   rq   rs   rt   ru   rv   rw   rx   ry   r{   r|   rc   r   r1   rO   rM   r   r      s         RR#I####!%K%%%%EDC"H""""FD$(N(((( $J$$$$
 
 
 [
 
 
rO   r   c                  ,   e Zd ZU dZded<   ded<   ded<   ded<   ded<   d	ed
<   dZded<   dZded<   dZded<    ee	          Z
ded<   dZded<   dZded<   dZded<   dZded<   dZded<   dZded<   dZded<   dZded<   d#d Zed$d"            ZdS )%TrustedDeviceBridgeStatez&Ephemeral trusted-device bridge state.r-   connection_path
push_tokenrm   zOptional[_WebSocketLike]	websockettopicr%   topics_by_hashNr9   r:   rq   rs   r4   r6   push_payloadrt   ru   rv   rw   r   rx   ry   r{   r   r|   r   r=   Nonec                @   t          |j                  | _        |j        | _        |j        | _        |j        | _        |j        | _        |j        | _        |j        | _        |j	        | _	        |j
        | _
        |j        | _        |j        | _        |j        | _        dS )zCPersist the latest bridge push metadata in the live bridge session.N)rR   r   r   rm   rq   rs   rt   ru   rv   rw   rx   ry   r{   r|   )rY   r   s     rM   apply_push_payloadz+TrustedDeviceBridgeState.apply_push_payload  s     !!566(5%/'3!'
 %	#$-") %	*9&1rO   r/   c                ^    t          | j        o| j                            d                    S )zIReturn whether Apple routed this bridge challenge to the legacy verifier._W)r/   rt   endswithrY   s    rM   #uses_legacy_trusted_device_verifierz<TrustedDeviceBridgeState.uses_legacy_trusted_device_verifier  s)     DJ<4:#6#6t#<#<===rO   )r   r   r=   r   )r=   r/   )r^   r_   r`   ra   rb   r:   rq   rs   r   rR   r   rt   ru   rv   rw   rx   ry   r{   r|   r   propertyr   r1   rO   rM   r   r      sk        00OOO''''JJJ""""#'M''''#I####!%K%%%%#(5#>#>#>L>>>>EDC"H""""FD$(N(((( $J$$$$2 2 2 2  > > > X> > >rO   r   c                  `    e Zd ZU dZded<   ded<   ded<   ded<   dZd	ed
<   dZded<   ddZdS )BridgeStepRequestz5Typed request body for Apple's bridge step endpoints.r-   rm   ry   r   intrq   Nr9   rw   r   rx   r=   r6   c                    | j         | j        | j        | j        d}| j        
| j        |d<   | j        ?t          | j        t                    rt          j	        | j        d          n| j        |d<   |S )z6Serialize the step request into Apple's JSON envelope.)rj   ry   ptknrp   Nrw   ),:)
separatorsrx   )
rm   ry   r   rq   rw   rx   rI   rR   jsondumpsrY   r   s     rM   as_jsonzBridgeStepRequest.as_json.  s      ,IO	#
 #
 =$"&-GJ;" dk400!
4;:>>>>[ H
 rO   r]   )r^   r_   r`   ra   rb   rw   rx   r   r1   rO   rM   r   r   #  sy         ??IIIOOONNN"H""""F     rO   r   c                  0    e Zd ZU dZded<   ded<   d	dZdS )
BridgeCodeValidateRequestzETyped request body for Apple's final bridge code validation endpoint.r-   rm   coder=   r%   c                     | j         | j        dS )z8Serialize the final bridge code-validation request body.)rj   r   rm   r   r   s    rM   r   z!BridgeCodeValidateRequest.as_jsonH  s      ,I
 
 	
rO   N)r=   r%   )r^   r_   r`   ra   rb   r   r1   rO   rM   r   r   A  sE         OOIII
 
 
 
 
 
rO   r   c                  >    e Zd ZU dZdZded<   dZded<   dZd	ed
<   dS )_ConnectionResponsez<Decoded server response for the initial websocket bootstrap.r,   r-   push_token_b64r   r   statusNr   server_timestamp_seconds)r^   r_   r`   ra   r   rb   r   r   r1   rO   rM   r   r   P  sH         FFNFOOOO.2222222rO   r   c                  2    e Zd ZU dZded<   ded<   ded<   dS )_PushMessagez8Decoded APNS-style push frame from the bridge websocket.bytesr   r   
message_idr   N)r^   r_   r`   ra   rb   r1   rO   rM   r   r   Y  s1         BBLLLOOONNNNNrO   r   c                  L    e Zd ZU dZdZded<   dZded<   dZded<   dZded	<   d
S )_ChannelSubscriptionResponsez:Decoded response to the bridge topic subscription request.r   r   r   r   retry_interval_secondsr1   r2   topicsN)	r^   r_   r`   ra   r   rb   r   r   r   r1   rO   rM   r   r   b  sY         DDJFOOOO"##### F      rO   r   c                  6    e Zd ZU dZded<   ded<   dZded<   dS )	_AcknowledgementMessagez?Decoded acknowledgment frame emitted by Apple's bridge service.r   r   r   r   r   delivery_statusN)r^   r_   r`   ra   rb   r   r1   rO   rM   r   r   l  s;         IILLLOOOOrO   r   c                  Z    e Zd ZU dZdZded<   dZded<   dZded<   dZd	ed
<   dZ	ded<   dS )_ServerMessagezFOne websocket frame decoded into its known top-level message variants.NzOptional[_ConnectionResponse]connection_responsezOptional[_PushMessage]push_messagez&Optional[_ChannelSubscriptionResponse]channel_subscription_responsez!Optional[_AcknowledgementMessage]push_acknowledgmentr1   ztuple[int, ...]field_numbers)
r^   r_   r`   ra   r   rb   r   r   r   r   r1   rO   rM   r   r   u  sp         PP9=====+/L////LP!PPPP=AAAAA%'M''''''rO   r   c                  *    e Zd ZdZd
dZddZddZd	S )_WebSocketLikezFProtocol for the minimal websocket operations used by the bridge flow.r   r   r=   r   c                    dS )z"Send one binary websocket message.Nr1   r   s     rM   send_binaryz_WebSocketLike.send_binary        rO   c                    dS )z,Read one complete websocket message payload.Nr1   r   s    rM   read_messagez_WebSocketLike.read_message  r   rO   c                    dS )zClose the websocket transport.Nr1   r   s    rM   closez_WebSocketLike.close  r   rO   Nr   r   r=   r   r=   r   r=   r   )r^   r_   r`   ra   r   r   r   r1   rO   rM   r   r     sV        PP1 1 1 1; ; ; ;- - - - - -rO   r   c                  $     e Zd ZdZd fdZ xZS )_InvalidNonceErrorzFSignal Apple's INVALID_NONCE response along with the server timestamp.server_timestamp_msr   r=   r   c                X    t                                          d           || _        dS )z9Capture the server timestamp returned with INVALID_NONCE.z!Invalid nonce from bridge server.N)super__init__r   )rY   r   	__class__s     rM   r   z_InvalidNonceError.__init__  s*    <===#6   rO   )r   r   r=   r   )r^   r_   r`   ra   r   __classcell__r   s   @rM   r   r     sC        PP7 7 7 7 7 7 7 7 7 7rO   r   c                  T     e Zd ZdZd fdZedd            Zdd
ZddZddZ	 xZ
S )_BootArgsHTMLParserz8Extract the JSON body from Apple's boot_args script tag.r=   r   c                r    t                                                       d| _        d| _        g | _        dS )zDInitialize parser state for the first matching boot_args script tag.FN)r   r   _collecting_found_chunks)rY   r   s    rM   r   z_BootArgsHTMLParser.__init__  s3     "$rO   r-   c                Z    d                     | j                                                  S )z)Return the collected boot_args JSON text.r,   )joinr   r   r   s    rM   r   z_BootArgsHTMLParser.payload  s$     wwt|$$**,,,rO   tagattrslist[tuple[str, Optional[str]]]c                    |dk    s| j         rdS d |D             }|                    d          pd                                }d|v rd| _        d| _         dS dS )z=Start collecting data when the boot_args script tag is found.scriptNc                    i | ]\  }}||	S r1   r1   )rK   keyr}   s      rM   
<dictcomp>z7_BootArgsHTMLParser.handle_starttag.<locals>.<dictcomp>  s    777:3C777rO   classr,   	boot_argsT)r   rQ   splitr   )rY   r   r   attr_mapclassess        rM   handle_starttagz#_BootArgsHTMLParser.handle_starttag  sq    (??dk?F77777<<((.B5577'!!#DDKKK "!rO   c                6    |dk    r| j         rd| _         dS dS dS )z3Stop collecting when the current script tag closes.r   FN)r   )rY   r   s     rM   handle_endtagz!_BootArgsHTMLParser.handle_endtag  s+    (??t/?$D ???rO   ry   c                L    | j         r| j                            |           dS dS )z9Append script contents while the boot_args tag is active.N)r   r   append)rY   ry   s     rM   handle_dataz_BootArgsHTMLParser.handle_data  s3     	&L%%%%%	& 	&rO   r   r=   r-   )r   r-   r   r   r=   r   )r   r-   r=   r   )ry   r-   r=   r   )r^   r_   r`   ra   r   r   r   r   r   r   r   r   s   @rM   r   r     s        BB% % % % % % - - - X-   % % % %
& & & & & & & &rO   r   	html_textr-   r=   c           	     H   t                      }|                    |            |                                 |j        }|st	          d          	 t          j        |          }n'# t
          j        $ r}t	          d          |d}~ww xY w|                    d          }t          |t                    st	          d          |                    d          }t          |t                    si }|                    d          }t          |t                    si }|                    d          }t          |t                    si }|                    d	          }	t          |	t                    sg }	|                    d
          }
|
t          |
          }
t          t          |                    d          pd          t          |                    d                    t          d |	D                       t          |          t          |          |
          S )zEExtract HSA2 boot args from the HTML returned by GET /appleauth/auth.zMissing HSA2 boot args payload.z!Malformed HSA2 boot args payload.NdirectzMissing HSA2 direct boot data.twoSVr@   rA   rB   rC   rD   r,   rE   c              3  D   K   | ]}t          |t                    |V  d S rG   rH   rJ   s     rM   rN   z'parse_boot_args_html.<locals>.<genexpr>  sE       
 
FC1H1H

 
 
 
 
 
rO   rP   )r   feedr   r   r   r   loadsJSONDecodeErrorrQ   rI   rR   rS   r-   r+   r/   rT   )r   parserpayload_textr   r   r  two_svr7   r8   r3   r:   s              rM   parse_boot_args_htmlr
    s2    !""F
KK	
LLNNN>L V23TUUU*\**   2/
 
	 [[""Ffd## U23STTTZZ  Ffd## !::&:;;*D11 "! 4 8 89R S S/66 '$&!::m,,LlD)) JJ}--M M**vzz*<==CDD ,?!@!@AA 
 
!-
 
 
 
 
 ""677"&'@"A"A#	 	 	 	s   A& &B
5BB
r}   r   r   c                    | dk     rt          d          t                      }	 | dz  }| dz  } | r|                    |dz             n$|                    |           t          |          S J)z#Encode an unsigned protobuf varint.r   z#Negative varints are not supported.T   r!      )r   	bytearrayr   r   )r}   partsto_writes      rM   _encode_varintr    s~    qyy>???KKE 4<! 	 LLD))))LL"""<< rO   ry   offsettuple[int, int]c                    d}d}|}	 |t          |           k    rt          d          | |         }|dz  }||dz  |z  z  }|dz  s||fS |dz  }|dk    s	||z
  d	k    rt          d
          g)zHDecode one protobuf varint from a byte string and return the new offset.r   TzTruncated protobuf varint.r   r  r  r!   ?   r   zMalformed protobuf varint.)lenr   )ry   r  r}   shiftstart_offsetbytes         rM   _read_varintr    s    EELUSYY67STTTF|!$+%''t 	!&= 
 2::,."4467STTTUrO   field_number	wire_typec                2    t          | dz  |z            |z   S )z-Encode one protobuf field header and payload.r    )r  )r  r  r}   s      rM   _encode_fieldr    s    <1,	9::UBBrO   c           	     ^    t          | dt          t          |                    |z             S )z)Encode a length-delimited protobuf field.r   )r  r  r  r  r}   s     rM   _encode_bytes_fieldr!    s'    q.U*D*Du*LMMMrO   c                H    t          | |                    d                    S )z%Encode a UTF-8 string protobuf field.utf-8)r!  encoder   s     rM   _encode_string_fieldr%    s    |U\\'-B-BCCCrO   c                >    t          | dt          |                    S )z*Encode an unsigned integer protobuf field.r   )r  r  r   s     rM   _encode_uint32_fieldr'  !  s    q.*?*?@@@rO   dict[int, list[Any]]c                   d}i }|t          |           k     rt          | |          \  }}|dz	  }|dz  }|dk    rt          | |          \  }}n_|dk    rGt          | |          \  }}||z   }|t          |           k    rt          d          | ||         }|}nt          d|           |                    |g                               |           |t          |           k     |S )z@Decode a minimal subset of protobuf wire types into field lists.r   r    r!   r   zTruncated protobuf field.z Unsupported protobuf wire type: )r  r  r   
setdefaultr   )	ry   r  fieldsr   r  r  r}   length
end_offsets	            rM   _decode_fieldsr.  &  s   F#%F
3t99

"400Vax$J	>>(v66ME66!^^)$77NFF  &JCII%%:;VWWW
*+EFF6>9>>   	,++225999+ 3t99

, MrO   messagec                   t          |           }d}|                    d          rE	 |d         d                             d          }n"# t          $ r}t	          d          |d}~ww xY wt          |                    ddg          d                   }d}|                    d          rt          |d         d                   }t          |||	          S )
z1Decode the server's websocket bootstrap response.r,   r   r   r"   z0Malformed bridge connection response push token.Nr   r    )r   r   r   )r.  rQ   decodeUnicodeDecodeErrorr   r   r   )r/  r+  r   r   r   r   s         rM   _decode_connection_responser3  C  s    G$$FNzz!}} 	#AYq\0099NN! 	 	 	6B 	 As##A&''F#zz!}} 5#&vay|#4#4 %!9   s   !A
 

A)A$$A)c                @   t          |           }t          |                    ddg          d                   }t          |                    ddg          d                   }t          |                    ddg          d                   }t	          |||          S )z9Decode one push-delivery frame from the bridge websocket.r   rO   r   r      )r   r   r   )r.  r   rQ   r   r   )r/  r+  r   r   r   s        rM   _decode_push_messager6  Y  s    G$$F&**Q&&q)**EVZZA3''*++JFJJq3%((+,,Ge
GLLLLrO   c           
        t          |           }g }|                    d          }|rt          t          |d                             }|                    dg           D ]y}t          t          |                    }|                    ddg          d         }t          |t                    r)|                    |                    dd                     zt          t          |                    ddg          d                   t          |                    ddg          d                   t          |                    ddg          d                   t          d	 |D                       
          S )z?Decode the server's response to the topic subscription message.r   r   rO   r#  ignorer   r    r5  c              3     K   | ]}||V  	d S rG   r1   rK   r   s     rM   rN   z8_decode_channel_subscription_response.<locals>.<genexpr>v  s'      88u%8U888888rO   )r   r   r   r   )	r.  rQ   r   rI   r   r1  r   r   rT   )r/  r+  r   payload_valuespayload_fieldsapp_response_valueapp_response_fieldstopic_values           rM   %_decode_channel_subscription_responser@  b  sd    G$$FFZZ]]N E'nQ.?(@(@AA"0"4"4Q";"; 	E 	E"07I1J1J"K"K-11!cU;;A>K+u-- Ek00(CCDDD'vzz!aS))!,--6::a!%%a())"6::a!#5#5a#8998888888	   rO   c                @   t          |           }t          |                    ddg          d                   }t          |                    ddg          d                   }t          |                    ddg          d                   }t	          |||          S )z=Decode a push acknowledgment frame from the bridge websocket.r   rO   r   r   r    )r   r   r   )r.  r   rQ   r   r   )r/  r+  r   r   r   s        rM   _decode_acknowledgement_messagerB  z  s    G$$F&**Q&&q)**EVZZA3''*++J&**Q,,Q/00O"'   rO   c                   t          |           }d}|                    t                    r-t          t	          |t                   d                             }d}|                    t
                    r-t          t	          |t
                   d                             }d}|                    t                    r-t          t	          |t                   d                             }d}|                    t                    r-t          t	          |t                   d                             }t          ||||t          t          |                              S )zDDecode all known top-level messages embedded in one websocket frame.Nr   )r   r   r   r   r   )r.  rQ   "SERVER_MESSAGE_CONNECTION_RESPONSEr3  r   SERVER_MESSAGE_PUSHr6  ,SERVER_MESSAGE_CHANNEL_SUBSCRIPTION_RESPONSEr@  SERVER_MESSAGE_PUSH_ACKrB  r   rT   sorted)r/  r+  r   r   r   r   s         rM   _decode_server_messagerI    sG   G$$Fzz455 
9&;<Q?@@
 
 Lzz%&& S+E&9L2Ma2P,Q,QRR$(!zz>?? 
(M&EFqIJJ)
 )
% zz)** 
=&01!455
 
 /!&C/F6NN++   rO   
public_keynonce	signaturec                   d                     t          d|           t          d|          t          dt          |                    t          dt          dt                              g          }t          d|          S )z6Encode the initial bridge websocket bootstrap message.rO   r   r   r       )r   r!  _encode_bridge_signaturer'  !NEW_CONNECTION_EXPIRATION_SECONDS)rJ  rK  rL  connection_messages       rM   _encode_connection_messagerR    s~     :..5))#;I#F#FGG'+LMM 		
	 	 q"4555rO   c                N    |                      t                    r| S t          | z   S )zEWrap the DER ECDSA signature using Apple's bridge signature envelope.)
startswithBRIDGE_SIGNATURE_PREFIX)rL  s    rM   rO  rO    s,     344 "Y..rO   allowed_topics	list[str]c                `    d                     d | D                       }t          d|          S )z@Encode the topic subscription message sent after bridge connect.rO   c              3  6   K   | ]}t          d |          V  dS )r   N)r%  r:  s     rM   rN   z-_encode_web_filter_message.<locals>.<genexpr>  s>        +0Q&&     rO   r    )r   r!  )rV  filter_payloads     rM   _encode_web_filter_messager[    sC    XX  4B    N q.111rO   r   r   c                    d                     t          d|           t          d|          g          }t          d|          S )z?Encode the acknowledgment frame for one delivered push message.rO   r   r   )r   r!  r'  )r   r   ack_payloads      rM   _encode_ack_messager^    sF    ((5)) J//	
 K q+...rO   c                t    t          j        |                     d                                                    S )z;Return Apple's websocket topic hash for a named APNS topic.r#  )hashlibsha1r$  	hexdigest)r   s    rM   _topic_hashrc    s*    <W--..88:::rO   topic_bytesr   Mapping[str, str]c                z    |                     |                                 |                     dd                    S )z=Resolve a hashed topic payload back to a readable topic name.r#  r8  )rQ   hexr1  )rd  r   s     rM   _topic_namerh    s2    koo//1C1CGX1V1VWWWrO   r   r6   c                   	 t          j        |                     d                    S # t          t           j        f$ r |                     dd          }Y nw xY w|                    d          }|dk    rd}d}d}t          ||d         |          D ]|\  }}|r|rd}n|dk    rd	}n|d
k    rd}|d
k    rd	}'|dk    r|dz  }3|dk    rC|dz  }|dk    r8	 t          j        |||dz                      c S # t           j        $ r Y  nw xY w}|                    d|dz             }|dk    t          d          )z<Extract the JSON object embedded in one bridge push payload.r#  r8  {r   FN)start\T"r   }z8Could not decode the trusted-device bridge push payload.)r   r  r1  r2  r  find	enumerater   )r   textrk  depth	in_stringescapedindex	characters           rM   _extract_json_payloadrw    s   1z'..11222 45 1 1 1~~gx001 IIcNNE
1**	 )$uvv,e D D D 	 	E9  &#GG$&&"GG#%% %IC 		c!!
c!!
A::#z$uuqy/@*ABBBBB/   		#uqy))1 1**4 /B  s!   &) ,AAC;;DDc                    	 t          j        |                     d          d                                          S # t          t
          f$ r}t	          d          |d}~ww xY w)z9Decode base64 bridge data and return it as lowercase hex.r"   Tvalidatez(Malformed base64-encoded bridge payload.N)base64	b64decoder$  rg  r   BinasciiError)r}   r   s     rM   _b64_to_hexr~    so    NW 5 5EEEIIKKK& N N NCDD#MNs   := A#AA#c                    t          j        t                              |                                         d          S )z/Encode hex bridge data as standard base64 text.r"   )r{  	b64encoder   fromhexr1  )r}   s    rM   _hex_to_b64r    s-    EMM%001188AAArO   timestamp_msc                b    d|                      ddd          z   t          j        d          z   S )z<Build the nonce format expected by Apple's bridge bootstrap.    r   bigF)signed)to_bytesosurandom)r  s    rM   _build_noncer    s0    \**1eE*BBBRZPQ]]RRrO   z<none>)prefixemptyr9   r  r  c               N    | s|S t          |           |k    r| S | d|          dS )zAShorten sensitive identifiers before logging them at debug level.Nz...)r  )r}   r  r  s      rM   _summarize_identifierr    s>      
5zzVGVGn!!!!rO   boot_contextc                   | j         }|                    d          }t          |t                    r?|r=d|v rt	          |          }|j        r|j        S |                    dd          d         S |                    d          }t          |t                    r|t          v rt          |         S t          d          )z@Resolve the websocket host Apple expects for the bridge session.webSocketUrl:///r   r   apnsEnvironmentz:Missing HSA2 websocket host for the trusted-device bridge.)	r7   rQ   rI   r-   r   hostnamer   r&   r   )r  bridge_dataweb_socket_urlparsedenvironments        rM   _resolve_websocket_hostr  $  s    3K __^44N.#&& /> /N""n--F '&##C++A..//"344K+s## 87R(R(R*;77
.D  rO   c                    | j                             d          }t          |t                    r|r|S t	          d          )z<Resolve the APNS topic Apple uses for trusted-device pushes.	apnsTopicz6Missing HSA2 APNS topic for the trusted-device bridge.)r7   rQ   rI   r-   r   )r  r   s     rM   _resolve_apns_topicr  8  sK    -11+>>E% % 
.@  rO   auth_endpointc                |    t          |           }|j        r|j        st          d          |j         d|j         S )z>Derive the websocket Origin header from the auth endpoint URL.z0Invalid auth endpoint for trusted-device bridge.r  )r   schemer  r   )r  r  s     rM   _derive_originr  C  sQ    m$$F= 
 
2>
 
 	
 m11111rO   c                  R    e Zd ZdZdd
ZddZddZddZddZddZ	d dZ
d!dZdS )"_RawWebSocketClientz7Minimal websocket client for Apple's webcourier bridge.urlr-   timeoutfloatorigin
user_agentr=   r   c                    || _         || _        || _        || _        t	                      | _        |                                 | _        dS )z=Open a websocket connection and prepare buffered frame reads.N)_url_timeout_origin_user_agentr  _buffer_open_socket)rY   r  r  r  r  s        rM   r   z_RawWebSocketClient.__init__P  s?     	% {{zz||rO   ssl.SSLSocketc           	     h   t          | j                  }|j        dk    s|j        st	          d| j                   |j        pd}|j        pd}|j        r| d|j         }t          j	        |j        |f| j
                  }t          j                    }|                    ||j                  }|                    | j
                   t          j        t#          j        d                                        d          }d	| d
d|j         ddd| j         d| j         dd| dg	}|                    d                    |                              d                     |                     |          }	|	                    d          \  }
}}d|
vrt	          d|
           i }|                    d          D ]]}|rd|vr	|                    dd          \  }}|                                ||                                                                <   ^t          j        t=          j        |t@          z                       d                    !                                                              d          }|"                    d          |k    rt	          d          |S )z=Perform the websocket HTTP upgrade and return the TLS socket.wsszUnsupported websocket URL: i  r  ?)server_hostname   r"   zGET z	 HTTP/1.1zHost: zUpgrade: websocketzConnection: UpgradezOrigin: zUser-Agent: zSec-WebSocket-Version: 13zSec-WebSocket-Key: z
z 101 zWebsocket upgrade failed: r   r   zsec-websocket-acceptz3Invalid websocket accept header from bridge server.)#r   r  r  r  r   portpathquerysocketcreate_connectionr  sslcreate_default_contextwrap_socket
settimeoutr{  r  r  r  r1  r  r  sendallr   r$  _read_http_response	partitionr   r   lowerr`  ra  WEBSOCKET_GUIDdigestrQ   )rY   r  r  resource
raw_socketcontextsecure_socketwebsocket_keyrequest_headersresponsestatus_line_headers_textheadersliner   r}   expected_accepts                     rM   r  z_RawWebSocketClient._open_  s   $)$$=E!!!69di99   {!c;%#< 	4"33V\33H-v.Et}UU
,..++J+XX  ///(B88??HH&8&&&&V_&& !%t|%%-4+--'1-11

 	fkk/::AA'JJKKK++M::'/'9'9&'A'A$Q+%%6:[::   #% &&v.. 	9 	9D 3d??C++JC+0;;==GCIIKK%%''(( *L-.8@@IIJJQQSS
 

&// 	 ;;-../AA6E   rO   sockc                V   d| j         vrI|                    d          }|st          d          | j                             |           d| j         vI| j                             d          dz   }t          | j         d|                                       d          }| j         d|= |S )zARead the HTTP upgrade response headers from the websocket socket.s   

   z*Unexpected EOF during websocket handshake.r5  Nz
iso-8859-1)r  recvr   extendro  r   r1  )rY   r  chunkmarkerry   s        rM   r  z'_RawWebSocketClient._read_http_response  s    --IIdOOE :@   L&&& -- "";//!3T\'6'*++22<@@L&!rO   sizer   r   c           	        t          | j                  |k     r| j                            t	          d|t          | j                  z
                      }|st          d          | j                            |           t          | j                  |k     t          | j        d|                   }| j        d|= |S )z?Read exactly ``size`` buffered bytes from the websocket socket.r  z-Unexpected EOF while reading websocket frame.N)r  r  r  r  maxr   r  r   )rY   r  r  ry   s       rM   _read_exactz_RawWebSocketClient._read_exact  s    $,$&&L%%c$s4<7H7H0H&I&IJJE :C   L&&& $,$&& T\%4%())L$rO   opcoder   c                b   d|z  }t          j        d          t          |          }t          |g          }|dk     r|                    d|z             n|dk     r>|                    d           |                    t          j        d|                     n=|                    d           |                    t          j        d|                     t          fd	t          |          D                       }| j
                            t          |          z   |z              d
S )z9Send one masked websocket frame to Apple's bridge server.r  r5  ~   i      !H   !Qc              3  :   K   | ]\  }}||d z           z  V  dS r5  Nr1   rK   ru  r  mask_keys      rM   rN   z2_RawWebSocketClient._send_frame.<locals>.<genexpr>  sF       
 
+65$D8EAI&&
 
 
 
 
 
rO   N)r  r  r  r  r   r  structpackr   rp  r  r  )rY   r  r   
first_byter,  headermasked_payloadr  s          @rM   _send_framez_RawWebSocketClient._send_frame  s,   F]
:a==WJ<((C<<MM$-((((e^^MM*%%%MM&+dF334444MM*%%%MM&+dF33444 
 
 
 
:CG:L:L
 
 
 
 
 	U6]]X5FGGGGGrO   c                <    |                      t          |           dS )z*Send one binary websocket message payload.N)r  OPCODE_BINARYr   s     rM   r   z_RawWebSocketClient.send_binary  s    00000rO   c                `  
 g }d}	 |                      d          \  }}|dz  }t          |dz            }t          |dz            }|dz  }|dk    r/t          j        d|                      d                    d	         }n4|dk    r.t          j        d
|                      d                    d	         }|r|                      d          nd
|                      |          }	|r(t	          
fdt          |	          D                       }	|t          k    rt          d          |t          k    r| 	                    t          |	           J|t          k    rW|d	k    r|}|                    |	           |r2|dt          fvrt          d|           d                    |          S )zDRead one complete websocket message, handling control frames inline.NTr      r  r  r  r  r   r  r   r5  rO   c              3  :   K   | ]\  }}||d z           z  V  dS r  r1   r  s      rM   rN   z3_RawWebSocketClient.read_message.<locals>.<genexpr>  sF          3>5$D8EAI..           rO   z3Bridge websocket closed before delivering a prompt.r   zUnsupported websocket opcode: )r  r/   r  unpackr   rp  OPCODE_CLOSEr   OPCODE_PINGr  OPCODE_PONGr   r  r   )rY   	fragmentsr  r  second_byteframe_opcodefinishedmaskedpayload_lengthr   r  s             @rM   r   z _RawWebSocketClient.read_message  s    !#	 $%	+&*&6&6q&9&9#J%,LJ-..H+,--F(4/N$$!'tT5E5Ea5H5H!I!I!!L3&&!'tT5E5Ea5H5H!I!I!!L.4=t''***#H&&~66G         BKGBTBT        |++:I   {**  g666{**q  %W%%% +#}!555>AAA   xx	***K%	+rO   c                :   t          | dd          dS 	 |                     t          d           n# t          $ r Y nw xY w	 | j                                         dS # t          $ r Y dS w xY w# 	 | j                                         w # t          $ r Y w w xY wxY w)zCAttempt a clean websocket close and always close the socket object.r  NrO   )getattrr  r  OSErrorr  r   r   s    rM   r   z_RawWebSocketClient.close  s    4D))1F	\3//// 	 	 	D	""$$$$$   ""$$$$   sW   1 A. 
>A. >A. A 
A+*A+.B0B
	B

BBBBN)
r  r-   r  r  r  r-   r  r-   r=   r   )r=   r  )r  r  r=   r-   )r  r   r=   r   )r  r   r   r   r=   r   r   r   r   )r^   r_   r`   ra   r   r  r  r  r  r   r   r   r1   rO   rM   r  r  M  s        AA$ $ $ $6 6 6 6p      H H H H*1 1 1 1*+ *+ *+ *+X     rO   r  c                      e Zd ZdZedddd<dZd=dZd>dZd?dZd@dZ	dAd Z
dBd$ZdCd(ZdDd*ZdDd+ZdDd,ZdEd.ZdFd6ZdGd7ZdHd:ZdId;ZdS )JTrustedDeviceBridgeBootstrapperzLBootstrap the trusted-device bridge flow captured in Apple's browser client.N)r  websocket_factoryprover_factoryr  r  r  :Optional[Callable[[str, float, str, str], _WebSocketLike]]r  1Optional[Callable[[], TrustedDeviceBridgeProver]]r=   r   c               L    || _         |pt          | _        |pt          | _        dS )z?Configure websocket and prover factories for bridge operations.N)r  r  _websocket_factoryr   _prover_factory)rY   r  r  r  s       rM   r   z(TrustedDeviceBridgeBootstrapper.__init__  s-     "3"J7J-J1JrO   sessionr   r  r-   r  re  r  r+   r  r   c                	   t          |          }t          |          }t          |          }t          |          |i}	|j        }
|                                 \  }}t                              d||||
           d}d}t          d          D ]}t          |p"t          t          j                    dz                      }|                    |t          j        t          j                                        }t#          |||          }|                                }d| d| }t                              d|t'          |                     |                     || j        ||          }d}	 |                     |          }|                                }t                              d	t'          |                     |                    t1          |g                     t                              d
|           |                                 }t5          |          }|
r|
|d<   t                              dt7          |          t'          |                     |                     |||||           |                     |||	          }|j                            d          |j         |k    rtC          d          t                              dt7          |j                   |j"        |j#                   tI          |||j         ||t5          |	          |
          }|%                    |           d}||s|&                                 c S c S # tN          $ r.}|j(        }|}t                              d|           Y d}~nd}~wtR          tT          j        tV          j,        f$ r?}|}t                              dd           Y d}~|s|&                                  nd}~wtB          $ r?}|}t                              dd           Y d}~|s|&                                  n=d}~ww xY w	 |s|&                                 # |s|&                                 w w xY wtC          d          |)zOBootstrap Apple's trusted-device bridge until the first prompt payload arrives.zaBootstrapping trusted-device bridge: auth_endpoint=%s websocket_host=%s topic=%s source_app_id=%sNr     zwss://z/v2/z@Opening trusted-device websocket: host=%s bootstrapPayloadLen=%dFz?Trusted-device bridge connected; received push token (%d bytes)z1Sent trusted-device webFilterMessage for topic=%sX-Apple-App-IdzCPosting trusted-device bridge step 0 with sessionUUID=%s ptknLen=%d)r  r  r  rm   r   rj   9Trusted-device bridge returned a mismatched session UUID.zOReceived trusted-device bridge payload: sessionUUID=%s nextStep=%s ruiURLKey=%s)r   r   rm   r   r   r   r:   TzOTrusted-device bridge received INVALID_NONCE; retrying with server timestamp %sz:Trusted-device websocket transport error during bootstrap.exc_infoz9Trusted-device bridge bootstrap failed before completion.z5Failed to bootstrap the trusted-device bridge prompt.)-r  r  r  rc  r:   _generate_keypairLOGGERdebugranger  r   timesignr   ECDSAr   SHA256rR  rg  r  r	  r  _wait_for_push_tokenr   r[  _generate_session_uuidrR   r  _post_bridge_step0_wait_for_bridge_pushr   rQ   rm   r   rq   rs   r   r   r   r   r   r  r  r  SSLError)rY   r  r  r  r  r  r   websocket_hostr  r   r:   rJ  private_keyr  
last_errorr  rK  rL  rQ  r   websocket_urlr   keep_websocket_openr   push_token_hexrm   bridge_headersr   bridge_stater   s                                 rM   rk  z%TrustedDeviceBridgeBootstrapper.start  s:    $L110>>..%e,,e4$2"&"8"8":":
Ko	
 	
 	
 '+*.
q g	& g	&A !HTY[[45G1H1HIIE#((0I0IJJI!;E9" " 14466OJ^JJJJMLLRO$$  
 //	 I #(R&!66yAA
!+!1!1U
OO   %%&@%&I&IJJJPRWXXX#::<<!%g  E7DN#34Y),77''   ''#"/*!-- (     $99un    !(,,];;G$1\AA>S   e),*CDD * ,	    8$3-!-!:'#'#7#7"/      //===&*##. + &OO%%%%%%& & &- &   "6 
e         V^S\:    
P!      + &OO%%% 8    
O!     * &OO%%%& + &OO%%% + &OO%%%%& 3C
 
	sO   2GM
P8%$N	Q$P82O,Q,P89P3Q3P88QQ-(tuple[bytes, ec.EllipticCurvePrivateKey]c                    t          j        t          j                              }|                                                    t
          j        j        t
          j        j	                  }||fS )zBGenerate the ephemeral P-256 keypair used for websocket bootstrap.)encodingformat)
r   generate_private_key	SECP256R1rJ  public_bytesr   EncodingX962PublicFormatUncompressedPoint)rY   r   rJ  s      rM   r  z1TrustedDeviceBridgeBootstrapper._generate_keypair  s]    -blnn== ++--::"+0 -? ; 
 

 ;&&rO   c                n    t          j                     dt          t          j                               S )z6Generate the browser-style bridge session UUID string.-)uuiduuid4r   r  r   s    rM   r  z6TrustedDeviceBridgeBootstrapper._generate_session_uuid  s*    *,,33TY[[!1!1333rO   r&  "Optional[TrustedDeviceBridgeState]c                    |dS |j         }d|_         |dS 	 |                                 dS # t          $ r  t                              dd           Y dS w xY w)zHClose and detach the websocket associated with an active bridge session.Nz-Trusted-device bridge websocket close failed.Tr  )r   r   r  r  r  )rY   r&  r   s      rM   r   z%TrustedDeviceBridgeBootstrapper.close  s     F *	!%F	OO 	 	 	LL?       	s   . &AAr   r/   c          
     
   |j         }|t          d          |j        rt          d          |j        dvrt          d          |j        st          d          |                                 }|                     ||          }	 t                              dt          |j
                  |j        t          |j        d	                     |                    |j        |           |                                }	t                              d
t          |j
                             |                     ||||dt          |	          |j        |j                   |                     ||j        |j                  }
|                     ||
           |j        st          d          	 t/          j        |j                            d          d                              d          }|                    dd          \  }}t9          |          }t9          |          }n/# t:          t<          t>          f$ r}t          d          |d}~ww xY wt                              dt          |j
                             	 |                     |          }n"# t:          $ r}t          d          |d}~ww xY w	 |!                    |           nS# t:          $ rF t                              dt          |j
                             Y | "                    |           dS w xY wt                              dt          |j
                             |                     ||||dt          |          |j        |j                   |                     ||j        |j                  }| #                    ||           |j$        st          d          t                              dt          |j
                             	 |%                    |j$                  }n"# t:          $ r}t          d          |d}~ww xY w| &                    |||||          }|j'        tP          k    }|j        dv rdnd}t                              d |t          |j
                  |j'                   |                     |||||tR          |j        |j                   || "                    |           S # tT          $ r}t          d!          |d}~wtV          tX          j-        t\          j/        f$ r}t          d"          |d}~ww xY w# | "                    |           w xY w)#z=Run Apple's bridge-specific trusted-device verification flow.Nz,Trusted-device bridge session is not active.zELegacy trusted-device verification should bypass the bridge verifier.>   2r   z;Trusted-device bridge is not ready for step 2 verification.z9Trusted-device bridge payload is missing the step-2 salt.zUStarting trusted-device bridge code verification: sessionUUID=%s nextStep=%s txnid=%s   r  z8Posting trusted-device bridge step 2 with sessionUUID=%sr   )r  r  r  r&  rq   ry   rw   rx   z<Trusted-device bridge step 4 payload is missing prover data.r"   Try  r#  r  r   z2Trusted-device bridge step 4 payload is malformed.zBProcessing trusted-device bridge step 4 payload for sessionUUID=%szPTrusted-device bridge prover rejected the step-4 confirmation for sessionUUID=%sFz8Posting trusted-device bridge step 4 with sessionUUID=%sr5  z=Trusted-device bridge final payload is missing encryptedCode.z8Decrypting trusted-device bridge code for sessionUUID=%sz<Failed to decrypt the trusted-device bridge validation code.)r  r  r  r&  r   >   6   r=  zTPosting trusted-device bridge completion step %s with sessionUUID=%s verifyStatus=%szQTrusted-device bridge verification failed while waiting for the next bridge push.zMTrusted-device bridge verification failed due to a websocket transport error.)0r   r   r   rq   ru   r
  _bridge_headersr  r  r  rm   rt   init_with_saltget_message1_post_bridge_stepr  rw   rx   r  r   r   _apply_expected_step4_pushry   r{  r|  r$  r1  r   r~  r   r2  r}  process_message1process_message2r   _apply_final_bridge_pushr{   decrypt_message_post_bridge_code_validatestatus_codeHTTP_STATUS_PRECONDITION_FAILEDBRIDGE_DONE_DATA_B64r   r  r  r  r  r  )rY   r  r  r  r&  r   r   proverr%  message1step4_payload
step4_databridge_message1_b64bridge_message2_b64bridge_message1_hexbridge_message2_hexr   message2final_payloadderived_codeverify_responseverification_succeededcompletion_steps                          rM   validate_codez-TrustedDeviceBridgeBootstrapper.validate_code  sf    !*	<>   ; 	<W   !11<M     	<K   %%''--g|DDO	%LLg%l&?@@&%l&8DDD	   !!,"3T:::**,,HLLJ%l&?@@   ""+&) **%.#* # 	 	 	 !66"+ M
 ++L-HHH$ @R  
#- %,,W55  &//  <F;K;KCQR;S;S8#%8&12E&F&F#&12E&F&F## 2MB   @H 
 LLT%l&?@@  !223FGG   @H ''(;<<<<   f),*CDD   \ JJ|$$$$$g LLJ%l&?@@   ""+&) **%.#* # 	 	 	 !66"+ M
 )),FFF. @S   LLJ%l&?@@  %55l6QRR   @R 
 #==+&)! >  O  +/NN # $0#9X#E#Eaa1OLLf%l&?@@+	   ""+&)))%.#* # 	 	 	 * JJ|$$$$ 4 	 	 	<c  6 	 	 	<_ 	
 JJ|$$$$s   D%S- .A8H' &S- 'I>II0S- J S- 
J9$J44J99S- =K S- 7L#
S- "L##CS- P S- 
P<'P77P<<BS- -
U 7T$U +T;;U  U Ur   r   r   c                   t          j                    | j        z   }t          j                    |k     r"|                                }t	          |          }|j        }|!t                              d|j                   e|j	        t          k    r_|j        rX	 t          j        |j                            d          d          S # t          t           f$ r}t#          d          |d}~ww xY w|j	        t$          k    r|j        t)          |j        dz            t                              d|j	                   t#          d	|j	         d
          t#          d          )zDWait for the bridge connection response that carries the push token.NzOIgnoring non-connection websocket frame while waiting for push token; fields=%sr"   Try  zMalformed bridge push token.r  z<Trusted-device bridge connection response returned status=%szBridge server returned status .z,Timed out waiting for the bridge push token.)r  	monotonicr  r   rI  r   r  r  r   r   	STATUS_OKr   r{  r|  r$  r   r}  r   STATUS_INVALID_NONCEr   r   )rY   r   deadliner/  server_messager   r   s          rM   r  z4TrustedDeviceBridgeBootstrapper._wait_for_push_tokenp  s   >##dl2n)),,..G3G<<N"0"D"*e"0    $*i77'6 8!++:AA'JJ!%    #M2   >6  $*.BBB'@L('@4G   LLN#*   7N1D1KNNN   3:
 
 	
s   -C C-C((C-r   r   r   c           	        t          j                    | j        z   }t          j                    |k     r|                                }t	          |          }|j        a|j        }t                              d|j        |j	        |j
        |j                   |j	        t          k    rt          d|j	         d          |j        A|j        }t                              d|j        |j        t!          |j        |                     |j        }	|	"t                              d|j                   |                    t+          |	j        |	j                             t                              d|	j        t!          |	j        |                     t!          |	j        |          |k    rt-          |	j                  }
t0                              |
          S t          d          )	z@Wait for, acknowledge, and decode the next relevant bridge push.NzxReceived channel subscription response during bridge bootstrap: messageId=%s status=%s retryIntervalSeconds=%s topics=%sz8Trusted-device bridge topic subscription failed (status ).z]Received bridge push acknowledgment during bootstrap: messageId=%s deliveryStatus=%s topic=%szLIgnoring non-push websocket frame during trusted-device bootstrap; fields=%sz7Acknowledged trusted-device push message id=%s topic=%sz8Timed out waiting for the trusted-device bridge payload.)r  r\  r  r   rI  r   r  r  r   r   r   r   r]  r   r   r   rh  r   r   r   r   r^  rw  r   r   r   )rY   r   r   r   r_  r/  r`  channel_responsepush_ackr   r   s              rM   r  z5TrustedDeviceBridgeBootstrapper._wait_for_bridge_push  s    >##dl2n)),,..G3G<<N;G#1#O  O$/$+$;$+   $*i77>?#3#:? ? ?  
 1=)=s',??	   *6L#b"0   !!#L$68OPP   LLI'L.??   <-~>>%GG+L,@AAG$11':::2F
 
 	
rO   r   c                   |j         |j         k    rt          d          t                              dt	          |j                   |j        t	          |j        d          |j        t          |j	                  t          |j
                             |j        dvr t          d|j        d|j         d          |                    |           d	S )
zBValidate a generic bridge push and merge it into the active state.r  zqDecoded trusted-device bridge payload: sessionUUID=%s nextStep=%s txnid=%s ec=%s has_data=%s has_encryptedCode=%sr:  r;  )Nr   z7Trusted-device bridge returned an error push (nextStep=z, ec=rb  N)rm   r   r  r  r  rq   rt   r|   r/   ry   r{   r   rY   r&  r   s      rM   _apply_bridge_pushz2TrustedDeviceBridgeBootstrapper._apply_bridge_push  s     $(AAA<K   	!,";<<"!,"4R@@@#"##,--	
 	
 	
 ")33<X)3X X<H<SX X X   	''55555rO   c           	        |                      ||           |j        dk    s|j        st          d          t                              dt          |j                  |j        t          |j        d                     dS )zBRequire the post-step-2 bridge push to contain step-4 prover data.4zATrusted-device bridge returned an unexpected post-step-2 payload.zKReceived trusted-device bridge payload: sessionUUID=%s nextStep=%s txnid=%sr:  r;  N)	rg  rq   ry   r   r  r  r  rm   rt   rf  s      rM   rB  z:TrustedDeviceBridgeBootstrapper._apply_expected_step4_push  s     	l;;;!S((0A(<S   	Y!,";<<"!,"4R@@@		
 	
 	
 	
 	
rO   c           	         |                      ||           |j        dvs|j        st          d          t                              dt          |j                  |j        t          |j        d                     dS )zGRequire the final bridge push to contain the encrypted validation code.>   ri  r<  r5  r=  z;Trusted-device bridge returned an unexpected final payload.zQReceived trusted-device bridge final payload: sessionUUID=%s nextStep=%s txnid=%sr:  r;  N)	rg  rq   r{   r   r  r  r  rm   rt   rf  s      rM   rE  z8TrustedDeviceBridgeBootstrapper._apply_final_bridge_push
  s     	l;;; "*:::. ; =M   	_!,";<<"!,"4R@@@		
 	
 	
 	
 	
rO   r%   c                F    t          |          }|j        r
|j        |d<   |S )z>Build the auth headers used for bridge-specific HTTP requests.r  )rR   r:   )rY   r  r&  r%  s       rM   r>  z/TrustedDeviceBridgeBootstrapper._bridge_headers#  s/     g% 	J/;/IN+,rO   rq   r   ry   rw   r9   rx   r6   c               d    t          |j        ||j        |||                                          S )z0Build the JSON payload for one bridge step POST.)rm   ry   r   rq   rw   rx   )r   rm   r   r   )rY   r&  rq   ry   rw   rx   s         rM   _bridge_step_jsonz1TrustedDeviceBridgeBootstrapper._bridge_step_json.  s>     !%2#.
 
 
 '))	rO   c                  |                     d| t                              |           |                     |||||          |          }	|	j        t
          t          t          hvrt          d| d|	j         d          |	S )zAPOST one bridge step and enforce the small set of valid statuses.POST)step)r&  rq   ry   rw   rx   r   r  zTrusted-device bridge step z failed with status r[  )	request_rawBRIDGE_STEP_PATH_TEMPLATEr*  rm  rH  HTTP_STATUS_OKHTTP_STATUS_NO_CONTENTHTTP_STATUS_CONFLICTr   )
rY   r  r  r  r&  rq   ry   rw   rx   r  s
             rM   rA  z1TrustedDeviceBridgeBootstrapper._post_bridge_stepA  s     &&P7>>I>NNPP'')#! (    ' 
 
 " (
 
 

 =JJ J2:2FJ J J   rO   rm   r   c                   |                     d| t           ||d|          }|j        t          t          t
          hvrt          d|j         d          |S )z>POST bridge step 0 immediately after obtaining the push token.ro  )rj   r   rq  z0Trusted-device bridge step 0 failed with status r[  )rr  BRIDGE_STEP_PATHrH  rt  ru  rv  r   )rY   r  r  r  rm   r   r  s          rM   r  z2TrustedDeviceBridgeBootstrapper._post_bridge_step0e  s     &&0.00+"   ' 
 
 " (
 
 

 7+'+ + +   rO   c                  |                     d| t           t          |j        |                                          |          }|j        t          t          t          t          hvrt          d|j         d          |S )zDPOST the decrypted bridge code to Apple's final validation endpoint.ro  r   rq  z9Trusted-device bridge code validation failed with status r[  )rr  BRIDGE_CODE_VALIDATE_PATHr   rm   r   rH  rt  ru  rv  rI  r   )rY   r  r  r  r&  r   r  s          rM   rG  z:TrustedDeviceBridgeBootstrapper._post_bridge_code_validate  s     &&9799*)6   gii ' 
 
 " +	(
 
 
 =+'+ + +   rO   )r  r  r  r  r  r  r=   r   )r  r   r  r-   r  re  r  r+   r  r-   r=   r   )r=   r'  r   )r&  r6  r=   r   )r  r   r  r-   r  re  r&  r   r   r-   r=   r/   )r   r   r=   r   )r   r   r   r-   r   re  r=   r   )r&  r   r   r   r=   r   )r  re  r&  r   r=   r%   )r&  r   rq   r   ry   r-   rw   r9   rx   r   r=   r6   )r  r   r  r-   r  re  r&  r   rq   r   ry   r-   rw   r9   rx   r   r=   r   )r  r   r  r-   r  re  rm   r-   r   r-   r=   r   )r  r   r  r-   r  re  r&  r   r   r-   r=   r   )r^   r_   r`   ra   WEBSOCKET_TIMEOUT_SECONDSr   rk  r  r  r   rY  r  r  rg  rB  rE  r>  rm  rA  r  rG  r1   rO   rM   r  r    s       VV
 3 LPK K K K K KF F F FP' ' ' '4 4 4 4   "o% o% o% o%b.
 .
 .
 .
`<
 <
 <
 <
|6 6 6 64
 
 
 
$
 
 
 
2	 	 	 	   &" " " "H   <     rO   r  )r   r-   r=   r+   )r}   r   r=   r   )ry   r   r  r   r=   r  )r  r   r  r   r}   r   r=   r   )r  r   r}   r   r=   r   )r  r   r}   r-   r=   r   )r  r   r}   r   r=   r   )ry   r   r=   r(  )r/  r   r=   r   )r/  r   r=   r   )r/  r   r=   r   )r/  r   r=   r   )r/  r   r=   r   )rJ  r   rK  r   rL  r   r=   r   )rL  r   r=   r   )rV  rW  r=   r   )r   r   r   r   r=   r   )r   r-   r=   r-   )rd  r   r   re  r=   r-   )r   r   r=   r6   )r}   r-   r=   r-   )r  r   r=   r   )r}   r9   r  r   r  r-   r=   r-   )r  r+   r=   r-   )r  r-   r=   r-   )xra   
__future__r   r{  r`  r   loggingr  r  r  r  r  r4  binasciir   r}  dataclassesr   r   html.parserr   typingr   r	   r
   r   r   urllib.parser   cryptography.hazmat.primitivesr   r   )cryptography.hazmat.primitives.asymmetricr   pydanticr   r   r   r   r   r   r   pyicloud.exceptionsr   r   pyicloud.hsa2_bridge_proverr   	getLoggerr^   r  rx  rs  rz  rP  r  r  r  r  rD  rE  rF  rG  r]  r^  rU  r  r1  rJ  r  r{  r&   rb   rt  ru  rv  rI  r+   re   r   r   r   r   r   r   r   r   r   r   	Exceptionr   r   r
  r  r  r  r!  r%  r'  r.  r3  r6  r@  rB  rI  rR  rO  r[  r^  rc  rh  rw  r~  r  r  r  r  r  r  r  r  r1   rO   rM   <module>r     s~   C C C " " " " " "     				  



    + + + + + + ( ( ( ( ( ( ( ( " " " " " " = = = = = = = = = = = = = = ! ! ! ! ! ! @ @ @ @ @ @ @ @ 8 8 8 8 8 8                         B A A A A A		8	$	$# 1 3 $) !%& " /0 , 	 % 'v'0077@@ 7  &1/ /        "%  $? ? ? ? ? ? ? ?D6 6 6 6 6i 6 6 6r $0
 0
 0
 0
 0
 0
 0
 0
f *> *> *> *> *> *> *> *>Z $       : $
 
 
 
 
 
 
 
 $3 3 3 3 3 3 3 3 $        $! ! ! ! ! ! ! ! $        $( ( ( ( ( ( ( (
- 
- 
- 
- 
-X 
- 
- 
-7 7 7 7 7 7 7 7!& !& !& !& !&* !& !& !&H2 2 2 2j       U U U U(C C C C
N N N N
D D D D
A A A A
   :   ,M M M M   0
 
 
 
       F6 6 6 6"/ / / /2 2 2 2/ / / /; ; ; ;
X X X X
$ $ $ $NN N N NB B B B
S S S S ,-8" " " " " "   (   2 2 2 2x x x x x x x xvX
 X
 X
 X
 X
 X
 X
 X
 X
 X
rO   