
    Mi                         U d 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  G d de      Z	 ej                  dd      Zeed<   dZeed	<   	 dd
edededee   def
dZefdededefdZdedefdZy)u  
Telnyx Webhook Signature Verification.
Story 3.01 — AIVA RLM Nexus PRD v2 — Track A

Verifies that webhook requests genuinely came from Telnyx.
Uses HMAC-SHA256 signature verification (Telnyx standard for webhook signing).

Telnyx webhook signing scheme:
  signed_payload = timestamp + "." + raw_body
  expected = HMAC-SHA256(signing_secret, signed_payload).hexdigest()

Replay protection: timestamps older than MAX_TIMESTAMP_AGE seconds are rejected.
    N)Optionalc                       e Zd ZdZy)SignatureErrorz7Raised when signature or timestamp format is malformed.N)__name__
__module____qualname____doc__     ;/mnt/e/genesis-system/core/interceptors/telnyx_signature.pyr   r      s    Ar   r   TELNYX_PUBLIC_KEY i,  MAX_TIMESTAMP_AGEpayload_bodytelnyx_signaturetelnyx_timestamp
public_keyreturnc                    |xs t         }|st        d      |r|j                         st        d      |st        d      	 t        |      }t        t        j                         |z
        t        kD  ry| dj                         | z   }t        |t              r|j                  d      n|}	 t        j                  ||t        j                        j!                         }	t%        |      }
t        j&                  |	|
      S # t        t
        f$ r}t        d|      |d}~ww xY w# t"        $ r}t        d	|       |d}~ww xY w)
um  
    Verify a Telnyx webhook signature.

    Telnyx uses HMAC-SHA256 over a signed payload formed by joining the
    timestamp and raw body with a literal dot:

        signed_payload = f"{timestamp}.".encode() + raw_body
        expected_sig   = HMAC-SHA256(signing_secret, signed_payload).hexdigest()

    The provided signature may arrive either as a plain hex string or as a
    base64-encoded string; both encodings are handled transparently.

    Args:
        payload_body:        Raw request body as bytes (must not be decoded).
        telnyx_signature:    Value of the ``telnyx-signature-ed25519`` header.
        telnyx_timestamp:    Value of the ``telnyx-timestamp`` header (Unix epoch, float str).
        public_key:          Signing secret to use. Falls back to the
                             ``TELNYX_PUBLIC_KEY`` environment variable when omitted.

    Returns:
        True  — signature is valid and timestamp is fresh.
        False — signature is invalid *or* timestamp is stale (replay protection).

    Raises:
        SignatureError: signature/timestamp is structurally malformed (not merely wrong).
    z@No signing key provided and TELNYX_PUBLIC_KEY env var is not setzEmpty signature providedzEmpty timestamp providedzMalformed timestamp: NF.zutf-8zFailed to compute HMAC: )r   r   stripfloat
ValueError	TypeErrorabstimer   encode
isinstancestrhmacnewhashlibsha256	hexdigest	Exception_decode_signaturecompare_digest)r   r   r   r   keytsexcsigned_payload	key_bytesexpected_hexprovided_hexs              r   verify_telnyx_signaturer/   "   s^   B ..CN
 	

 #3#9#9#;788788T#$
 499;00  00299;lJN /9c.Bszz'*IH HHNN
 )+	 	 **:;L |\::3 	" T45E4HIJPSST$  H7u=>CGHs0   D 03D* D'D""D'*	E3EEmax_agec                     	 t        |       }t        t        j                         |z
        |k  S # t        t        f$ r Y yw xY w)a  
    Check whether a Telnyx webhook timestamp is within the acceptable age window.

    Convenience helper for callers that need a standalone freshness check
    without performing full signature verification.

    Args:
        telnyx_timestamp: Unix epoch string from the ``telnyx-timestamp`` header.
        max_age:          Maximum acceptable age in seconds (default: 300).

    Returns:
        True if timestamp is fresh, False if stale or unparseable.
    F)r   r   r   r   r   )r   r0   r)   s      r   verify_timestamp_freshnessr2   o   sD    "#$499;#$//	" s   -0 AA	signaturec                 
   | j                         }|st        d      t        d |D              r|j                         S 	 t	        j
                  |d      }|j                         S # t        $ r}t        d      |d}~ww xY w)u  
    Normalise a webhook signature to a lowercase hex string.

    Telnyx may send the digest as:
      * Plain hex (32-byte HMAC → 64 hex chars)
      * Base64-encoded bytes

    Args:
        signature: Raw value from the webhook header.

    Returns:
        Lowercase hex representation of the signature bytes.

    Raises:
        SignatureError: if the signature cannot be decoded.
    z>Malformed signature encoding: empty after stripping whitespacec              3   $   K   | ]  }|d v  
 yw)0123456789abcdefABCDEFNr
   ).0cs     r   	<genexpr>z$_decode_signature.<locals>.<genexpr>   s     
;Q1((
;s   T)validatez?Malformed signature encoding: could not decode as hex or base64N)r   r   alllowerbase64	b64decodehexr%   )r3   strippeddecoded_bytesr*   s       r   r&   r&      s    "  H]^^ 
;(
;;~~((DA  "" M
	s   &A( (	B1A==B)N)r	   r=   r"   r    osr   typingr   r%   r   getenvr   r   __annotations__r   intbytesboolr/   r2   r&   r
   r   r   <module>rI      s       	  	Y 	 ##6; 3 ;  3  !%	J;J;J; J; 	J;
 
J;^ % 
8! ! !r   