o
    l>j;S                     @  s  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mZ ddl	m
Z
 ddlmZ ddlmZ dd	d
ddZdZdZdZdZdZdZeddZed e ZeddZeddZeddZeddZdZdZddiZeddG dd  d Z e ddZ!e eeZ"dcddd'd(Z#ded*d+Z$dfd,d-Z%dgd/d0Z&dhd1d2Z'did4d5Z(djd6d7Z)dkd:d;Z*dld=d>Z+dmd@dAZ,dndEdFZ-dodHdIZ.dpdqdJdKZ/drdNdOZ0eddG dPdQ dQZ1eddG dRdS dSZ2G dTdU dUZ3G dVdW dWZ4dsd[d\Z5dtd]d^Z6G d_d` d`Z7G dadb dbZ8dS )uz?Pure-Python bridge prover for Apple's trusted-device HSA2 flow.    )annotationsN)	dataclass)Optional)
InvalidTag)AESGCMi @        @   )nrpdklens   com.apple.security.webprovers   com.apple.security.webverifiers	   SPAKE2Web    s   webVerifiers	   webProver@FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF      @5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B@FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551@6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296@4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5B02886e2f97ace46e55ba9dd7242579f2993b64e16ef3dcab95afd497333d8fa12fB03d8bbd6c639c62937b04d997f38c3770719c629d7014d49a24b4f98baa1292b49)   r   T)frozenc                   @  s0   e Zd ZU dZded< ded< ed
ddZd	S )_Pointz:Affine P-256 point used by the bridge SPAKE2 math helpers.Optional[int]xyreturnboolc                 C  s   | j du p	| jdu S )z3Return whether this point is the point at infinity.N)r   r   self r"   P/home/thesage/.local/lib/python3.10/site-packages/pyicloud/hsa2_bridge_prover.pyis_infinity0   s   z_Point.is_infinityNr   r   )__name__
__module____qualname____doc____annotations__propertyr$   r"   r"   r"   r#   r   )   s   
 r   valueintlengthr   r   bytesc                 C  s*   |du rt d|  d d }| |dS )z)Encode an integer using big-endian bytes.Nr      r   big)max
bit_lengthto_bytes)r,   r.   r"   r"   r#   _int_to_bytes:   s   r5   strc                 C  s   t | dS )z&Decode a base64 string into raw bytes.ascii)base64	b64decodeencoder,   r"   r"   r#   _b64_to_bytesA      r<   c                 C  s   t | dS )z+Encode raw bytes as an ASCII base64 string.r7   )r8   	b64encodedecoder;   r"   r"   r#   _bytes_to_b64F   r=   r@   pointc                 C  s2   | j rtddt| jd  t| jd  S )z:Encode a P-256 point using SEC1 uncompressed point format.z$Cannot encode the point at infinity.04r   )r$   
ValueErrorr5   r   hexr   rA   r"   r"   r#   _encode_pointK   s   $rF   c                 C  s   t | }t|dkr'|d dkr'tt|dd dt|dd d}nGt|dkrj|d dv rjt|dd d}t|d	tt|  t	 t }t|td d t}|d@ |d d@ krd| t }t||}nt
d
t|svt
d|S )zGDecode a compressed or uncompressed SEC1 point into affine coordinates.A   r      r   !   r1   )   r   Nr   z!Unsupported P-256 point encoding.zInvalid P-256 point.)r/   fromhexlenr   r-   
from_bytespow_P256_P_P256_A_P256_BrC   _is_on_curve)r,   rawrA   x_coordrhsy_coordr"   r"   r#   _decode_pointR   s"   

rW   r   c                 C  sT   | j rdS | jdur| jdusJ t| jdtt| jdtt| j  t  t dkS )z:Return whether a point lies on the configured P-256 curve.FNrJ   r   r   )r$   r   r   rN   rO   rP   rQ   rE   r"   r"   r#   rR   i   s   rR   c                 C  s6   | j r| S | jdur| jdusJ t| j| j t S )z-Return the additive inverse of a P-256 point.N)r$   r   r   r   rO   rE   r"   r"   r#   _negatet   s   rX   leftrightc                 C  s  | j r|S |j r
| S | jdur| jdusJ |jdur |jdus"J | j|jkr4| j|j t dkr4tS | j|jkr]| j|jkr]| jdkrGtS d| j | j t td| j dt t }n|j| j t|j| j dt t }|| | j |j t }|| j|  | j t }t||S )zAdd two affine P-256 points.Nr   r   rJ   )r$   r   r   rO   	_INFINITYrP   rN   r   )rY   rZ   sloperT   rV   r"   r"   r#   _add_points|   s$    
$$
r^   scalarc                 C  s@   |t ; }t}| }|r|d@ rt||}t||}|dL }|s
|S )z8Multiply a P-256 point by a scalar using double-and-add.r   )_P256_ORDERr\   r^   )rA   r_   resultaddendr"   r"   r#   _multiply_point   s   

rc   partsc                  G  s8   t  }| D ]}|t|dd || qt|S )zGConcatenate transcript parts using the bridge's length-prefixed format.r   little)	bytearrayextendrL   r4   r/   )rd   outputpartr"   r"   r#   _concat_length_prefixed   s
   rj   ikmsaltinfoc           	      C  s   t  j}|sd| }t|| t j }t }d}d}t||k rBt||| t|g t j }|	| |d7 }t||k s"t|d| S )zBDerive key material using the bridge worker's HKDF-like expansion.        r   N)
hashlibsha256digest_sizehmacnewdigestrf   rL   r/   rg   )	rk   rl   rm   r.   hash_lenprkblockspreviouscounterr"   r"   r#   
_hkdf_like   s&   

r{   requested_lengthc                 C  s   d| v rdS |S )zEReturn the bridge-specific output length for a given HKDF info label.   ConfirmationKeysr	   r"   )rm   r|   r"   r"   r#   _confirmation_key_length   s   r~   c                 C  s   t | d|t||dS )z:Derive one bridge sub-key from raw shared-secret material.ro   )rk   rl   rm   r.   )r{   r~   )rk   rm   r.   r"   r"   r#   _derive_key   s   r   raw_key_hextuple[str, str]c                 C  s2   t | }t|tt}t|tt}| | fS )z@Split the raw bridge key into prover and verifier AES/HMAC keys.)r/   rK   r   _VERIFIER_KEY_INFO_KEY_LENGTH_PROVER_KEY_INFOrD   )r   raw_keyverifier_key
prover_keyr"   r"   r#    _derive_prover_and_verifier_keys   s   
r   c                   @  H   e Zd ZU dZded< ded< ded< dd	d
ZdddZdddZdS )_ClientSharedSecretzCClient-side shared-secret transcript and derived confirmation keys.r/   
transcriptr6   share_pshare_vr   Nonec                 C  st   t | j }t| d| t|dd}t| d|dd  t| d|dd  t|dt}t| d	| dS )
FDerive confirmation keys and the final shared key from the transcript._hash_transcriptr}   r	   _confirm_clientNr   _confirm_server	   SharedKey_shared_key)rp   rq   r   ru   object__setattr__r   r   )r!   ru   confirmations
shared_keyr"   r"   r#   __post_init__   s   z!_ClientSharedSecret.__post_init__c                 C     t | jt| jtj S )z.Return the prover's HMAC confirmation message.)	rs   rt   r   r/   rK   r   rp   rq   	hexdigestr    r"   r"   r#   get_confirmation      
z$_ClientSharedSecret.get_confirmationmessage_hexc                 C  4   t | jt| jtj }||krt	d| j
S )z?Verify the server confirmation and return the shared key bytes.z invalid confirmation from server)rs   rt   r   r/   rK   r   rp   rq   r   rC   r   r!   r   expectedr"   r"   r#   verify      
z_ClientSharedSecret.verifyNr   r   r   r6   r   r6   r   r/   r&   r'   r(   r)   r*   r   r   r   r"   r"   r"   r#   r      s   
 


r   c                   @  r   )_ServerSharedSecretzCServer-side shared-secret transcript and derived confirmation keys.r/   r   r6   r   r   r   r   c                 C  sb   t | j }t|dd}t| d|dd  t| d|dd  t| dt|dt dS )	r   r}   r	   r   Nr   r   r   r   )rp   rq   r   ru   r   r   r   r   )r!   ru   r   r"   r"   r#   r     s   
z!_ServerSharedSecret.__post_init__c                 C  r   )z0Return the verifier's HMAC confirmation message.)	rs   rt   r   r/   rK   r   rp   rq   r   r    r"   r"   r#   r     r   z$_ServerSharedSecret.get_confirmationr   c                 C  r   )z?Verify the prover confirmation and return the shared key bytes.z invalid confirmation from client)rs   rt   r   r/   rK   r   rp   rq   r   rC   r   r   r"   r"   r#   r     r   z_ServerSharedSecret.verifyNr   r   r   r   r"   r"   r"   r#   r      s   
 

r   c                   @  s.   e Zd ZdZddd	ZdddZdddZdS )_ClientHandshakez=Client-side SPAKE2 handshake state for Apple's bridge prover.x_scalarr-   w0w1r   r   c                C  "   || _ || _|| _d| _d| _dS )z@Initialize the prover handshake with the derived SPAKE2 scalars.N)_x_w0_w1_message1_pointr   )r!   r   r   r   r"   r"   r#   __init__+  
   
z_ClientHandshake.__init__r6   c                 C  4   t tt| jttt| j}|| _t|| _	| j	S )z)Return the prover's first SPAKE2 message.)
r^   rc   
_GENERATORr   rW   	_SPAKE2_Mr   r   rF   r   r!   rA   r"   r"   r#   get_message9     

z_ClientHandshake.get_messageserver_message_hexr   c                 C  s   | j du s
| jdu rtdt|}|jrtdt|tttt| j	}t|| j
}t|| j}tttttttttttttt| j tt|tt|tt|t| j	
}t|| j|dS )z8Finish the handshake using the verifier's first message.N(get_message must be called before finishinvalid curve pointr   r   r   )r   r   rC   rW   r$   r^   rX   rc   	_SPAKE2_Nr   r   r   rj   _SPAKE2_CONTEXT_CLIENT_IDENTITY_SERVER_IDENTITYr/   rK   rF   r   r5   r   )r!   r   server_pointadjustedy_pointv_pointr   r"   r"   r#   finishC  s8   z_ClientHandshake.finishN)r   r-   r   r-   r   r-   r   r   r   )r   r6   r   r   r&   r'   r(   r)   r   r   r   r"   r"   r"   r#   r   (  
    


r   c                   @  s.   e Zd ZdZdd	d
ZdddZdddZdS )_ServerHandshakezAServer-side SPAKE2 handshake state used by the local test helper.y_scalarr-   r   verifier_pointr   r   r   c                C  r   )zEInitialize the verifier handshake with its scalar and verifier point.N)_yr   _verifier_pointr   r   )r!   r   r   r   r"   r"   r#   r   h  r   z_ServerHandshake.__init__r6   c                 C  r   )z+Return the verifier's first SPAKE2 message.)
r^   rc   r   r   rW   r   r   r   rF   r   r   r"   r"   r#   r   v  r   z_ServerHandshake.get_messageclient_message_hexr   c                 C  s   | j du s
| jdu rtdt|}|jrtdt|tttt| j	}t|| j
}t| j| j
}tttttttttttttt|tt| j tt|tt|t| j	
}t||| jdS )z?Finish the verifier handshake using the prover's first message.Nr   r   r   )r   r   rC   rW   r$   r^   rX   rc   r   r   r   r   rj   r   r   r   r/   rK   rF   r   r5   r   )r!   r   client_pointr   r   verifier_sharer   r"   r"   r#   r     s8   z_ServerHandshake.finishN)r   r-   r   r-   r   r   r   r   r   )r   r6   r   r   r   r"   r"   r"   r#   r   e  r   r   passwordsalt_b64tuple[int, int]c                 C  sT   t j| dfdt|it}t|d }t|d| dt||d dfS )z=Derive the SPAKE2 scalars from the user code and bridge salt.utf-8rl   rJ   Nr1   )rp   scryptr:   r<   _SCRYPT_PARAMSrL   r-   rM   )r   r   derivedmidpointr"   r"   r#   _compute_w0_w1  s   r   c                  C  s"   d} | dkrt t} | dks| S )z<Return a random scalar in the non-zero P-256 subgroup range.r   )secrets	randbelowr`   )r_   r"   r"   r#   _random_nonzero_scalar  s
   
r   c                   @  sj   e Zd ZdZdddZdd	d
Zd ddZd!ddZd ddZd"ddZ	d#ddZ
d ddZd$ddZdS )%TrustedDeviceBridgeProverz3Client-side prover mirroring Apple's prover worker.r   r   c                 C  s(   d| _ d| _d| _d| _d| _d| _dS )zBInitialize empty prover state for one bridge verification attempt.NF)_client_shared_secret_raw_key	_verified_verifier_key_prover_keyr    r"   r"   r#   r     s   
z"TrustedDeviceBridgeProver.__init__r   r6   codec                 C  sB   t ||\}}tt ||d| _d| _d| _d| _d| _d| _dS )zBInitialize the prover with Apple's salt and the user-entered code.)r   r   r   NF)	r   r   r   r   r   r   r   r   r   )r!   r   r   r   r   r"   r"   r#   init_with_salt  s   
z(TrustedDeviceBridgeProver.init_with_saltc                 C     | j du r	td| j  S )z)Return the prover's first bridge message.Nz1init_with_salt must be called before get_message1)r   rC   r   r    r"   r"   r#   get_message1     

z&TrustedDeviceBridgeProver.get_message1r   c                 C  s(   | j du r	td| j || _|  S )zHProcess Apple's first bridge message and return the prover confirmation.Nz5init_with_salt must be called before process_message1)r   rC   r   r   get_message2)r!   r   r"   r"   r#   process_message1  s   
z*TrustedDeviceBridgeProver.process_message1c                 C  r   )zDReturn the prover confirmation generated from the shared transcript.N3process_message1 must be called before get_message2r   rC   r   r    r"   r"   r#   r     r   z&TrustedDeviceBridgeProver.get_message2dict[str, object]c                 C  sH   | j du r	td| j | }|| _t|\| _| _d| _d|dS )z@Verify Apple's confirmation and persist the derived bridge keys.Nz7process_message1 must be called before process_message2T)
isVerifiedkey)	r   rC   r   rD   r   r   r   r   r   r!   r   r   r"   r"   r#   process_message2  s   

z*TrustedDeviceBridgeProver.process_message2r   c                 C  s   | j S )z>Return whether the bridge confirmation exchange has completed.)r   r    r"   r"   r#   is_verified  s   z%TrustedDeviceBridgeProver.is_verifiedc                 C  s   | j du r	td| j S )z0Return the raw shared bridge key as hexadecimal.NzNo bridge key is available yet.)r   rC   r    r"   r"   r#   get_key  s   
z!TrustedDeviceBridgeProver.get_keyciphertext_b64c                 C  s   | j du r	tdzDt|}|d }t| \}}|dd|  }|d| d| |  }|d| | d }tt| j ||| t|g}	|	dW S  t	t
ttfyb }
 ztd|
d}
~
ww )z0Decrypt Apple's final encrypted validation code.N%Bridge verifier key is not available.r   r   r   zMalformed bridge payload)r   rC   r<   _AES_GCM_LAYOUTSr   r/   rK   decryptr?   
IndexErrorKeyErrorr   UnicodeDecodeError)r!   r   payloadversion	iv_length
tag_lengthivtag
ciphertext	plaintextexcr"   r"   r#   decrypt_message  s&   

z)TrustedDeviceBridgeProver.decrypt_messageNr   )r   r6   r   r6   r   r   r   r   r6   r   r6   )r   r6   r   r   r%   )r   r6   r   r6   )r&   r'   r(   r)   r   r   r   r   r   r   r   r   r  r"   r"   r"   r#   r     s    

	






r   c                   @  sL   e Zd ZdZdddZdd	d
ZdddZdddZdddZdddZ	dS ) _TrustedDeviceBridgeServerProverz?Internal test helper mirroring Apple's server-side bridge flow.r   r6   r   r   r   c                C  sF   t ||\}}tt|}tt ||d| _d| _d| _d| _d| _	dS )zEInitialize the local verifier helper with the same password and salt.)r   r   r   N)
r   rc   r   r   r   _serverr   r   r   r   )r!   r   r   r   r   r   r"   r"   r#   r     s   

z)_TrustedDeviceBridgeServerProver.__init__c                 C  s
   | j  S )z+Return the verifier's first bridge message.)r  r   r    r"   r"   r#   r     s   
z-_TrustedDeviceBridgeServerProver.get_message1r   c                 C  s   | j || _|  S )z@Process the prover message and return the verifier confirmation.)r  r   r   r   )r!   r   r"   r"   r#   r   #  s   z1_TrustedDeviceBridgeServerProver.process_message1c                 C  r   )zFReturn the verifier confirmation generated from the shared transcript.Nr   r   r    r"   r"   r#   r   (  r   z-_TrustedDeviceBridgeServerProver.get_message2r   c                 C  s<   | j du r	td| j | }|| _t|\| _| _|S )zCVerify the prover confirmation and persist the derived bridge keys.Nz6process_message1 must be called before verify_message2)r   rC   r   rD   r   r   r   r   r   r"   r"   r#   verify_message2.  s   
z0_TrustedDeviceBridgeServerProver.verify_message2r  c           
      C  s   | j du r	tdd}t| \}}t|}tt| j ||	dt|g}|d|  }|| d }t|g| | | }	t
|	S )zIEncrypt a plaintext validation code using Apple's AES-GCM payload layout.Nr   r   r   )r   rC   r   r   token_bytesr   r/   rK   encryptr:   r@   )
r!   r  r   r   r   r   	encryptedr  r   r   r"   r"   r#   encrypt_message7  s   

z0_TrustedDeviceBridgeServerProver.encrypt_messageN)r   r6   r   r6   r   r   r   )r   r6   r   r6   r  )r  r6   r   r6   )
r&   r'   r(   r)   r   r   r   r   r  r  r"   r"   r"   r#   r    s    




	r  )N)r,   r-   r.   r   r   r/   )r,   r6   r   r/   )r,   r/   r   r6   )rA   r   r   r6   )r,   r6   r   r   )rA   r   r   r   )rA   r   r   r   )rY   r   rZ   r   r   r   )rA   r   r_   r-   r   r   )rd   r/   r   r/   )
rk   r/   rl   r/   rm   r/   r.   r-   r   r/   )rm   r/   r|   r-   r   r-   )r	   )rk   r/   rm   r/   r.   r-   r   r/   )r   r6   r   r   )r   r6   r   r6   r   r   )r   r-   )9r)   
__future__r   r8   rp   rs   r   dataclassesr   typingr   cryptography.exceptionsr   +cryptography.hazmat.primitives.ciphers.aeadr   r   r   r   r   r   r   r   r-   rO   rP   rQ   r`   _P256_GX_P256_GYr   r   r   r   r\   r   r5   r<   r@   rF   rW   rR   rX   r^   rc   rj   r{   r~   r   r   r   r   r   r   r   r   r   r  r"   r"   r"   r#   <module>   sx    















	


%'=
=
V