
    Jj"                       S r SSKJr  SSKrSSKrSSKrSSKrSSKrSSKJ	r	  SSK
rSSKJr  SSKJr  \" S5      rSS jrSS	 jrSS
 jrSS jrSS jrSS jrSS jrSS jrSS jrS S jrS!S jrS"S jrS#S jrS$S%S jjrS&S jrS'S jr g)(a  Agent identity, server fingerprint, and secret storage.

Responsibilities:
  * generate a stable server fingerprint (used for de-dup / re-registration)
  * persist the backend-assigned server_id into agent.conf
  * read/write the agent token under a root-only secrets dir

SECURITY: install tokens are NEVER written here. Only the long-lived agent
token is persisted (v1 returns no refresh token). The token is stored as a
plain file with restrictive permissions (0600) inside the root-only secrets
directory (0700). The agent has no third-party dependencies, so it installs and
runs on a clean Linux server without pip; sealing/encryption is a production
TODO that must not reintroduce a runtime dependency.
    )annotationsN)Path)Config)
get_loggeridentityc                T    [         R                  " SU R                  S5      USS5      $ )zFDerive 256-bit key from fingerprint and salt using PBKDF2-HMAC-SHA256.sha256utf-8i'      )hashlibpbkdf2_hmacencode)fingerprintsalts     2D:\Programs\laragon\www\wolf\agent\src\identity.py_derive_keyr       s'    x););G)DdESUVV    c                N   [         R                  " S5      n[         R                  " S5      n[        X5      nU R                  S5      n[	        [        U5      5      nSn[        S[        U5      S5       H  nUR                  SS5      n	[        R                  " XCU	-   [        R                  5      R                  5       n
[        S[        U5      U-
  5      n[        U5       H  nXXU-      X   -  XhU-   '   M     US-  nM     X#-   [        U5      -   nSUR                  5        3$ )	zQEncrypt token using standard HMAC-CTR stream cipher bound to machine fingerprint.   r
   r   r      big   wp_enc:)osurandomr   r   	bytearraylenrangeto_byteshmacnewr   r	   digestminbyteshex)tokenr   r   ivkey	plaintext
ciphertextblock_indexoffsetcounter_byteskeystream_block
chunk_sizeipayloads                 r   encrypt_tokenr2   %   s   ::b>D	BB
k
(CW%I3y>*JK3y>2.#,,Q6((3](:GNNKRRTS^f45
z"A%.z%:_=O%OJz" #q / i%
++GW[[]O$$r   c                t   U R                  S5      (       d  U $ [        R                  U SS 5      n[        U5      S:  a  [	        S5      eUSS nUSS nUSS n[        X5      n[        [        U5      5      nSn[        S[        U5      S5       H  n	UR                  SS	5      n
[        R                  " XdU
-   [        R                  5      R                  5       n[        S[        U5      U	-
  5      n[        U5       H  nXYU-      X   -  XyU-   '   M     US
-  nM     [        U5      R                  S5      $ )zYDecrypt token using standard HMAC-CTR stream cipher (returns plaintext if not encrypted).r      Nr   z&Invalid encrypted token payload lengthr   r   r   r   r   r
   )
startswithr$   fromhexr   
ValueErrorr   r   r   r   r    r!   r   r	   r"   r#   decode)encrypted_datar   r1   r   r'   r*   r(   r)   r+   r,   r-   r.   r/   r0   s                 r   decrypt_tokenr:   <   s0   $$Y//mmN12./G
7|bABB3B<D	BBJ
k
(C#j/*IK3z?B/#,,Q6((3](:GNNKRRTS_v56
z"A$.z$:_=O$OIqj! #q 0 ""7++r   c                     [        U SSS9 nUR                  5       R                  5       =(       d    S sS S S 5        $ ! , (       d  f       g = f! [         a     g f = f)Nrr
   encoding)openreadlinestripOSError)pathhandles     r   _read_first_linerE   Y   sJ    $g.&??$**,4 /.. s+   A '>	A 
AA A 
AAc                 4    [         R                  " 5       n U S $ )z9Best-effort stable MAC-derived value from uuid.getnode().012x)uuidgetnode)nodes    r   _primary_macrK   a   s    <<>D4[r   c                    [        S5      =(       d    [        S5      =(       d    S[        R                  " 5       [        5       /n [        R
                  " SR                  U 5      R                  S5      5      R                  5       nSUSS  3$ )	aN  Build a stable, non-secret server fingerprint.

Combines machine-id, hostname and a MAC-derived value, then hashes them so
the result is opaque and contains no directly identifying data. The same
server should produce the same fingerprint across reinstalls, which lets the
control plane detect re-registration vs. a brand new server.
z/etc/machine-idz/var/lib/dbus/machine-id |r
   fp_Nr   )	rE   socketgethostnamerK   r   r	   joinr   	hexdigest)partsr"   s     r   generate_fingerprintrU   g   sz     	*+ 	67	E ^^CHHUO227;<FFHFr   c                   U R                   U R                  U R                  U R                  U R                  U R
                  U R                  U R                  4 H  nUR                  SSS9  M     [        U R                   S5        [        U R                  S5        [        U R                  S5        U R                  R                  SSS9  [        U R                  S5        g)z?Create the runtime directory tree with appropriate permissions.Tparentsexist_ok  N)etc_diropt_dirvar_dirlog_dir	spool_dirjobs_dirdiscovery_dir	cache_dirmkdir_chmodsecrets_dir)config	directorys     r   ensure_dirsrh   z   s     			 	t4	 6>>5!
6>>5!
6>>5! TD9
6u%r   c                T     U R                  U5        g ! [        [        4 a     g f = fN)chmodrB   NotImplementedError)rC   modes     r   rd   rd      s+    

4() s    ''c                    UR                   R                  SSS9  [        UR                   S5        [        5       n[	        X#5      nUR                  USS9  [        US5        [        R                  SU5        g)	zWrite a secret as an encrypted file with 0600 permissions in a 0700 dir.

The secret is encrypted bound to the machine fingerprint.
TrW   rZ   r
   r=     zstored secret securely at %sN)parentrc   rd   rU   r2   
write_textloginfo)rf   rC   valuer   encrypted_values        r   write_secretrv      sd    
 	KKdT2
4;;&(K#E7OOOOgO6
4HH+T2r   c                     UR                  SS9R                  5       nU(       d  g [        5       n[        X#5      $ ! [         a     g f = f)Nr
   r=   )	read_textrA   rU   r:   rB   )rf   rC   datar   s       r   read_secretrz      sK    ~~w~/557*,T// s   $< < 
A	A	c                .   U R                   R                  R                  SSS9  / nU R                   R                  5       (       a'  U R                   R	                  SS9R                  5       nSn[        U5       H9  u  pVUR                  5       R                  U S35      (       d  M.  U SU 3X5'   Sn  O   U(       d  UR                  U SU 35        U R                   R                  SR                  U5      S-   SS9  [        U R                   S5        g	)
z=Insert or update a KEY=VALUE line in agent.conf (idempotent).TrW   r
   r=   F=
ro   N)	conf_filerp   rc   is_filerx   
splitlines	enumeraterA   r5   appendrq   rR   rd   )rf   r(   rt   linesreplacedindexlines          r   set_conf_valuer      s    
!!$!>E!!  **G*<GGIH '::<""cU!9--!U!E7+ELH	 (
 uAeW%&
		% 04 7'J
6U#r   c                T    [         R                  " U R                  XS:H  S9  Xl        g)z<Persist agent status in runtime state (never in agent.conf).revoked)statusr   N)state_moduleupdate_state
state_filer   )rf   r   s     r   save_statusr      s'    &Y3F Mr   c                d    U=(       d    Sn[         R                  " U R                  US9  X l        g)zJPersist the latest heartbeat/registration error (single-line) or clear it.rM   )
last_errorN)r   r   r   r   )rf   errorrt   s      r   save_last_errorr      s'    KREf//EBr   c                    SU0nU(       a  SUS'   Ub  X#S'   [         R                  " U R                  40 UD6  Xl        SU;   a  [	        US   5      U l        Ub  X l        gg)zLRecord that registration is still owed (e.g. installer could not reach API).registration_pendingpending_installr   Nr   )r   r   r   r   strr   r   )rf   pendingr   changess       r   set_registration_pendingr      sp    "8'!BG- %f//;7;")7GH-.! r   c                    [        U SU5        [        X R                  U5        [        R                  " U R
                  SSSSS9  SU l        SU l        SU l        [        R                  SU5        g)u   Persist registration results: server_id to conf, agent token to secrets.

v1 contract returns only {agent_token, server_id, status} — there is no
agent_id or refresh_token to store.
WOLFPANEL_SERVER_ID
registeredrM   F)r   r   r   r   z#credentials stored for server_id=%sN)r   rv   agent_token_filer   r   r   r   r   r   rr   rs   )rf   	server_idagent_tokens      r   store_credentialsr      sl     60)<00+>" !FMF"'FHH2I>r   c                ,    [        X R                  5      $ rj   )rz   r   )rf   s    r   load_agent_tokenr      s    v6677r   )r   r   r   r$   returnr$   )r&   r   r   r   r   r   )r9   r   r   r   r   r   )rC   r   r   
str | None)r   r   )rf   r   r   None)rC   r   rm   intr   r   )rf   r   rC   r   rt   r   r   r   )rf   r   rC   r   r   r   )rf   r   r(   r   rt   r   r   r   )rf   r   r   r   r   r   )rf   r   r   r   r   r   rj   )rf   r   r   boolr   r   r   r   )rf   r   r   r   r   r   r   r   )rf   r   r   r   )!__doc__
__future__r   r    r   r   rP   rH   pathlibr   stater   rf   r   loggerr   rr   r   r2   r:   rE   rK   rU   rh   rd   rv   rz   r   r   r   r   r   r    r   r   <module>r      s    #  	       W
%.,:&&03 $("?*8r   