1"""Signer interface""" 
    2 
    3from __future__ import annotations 
    4 
    5import logging 
    6from abc import ABCMeta, abstractmethod 
    7from typing import Callable 
    8 
    9from securesystemslib.signer._key import Key 
    10from securesystemslib.signer._signature import Signature 
    11 
    12logger = logging.getLogger(__name__) 
    13 
    14# NOTE Signer dispatch table is defined here so it's usable by Signer, 
    15# but is populated in __init__.py (and can be appended by users). 
    16SIGNER_FOR_URI_SCHEME: dict[str, type] = {} 
    17"""Signer dispatch table for ``Signer.from_priv_key()`` 
    18 
    19See ``securesystemslib.signer.SIGNER_FOR_URI_SCHEME`` for default URI schemes, 
    20and how to register custom implementations. 
    21""" 
    22 
    23# SecretsHandler is a function the calling code can provide to Signer: 
    24# SecretsHandler will be called if Signer needs additional secrets. 
    25# The argument is the name of the secret ("PIN", "passphrase", etc). 
    26# Return value is the secret string. 
    27SecretsHandler = Callable[[str], str] 
    28 
    29 
    30class Signer(metaclass=ABCMeta): 
    31    """Signer interface that supports multiple signing implementations. 
    32 
    33    Usage example:: 
    34 
    35        signer = Signer.from_priv_key_uri(uri, pub_key) 
    36        sig = signer.sign(b"data") 
    37 
    38    Note that signer implementations may raise errors (during both 
    39    ``Signer.from_priv_key_uri()`` and ``Signer.sign()``) that are not 
    40    documented here: examples could include network errors or file read errors. 
    41    Applications should use generic try-except here if unexpected raises are 
    42    not an option. 
    43 
    44    See ``SIGNER_FOR_URI_SCHEME`` for supported private key URI schemes. 
    45 
    46    Interactive applications may also define a secrets handler that allows 
    47    asking for user secrets if they are needed:: 
    48 
    49        from getpass import getpass 
    50 
    51        def sec_handler(secret_name:str) -> str: 
    52            return getpass(f"Enter {secret_name}: ") 
    53 
    54        signer = Signer.from_priv_key_uri(uri, pub_key, sec_handler) 
    55 
    56    Applications can provide their own Signer and Key implementations:: 
    57 
    58        from securesystemslib.signer import Signer, SIGNER_FOR_URI_SCHEME 
    59        from mylib import MySigner 
    60 
    61        SIGNER_FOR_URI_SCHEME[MySigner.MY_SCHEME] = MySigner 
    62 
    63    This way the application code using signer API continues to work with 
    64    default signers but now also uses the custom signer when the proper URI is 
    65    used. 
    66    """ 
    67 
    68    @abstractmethod 
    69    def sign(self, payload: bytes) -> Signature: 
    70        """Signs a given payload by the key assigned to the Signer instance. 
    71 
    72        Arguments: 
    73            payload: The bytes to be signed. 
    74 
    75        Returns: 
    76            Returns a "Signature" class instance. 
    77        """ 
    78        raise NotImplementedError  # pragma: no cover 
    79 
    80    @classmethod 
    81    @abstractmethod 
    82    def from_priv_key_uri( 
    83        cls, 
    84        priv_key_uri: str, 
    85        public_key: Key, 
    86        secrets_handler: SecretsHandler | None = None, 
    87    ) -> Signer: 
    88        """Factory constructor for a given private key URI 
    89 
    90        Returns a specific Signer instance based on the private key URI and the 
    91        supported uri schemes listed in ``SIGNER_FOR_URI_SCHEME``. 
    92 
    93        Args: 
    94            priv_key_uri: URI that identifies the private key 
    95            public_key: Key that is the public portion of this private key 
    96            secrets_handler: Optional function that may be called if the 
    97                signer needs additional secrets (like a PIN or passphrase). 
    98                secrets_handler should return the requested secret string. 
    99 
    100        Raises: 
    101            ValueError: Incorrect arguments 
    102            Other Signer-specific errors: These could include OSErrors for 
    103                reading files or network errors for connecting to a KMS. 
    104        """ 
    105 
    106        scheme, _, _ = priv_key_uri.partition(":") 
    107        if scheme not in SIGNER_FOR_URI_SCHEME: 
    108            raise ValueError(f"Unsupported private key scheme {scheme}") 
    109 
    110        signer = SIGNER_FOR_URI_SCHEME[scheme] 
    111        return signer.from_priv_key_uri(priv_key_uri, public_key, secrets_handler)  # type: ignore 
    112 
    113    @property 
    114    @abstractmethod 
    115    def public_key(self) -> Key: 
    116        """ 
    117        Returns: 
    118            Public key the signer is based off. 
    119        """ 
    120        raise NotImplementedError